diff --git a/.claude/memory/MEMORY.md b/.claude/memory/MEMORY.md index b1891785a..2b757063e 100644 --- a/.claude/memory/MEMORY.md +++ b/.claude/memory/MEMORY.md @@ -16,5 +16,25 @@ - Key point: UDPLink only passes through jwt_token, ultimately used by curl command inside Web Wireshark container to authenticate with GNS3 capture stream API - **[Xpra HTML5 Client](./xpra-html5-client.md)** - Xpra HTML5 client menu control parameters for customizing the web interface +### RBAC & User Isolation +- **[RBAC User Isolation Design](./rbac-user-isolation-design.md)** — Three-step permission check design: ACE batch check → created_by filtering → resource pools + +### Appliance Management +- **[GNS3 Appliance Loading](./gns3-appliance-loading.md)** - How GNS3 loads appliance files from builtin and custom directories with priority rules + ### uBridge Permission - **[uBridge Permission Issue](./gns3-ubridge-permission.md)** - Docker containers fail to start due to missing CAP_NET_ADMIN/CAP_NET_RAW capabilities on uBridge +- **[Docker iptables FORWARD blocks bridge](./docker-iptables-forward-bridge.md)** - Docker sets FORWARD chain to DROP, blocking kernel bridge forwarding; `sudo iptables -P FORWARD ACCEPT` to fix + +### Docker Container Stop Delay +- **[Docker Container Stop Delay](./docker-container-stop-delay.md)** - Some containers take ~5s to stop because they don't handle SIGTERM (AlpiNet, OstinatoWireshark) + +### Device Console / Copilot Known Bugs +- **[XRd Console --More-- Pager Bug](./xrd-console-more-pager-bug.md)** - FIXED: root cause was our own 80x24 initial PTY geometry for the docker_exec console (the XR pager reads PTY rows, not `terminal length`); initial geometry is now 511x10000. Copilot reconnect-before-retry + session_log still open; `--More--` auto-answer kept as fallback design + +### MCP Service +- **[MCP Service Design](./mcp-service-design.md)** - MCP (Model Context Protocol) service architecture using FastMCP with SSE transport, JWT auth, 29 tools across 5 domains +- **[MCP Tool Description Location](./mcp-tool-description-guide.md)** - Where to define MCP tool descriptions: in `@mcp.tool()` functions in `__init__.py`, not in `*_TOOLS` arrays + +### Python Code Verification +- **[Import Validation](./python-import-validation.md)** - Use actual module imports (`python -c "from ... import ..."`) instead of `py_compile` to catch missing imports diff --git a/.claude/memory/docker-container-stop-delay.md b/.claude/memory/docker-container-stop-delay.md new file mode 100644 index 000000000..ce38b8d20 --- /dev/null +++ b/.claude/memory/docker-container-stop-delay.md @@ -0,0 +1,41 @@ +--- +name: docker-container-stop-delay +description: Docker containers not responding to SIGTERM cause ~5s stop delays when closing a project +metadata: + type: reference +--- + +# Docker Container Stop Delay Analysis + +## Background +When stopping a GNS3 project, some Docker containers take ~5s to exit while others stop instantly. + +## Root Cause +Docker's `stop` command sends SIGTERM and waits `t` seconds (GNS3 sets `t=5`) before sending SIGKILL. Containers that don't handle SIGTERM are stuck waiting for the full timeout. + +## Affected Containers + +| Container | PID 1 | Why it's slow | +|-----------|-------|---------------| +| **AlpiNet** (alpine) | `dumb-init` → `bash -i` | Interactive bash ignores SIGTERM by design | +| **OstinatoWireshark** | `bash` (PID 1) | Linux kernel won't apply default signal actions to PID 1 without an explicit handler; interactive bash doesn't install one | + +## Normal Containers (for comparison) + +| Container | PID 1 | Why fast | +|-----------|-------|----------| +| Chromium | `/usr/bin/chromium` | Chromium handles SIGTERM natively | +| webterm | `dumb-init` → firefox | Firefox responds to SIGTERM immediately | + +## Related Files +- `gns3-registry/docker/alpinet/Dockerfile` +- `gns3-registry/docker/ostinato-wireshark/Dockerfile` +- `gns3-registry/docker/ostinato-wireshark/entry.sh` +- `gns3-registry/docker/chromium/Dockerfile` +- `gns3-registry/docker/ipterm/web/Dockerfile` +- `gns3-server/gns3server/compute/docker/docker_vm.py:1040` — stop timeout parameter `t=5` + +## Note +This is not a GNS3 server bug (except a minor `or` vs `and` logic issue at `docker_vm.py:1037` which doesn't affect behavior). The root cause is in the Docker images themselves. + +See also: [[docker-container-stop-delay]] diff --git a/.claude/memory/docker-iptables-forward-bridge.md b/.claude/memory/docker-iptables-forward-bridge.md new file mode 100644 index 000000000..2716fe7ab --- /dev/null +++ b/.claude/memory/docker-iptables-forward-bridge.md @@ -0,0 +1,38 @@ +--- +name: docker-iptables-forward-bridge +description: Docker iptables FORWARD DROP blocks kernel bridge forwarding, fix and symptoms +metadata: + type: reference +--- + +Docker sets the iptables `FORWARD` chain default policy to `DROP` when the +Docker daemon starts. This blocks **all** forwarded traffic through Linux +kernel bridges on the host — including `gns3br{N}` bridges created by the +builtin Ethernet Switch (ubridge `brctl`). + +## Symptoms + +- Nodes connected to the switch can send frames into the bridge (visible in + `tcpdump -i gns3br{N}`) but never receive forwarded unicast frames. +- `bridge fdb show` may fail to learn MAC addresses (frames dropped before + the bridge learning path). +- ARP and multicast/broadcast may appear to work because they flood, but + unicast replies never reach the destination. +- OSPF Hello / CDP visible on both sides but ICMP echo reply never returns. +- `ubridge bridge get_stats` shows symmetric IN/OUT counts (relay is fine), + `bridge fdb show` shows learned MACs, `bridge link show` shows `state forwarding` + on all ports — yet unicast still doesn't work. + +## Fix + +Run once per host boot, or make persistent via iptables-persistent / firewall config: + +```bash +sudo iptables -P FORWARD ACCEPT +``` + +## Related + +- [[ethernet-switch-ubridge-brctl-migration]] — the kernel bridge that hits this +- [[gns3-server-linux-only]] — datapath constraint +- [[gns3-ubridge-permission]] — another host-level prerequisite (CAP_NET_ADMIN) diff --git a/.claude/memory/gns3-appliance-loading.md b/.claude/memory/gns3-appliance-loading.md new file mode 100644 index 000000000..1032f0373 --- /dev/null +++ b/.claude/memory/gns3-appliance-loading.md @@ -0,0 +1,20 @@ +--- +name: gns3-appliance-loading +description: GNS3 appliance file loading mechanism and storage locations +metadata: + type: reference +--- + +GNS3 loads appliance (.gns3a) files from two locations with specific priority order: + +1. **Builtin appliances directory**: `~/.local/share/GNS3/appliances/` + - Stores automatically downloaded devices from GNS3 registry + - Maintained and updated by the system automatically + +2. **Custom appliances directory**: `~/GNS3/appliances/` + - Stores user-customized or modified appliance files + - Manually managed by users + +**Loading priority**: System loads builtin appliances first, then custom appliances. If both directories contain devices with the same `device_id`, the custom appliance overwrites the builtin one. This design allows users to customize devices without having their modifications overwritten by automatic registry updates. + +**Implementation**: See `gns3server/controller/appliance_manager.py` in the `load_appliances()` method (lines 314-351). diff --git a/.claude/memory/mcp-service-design.md b/.claude/memory/mcp-service-design.md new file mode 100644 index 000000000..6c3d80ff9 --- /dev/null +++ b/.claude/memory/mcp-service-design.md @@ -0,0 +1,88 @@ +--- +name: mcp-service-design +description: MCP (Model Context Protocol) service architecture and tool design for GNS3 server +metadata: + type: project +--- + +# MCP (Model Context Protocol) Service Design + +## Background + +Provide a standard MCP interface for GNS3 Server, allowing AI assistants (Claude Code, Claude Desktop) to interact with GNS3 network simulations through the Model Context Protocol. + +## Decision/Implementation + +### Transport +- **SSE (Server-Sent Events)** with JWT token authentication +- Endpoint: `/v3/mcp/transport/sse` +- Message endpoint: `/v3/mcp/transport/messages/` + +### Authentication +- JWT token obtained via `/v3/access/users/authenticate` +- Two ways to pass token: + - `Authorization: Bearer ` header (Claude Code via `-H`) + - `?token=` query param (Claude Desktop, EventSource limitation) +- Token validated using GNS3's existing `auth_service` +- Token stored in `contextvars.ContextVar` for per-session isolation +- Python ≥ 3.9 `asyncio.to_thread` propagates contextvars to threads + +### Architecture +``` +Claude Code / Desktop → SSE → Auth Wrapper → FastMCP Server → Tool Handler → Gns3Connector → GNS3 REST API +``` + +### Tool Organization +Tools are separated by domain into individual files under `gns3server/api/routes/mcp/`: + +| File | Domain | Tool Count | +|------|--------|:----------:| +| `projects.py` | Project CRUD, open/close/stats | 7 | +| `nodes.py` | Node CRUD, start/stop/reload/suspend, console WS | 10 | +| `links.py` | Link CRUD | 5 | +| `templates.py` | Template CRUD | 5 | +| `computes.py` | Compute list/get/images | 3 | + +**Total: 30 tools** + +### Handler Pattern +- Synchronous functions receiving `(params: dict, gns3_ctx: dict)` +- Run via `asyncio.to_thread()` to avoid blocking the event loop +- `gns3_ctx` contains `server_url` and `jwt_token` +- `Gns3Connector` is created per-handler from `gns3_client.connector` (per-handler instantiation keeps each tool call isolated) + +### Token Lifetime +- Default: 1440 minutes (24 hours) +- Configurable via `jwt_access_token_expire_minutes` in `gns3_server.conf` + +## Rationale +- **Why not Direct Controller calls**: MCP layer calls GNS3's own REST API through Gns3Connector, keeping full decoupling and supporting future multi-user/multi-instance scenarios +- **Why not Streamable HTTP**: Claude Code supports SSE natively via `--transport sse` with custom headers; Streamable HTTP session manager lifecycle conflicts with FastAPI mount +- **Why not stdio**: stdio is local-only; SSE supports both local and remote deployments + +## Related Files +- `gns3server/agent/mcp/__init__.py` — FastMCP server, `@mcp.tool()` decorators, auth wrapper (`_resolve_token` exchanges API keys for JWTs) +- `gns3server/agent/mcp/*.py` — tool handlers for projects/templates/computes/snapshots/drawings/symbols/appliances/images +- `gns3server/agent/gns3_copilot/gns3_client/api_handlers.py` — shared node/link handler layer (single implementation, consumed by both MCP tools and copilot `tools_v2`; tests must patch `_get_connector` HERE, not in mcp modules) +- `gns3server/agent/gns3_copilot/gns3_client/connector.py` — Gns3Connector (JWT auth + http_call only; the old `custom_gns3fy.py` Node/Link/Project wrappers were removed) +- `gns3server/agent/gns3_copilot/gns3_client/project_inventory.py` — nodes/links aggregation feeding the topology context and Nornir inventory + +## Configuration + +### Claude Code +```bash +claude mcp add --transport sse My_GNS3_Server \ + http://host:3080/v3/mcp/transport/sse \ + -H "Authorization: Bearer " +``` + +### Claude Desktop +```json +{ + "mcpServers": { + "My_GNS3_Server": { + "url": "http://host:3080/v3/mcp/transport/sse?token=" + } + } +} +``` diff --git a/.claude/memory/mcp-tool-description-guide.md b/.claude/memory/mcp-tool-description-guide.md new file mode 100644 index 000000000..ca4ebcf42 --- /dev/null +++ b/.claude/memory/mcp-tool-description-guide.md @@ -0,0 +1,43 @@ +--- +name: mcp-tool-description-location +description: Where to define MCP tool descriptions so AI can see them +metadata: + type: reference +--- + +# MCP Tool Description Location + +## Key Point +MCP tool descriptions are defined in `@mcp.tool()` decorator functions in `__init__.py`, NOT in the `*_TOOLS` arrays in individual module files. + +## Correct Location +**File**: `gns3server/api/routes/mcp/__init__.py` + +**Example**: +```python +@mcp.tool() +async def update_link( + project_id: Annotated[str, Field(description="UUID of the project")], + link_id: Annotated[str, Field(description="UUID of the link to update")], + **kwargs: Any, +) -> list[dict[str, Any]]: + """Update a link's properties. + + Put detailed descriptions here, especially for complex parameters. + Include format requirements, ranges, and examples. + """ + # implementation +``` + +## Wrong Location +- ❌ `LINK_TOOLS` in `gns3server/api/routes/mcp/links.py` +- ❌ `TEMPLATE_TOOLS` in `gns3server/api/routes/mcp/templates.py` + +## Activation +**Must restart GNS3 server** for description updates to take effect. + +## Description Requirements +- Be explicit about data formats (arrays vs single values) +- Include parameter ranges and constraints +- Provide usage examples +- Prevent common errors in the description itself diff --git a/.claude/memory/python-import-validation.md b/.claude/memory/python-import-validation.md new file mode 100644 index 000000000..18d3a012e --- /dev/null +++ b/.claude/memory/python-import-validation.md @@ -0,0 +1,30 @@ +# Python Import Validation + +## Background + +When checking if modified Python code is correct, `py_compile` only validates syntax (e.g., balanced parentheses, valid keywords). It does **not** catch missing imports or other runtime errors (e.g., using `UUID()` without importing `UUID`). + +## Decision/Implementation + +Use actual module imports to verify code correctness: + +```bash +# ✅ This catches missing imports and runtime errors +venv/bin/python -c " +from gns3server.api.routes.controller.dependencies.authentication import get_user_from_token +from gns3server.api.routes.mcp.__init__ import _resolve_token +print('All imports OK') +" + +# ❌ This only checks syntax, not references +venv/bin/python -c "import py_compile; py_compile.compile('file.py', doraise=True)" +``` + +## Related Files + +`gns3server/api/routes/controller/dependencies/authentication.py` — missed `from uuid import UUID` +`gns3server/api/routes/mcp/__init__.py` — missed `from uuid import UUID` + +## Why + +A `NameError` at runtime is far more expensive than a failed import check. Real import testing catches the full dependency chain. diff --git a/.claude/memory/rbac-user-isolation-design.md b/.claude/memory/rbac-user-isolation-design.md new file mode 100644 index 000000000..630e3c762 --- /dev/null +++ b/.claude/memory/rbac-user-isolation-design.md @@ -0,0 +1,231 @@ +--- +name: rbac-user-isolation-design +description: GNS3 RBAC user isolation design and implementation thought process +metadata: + type: project +--- + +## RBAC User Isolation Design Summary + +### Core Problem +GNS3 3.0 has a complete RBAC framework (ACE + Role + Privilege), but lacks user isolation implementation, causing users to see resources they shouldn't have access to. + +### Design Conflict +Traditional RBAC's **path permission model** fundamentally conflicts with **user data isolation**: +- **Path permission model**: Controls which paths users can access (e.g., `/projects`) +- **User data isolation**: Controls which specific resources users can access (e.g., alice's project vs bob's project) + +### Final Implementation Approach + +#### Three-Step Permission Check Logic + +```python +# Step 1: Batch ACE + resource pool check (3 DB queries regardless of project count) +direct_ace_ids, pool_accessible_ids = await rbac_repo.get_accessible_project_ids( + current_user.user_id, "Project.Audit", all_project_ids +) + +# Step 2: Filter direct ACE projects by created_by (user's own projects) +# Direct project sharing is only available through resource pools +for p in controller.projects.values(): + if p.id in direct_ace_ids and p.created_by == current_user.username: + projects.append(p.asdict()) + +# Step 3: Resource pool projects (no created_by filter) +for p in controller.projects.values(): + if p.id in pool_accessible_ids: + projects.append(p.asdict()) +``` + +##### Super Admin Bypass +```python +if current_user.is_superadmin: + return [p.asdict() for p in controller.projects.values()] +``` +Super admins skip all three steps and see every project. + +##### seen_project_ids Deduplication +A simple `seen_project_ids` set prevents the same project from appearing twice when it exists in both direct ACE and pool results. This is a lightweight dedup, not the complex blocking mechanism from earlier rejected designs. + +### Key Design Decisions + +#### 1. ACE vs created_by Relationship +- **ACE for basic access control**: Whether user can access the system +- **created_by for user isolation**: Which specific resources user can access +- **Step 2 filtering is critical**: Even with broad ACE, created_by filtering ensures user isolation + +#### 2. Project Sharing Mechanism +- **Project sharing only through resource pools**: Cannot configure ACE directly for specific projects +- **Avoids complexity of direct ACE sharing**: Prevents permission configuration chaos + +#### 3. Broad ACE Fault Tolerance +``` +Even with configuration: ACE: Users group + User role + "/" + propagate: True +Result: Users still only see projects they created +Reason: Step 2 created_by filtering removes other users' projects +``` + +### RbacRepository.get_accessible_project_ids() + +The core batch-check method in `gns3server/db/repositories/rbac.py` uses 3 DB queries: + +1. **User ACEs**: Direct ACE entries matching user + privilege +2. **Group ACEs**: Group-level ACE entries via `UserGroup` membership +3. **All resources with pool memberships**: Preloads all resources and their pool relationships + +Then it computes two sets: +- **`direct_ace_ids`**: Project paths matching user or group ACEs (path-based check) +- **`pool_accessible_ids`**: Projects in pools the user/group can access (pool-based check) + +Pool IDs are precomputed into a `pool_id -> set(project_ids)` map for O(1) lookup. + +### Problems Solved + +#### Problem 1: Missing User Isolation +- **Issue**: All users can see all projects +- **Solution**: Filter by created_by to implement user isolation + +#### Problem 2: Broad ACE Breaking Isolation +- **Issue**: `path: "/" + propagate: true` breaks user isolation +- **Solution**: Step 2 created_by filtering ensures user isolation even with broad ACE + +#### Problem 3: Permission Check Order Conflicts +- **Issue**: seen mechanism blocks subsequent checks (from earlier design phases) +- **Solution**: Simple pipeline-style filtering with lightweight dedup set + +### Technical Details + +#### API Layer Permission Check +```python +@router.get("/projects") # Note: no has_privilege decorator +async def get_projects(current_user=..., rbac_repo=...): + # Permission checks in business logic +``` + +#### Duplicate Prevention +- Simple `seen_project_ids` set for deduplication between direct ACE and pool results +- Not the complex blocking mechanism from earlier phases + +#### Performance Considerations +- Super admin path: O(1) — returns all projects directly +- Regular user path: 3 fixed DB queries regardless of project count +- Path-based ACE check: O(n * m) in worst case, where n = projects, m = ACE entries +- Pool lookup: O(1) via precomputed pool->project map + +### Use Cases + +#### Scenario 1: Personal Use +``` +alice creates project → alice only sees alice's projects ✅ +bob creates project → bob only sees bob's projects ✅ +No extra configuration needed, automatic user isolation +``` + +#### Scenario 2: Team Collaboration +``` +Admin creates resource pool → adds projects → team members can access +alice creates project → alice still sees her own projects ✅ +Team sharing + user isolation coexist +``` + +#### Scenario 3: Broad ACE Configuration +``` +ACE: Users group + "/" + propagate: True +alice still only sees alice's projects ✅ +User isolation unaffected by ACE configuration +``` + +### Design Principles + +#### 1. Separation of Concerns +- **Basic access permission**: Controlled by ACE +- **Data ownership**: Controlled by created_by +- **Team sharing**: Controlled by resource pools + +#### 2. Defensive Design +- User isolation remains effective even with improper ACE configuration +- Secure by default: users only see their own resources + +#### 3. Simplicity +- Avoid complex seen mechanisms and mutual exclusion logic +- Clear pipeline-style check order + +### Relationship with Original ACE System + +#### Preserved Components +- ✅ ACE framework (permission check mechanism) +- ✅ Role and privilege definitions +- ✅ Resource pool functionality + +#### Improved Components +- ✅ Added user isolation (created_by filtering) +- ✅ Clarified project sharing mechanism (only through resource pools) +- ✅ Simplified permission check logic + +#### Removed Components +- ❌ Removed `/projects` path privilege check on `get_projects` route +- ❌ Removed the old complex `seen_project_ids` blocking mechanism +- ❌ Avoided "can see all non-pool projects" privilege leak + +### Implementation Details +- **Main modification**: `gns3server/api/routes/controller/projects.py` +- **Function**: `get_projects()` +- **Batch check method**: `gns3server/db/repositories/rbac.py::RbacRepository.get_accessible_project_ids()` +- **Privilege dependency**: `gns3server/api/routes/controller/dependencies/rbac.py::has_privilege()` +- **Branch**: `feature/simple-user-isolation` +- **Base branch**: `master` + +### Key Commits +1. Implemented basic created_by filtering +2. Implemented three-layer permission check (with logic issues) +3. Fixed to correct three-step check logic + +### Design Evolution Process + +#### Phase 1: Simple created_by Filtering +```python +user_projects = [p for p in all_projects() if p.created_by == user.username] +``` +**Issue**: Didn't integrate with ACE system + +#### Phase 2: Three-Layer Check (Wrong Version) +```python +# Used complex seen_project_ids blocking mechanism +# Issue: Broad ACE breaks user isolation +``` + +#### Phase 3: Three-Step Check (Correct Version) +```python +# Step 1: Batch ACE check via get_accessible_project_ids() +# Step 2: Filter direct_ace_ids by created_by +# Step 3: Resource pool projects (no created_by filter) +``` +**Solution**: User isolation works even with broad ACE + +### Relationship with RBAC Roadmap +This implementation addresses items mentioned in the roadmap: +- **Phase 1 (MVP)**: Basic project isolation implementation +- **Auto-ACE on create**: Still needs to be implemented separately +- **Template isolation**: Can use same design pattern + +### Unsolved Issues + +1. **Auto-ACE on project creation**: Part of roadmap Phase 1 — `create_project()` sets `created_by` but doesn't create ACE entries +2. **Template and image isolation**: Can apply same design pattern +3. **Default ACE configuration**: Need reasonable default permissions for Users group + +### Design Limitations + +1. **Project sharing only through resource pools**: No direct ACE configuration for sharing +2. **ACE configuration required**: Users need basic ACE to access system +3. **Performance considerations**: ACE check queries database for each project path + +### Future Improvement Directions + +1. **Auto-create ACE**: Automatically add ACE for creator when creating projects +2. **Default ACE strategy**: Configure reasonable default permissions for Users group +3. **Performance optimization**: Cache ACE check results to reduce database queries + +This design achieves effective user isolation while maintaining RBAC system integrity, and solves the problem of broad ACE configurations breaking isolation. + +**Key insight**: The Step 2 created_by filtering is the critical innovation that allows ACE and user isolation to coexist properly. diff --git a/.claude/memory/xrd-console-more-pager-bug.md b/.claude/memory/xrd-console-more-pager-bug.md new file mode 100644 index 000000000..d796d3a4a --- /dev/null +++ b/.claude/memory/xrd-console-more-pager-bug.md @@ -0,0 +1,72 @@ +# XRd Console --More-- Pager Bug (PTY window size paging vs terminal length 0) + +## Background + +Copilot commands with long output consistently failed against XRd (IOS XRv 9000 container) nodes: `device_show_run` running `show ipv4 interface brief` always reported `netmiko_multiline (failed)` (even as a single command); short-output commands like `show ipv4 interface ` worked fine. + +Failure sequence (from the 2026-08-18 logs): + +1. First failure: `ReadTimeout: Pattern not detected: 'RP/0/RP0/CPU0:ios\#'` — the command echo matched, but the prompt never appeared within 60s +2. The copilot's single-retry reused the same netmiko session (nornir caches it on the host object) with half-consumed output in the buffer → the second failure died earlier in `command_echo_read` — a follow-on effect of the dirty session, not an independent fault + +## Root Cause (fully traced) + +The XRd node uses the `docker_exec` console type (`gns3server/compute/docker/vendor_docker_vm.py`, `_LazyExecTelnetServer`): a telnet TCP server whose backend is a `docker exec` PTY. Three facts combine: + +1. The XR pager (at least for table-engine/TABLAST-style commands) pages on the **PTY window size (TIOCGWINSZ)**, not the CLI-level `terminal length` — `show terminal` happily reports `Length: 0 lines` while output still pages +2. `_LazyExecTelnetServer.client_connected_hook` explicitly resized the exec PTY to **80×24** "before NAWS" (`await self._on_naws(80, 24)`) — the 24 rows were **our own initial geometry, not a Docker default**. Live test: `show ipv4 interface brief` paged after exactly 24 lines +3. netmiko's telnetlib never negotiates **NAWS**, so the initial geometry is never corrected for copilot/bare-telnet clients. Real NAWS clients resize to their own geometry via the existing `_on_naws` → `POST /exec/{id}/resize` wiring + +### Dead ends (do not retry) + +- `show ipv4 interface brief | no-more` → `% Invalid input detected`. `| no-more` is a **Junos** pipe modifier; IOS-XR does not have it +- `terminal length 0`: takes effect at the CLI layer, but this pager ignores it +- The XR CLI has no command to change PTY rows + +### Related fact: paramiko vs docker_exec + +paramiko (SSH) cannot connect to a `docker_exec` console — the client-side endpoint is a plain telnet server (`AsyncioTelnetServer`); only `console_type: ssh` (standard attach path, `AsyncioSSHServer`) speaks SSH. The copilot correctly uses netmiko `*_telnet` drivers (netmiko's vendored `_telnetlib`, stdlib-free on Python 3.13). + +## Decision/Implementation + +### Fix (implemented 2026-08-18, branch `feat/docker-exec-default-pty-geometry`) + +Change the initial exec geometry in `vendor_docker_vm.py` `client_connected_hook` from 80×24 to **511×10000** (`await self._on_naws(511, 10000)`): + +- Tall/wide default so CLIs that page on PTY rows never hit `--More--` for clients that never send NAWS (netmiko, bare telnet) +- Width 511 matches netmiko's `terminal width 511` convention +- Real NAWS clients still resize to their actual geometry right after connecting (existing `_on_naws` path unchanged) +- Test: `test_first_connect_sets_tall_default_pty_geometry` in `tests/compute/docker/test_vendor_docker_vm.py` + +### Fallback design (NOT implemented — keep if the pager ever resurfaces on another console type) + +Channel-level loop answering `--More--` in the copilot display tool for `cisco_xr*`: prompt regex breaks; **tail-anchored** `re.search(r"--More--\s*$", buf)` with a ~150ms quiet double-confirmation before `write_channel(" ")`. Never put `--More--` into netmiko's `expect_string` — expect `re.search`es accumulated output, so content containing "More" would false-trigger. + +### Still-open copilot improvements (agreed, not yet implemented) + +1. Reconnect before retry: `_run_all_device_configs_with_single_retry` (display tool) and the config tool's retry should `task.host.close_connection("netmiko")` first — retrying on a dirty session is doomed +2. `session_log_file` in hosts_data netmiko extras — the copilot has no session_log anywhere, which made this bug a guessing game + +## Rationale + +Resizing the PTY at exec creation attacks the root (geometry is fixed before the CLI outputs anything); it covers every consumer of the docker_exec console (copilot, MCP, bare telnet) with a one-line change, while the `--More--` auto-answer design remains as a generic fallback for consoles without a resize path. + +## Related Files + +- `gns3server/compute/docker/vendor_docker_vm.py` — `_LazyExecTelnetServer`: `_on_naws` (exec resize), `_create_exec` (Tty=True, TERM=xterm), `client_connected_hook` (initial geometry — the fix) +- `gns3server/compute/docker/docker_vm.py:1074-1087` — standard console path: NAWS → `containers/{cid}/resize` +- `gns3server/agent/gns3_copilot/tools_v2/display_tools_nornir.py:284-341` — dirty-session retry (open item) +- `gns3server/agent/gns3_copilot/utils/get_gns3_device_port.py` — hosts_data (session_log extras insertion point) + +## Examples + +Live console transcript (2026-08-17): + +``` +RP/0/RP0/CPU0:ios#show terminal +Length: 0 lines, Width: 511 columns <- CLI-layer setting in effect + +RP/0/RP0/CPU0:ios#show ipv4 interface brief +(exactly 24 lines: timestamp + blank + header + 21 interface rows) + --More-- <- the 80x24 initial exec geometry was paging +``` diff --git a/.claude/skills/gns3-api-test-writing/SKILL.md b/.claude/skills/gns3-api-test-writing/SKILL.md new file mode 100644 index 000000000..6149bf21f --- /dev/null +++ b/.claude/skills/gns3-api-test-writing/SKILL.md @@ -0,0 +1,58 @@ +--- +name: gns3-api-test-writing +description: Use this skill when writing pytest tests for gns3-server API routes — the conftest fixture model, auth token variants, config isolation, and the shared-client order-dependency trap. +version: 1.0.0 +--- + +# Writing pytest API Route Tests + +## Test Environment + +- Run tests with the repo venv: `venv/bin/python -m pytest tests/api/routes/controller/test_xxx.py` (there is no system python/pytest). +- The app runs in-process (httpx `ASGIWebSocketTransport`); the database is in-memory sqlite; superadmin `admin` is seeded when the users table is created. +- `pytestmark = pytest.mark.asyncio`. Tests run on function-scoped event loops while class-scoped fixtures bind to the class loop — this works, but don't move class fixtures to function scope casually. + +## The Fixture Model (tests/conftest.py) + +Class-scoped: `app`, `db_session`, `base_client` (**one** shared httpx `AsyncClient`), `test_user` (idempotent `user1` in the "Users" group). + +`client` (admin), `authorized_client` (user1), `unauthorized_client`, `compute_client` are class-scoped wrappers that each **rewrite the shared `base_client.headers` at instantiation time**. They are instantiated lazily — by the first test that requests them. + +### The order-dependency trap + +- `unauthorized_client` is a passthrough: it sets no header and only behaves as "unauthorized" if nothing set a token on `base_client` before it. +- Mixing auth variants in one test class makes the default `Authorization` header depend on fixture instantiation order → tests pass alone but fail in a class run (or the reverse). +- Symptom signature: a 401/403 assertion receives 200. That is fixture pollution, **not** an RBAC/auth bug in the product. + +### Rule: per-request headers for auth variants + +Never rely on the client's default header for 401/403/specific-user tests. Send the token explicitly (request-level headers override client defaults — the `test_users.py` idiom): + +```python +from gns3server.services import auth_service +from gns3server.services.authentication import DEFAULT_JWT_SECRET_KEY + +token = auth_service.create_access_token(test_user.username, secret_key=DEFAULT_JWT_SECRET_KEY) +response = await client.get(url, headers={"Authorization": f"Bearer {token}"}) # specific user +response = await client.get(url, headers={"Authorization": "Bearer invalid_token"}) # 401 +``` + +Note the import: `auth_service` lives in `gns3server.services`, NOT `gns3server.services.authentication` (which only exports `DEFAULT_JWT_SECRET_KEY` and the class). + +Always pass `secret_key=DEFAULT_JWT_SECRET_KEY`: the autouse `run_around_tests` resets `Config` per test and forces the default secret, so a token minted with the config-of-the-moment dies in the next test. + +## Config-Isolated Tests + +- Request the function-scoped `config` fixture whenever the test reads/writes configuration or the endpoint under test reloads it. It points `Config` at `tmpdir/server.conf` (accessible as `config._main_config_file`). +- Any endpoint that triggers a config reload re-reads `/gns3_jwt_secret_key`, which invalidates class-scoped bearer tokens. Fix: write `DEFAULT_JWT_SECRET_KEY` to that file first — see the `stable_jwt_secret` fixture in `tests/api/routes/controller/test_settings.py`. + +## Misc Gotchas + +- Build URLs with `app.url_path_for("route_function_name")`. Router introspection is unreliable (lazy `_IncludedRouter` wrapper) — check `GET /openapi.json` instead. +- pydantic v2: an empty `SecretStr('')` serializes as `""`, **not** the mask — assert `in ("", SECRET_MASK)` for secrets that may be unset in tests. +- New privileges are seeded only at table creation (`gns3server/db/models/privileges.py`); the fresh in-memory test DB always has them, but existing deployments need manual grants. +- Error mapping: `ControllerBadRequestError` → 400, `ControllerError`/`HTTPException(409)` → 409, request schema violations → 422. + +## Failure-Diagnosis Heuristic + +**Passes in isolation, fails in a class run → suspect shared fixture state first** (`base_client.headers`, the `Config` singleton, class-scoped DB rows) — never the product code. Reproduce with `-k "test_a or test_b"` pairs to find the polluting test. Do not add debug prints to product code to chase test-order issues; make the test order-independent with explicit per-request headers instead. diff --git a/.claude/skills/gns3-api-testing/SKILL.md b/.claude/skills/gns3-api-testing/SKILL.md new file mode 100644 index 000000000..989e670f0 --- /dev/null +++ b/.claude/skills/gns3-api-testing/SKILL.md @@ -0,0 +1,172 @@ +--- +name: gns3-api-testing +description: Use this skill when testing GNS3 server REST API endpoints with curl — covers JWT auth, common patterns, and marker/link examples. +version: 1.0.0 +--- + +# GNS3 Server API Testing with curl + +## Core Principle + +Fixed routine for testing the GNS3 server API: **get a JWT token first, then send `Authorization: Bearer ` with every request.** +Default address `http://127.0.0.1:3080`, API prefix `/v3`. + +--- + +## Authentication (always first) + +```bash +TOKEN=$(curl -s -X POST http://127.0.0.1:3080/v3/access/users/authenticate \ + -H "Content-Type: application/json" \ + -d '{"username":"admin","password":"admin"}' \ + | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])") +``` + +Persist to a file for reuse (avoids re-logging in each time): + +```bash +echo "$TOKEN" > /tmp/gns3_token.txt +TOKEN=$(cat /tmp/gns3_token.txt) +``` + +Then attach to every request: +```bash +AUTH="Authorization: Bearer $TOKEN" +curl -s -H "$AUTH" http://127.0.0.1:3080/v3/... +``` + +> **Endpoint note**: login is `/v3/access/users/authenticate`, **not** `/v3/auth/login`. +> OpenAPI spec is at `/openapi.json` (not `/v3/openapi.json`). + +--- + +## Common Variables + +```bash +BASE="http://127.0.0.1:3080/v3" +PID= +LID= +NID= +AUTH="Authorization: Bearer $TOKEN" +``` + +--- + +## Generic Request Patterns + +### GET (query) +```bash +curl -s -H "$AUTH" $BASE/projects/$PID/links | python3 -m json.tool +``` + +### POST (create) — with JSON body +```bash +curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"name":"foo","bpf":"icmp"}' \ + $BASE/projects/$PID/links/$LID/markers +``` + +### HTTP status code only (body not needed) +```bash +curl -s -o /dev/null -w "HTTP %{http_code}\n" -X DELETE -H "$AUTH" \ + $BASE/projects/$PID/links/$LID/markers/global-icmp +``` + +### Extract a field from the response +```bash +LID=$(curl -s -H "$AUTH" -X POST ... | python3 -c "import sys,json; print(json.load(sys.stdin)['link_id'])") +``` + +--- + +## Status Code Reference + +| Code | Meaning | +|---|---| +| 200 | GET/PUT succeeded | +| 201 | POST created | +| 204 | DELETE succeeded (no body) | +| 401 | Not authenticated (token missing/expired) | +| 404 | Resource not found | +| 409 | Conflict (e.g. per-link edit of an inherited marker) | +| 422 | Schema validation failed (e.g. marker name starting with `global`) | + +--- + +## Marker Cheat Sheet + +### Project-level global marker definitions (inheritance) +```bash +# Create a def → fans out to every link automatically +curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"name":"icmp","bpf":"icmp","tag":1,"color":"#ff5722"}' \ + $BASE/projects/$PID/marker-definitions + +# List all defs + the link_ids each is bound to +curl -s -H "$AUTH" $BASE/projects/$PID/marker-definitions + +# Update a def → syncs to every link +curl -s -X PUT -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"bpf":"icmp","tag":99}' \ + $BASE/projects/$PID/marker-definitions/icmp + +# Delete a def → removes the inherited marker from every link +curl -s -X DELETE -H "$AUTH" $BASE/projects/$PID/marker-definitions/icmp +``` + +### Per-link markers +```bash +# List markers on a link +curl -s -H "$AUTH" $BASE/projects/$PID/links/$LID/markers + +# Create a private marker (name cannot start with "global") +curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"bpf":"tcp port 80"}' \ + $BASE/projects/$PID/links/$LID/markers + +# Delete (inherited markers return 409) +curl -s -X DELETE -H "$AUTH" $BASE/projects/$PID/links/$LID/markers/ +``` + +### Project-level aggregation query +```bash +curl -s -H "$AUTH" $BASE/projects/$PID/markers # all markers across links, flattened +``` + +--- + +## Link / Node Cheat Sheet + +```bash +# List all links in a project (includes the markers field) +curl -s -H "$AUTH" $BASE/projects/$PID/links + +# List nodes (check ports[].link_id to find free ports) +curl -s -H "$AUTH" $BASE/projects/$PID/nodes + +# Create a VPCS +curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"name":"t1","node_type":"vpcs","compute_id":"local"}' \ + $BASE/projects/$PID/nodes + +# Start a node +curl -s -o /dev/null -X POST -H "$AUTH" $BASE/projects/$PID/nodes/$NID/start + +# Create a link (both ends: node + adapter/port) +curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \ + -d "{\"nodes\":[{\"node_id\":\"$N1\",\"adapter_number\":0,\"port_number\":0},{\"node_id\":\"$N2\",\"adapter_number\":0,\"port_number\":0}]}" \ + $BASE/projects/$PID/links +``` + +> **Port occupancy**: VPCS has only one interface (port 0); once linked it cannot connect again. +> Confirm `ports[].link_id` is empty before creating a link; `"Port is already used"` means the port is taken. + +--- + +## Gotchas + +- **`POST /links` response may show `markers: []`** — the create response is serialized before the inheritance hook runs. + The inherited marker is actually applied; check `GET /links/{lid}/markers` or refresh `GET /links` to see it. +- **Restart gns3server after code changes** — the Python process does not hot-reload. +- **Wrap JSON bodies in single quotes** in the shell (double quotes inside); to interpolate a shell variable use `\"$VAR\"`. +- **Pipe long output through `python3 -m json.tool`** to pretty-print; extract fields with `python3 -c "import sys,json; ..."`. diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..9d1981d05 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,57 @@ +# Version control +.git +.gitignore +.gitattributes + +# CI / GitHub / Docker +.github +.whitesource +.dockerignore + +# Editor / IDE +.idea +.vscode +.settings +.project +.pydevproject +.mr.developer.cfg + +# Claude +.claude + +# Python build artifacts +__pycache__ +*.py[cod] +*.so +*.egg +*.egg-info +build/ +dist/ +eggs/ +parts/ +var/ +sdist/ +develop-eggs/ +.installed.cfg +lib/ +lib64/ +.ropeproject + +# Test & coverage +tests/ +pytest.ini +.coveragerc +.coverage +.coverage* +.tox +.cache +.pytest_cache +nosetests.xml + +# Virtualenv +env/ +venv/ +.venv/ + +# Editor backup files +*~ diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..21e1bf803 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,40 @@ +name: Bug report +description: Report a bug so we can fix it. +title: "[Bug]: " +labels: ["bug"] +body: + - type: textarea + id: what-happened + attributes: + label: What happened? + description: A clear description of the bug. + validations: + required: true + - type: textarea + id: reproduce + attributes: + label: Steps to reproduce + description: How can we reproduce this? Numbered steps if possible. + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected behavior + description: What did you expect to happen instead? + - type: input + id: version + attributes: + label: Version / commit + description: Which version or commit hash are you on? + - type: textarea + id: environment + attributes: + label: Environment + description: OS, runtime version, anything else that might be relevant. + - type: textarea + id: logs + attributes: + label: Relevant logs + description: Paste any relevant log output. This is automatically rendered as code. + render: shell diff --git a/.github/workflows/add-new-issues-to-project.yml b/.github/workflows/add-new-issues-to-project.yml index aa8252552..5296e7a1f 100644 --- a/.github/workflows/add-new-issues-to-project.yml +++ b/.github/workflows/add-new-issues-to-project.yml @@ -10,7 +10,7 @@ jobs: name: Add issue to project runs-on: ubuntu-latest steps: - - uses: actions/add-to-project@v1.0.1 + - uses: actions/add-to-project@v2 with: project-url: https://github.com/orgs/GNS3/projects/3 github-token: ${{ secrets.ADD_NEW_ISSUES_TO_PROJECT }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 06ab2cc62..7479a13b2 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,11 +56,11 @@ jobs: # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -88,6 +88,6 @@ jobs: exit 1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@v4 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 75f1d5e43..3978699ee 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -10,12 +10,37 @@ on: jobs: build: runs-on: ubuntu-latest - + env: + DOCKERHUB_ORG: ${{ vars.DOCKERHUB_ORG || 'gns3' }} + steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v7 + + - name: Check for stable release + id: ver + run: | + TAG="${GITHUB_REF_NAME#v}" + echo "tag=$TAG" >> $GITHUB_OUTPUT + if echo "$TAG" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "stable=true" >> $GITHUB_OUTPUT + else + echo "stable=false" >> $GITHUB_OUTPUT + fi + + - name: Set lowercase image name vars + if: steps.ver.outputs.stable == 'true' + id: names + run: | + echo "owner=$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT" + echo "repo=$(echo '${{ github.event.repository.name }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT" + + - name: Set up Docker Buildx + if: steps.ver.outputs.stable == 'true' + uses: docker/setup-buildx-action@v3 - name: Login to GitHub Container Registry + if: steps.ver.outputs.stable == 'true' uses: docker/login-action@v4 with: registry: ghcr.io @@ -23,18 +48,21 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Login to Docker Hub + if: steps.ver.outputs.stable == 'true' uses: docker/login-action@v4 with: registry: docker.io username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build and push to GitHub Container Registry - run: | - docker build -t ghcr.io/$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]'):latest . - docker push ghcr.io/$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]'):latest - - - name: Build and push to Docker Hub - run: | - docker build -t gns3/${{ github.event.repository.name }}:latest . - docker push gns3/${{ github.event.repository.name }}:latest + - name: Build and push + if: steps.ver.outputs.stable == 'true' + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: | + ${{ env.DOCKERHUB_ORG }}/${{ github.event.repository.name }}:${{ steps.ver.outputs.tag }} + ${{ env.DOCKERHUB_ORG }}/${{ github.event.repository.name }}:latest + ghcr.io/${{ steps.names.outputs.owner }}/${{ steps.names.outputs.repo }}:${{ steps.ver.outputs.tag }} + ghcr.io/${{ steps.names.outputs.owner }}/${{ steps.names.outputs.repo }}:latest diff --git a/.github/workflows/publish-api-documentation.yml b/.github/workflows/publish-api-documentation.yml index f2d635030..acd816f53 100644 --- a/.github/workflows/publish-api-documentation.yml +++ b/.github/workflows/publish-api-documentation.yml @@ -12,18 +12,18 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 ref: "gh-pages" - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: 3.12 - - name: Merge changes from 3.0 branch + - name: Merge changes from 3.1 branch run: | git config user.name github-actions git config user.email github-actions@github.com - git merge origin/3.0 -X theirs + git merge origin/3.1 -X theirs - name: Install GNS3 server and dependencies run: | python -m pip install --upgrade pip diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index e8968d9ce..20c1cf5ea 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -4,41 +4,32 @@ on: push: branches: - master - - 3.0 + - 3.1 pull_request: branches: - master - - 3.0 + - 3.1 jobs: build: - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest strategy: matrix: - os: ["ubuntu-latest"] python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - - name: Display Python version - run: python -c "import sys; print(sys.version)" + cache: pip - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install .[ai-copilot,dev] - - - name: Install Windows specific dependencies - if: runner.os == 'Windows' - run: | - python -m pip install -r win-requirements.txt - curl -O "http://www.win10pcap.org/download/Win10Pcap-v10.2-5002.msi" - msiexec /i "Win10Pcap-v10.2-5002.msi" /qn /norestart + python -m pip install .[ai-features,dev] - name: Lint with flake8 run: | diff --git a/.gitignore b/.gitignore index 28a535038..7677a0718 100644 --- a/.gitignore +++ b/.gitignore @@ -85,3 +85,5 @@ venv # Tiktoken cache files gns3server/agent/gns3_copilot/cache/tiktoken/ +gns3.log +/configs/ diff --git a/.whitesource b/.whitesource index e112468f5..bb071b4a2 100644 --- a/.whitesource +++ b/.whitesource @@ -2,13 +2,44 @@ "scanSettings": { "configMode": "AUTO", "configExternalURL": "", - "projectToken" : "", - "baseBranches": ["master", "2.2", "3.0"] + "projectToken": "", + "baseBranches": [] + }, + "scanSettingsSAST": { + "enableScan": false, + "scanPullRequests": false, + "incrementalScan": true, + "baseBranches": [], + "snippetSize": 10 }, "checkRunSettings": { - "vulnerableCheckRunConclusionLevel": "failure" + "vulnerableCheckRunConclusionLevel": "failure", + "displayMode": "diff", + "useMendCheckNames": true + }, + "checkRunSettingsSAST": { + "checkRunConclusionLevel": "failure", + "severityThreshold": "high" }, "issueSettings": { - "minSeverityLevel": "LOW" - } + "minSeverityLevel": "LOW", + "issueType": "DEPENDENCY" + }, + "issueSettingsSAST": { + "minSeverityLevel": "high", + "issueType": "repo" + }, + "remediateSettings": { + "workflowRules": { + "enabled": true + } + }, + "imageSettings":{ + "imageTracing":{ + "enableImageTracingPR": false, + "addRepositoryCoordinate": false, + "addDockerfilePath": false, + "addMendIdentifier": false + } + } } \ No newline at end of file diff --git a/CHANGELOG b/CHANGELOG index 42582a073..113e141a2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,434 @@ # Change Log +## 3.1.0a5 19/08/2026 + +* Bundle web-ui v3.1.0a5 +* Sync appliances +* copilot: sync CONSOLE_TYPES with the server ConsoleType enum +* feat: Enhance information displayed on the system status dashboard +* docker: cap GNS3_STOP_TIMEOUT at 210 s (controller stop budget) +* docker: harden the shm/devices/extra_configs/masking work (code review) +* docker: make the vendor graceful-stop grace period configurable (GNS3_STOP_TIMEOUT) +* docker: graceful stop for vendor NOS containers (SIGTERM + 60s grace) +* fix: UDP port allocation race causing link self-loop (one-way links) +* add support basic support for OpenBSD. +* docker: use the container's chown (not busybox) in vendor volume/perms path +* docker: mask systemd-udevd in privileged containers (GNS3_MASK_UDEV) +* docker: persist extra_configs in the template DB table +* docker: inject config files into containers via extra_configs +* docker: warn about low inotify/file-max and missing FUSE at connect +* docker: inject /dev/shm size and host devices via HostConfig from env +* appliance: expose custom_adapters on the v1-6 appliance model +* appliance: accept docker_exec console type in Docker appliances +* fix: recreate docker_exec console on reconnect after CLI exit +* vendor: drop the hardcoded /etc/network mount for SKIP_INIT containers +* vendor: skip /etc/network in volume bridge and permission passes +* fix: run _fix_permissions container-side on /gns3volumes mount targets +* fix: host-side permission fix + SKIP_INIT volume persistence docs +* refactor: move vendor NOS Docker support into VendorDockerVM subclass +* docker_exec console: wrap exec command in while-true loop so sr_cli restarts on quit +* Prototype: vendor NOS Docker node support (SR Linux etc.) — docker_exec console via Docker exec API (pty + hijacked HTTP + NAWS resize) +* fix: update CI configuration to use a single OS and remove Windows-specific dependencies +* fix: breaking change with dependency FastAPI v0.137.0 https://fastapi.tiangolo.com/release-notes/#specific-breaking-changes +* fix: suppress redundant console port setter log during Docker node create +* revert: restore force_close=True in compute._session() +* perf: parallelize batch NIO creation across nodes +* fix: scope marker reconcile to the current bridge/NIO +* cleanup: remove stray debug print in Dynamips node creation +* fix: reconcile marker filters in _ubridge_apply_markers (delete/update) +* log: suppress marker sink stats when no matches received +* log: add periodic marker.match throughput statistics +* marker: route marker.match to a dedicated project WS channel +* perf: raise marker UDP receive buffer from ~208KB to 8MB +* perf: move project-open marker inheritance to prepare phase +* perf: parallelise compute-side batch NIO update per node +* perf: batch update/delete marker-def fan-out too (full project-level) +* perf: batch marker-def fan-out to one PUT /nios/batch per compute +* log: add marker fan-out timing for project-open and interactive def-change +* tune: raise start_all concurrency from 3 to 10 +* revert: drop _connect_nio thread-pool executor, restore async _ubridge_send +* fix: use socket.fromfd in send_batch_sync for Python 3.13 compat +* log: demote all per-node lifecycle INFO logs to DEBUG +* fix: await Dynamips.create_nio (it is async, unlike sync base) +* fix: use bound-method param count for Dynamips create_nio detection +* fix: Dynamips create_nio(node, nio_settings) takes extra arg + add tests +* log: add start/stop/close progress lines; revert start concurrency to 3 +* log: lower per-node lifecycle logs in base_node to DEBUG +* perf: raise start/stop/suspend/reset-console concurrency from 3 +* fix: race in concurrent node creation sending duplicate POST /projects +* log: lower per-node docker lifecycle logs to DEBUG +* log: emit progress lines for node and link loading on project open +* cleanup: remove project-open stage timing logs, lower NIO-added log to debug +* perf: skip per-link topology dump during project-open prepare +* perf: batch NIO dispatch on project open (one HTTP per compute) +* perf: cache compute.host_ip + add UDPLink.create timing log +* perf: dedicated 500-worker thread pool for ubridge batch I/O +* perf: offload per-NIO ubridge commands to thread-pool executor +* perf: enable TCP keep-alive for local compute HTTP requests +* debug: add per-command timing to _connect_nio ubridge calls +* perf: parallelize UDP port allocation and NIO creation in UDPLink.create +* fix: SIGKILL docker container on stop instead of 5s grace period +* fix: add event handling for deleted projects +* fix: tests after merging +* server: raise the open-files (RLIMIT_NOFILE) limit at startup +* marker: batch topology dumps in bulk fan-out (per-def on 500+ links was ~1 minute) +* marker: concurrent (bounded) definition fan-out +* marker: forward data_link_type on per-link marker create +* iou: skip already-installed markers in _ubridge_apply_markers (NIO update idempotency) +* marker: serial-link (WAN) support via data_link_type -> uBridge linktype +* iou: forward data_link_type on capture start so serial pcaps use the correct linktype +* ubridge: default the control transport to unix +* ubridge: require version >= 1.2.0 +* fix: add missing HTTPException import +* mcp: drop direction from marker_definition tool +* marker: tighten marker name max length from 128 to 32 +* marker: drop deleted marker from port NIO cache to stop empty pcap on restart +* marker: validate def BPF once, skip re-validation on inherited fan-out +* marker: point directional defs at BPF, refresh implementation doc +* marker: incremental apply, clear bridge map on uBridge stop +* marker: fine-grained filter ops, clean pcap on remove +* marker: restore direction on project load, reject tx/rx on definitions +* schema: accept direction='both' in marker schemas, normalize to None +* qemu: demote QEMU monitor connect and set_link logs to DEBUG +* marker: key _marker_filter_bridges by (name, link_id) so multi-link nodes toggle every copy +* marker: make per-filter toggle fall back to NIO rebuild when the marker isn't installed +* marker: make per-filter toggle a no-op when the marker isn't installed +* marker: rework pause/resume from project-wide to per-definition +* marker: fix PUT marker with an enabled-only body (bpf no longer required) +* marker: persist project-wide markers_paused to the .gns3 file +* marker: document pause/resume and instant enabled toggle +* marker: test enabled/pause/resume and instant toggle +* marker: drive uBridge enabled/pause/resume in real time +* marker: add direction-clear, routing, and ubridge transport tests +* marker: support clearing direction via explicit null / "both" +* marker: let callers pin the capture node via capture_node_id +* Name AF_UNIX sockets by node id for self-describing debuggability +* Detect unsupported -U flag and fail fast with a clear error +* Add configurable uBridge control channel transport (tcp/unix) +* fix(import): unvalidated symlink creation in import_project +* fix(qemu): move the bios options after the network ones +* fix(qemu): fix addition of QEMU RNG device causes interface names to change +* bugfix: docker workflow needs lowercase name +* fix(qemu): remove trailing space from RNG object argument +* Add MCP tools for traffic-insight marker feature +* Improve MCP tool descriptions for link_update, device_show_run, and link_reset +* Do not start nodes when deleting a project +* fix: correct always-true state check in DockerVM.stop() +* add bug report issue template +* Adds controller and compute API support for explicitly pulling or updating Docker images. +* feat(tests): increase maximum open file descriptors for test runs on Unix +* (feat): Implement functional Layer 1 keepalive support for IOU/IOL nodes +* Fix typo with ovmf_firmware_dir +* Fix issues after merging +* fix(builtin): hide GNS3 internal bridge interfaces from Cloud node +* feat(builtin): NAT node returns its interface IP info in asdict() +* fix(controller): refresh cloud/nat node interfaces from compute on GET +* refactor(builtin): use deterministic bridge name from switch UUID +* fix(builtin): bring kernel bridge UP after creation +* feat: migrate builtin EthernetSwitch from Dynamips ethsw to ubridge brctl +* fix(mcp): remove unreliable node_reload / node_reload_all tools +* fix(rbac): guard None current_user on websocket auth failure +* fix(mcp): thread token_version into console/download token minting +* fix(mcp): pass user.token_version when generating temp JWT from API key +* fix(marker): avoid reentrant-lock deadlock when uBridge starts lazily +* fix(marker): per-link attribution via link field in MARK signals +* fix(marker): set nio.markers on iou/dynamips/cloud nio update +* fix(marker): reject reserved global-prefix names only on create +* fix(marker): clean inherited markers on def delete +* feat(marker): add highlight_duration render hint +* fix(marker): fix inheritance hook, topology load, update sync, and asdict +* fix(marker): export MarkerDefinitionCreate from schemas package +* fix(marker): replace pydantic lookahead pattern with field_validator +* feat(marker): add project-level marker definition inheritance +* fix(marker): reserve "global" name prefix for future marker inheritance +* fix(marker): extend marker support to dynamips and cloud nodes +* fix(marker): enable marker support for docker nodes +* fix(marker): tolerate port conflict on marker listener startup +* fix(marker): validate marker name and expose enabled via REST +* fix(marker): uniquify auto-generated marker names per link +* feat(marker): add project-level marker aggregation endpoint +* feat(marker): add IOU node marker support via iol_bridge +* refactor(marker): converge to filter single-path model, remove dual-apply endpoints +* fix(marker): restore markers from topology on project load +* feat(marker): add optional color field to marker data model +* fix(marker): use allowlist in _choose_marker_side, align with _get_filter_node +* fix(marker): stop auto-deleting markers on node stop in node_updated +* fix(marker): ride markers on NIO so they survive node restart and project reload +* config: set marker_listen_port default to 3070 and document in config templates +* fix(marker): add _choose_marker_side to exclude non-uBridge node types +* Only set IOU images to be executable when importing images +* fix(import): project import does not move images symlinks into place +* Search for system UEFI that is compatible with Python < 3.12 +* UEFI improvements for Qemu VMs * Add OVMF firmware directory configuration * Automatically add a Random Number Generator (RNG) device when using uefi option is enabled +* fix(docker): handle container name conflict automatically +* fix: gns3-server crashes on startup if "Open this project in the background" is active but there is a problem with that project +* Handle HTTPNotFound exception when retrieving compute status +* Replace ControllerForbiddenError with aiohttp.web.HTTPForbidden for project deletion error handling +* Fix: Check compute connectivity before open() during project deletion +* Use typed InterfaceStatus enum and simplify stats fallback in interfaces() +* Expose full interface address list and link state in cloud node API + +## 2.2.61 30/07/2026 + +* Sync appliances +* fix(import): unvalidated symlink creation in import_project +* fix(qemu): fix addition of QEMU RNG device causes interface names to change +* fix(qemu): remove trailing space from RNG object argument +* fix: do not start nodes when deleting a project +* fix: correct always-true state check in DockerVM.stop() + +## 2.2.60 15/07/2026 + +* Sync appliances +* Only set IOU images to be executable when importing images +* fix(import): project import does not move images symlinks into place +* Search for system UEFI that is compatible with Python < 3.12 +* Add OVMF firmware directory configuration +* Automatically add a Random Number Generator (RNG) device when using uefi option is enabled +* fix(docker): handle container name conflict automatically +* fix: gns3-server crashes on startup if "Open this project in the background" is active but there is a problem with that project +* Handle HTTPNotFound exception when retrieving compute status +* Fix: Check compute connectivity before open() during project deletion +* API endpoints to manage base configuration files for templates + +## 3.1.0a4 09/07/2026 + +* Bundle web-ui v3.1.0a4 +* Add jwt_refresh_token_expire_minutes to sample configuration +* Remove deleted web-ui files from git +* .dockerignore ignore .dockerignore +* typo made me reconsider and move out to env +* defaulted DOCKERHUB_ORG repository variable +* Remove API docs from 2.2 after merging +* Update GitHub Actions workflows +* Fix web-wireshark docker build broken by xpra 6.5 release +* Update CI to install [ai-features,dev] instead of [ai-copilot,dev] +* Make AI features (AI Copilot + MCP) optional via [ai-features] extra +* Add refresh token mechanism documentation under docs/features/ +* Add /refresh to allowed public endpoints list in route auth test +* Add stateless JWT refresh token mechanism +* Add project/node/link handler tests: 39 total, covering list/get/create/delete/start/stop/suspend/reload/console/update/fields +* Add MCP tool parameter consistency tests +* Fix appliance_install: add version parameter +* Add coordinate system note to docs +* Document canvas coordinate system in node_create x/y params +* Update docstring: batch concurrency from 10 to 100 +* Remove unused import time +* Add list type check before nodes[0] access in _normalize_link_nodes +* Add fields type validation in create handlers +* Use pop() instead of pop(0) for O(1) port removal +* Fix review issues: key_prefix length, count validation, WAL log, timeout comment, pointless temp var +* Fix: pass name from TemplateUsage to add_node_from_template +* Remove unused imports (logging, log, select) +* Add warning for unconsumed pre-allocated UDP ports after link creation +* Remove FIXME comment about middleware in server.py +* Rename device_command_run_handler → device_show_run_handler to match tool name +* Rename device_command_run → device_show_run for clarity +* Fix device_command_run KeyError('commands'): tool desc said show_commands but backend expects commands +* Fix template_list return type annotation to match _run_handler_sync envelope +* Add performance optimization documentation +* Update MCP service docs: API key format, auth flow, tool parameters, concurrency +* Increase HTTP connection pool to 500/1000 +* Increase BATCH_MAX_WORKERS and Pool concurrency from 20 to 100 +* Remove final timing artifact in projects.py +* Remove remaining dead timing variables and imports +* Remove database warmup (proven ineffective - real bottleneck was bcrypt blocking event loop) +* Clean up all timing/debug logs +* Add memory: import validation best practice +* Fix: add missing UUID imports +* Generate fresh JWT on API key auth instead of returning raw key +* Optimize API key auth: O(1) lookup via UUID-embedded key format +* Fix: offload bcrypt.checkpw to thread pool to prevent blocking event loop +* Add timing to API key auth path and log api_keys count +* Replace SELECT 1 warmup with full database file read to warm OS page cache +* Add timing logs to auth dependency chain to identify 6s pre-handler delay +* Add granular timing to get_template: separate execute vs fetch time +* Warm up database connection pool on startup to avoid 8s cold-start penalty on first API request +* Fix: register WAL PRAGMA on sync_engine instead of Engine class for async compat +* Add timing logs to get_template to identify DB query bottleneck +* Add timing middleware to log slow requests (>1s) with [CTRL-TIMING] prefix +* Fix: _time → time in compute.py timing log +* Add [CTRL-TIMING] logs to controller create_node flow +* Fix: pass template_id to batch mode handler so top-level template_id works as default +* Add detailed timing logs to MCP node creation and HTTP client +* Add fields filter to template_list tool with description for AI +* Fix: add missing _filter_link_response function +* Pass name parameter through to controller API when creating node from template +* Add validation to compact link format with clear error messages +* Add compact array format for link node entries to reduce token usage +* Add fields filter to link_create tool +* Reduce md5sum cache write failure log level from error to warning +* Add fields filter to node_create tool description for AI +* Optimize MCP create_node: support inherited template_id and default fields filter +* Increase MCP HTTP client timeout from 10s to 30s +* Enable SQLite WAL mode to fix 'database is locked' errors under concurrent API requests +* Cache IOU image default values per image path to avoid redundant subprocess calls +* Increase node and link creation concurrency from 5 to 20 +* Fix: revert IOU lock optimization, serialize IOU node creation for correct application_id assignment +* Performance: accelerate project opening with parallel link creation and batch UDP port allocation +* feat: Add batch link_ids to link_delete/link_reset, fields filter to link_list +* feat: Add fields filter to link_list +* feat: Add batch node_ids to node_delete +* fix: Convert http to ws scheme in node_console WebSocket URL +* feat: Add batch link_ids to link_capture_download +* feat: Add batch link_ids to link_capture_start/stop +* fix: Store username in gns3_ctx during auth, use for short-lived download JWTs +* fix: Generate independent short-lived JWT for pcap download +* revert: Remove _configs_map changes in tools_v2 (handled by template renderer now) +* fix: Merge commands for duplicate device_names in _configs_map +* fix: Actually pass template param to device_config/command handlers +* fix: Correct Jinja2 template commands_field per tool type +* feat: Jinja2 template support in device_command_run +* feat: Jinja2 template support in device_config_send +* feat: Add batch node_ids support to node_start/stop/reload/suspend +* feat: Add fields filter to appliance_list +* feat: Add fields filter to node_list +* feat: node_get fields filter — match controller Node schema fields +* fix: Set auto_close=False on project_create so projects stay open when clients disconnect +* feat: Add batch mode to node_create and link_create (parallel, max 10 workers) +* fix: Pass API key directly instead of generating short-lived JWT +* feat: API key lifecycle — revoke/restore/delete +* fix: Rename revoke_api_key → delete_api_key +* fix: Hard-delete API keys instead of soft delete (revoked flag) +* feat: Support API keys in REST API authentication (reuse gns3_ prefix keys) +* fix: Lazily access db engine for API key validation +* fix: Add missing updated_at column to api_keys table +* fix: Export ApiKeyCreate from schemas package +* feat: Add API Key support for MCP authentication +* fix: Validate JWT token exp claim — was silently ignored after migration to joserfc +* fix: Add image field to template_create, document type-specific params in description +* fix: Remove .json() calls on 204 responses for prune/install images +* fix: Skip always-running nodes in start_all/stop_all +* fix: Add missing rotation parameter to drawing_update MCP tool +* fix: Map MCP device_command_run parameter to tool's expected field name +* fix: Update link_reset description to match actual behavior (delete + recreate) +* fix: Remove unsupported description param from project_create +* fix: Fix symbol_get/upload/delete handlers for correct API paths +* feat: Log registered MCP tools at startup +* fix: Type compute_id as uuid.UUID to reject non-UUID values at MCP input layer +* fix: Require UUID for compute_get/images, remove 'local' string default +* feat: Add device configuration MCP tools (config_send, command_run, vpcs_config_set) +* refactor: unify MCP tool naming to _ convention +* feat: Add symbol upload/delete, project load, and locked check MCP tools +* feat: Add image management MCP tools +* feat: Add symbol and appliance MCP tools +* feat: Add node bulk ops, project lock, and server info MCP tools +* feat: Add snapshot and drawing MCP tools +* refactor: unify MCP handlers to use http_call directly, relocate node file ops to Node class +* feat: Add node file operations as MCP tools (list, get, write, delete) +* Add comment about rootful Docker permissions at container start +* Fix _fix_permissions test: set process.returncode=0 and update assertion +* Fix list_node_files PermissionError on os.scandir +* Fix _fix_permissions error handling and list_node_files PermissionError +* Add async_iterable_to_stream utility to avoid aiohttp compatibility issues +* Add descriptive detail to 403 errors in compute file endpoints +* Fix silent file write failure in write_compute_project_file +* feat: Node file streaming, recursive listing, file type detection, and file delete +* Update README tool descriptions: .txt → .md +* Update README tool descriptions to mention Markdown format +* Add MCP project tools: update, duplicate, and README operations + +## 3.1.0a3 06/06/2026 + +* Bundle web-ui v3.1.0a3 +* fix: update MCP link tools descriptions with detailed filter info +* fix: correct MCP nodes and links tool parameter handling for nested kwargs +* fix: correct MCP template tool parameter handling for nested kwargs +* feat: add client information logging to MCP connection rejection +* refactor: replace MCP ready state polling with asyncio.Event +* fix: revert duplicate image check to fix failing tests +* fix (templates): Add ordering to handle the duplicate cases gracefully +* fix(templates): Database error detected when saving a template with a disk image change +* fix: return 503 error on MCP server ready timeout +* fix: add MCP server ready check to prevent initialization errors +* fix: resolve MCP server URL host via default route IP when bound to 0.0.0.0 +* fix: correct MCP transport security config to actually allow all hosts by default +* feat: add configurable MCP transport security settings via gns3_server.conf +* fix: add load_feature_skills() to properly load network planning features +* refactor: convert all MCP tool parameter descriptions to Annotated+Field +* fix: remove platformdirs upper bound to resolve fastmcp-slim dependency conflict +* fix: add missing fastmcp dependency to resolve CI test failures +* refactor: move all imports to top of __init__.py +* test: add /v3/mcp/ to allowed public endpoints +* fix: remove console_host/port from get_node_console_info +* feat: add get_node_console_info tool +* feat: add 3 Compute MCP tools - Add list_computes, get_compute, get_compute_images - Total MCP tools: 29 +* feat: add 5 Template MCP tools +* feat: add Node and Link MCP tools, update copyright - Add 9 node tools and 5 link tools - Update copyright year to 2026, add author +* feat: complete MCP SSE transport with JWT auth +* feat: support Authorization header and query param for MCP token +* feat: implement standard MCP protocol with SSE transport +* feat: add MCP (Model Context Protocol) service with project tools +* Add project memory: Docker container stop delay analysis +* Remove extra blank line from merge +* Revert container state detection in create() +* Fix Docker VM tests for container status detection on node creation +* Add running project check for fast duplication +* Move running project check before fast duplication +* Add running project check for fast duplication +* Fix Docker container status detection on node creation +* Fix web-ui update script to handle custom GitHub URL changes +* Fix unnecessary Docker container recreation when renaming a project +* Fix project rename and duplicate issues +* Fix double deletion issue in remove_resource_from_pool +* Complete fix: delete resource records when deleting resource pool +* Apply fix from PR #2315: delete resource from resource table when removing from pool +* Remove deprecated 'PermissionsStartOnly' setting for Systemd service. Ref #1830 +* Optimize project loading by implementing parallel node creation +* Fix delay filter validation: ensure delay: [0, X] returns proper error message +* Fix packet filter validation tests: use correct ubridge filter type names +* Update tests to match show_interface_labels default change +* Set default value of show_interface_labels to True +* Optimize project variable updates to use parallel node processing +* Fix ghost Docker nodes causing 60-second VNC timeout on variable updates +* Fix Docker container variable compatibility with Pydantic models +* Fix delay latency minimum: ubridge rejects latency <= 0 +* Improve packet filter validation: use tcpdump, handle multi-line BPF, safe project load +* Add packet filter parameter validation to prevent ubridge errors +* chore: update GNS3 skills repository to official organization +* docs: update RBAC user isolation design doc to match actual implementation +* docs: update skills repo URL in command-security.md +* docs: remove Chinese overview docs, keep only English versions +* docs: add overview docs for packet analysis, fault injection, and AI assistant +* feat: add mermaid-to-SVG conversion script with environment setup +* test: fix privilege count assertions after adding LLMConfig privileges +* docs: add user node limit roadmap +* test: fix RBAC test to match implementation logic +* test: add --prefix and --cleanup-only parameters to benchmark script +* perf: batch RBAC permission checking for GET /projects +* test: add benchmark script for GET /projects performance testing +* fix: prevent duplicate projects when user projects are in resource pools +* fix: check both regular ACEs and resource pool ACEs for proper access control +* test: update RBAC test to use test_user.username for user isolation +* docs: add Phase 9 and 10 user self-registration and email service +* docs: add Phase 8 per-user project namespace to roadmap +* docs: add Phase 7 resource pool renaming to roadmap +* feat: add alembic migration for LLMConfig privileges +* feat: add independent LLMConfig permissions for AI profile management +* docs: add Phase 6 frontend permission query API to roadmap +* docs: add Phase 5 ACE architecture refactoring plan to roadmap +* feat: remove resource pools from 'all endpoints' list +* refactor: add efficient get_aces_for_path method for resource pool checks +* feat: prevent deletion of resource pools used by ACE configurations +* docs: update RBAC user isolation roadmap and add design memory +* feat: fix permission check logic to properly handle ACE and user isolation +* feat: implement layered permission checks for proper user isolation and sharing +* feat: implement simple user isolation based on project ownership +* fix: clean BPF syntax error message and specify loopback interface +* feat: add BPF syntax validation using tshark +* feat: add show_filters_icon parameter to packet filter tool +* docs: add GNS3 appliance loading mechanism to memory +* feat: add packet filter management tool for GNS3-Copilot fault injection +* fix: update test_json expected output to include show_filters_icon field +* fix: add getattr fallback to show_filters_icon property for backward compatibility +* feat: add show_filters_icon property to Link for controlling Web UI filter icon display +* fix: ensure show_filters_icon is always returned in API responses +* feat: add show_filters_icon property to Link for controlling Web UI filter icon display +* fix: remove explicit paramiko pin to resolve dependency conflict with netmiko +* fix: update netmiko to 4.7.0 and pin paramiko>=5.0.0 to fix CVE-2026-44405 +* fix: close DockerHTTPClient session to prevent UnixConnector leak in Web Wireshark + + ## 3.1.0a2 12/05/2026 * Bundle web-ui v3.1.0a2 diff --git a/Dockerfile b/Dockerfile index edbeab421..64ee3ff4b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ RUN add-apt-repository ppa:gns3/ppa && apt update && DEBIAN_FRONTEND=noninteract COPY . /gns3server -RUN mkdir -p ~/.config/GNS3/3.0/ -RUN cp scripts/gns3_server.conf ~/.config/GNS3/3.0/ +RUN mkdir -p ~/.config/GNS3/3.1/ +RUN cp scripts/gns3_server.conf ~/.config/GNS3/3.1/ RUN python3 -m pip install --break-system-packages --ignore-installed . diff --git a/README.md b/README.md index ab6187287..4144beafd 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # GNS3 server repository [![Style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) -[![GitHub Actions tests](https://github.com/GNS3/gns3-server/workflows/testing/badge.svg?branch=3.0)](https://github.com/GNS3/gns3-server/actions?query=workflow%3Atesting+branch%3A3.0) +[![GitHub Actions tests](https://github.com/GNS3/gns3-server/workflows/testing/badge.svg?branch=3.1)](https://github.com/GNS3/gns3-server/actions?query=workflow%3Atesting+branch%3A3.0) [![Latest PyPi version](https://img.shields.io/pypi/v/gns3-server.svg)](https://pypi.python.org/pypi/gns3-server) [![Snyk scanning](https://snyk.io/test/github/GNS3/gns3-server/badge.svg)](https://snyk.io/test/github/GNS3/gns3-server) @@ -58,11 +58,11 @@ python3 -m pip install gns3-server GNS3 server supports optional features that can be installed as needed: -**AI Copilot** (Optional): +**AI Features** (Optional — includes AI Copilot and MCP): ```shell -python3 -m pip install gns3-server[ai-copilot] +python3 -m pip install gns3-server[ai-features] ``` -AI-powered assistant for network topology design and automation. +AI-powered assistant for network topology design, automation, and MCP protocol support for AI agent integration. **Development** (For contributors): ```shell @@ -78,7 +78,7 @@ Browser-based packet capture analysis using Wireshark in a Docker container. **Combination Installation**: You can install multiple optional features together: ```shell -python3 -m pip install gns3-server[ai-copilot,dev] +python3 -m pip install gns3-server[ai-features,dev] ``` **Why optional?** @@ -89,17 +89,15 @@ python3 -m pip install gns3-server[ai-copilot,dev] **Note:** If you install without optional extras, the server will work normally but optional features will be disabled. You can add features later by running the appropriate install command. -**Uninstalling AI Copilot:** +**Uninstalling AI Features:** -To remove AI Copilot dependencies: +To remove AI Features dependencies (AI Copilot + MCP): ```shell -gns3server-uninstall-ai-copilot +gns3server-uninstall-ai-features ``` -This will remove all AI Copilot dependencies while keeping the core functionality intact. The server will continue to work, but AI features will return a 501 (Not Implemented) status code. - -The downside of this method is you will have to manually install all dependencies (see below). +This will remove all AI Copilot and MCP dependencies while keeping the core functionality intact. The server will continue to work, but AI features will be disabled. Please see our [documentation](https://docs.gns3.com/docs/getting-started/installation/linux) for more details. @@ -127,7 +125,7 @@ These commands will install the server with core Python dependencies: ```shell git clone https://github.com/GNS3/gns3-server cd gns3-server -git checkout 3.0 +git checkout 3.1 python3 -m venv venv-gns3server source venv-gns3server/bin/activate python3 -m pip install . @@ -137,7 +135,7 @@ python3 -m gns3server **For AI Copilot development**, install with additional dependencies: ```shell -python3 -m pip install .[ai-copilot,dev] +python3 -m pip install .[ai-features,dev] ``` **For development (tests and linting)**: diff --git a/ai-requirements.txt b/ai-requirements.txt index 81cfccf2b..135af4c09 100644 --- a/ai-requirements.txt +++ b/ai-requirements.txt @@ -1,7 +1,7 @@ # ============================================================================== # GNS3 Copilot AI Agent Dependencies # ============================================================================== -# Install with: pip install gns3-server[ai-copilot] +# Install with: pip install gns3-server[ai-features] # Or directly: pip install -r ai-requirements.txt # ============================================================================== @@ -30,7 +30,7 @@ tiktoken>=0.8.0 langsmith>=0.7.7 # Network Automation -netmiko>=4.6.0 +netmiko>=4.7.0 nornir>=3.5.0 nornir-netmiko>=1.0.1 nornir-utils>=0.2.0 diff --git a/dev-requirements.txt b/dev-requirements.txt index 8be7e2fb7..55aac6eaa 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,7 +1,6 @@ -pytest==9.0.3 # fix CVE-2025-71176; Python 3.10+ required +pytest==9.1.1 flake8==7.3.0 pytest-timeout==2.4.0 -pytest-asyncio==1.2.0; python_version == '3.9' # version 1.2.0 is the last one supporting Python 3.9 -pytest-asyncio==1.3.0; python_version >= '3.10' +pytest-asyncio==1.4.0 httpx==0.28.1 httpx_ws==0.7.2 # upgrading leads to failures in tests \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index 7b40b951c..c347479cc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -72,6 +72,15 @@ Unified error response format across all GNS3 API endpoints. Documents HTTP stat ### Web Wireshark (`features/web-wireshark-business-process.md`) Web-based packet capture analysis using Docker + xpra HTML5 client. Zero-install Wireshark experience directly in the browser, integrated with GNS3 topologies. +### Marker (Traffic Insight) (`features/marker-traffic-insight.md`) +Real-time traffic insight via per-link BPF markers and project-level inherited definitions. A marker taps a link in uBridge, emitting match notifications and pcap capture on BPF hit; definitions fan out to every capable link automatically. + +### Docker exec Console (Vendor NOS) (`features/docker-exec-console.md`) +Console for vendor NOS containers (SR Linux, XRd, …) whose CLI is a TUI off PID 1: runs the vendor CLI via the Docker exec API, plus `GNS3_SKIP_INIT`/`GNS3_INTERFACE_NAMES` boot knobs and SKIP_INIT volume persistence. + +### Cisco XRd Control Plane (`features/vendor-nos-xrd.md`) +Cisco XRd as a GNS3 Docker router: vendor path + shm/device injection (`GNS3_SHM_SIZE`/`GNS3_DEVICES`), config-file injection (`extra_configs`), udev masking (`GNS3_MASK_UDEV`) so privileged systemd containers don't disturb the host, and the host-readiness check. + --- ## GNS3 AI Copilot (`gns3-copilot/`) @@ -100,6 +109,7 @@ Web-based packet capture analysis using Docker + xpra HTML5 client. Zero-install ## Known Issues (`bugs/`) - [Telnet Server Connection Race Condition](bugs/telnet-server-connection-race-condition.md) — `getpeername()` error when client disconnects during connection setup (High severity, Open) +- [Docker Link UDP Self-Loop](bugs/link-udp-self-loop.md) — intermittent one-way link (both ends handed the same UDP port by an allocation race); **fixed** (Medium severity) --- @@ -117,4 +127,4 @@ Quick-start guide for Ubuntu 24.04: install via PPA, set up dependencies, and ru --- -_Last updated: 2026-04-20_ +_Last updated: 2026-08-14_ diff --git a/docs/bugs/link-udp-self-loop.md b/docs/bugs/link-udp-self-loop.md new file mode 100644 index 000000000..e1e05e7cf --- /dev/null +++ b/docs/bugs/link-udp-self-loop.md @@ -0,0 +1,101 @@ + + +> This documentation is organized by AI with reference to actual code. AI can make mistakes — please verify against the source code when in doubt. + +# Docker Link UDP Self-Loop Bug (One-Way Link) + +## Bug Report + +**Date**: 2026-08-14 +**Severity**: Medium (one-way connectivity, CPU burn from packet duplication; intermittent) +**Status**: **Fixed** — root cause found and unit-tested (same day) +**Component**: UDP port allocation — `gns3server/compute/port_manager.py` +(`get_free_udp_port` find-then-add race); secondary: `gns3server/controller/udp_link.py` +(`_prepare` accumulated stale `_link_data` on reset) + +## Symptoms + +Two Docker nodes (observed with Cisco XRd; node-type agnostic) linked on the +same compute cannot ping each other. Packet capture on the link shows only **one** +side sending ARP. The other side's traffic never appears on the link at all. + +## Evidence (from the live occurrence) + +Captured simultaneously on both nodes' uBridge `bridge1` (see diagnostics below): + +| Direction | Result | +|---|---| +| A → B | works — A's ARP requests arrive at B's bridge, B replies | +| B → A | dead — B's replies/ICMP appear only on **B's own bridge** (duplicated ×2–×3), nothing arrives at A | +| B's `ethN` counters | RX ≈ TX ≈ 5000+ — B receives its own transmissions back | +| A's `ethN` counters | TX > 0, RX = 0 — never receives anything | + +## Root Cause (confirmed) + +**`PortManager.get_free_udp_port` had an unguarded find-then-add sequence.** +A link allocates the UDP port for **both ends concurrently** +(`asyncio.gather` in `UDPLink._prepare` → two `POST /ports/udp`). The route +handler is a sync `def`, so FastAPI executes the two requests **in parallel +threads**. Both threads ran `find_unused_port` (socket-probing, GIL-releasing) +before either reached `_used_udp_ports.add(port)` — the set add is idempotent, +so no error was raised and **both ends were handed the same port number**: +`lport == rport` on both NIOs, a literal self-loop. + +Why it was *silent* and *asymmetric*: + +- uBridge sets `SO_REUSEADDR` on UDP NIO sockets (`ubridge/src/nio_udp.c`), so + the second bind of the same port **succeeds** instead of failing with + `EADDRINUSE` — link creation returned success. +- With two sockets bound to the same port, the kernel delivers to one of them + (last bound wins). The node that started later — in the live case B, + restarted ~77 s after A — received **everything**: A's packets *and* its own + transmissions echoed back. The 77 s restart did not cause the corruption; it + only decided which end starves. +- The same-compute condition is part of the trigger: both allocations hit the + same `PortManager` instance (a cross-compute link races two processes and + cannot self-collide). + +A second, smaller defect was found while auditing: `UDPLink._prepare()` +**appended** to `self._link_data` but the committed NIOs are always taken from +indices 0/1 — after `reset()` (delete + create on the same object) the stale, +already-released port pair was re-committed and the freshly allocated ports +were leaked. + +## Fix + +| Change | Where | +|---|---| +| `threading.RLock` making find-then-add (and reserve/release) atomic for TCP and UDP | `gns3server/compute/port_manager.py` | +| `_prepare()` rebuilds `_link_data` from scratch instead of appending | `gns3server/controller/udp_link.py` | +| Regression tests: threaded allocation never returns duplicates (red on the old code); `reset()` commits the fresh mirrored pair with `lport != rport` | `tests/compute/test_port_manager.py`, `tests/controller/test_udp_link.py` | + +## Diagnostics (uBridge console is the fast path) + +1. **uBridge console** — each node's uBridge listens on a Unix socket + `/run/user/1000/gns3/ubridge-.sock`; connect and send: + `bridge list` (NIO count per bridge), and + `bridge start_capture bridge "/tmp/ub-.pcap"` / + `bridge stop_capture bridge` to capture what the bridge actually forwards. + Comparing the two ends' pcaps localizes the break immediately. +2. **Container counters** — `docker exec ip -s link show ethN`: + TX>0/RX=0 → peer never returns; RX≈TX huge with µs-scale duplicates → self-loop. +3. **UDP sockets** — `ss -uln` (no `-p`; uBridge runs setuid-root so process names + are hidden, ports are visible): a two-node link owns exactly two "orphan" UDP + ports. **One port instead of two = this bug.** +4. **Recovery** — delete and re-create the link (or stop/start both nodes); + with the fix, the corruption no longer occurs in the first place. + +Note: a Docker node's in-container `ethN` is a **TAP device** whose file descriptor +lives inside uBridge (the interface is created host-side, then moved into the +container namespace and renamed). There is no veth host end to look for — do not +waste time hunting for one in the host namespace. + +## Related + +- `docs/features/vendor-nos-xrd.md` — troubleshooting table entry pointing here. +- Link-create batch optimization (`controller/udp_link.py`, project-open bulk path) + was exonerated: the pool path allocates sequentially in one handler and cannot + self-collide. diff --git a/docs/development-setup.md b/docs/development-setup.md index 3403972eb..2ee546560 100644 --- a/docs/development-setup.md +++ b/docs/development-setup.md @@ -104,7 +104,7 @@ source venv/bin/activate # pip install -e . -i https://mirrors.aliyun.com/pypi/simple/ pip install -e . && gns3server-web-wireshark-setup -pip install -e .[ai-copilot] +pip install -e .[ai-features] pip install -e .[dev] ``` @@ -114,10 +114,10 @@ Run the server: python3 -m gns3server ``` -## Optional: Install AI Copilot Development Dependencies +## Optional: Install AI Features Development Dependencies ```bash -python3 -m pip install .[ai-copilot,dev] +python3 -m pip install .[ai-features,dev] ``` ## Optional: Expand LVM Root Partition diff --git a/docs/features/builtin-ethernet-switch-ubridge.md b/docs/features/builtin-ethernet-switch-ubridge.md new file mode 100644 index 000000000..45e5430c0 --- /dev/null +++ b/docs/features/builtin-ethernet-switch-ubridge.md @@ -0,0 +1,266 @@ + + +> This documentation is AI-generated with reference to actual code and verified +> against real-kernel testing (Linux 7.1.2-1-default). AI can make mistakes — +> please verify against the source code when in doubt. + +# Builtin Ethernet Switch — uBridge brctl Backend + +## Overview + +The historical GNS3 Ethernet Switch was an emulated L2 device inside Dynamips +(`ethsw`). This implementation replaces it with a **real Linux kernel bridge** +driven through uBridge's `brctl` module — one bridge per switch node. The +migration makes the switch a first-class builtin node (no Dynamips dependency) +and enables native-kernel-speed L2 switching with VLAN filtering and QinQ. + +| | Old (Dynamips ethsw) | New (uBridge brctl) | +|---|---|---| +| Switching engine | Dynamips user-space emulation | Linux kernel bridge (netlink) | +| VLAN model | ethsw ACL per port | Kernel VLAN filtering + PVID/untagged | +| QinQ | 0x8100/0x88A8/0x9100/0x9200 | 0x8100 (802.1Q) / 0x88A8 (802.1ad) | +| Data path | Node NIO ↔ ethsw NIO (Dynamips) | Node NIO ↔ uBridge relay ↔ TAP ↔ kernel bridge | +| Console | Inactive (reserved TCP port) | None (console_type=none) | +| Node type | `dynamips`-routed | `builtin` (always-on) | + +## Architecture + +``` + ┌───────────┐ ┌──────────────┐ ┌───────────────┐ ┌───────────┐ + │ Peer A │ │ uBridge │ │ Kernel │ │ Peer B │ + │ (Dynamips │◄───►│ per-port │◄───►│ Bridge │◄───►│ (IOU / │ + │ / IOU / │ UDP │ relay │ TAP │ gns3{id[:6]}│ TAP │ QEMU / …) │ + │ QEMU) │ │ nio_tap↔udp │ │ vlan_filter │ │ │ + └───────────┘ └──────────────┘ └──────┬────────┘ └───────────┘ + │ + ┌─────┴─────┐ + │ ... more │ + │ ports │ + └───────────┘ +``` + +Each switch port is a **dual-role TAP** — uBridge holds the file descriptor as a +`nio_tap` relay endpoint, and the same TAP is enslaved to the kernel bridge via +`brctl addif`. This is the same pattern the Cloud node already uses for host +bridges (`cloud.py:_add_linux_ethernet`). uBridge is **only** the per-port UDP +transport; the kernel bridge performs the actual MAC learning, forwarding, and +VLAN filtering. + +### Component map + +| File | Role | +|------|------| +| `compute/builtin/nodes/ethernet_switch.py` | Node implementation | +| `api/routes/compute/ethernet_switch_nodes.py` | REST endpoints (repointed to Builtin) | +| `schemas/compute/ethernet_switch_nodes.py` | Request/response models (unchanged) | +| `controller/udp_link.py` | Link creation — pushes NIO to switch via standard adapter endpoint | + +## Lifecycle + +### `create()` → `start()` + +1. `_start_ubridge(require_privileged_access=True)` — launch uBridge instance +2. `_ensure_bridge()`: + - Derive deterministic bridge name: `gns3` + first 6 hex chars of `self.id` + - `brctl delete` (best-effort — crash recovery, cleans stale interfaces) + - `brctl create` + - `link set … up` (bridge is DOWN after create) + - `brctl vlanfiltering … on` + +### `add_nio(nio, port_number)` + +Per port, one uBridge relay bridge `{node_id}-{port}` is wired: + +``` +bridge create {node_id}-{port} +bridge add_nio_tap {node_id}-{port} "{tap}" ← uBridge holds TAP fd +brctl addif "{bridge}" "{tap}" ← enslave to kernel bridge +brctl vlan_del/vlan_add … ← apply port VLAN mode +bridge add_nio_udp {node_id}-{port} lport rhost rport +bridge reset_packet_filters {node_id}-{port} ← from _ubridge_apply_filters +bridge start {node_id}-{port} +``` + +Captures and marker signals are applied via the existing `_ubridge_apply_filters` +and `_ubridge_apply_markers` helpers from `BaseNode`. The controller therefore allows +packet filters and traffic-insight markers on switch links (including switch-to-switch): +`ethernet_switch` is a marker/filter-capable node type, and the compute API exposes the +matching NIO-update and per-marker endpoints. The `ethernet_hub` — still Dynamips-hosted, +no uBridge — remains excluded. + +### `remove_nio(port_number)` + +``` +brctl delif "{bridge}" "{tap}" +bridge delete {node_id}-{port} +release_udp_port(nio.lport) +``` + +### `close()` + +``` +for each port: release UDP port +brctl delete "{self._bridge_name}" ← kernel bridge teardown +_stop_ubridge() ← destroys remaining TAPs +``` + +**Cleanup paths:** + +| Scenario | Bridge cleanup | TAP cleanup | +|----------|---------------|-------------| +| Normal project close | `close()` → `brctl delete` | uBridge stops → TAP fd closed → kernel destroys | +| gns3server crash / kill | Next `_ensure_bridge()` → `brctl delete` before `create` | uBridge dies → TAP fd closed by kernel | +| Manual project-file deletion after crash | Leaked (no GNS3 record of `gns3{id[:6]}`) | Leaked (same — but uBridge probably dead, TAPs gone with it) | + +## Port mode → VLAN translation + +All VLAN operations ride on the `brctl` hypervisor module (`../ubridge/doc/brctl.md`). +The kernel bridge must have `vlan_filtering on` before any `vlan_*` call. + +### access VLAN N + +``` +brctl vlan_del {br} {tap} 1 ← remove default PVID 1 +brctl vlan_add {br} {tap} N pvid untagged +``` + +### dot1q trunk (native VLAN V) + +A dot1q trunk in ESW is "admit all VLANs tagged, native VLAN PVID + untagged": +``` +brctl vlan_del {br} {tap} 1 +brctl vlan_add {br} {tap} 1 vid 4094 ← admit all VIDs tagged +brctl vlan_add {br} {tap} V pvid untagged ← override native +``` + +### qinq (outer VLAN O, ethertype 0x88A8) + +Bridge-level (once): +``` +brctl setvlanproto {br} 0x88a8 ← switch to 802.1ad (outer S-tag) +``` + +Port-level: +``` +brctl vlan_del {br} {tap} 1 +brctl vlan_add {br} {tap} O pvid untagged ← S-tag push for untagged ingress +``` + +Ethertype 0x8100 qinq ports are treated as plain access ports (the bridge +defaults to 0x8100; no `setvlanproto` needed). Ethertype 0x9100/0x9200 are +not supported by the kernel bridge — see § Limitations. + +### Runtime reconfiguration (`update_port_settings`) + +On `ports_mapping` update, existing port VLANs are reset before re-apply: +``` +brctl delif {br} {tap} ← release from bridge (clears VLAN state) +brctl addif {br} {tap} ← re-enslave (resets to default PVID 1) +brctl vlan_del/vlan_add … ← apply new mode +``` + +This prevents stale VLAN membership from a previous mode leaking into the new +configuration (e.g., access→trunk transition leaving old access VLAN behind). + +## Bridge naming + +Deterministic from the switch's UUID: `gns3` + first 6 hex chars (no dashes). + +``` +gns3a1b2c3 ← bridge (10 chars, ≤ 15 IFNAMSIZ limit) +gns3a1b2c3-0 ← tap for port 0 (12 chars) +gns3a1b2c3-1 ← tap for port 1 (12 chars) +``` + +- 6 hex = 48 bits of entropy — collision risk is astronomically low even with + thousands of switches on the same host. +- **Crash recovery**: `brctl delete` (best-effort, ignore if not found) then + `brctl create` — stale interfaces from a previous abnormal shutdown are + reclaimed automatically when the switch is re-created. + +## Controller integration + +No controller or API contract changes are required. The migration is entirely +compute-internal: + +- `node_types.BUILTIN_NODE_TYPES` already classified `ethernet_switch` as a + builtin, always-running node. +- `udp_link.create()` pushes the NIO to the switch via the standard + `POST /adapters/0/ports/{p}/nio` endpoint (same as Dynamips). +- The REST API paths, request/response schemas, and port model + (`EthernetSwitchPort`: type/vlan/ethertype) are unchanged. +- `/start`, `/stop`, `/suspend`, `/reload` return 405 (switch is always-on). + +The sole observable difference: the `console` field in the response is now +`null` (the switch has no console; `console_type="none"` makes `BaseNode` +skip TCP port reservation). The old Dynamips ethsw returned an unused TCP +port number. Both are valid under `Optional[int]`. + +## Known limitations + +### Default PVID 1 must be deleted explicitly + +A port freshly enslaved to a `vlan_filtering` bridge inherits default PVID 1 +(PVID + Egress Untagged). Access/trunk mode application must issue +`vlan_del … 1` first — `vlan_add … pvid` moves the PVID but does not remove +the old PVID's membership. This matches iproute2 semantics and is documented +in `../ubridge/doc/brctl.md#limitations`. + +### QinQ is outer-tag (S-VLAN) only + +With `setvlanproto 0x88a8` the bridge filters on the outer S-tag; the inner +C-tag passes through transparently. Selective QinQ (inner-VLAN classification +or remapping) requires `IFLA_BRIDGE_VLAN_TUNNEL_INFO` which is not implemented. +Documented in `../ubridge/doc/brctl.md#limitations`. + +### Ethertype 0x9100 / 0x9200 + +The GNS3 schema allows legacy QinQ ethertypes `0x9100` and `0x9200`, but the +Linux kernel bridge only supports `0x8100` (802.1Q) and `0x88a8` (802.1ad). +Configuring these on a qinq port produces a `NodeError` at creation/update +time. Handling policy (map to 0x88A8 + warn vs. reject with error) is +pending per design discussion. + +### No FDB read/write + +The `brctl` module exposes no `fdb_show`/`fdb_flush`. The kernel bridge +learns and ages MAC entries autonomously; uBridge has never exposed MAC-table +access and gns3-server does not consume it. Consumers that need the FDB +(e.g., a WebUI switch view) should read `/sys/class/net/
/brforward` or +`bridge fdb show dev
` directly, without uBridge involvement. + +## Troubleshooting + +### Docker iptables: FORWARD chain DROP + +Docker sets the iptables `FORWARD` chain default policy to `DROP` when the +Docker daemon starts. This blocks **all** forwarded traffic through kernel +bridges on the host, including `gns3*` bridges. + +**Symptoms**: nodes can send frames into the bridge (visible in `tcpdump -i +gns3*`) but never receive unicast replies. ARP and multicast may work +because they flood, but unicast forwarding silently fails. + +**Fix**: +```bash +sudo iptables -P FORWARD ACCEPT +``` + +### Bridge left DOWN after creation + +`brctl create` creates the bridge but leaves it administratively DOWN. +The node now sends `link set … up` after `brctl create`. If forwarding +is not working, verify: +```bash +ip -d link show gns3* | grep -E "state|vlan_filtering" +``` + +### Kernel version differences + +This implementation has been tested on Linux 7.1.2-1-default (x86_64) with +uBridge installed via `make install` (cap_net_admin,cap_net_raw=ep). The +ubridge `brctl` module has a 168-test suite covering kernel-side VLAN +behaviour on this kernel. diff --git a/docs/features/docker-exec-console.md b/docs/features/docker-exec-console.md new file mode 100644 index 000000000..41a3a641b --- /dev/null +++ b/docs/features/docker-exec-console.md @@ -0,0 +1,571 @@ + + +> This documentation is organized by AI with reference to actual code. AI can make mistakes — please verify against the source code when in doubt. + + +# Docker exec Console (Vendor NOS Containers) + +## Overview + +GNS3 Docker nodes normally expose their console by attaching to the container's +PID 1 stdio. That works for CLIs that run as PID 1 (e.g. FRR's `vtysh`), but it +does **not** work for vendor NOS containers (Nokia SR Linux, Arista cEOS, +Juniper cRPD, …) whose CLI is a separate, full-screen TUI process that is *not* +on PID 1. For those, attaching to PID 1 only shows boot logs and never yields a +CLI prompt. + +The `docker_exec` console type solves this. It runs a chosen command inside the +running container via the Docker exec API (with a pty) and bridges it to the +GNS3 console, so the vendor's native TUI CLI renders in the Web UI (xterm.js) +exactly as if you had run `docker exec -it ` in a real +terminal. + +Two companion environment knobs (`GNS3_SKIP_INIT`, `GNS3_INTERFACE_NAMES`) make +the container itself boot and wire correctly for vendor NOS images. Together +they let a vendor NOS run as a first-class GNS3 Docker router node. + +> Prototype status: the knobs are environment-driven and intentionally avoid +> schema changes, so existing Docker nodes (FRR, ipterm, …) are unaffected. +> `console_type: "docker_exec"` is added to the `ConsoleType` enum. + +## Environment knobs + +All are read from the node's `environment` field. Entries prefixed with +`GNS3_` are **not** forwarded into the container (existing GNS3 behaviour), so +they stay host-side configuration. The console-relevant ones (the full vendor +set, incl. `GNS3_SHM_SIZE` / `GNS3_DEVICES` / `GNS3_MASK_UDEV` / +`GNS3_STOP_TIMEOUT`, is documented in [vendor-nos-xrd.md](./vendor-nos-xrd.md)): + +| Variable | Purpose | +|----------|---------| +| `GNS3_SKIP_INIT=1` | Do **not** prepend `/gns3/init.sh` to the entrypoint. Vendor NOS images must run their own entrypoint (e.g. SR Linux's `sr_linux`); GNS3's init script (busybox bootstrap, `ifup`, eth wait) interferes with them. | +| `GNS3_INTERFACE_NAMES=mgmt0,e1-1,e1-2,e1-3` | Rename the injected interfaces in adapter order instead of the default `eth{N}`. SR Linux expects `mgmt0` + `e1-N`; without this it does not recognise its datapath. | +| `GNS3_CONSOLE_CMD=/opt/srlinux/bin/sr_cli` | Command run by the `docker_exec` console inside the container. | +| `GNS3_CONSOLE_RESIZE=0` | Ignore client-driven console resizes (WS terminal-size control frames / telnet NAWS) and keep the tall no-paging PTY geometry. Set for CLIs that page on the PTY window size (IOS-XR) — see [Terminal geometry](#terminal-geometry-and-size-forwarding). | + +## Architecture: `VendorDockerVM` subclass + +All vendor-specific logic lives in a `VendorDockerVM(DockerVM)` subclass in +`gns3server/compute/docker/vendor_docker_vm.py` — `docker_vm.py` itself stays +on its baseline behaviour and is never touched by this feature. + +`DockerVM` exposes four small extension hooks (pure refactorings, zero +behaviour change for existing nodes): + +| Hook | Baseline behaviour | `VendorDockerVM` override | +|------|--------------------|---------------------------| +| `_prepare_init_and_interface_env(params)` | prepend `/gns3/init.sh`, set `GNS3_MAX_ETHERNET=eth{N-1}` | conditional init.sh (`GNS3_SKIP_INIT`), `GNS3_MAX_ETHERNET` follows the interface rename | +| `_start_console_server()` | telnet/ssh/http console dispatch | adds the `docker_exec` branch | +| `_get_container_ifname(adapter_number)` | `eth{N}` | `GNS3_INTERFACE_NAMES` lookup, fallback `eth{N}` | +| `_cleanup_console_resources()` | no-op | closes the docker-exec pty socket before restart/stop | + +### Class selection + +The Docker manager picks the class per node in `Docker.create_node()` +(`gns3server/compute/docker/__init__.py`): + +```python +def _select_node_class(self, **kwargs): + if kwargs.get("console_type") == "docker_exec": + return VendorDockerVM + return DockerVM +``` + +`console_type == "docker_exec"` is the **only** trigger — every other console +type (telnet, vnc, ssh, http, …) keeps using the unmodified `DockerVM`. All +vendor features are opt-in: without the `GNS3_*` environment variables a +`VendorDockerVM` instance behaves identically to `DockerVM` (init.sh still +runs, interfaces stay `eth{N}`, the exec command defaults to `/bin/sh`), so a +regular container can use `docker_exec` too. + +## The `docker_exec` console type + +Setting `console_type: "docker_exec"` makes the node's primary console port run +`_start_docker_exec_console()` instead of the attach-to-PID-1 path. + +### Console architecture + +```mermaid +graph LR + A[Web UI xterm.js] -->|console WS: text frames| B[Controller forward] + B -->|WS: text + binary| C[GNS3 Compute telnet server] + C -->|binary pty stream| D[Docker exec API] + D -->|Tty:true pty| E[sr_cli / vendor CLI] + A -.->|binary control frame {"cols","rows"}| B + B -.-> C + C -.->|POST exec/.../resize| D +``` + +The console uses GNS3's **existing shared/broadcast telnet model**: a single +exec instance (one CLI session) is broadcast to every console client, exactly +like the primary console shares one PID 1. There is deliberately **no +per-client session isolation** — this matches how every other GNS3 console +behaves. The PTY geometry is likewise shared: the last client resize wins +(see [Terminal geometry](#terminal-geometry-and-size-forwarding)). + +### Implementation + +**File**: `gns3server/compute/docker/vendor_docker_vm.py` — +`_start_docker_exec_console()` + +A small subclass `_LazyExecTelnetServer(AsyncioTelnetServer)` implements the +console. Key points: + +1. **Lazy exec creation.** The exec is created on the **first client + connection** (`client_connected_hook`), not when the node starts. This is + essential: vendor CLIs (e.g. `sr_cli` via `prompt_toolkit`) send a + cursor-position request (`\e[6n`, CPR) during startup and block waiting for + the terminal's answer. If the exec starts at node-start time there is no + xterm.js client to answer, the probe times out, and the TUI degrades (no + status bar, "Terminal doesn't support CPR" warning). Creating the exec on + first connect means the probe runs with a real xterm.js attached, which + answers CPR → full TUI. After creation the exec is shared by all clients. + +2. **Exec API with a pty.** `POST containers/{cid}/exec` with + `Tty: true`, `User: "root"` (vendor CLIs reject the image's default + unprivileged user — SR Linux returns *"User 'user' is not authorized to use + CLI"* otherwise), and `Env: ["TERM=xterm"]` (the TUI library needs a + recognised terminal). + +3. **No while-true wrapper.** The command runs as `sh -c ""` (no + restart loop). When the CLI exits (`quit`, the NOS's own idle timeout, or a + crash) the exec pty closes, the broadcast task ends, and the next client + connection **recreates** the exec (see *Reconnection*). A `while true` + wrapper would restart the CLI mid-session with no client attached to + answer its startup CPR probe, producing a blank/degraded screen on + reconnect. + +### Reconnection + +The exec is created lazily and **recreated on reconnect if it has died**. +`client_connected_hook` checks `_upstream_alive()` (exec id set, writer open, +broadcast task not done) before each connect: + +- **First connect / dead upstream** → (re)create the exec. Because a client is + now attached, the CLI's startup CPR probe is answered by xterm.js → full + TUI. A half-dead writer is closed first to avoid a socket leak. +- **Live upstream** → reuse the existing exec, just send `Ctrl-L` to redraw + for the new client. + +This is what makes the console survive `quit`, idle timeout, and CLI +crashes: the death is detected (pty EOF ends the broadcast task) and the +next connection spins up a fresh exec with a terminal present. The +`_LazyExecTelnetServer` is extracted to module level specifically so this +reconnect logic is unit-tested. + +4. **Hijacked raw-HTTP start.** The exec is started with + `POST exec/{eid}/start` sent as a raw HTTP upgrade over the Docker unix + socket (`asyncio.open_unix_connection`), the same approach docker-py uses. + This is required because aiohttp's websocket client (`ws_connect`) is + rejected by Docker's exec-start endpoint (HTTP 400), while a raw POST + upgrade succeeds (101). With `Tty:true` the response body is a raw, + non-multiplexed bidirectional pty byte stream — no frame demux needed. + +5. **NAWS → exec resize.** The telnet server runs with `naws=True`; the + `window_size_changed_callback` (`_on_naws`, gated by + `GNS3_CONSOLE_RESIZE`) calls `POST exec/{eid}/resize?h=&w=` so the TUI + lays out for the client's window size. The internal `_resize_exec` path + (creation-time default, restore-on-idle) is not gated. See + [Terminal geometry](#terminal-geometry-and-size-forwarding). + +6. **Binary passthrough + redraw.** `binary=True` so TUI escape sequences reach + xterm.js intact; `echo=False` (the pty echoes). On every client (re)connect + a `Ctrl-L` (`\x0c`) is sent to the pty so a TUI that already drew its + screen for a previous client redraws for the new one (otherwise a + reconnect shows a blank screen until the next output). + +### Terminal geometry and size forwarding + +The exec PTY geometry is a shared resource with three consumers that want +different things: + +- **Browser clients (xterm.js)** need the PTY to match their real window, or + TUI CLIs misrender and over-render (below). +- **Non-NAWS clients** (netmiko, bare telnet — no terminal-size negotiation) + need the PTY *tall*: CLIs that page on the PTY window size (the IOS-XR + pager ignores `terminal length 0`) park at `--More--` on a 24-row PTY. +- **Concurrent sessions share one exec** — one browser resize changes what + every attached client sees. + +Resolution: + +1. **Tall default.** The exec is created at 511×10000 (width 511 matches + netmiko's `terminal width 511` convention). Non-NAWS clients get no paging + and no hard wrapping. +2. **WS terminal-size forwarding.** Console WebSocket clients may send + **binary control frames** — UTF-8 JSON `{"cols": N, "rows": N}` — + alongside text frames carrying terminal data (xterm.js's AttachAddon only + sends text, so binary is an unambiguous side channel; valid ranges are + cols 2–5000, rows 2–100000, anything else is silently ignored). The + controller forwards binary frames (previously only text was forwarded — + and a binary frame would have crashed the old `receive_text` loop), and + the compute side turns them into a telnet NAWS subnegotiation for + telnet-based consoles (docker_exec included) or an asyncssh + `change_terminal_size` for SSH consoles + (`base_node.py` `start_websocket_console`). +3. **Races.** A size frame that arrives before/during the exec creation is + remembered and applied right after creation — it is **not** overwritten by + the tall default. When the **last** client disconnects the exec goes back + to 511×10000, so a later non-NAWS client attaching to the still-live exec + doesn't inherit a browser geometry and hit PTY-window paging. +4. **`GNS3_CONSOLE_RESIZE=0`** makes the console ignore client resizes + entirely (the tall default is then permanent). Set it for paging CLIs + where a browser resize would break concurrent netmiko sessions on the + shared exec — XRd, which is line-oriented and doesn't need browser + resizing at all. + +**Why the browser must send its size — the SR Linux flicker.** `sr_cli` is a +prompt_toolkit TUI that anchors its layout with cursor-position requests +(CPR), which xterm.js answers. On a 10000-row PTY canvas the CPR-anchored +model conflicts with the winsize model, and every incremental render re-emits +the accumulated output: measured with a CPR-answering client, one `info` +command produces **~145 KB instead of ~60 KB** (~7× duplicated lines either +way — the CLI re-renders its output region as a scroll-append stream; that +part is inherent to `sr_cli` and identical outside GNS3, verified via manual +`docker exec`). The inflation is driven by **rows** (24/32 → normal, 10000 → +pathological, at any width) and is invisible without CPR answers — which is +why plain-telnet probes and real xterm.js sessions behaved so differently. +In the Web UI the excess renders as frequent full-screen clear/redraw — the +"flicker". With the browser's real size forwarded (rows ≈ 30), output volume +and rendering return to normal. + +**File**: `gns3server/compute/base_node.py` — the console WebSocket guard now +allows `docker_exec` (alongside `telnet`/`ssh`), since the WS bridge connects to +the console TCP port exactly as it does for telnet. The same WS handler also +intercepts binary control frames and propagates client terminal sizes (see +[Terminal geometry](#terminal-geometry-and-size-forwarding)). + +### Why earlier approaches failed (context) + +- `script` + `docker exec -it`: the `script` pty had size 0 (no NAWS) → the TUI + could not lay out → blank. +- `docker exec -i` (no `-t`) + `sr_cli -d` (dumb mode): line-mode output was + block-buffered and visually messy. +- Direct pipe relay: telnet `CRLF` polluted line input. + +The exec-API approach fixes all of these: a real pty (`Tty:true`), a real size +(NAWS resize), and a real terminal emulator (xterm.js answering CPR). + +## Configuration + +### SR Linux node example + +```json +{ + "name": "srlinux-1", + "node_type": "docker", + "image": "ghcr.io/nokia/srlinux:latest", + "adapters": 4, + "console_type": "docker_exec", + "start_command": "sudo -E bash -c 'touch /.dockerenv && /opt/srlinux/bin/sr_linux'", + "environment": "GNS3_SKIP_INIT=1\nGNS3_INTERFACE_NAMES=mgmt0,e1-1,e1-2,e1-3\nGNS3_CONSOLE_CMD=/opt/srlinux/bin/sr_cli" +} +``` + +- `start_command` is the SR Linux launch line (as used by containerlab). +- Connect the node's ports as usual — links still use GNS3's UDP NIO datapath + (container-agnostic); the rename only affects the in-container interface name. +- For the Web UI port **labels** to match (display `mgmt0`/`e1-1` instead of + `Ethernet0..3`), set `custom_adapters` per port + (`{"adapter_number": 0, "port_name": "mgmt0"}`, …). Port labels are a + controller-side concept, independent of the compute-side interface rename. + +### Appliance (`gns3a`) packaging + +A SR Linux appliance lives in `gns3-registry/appliances/srlinux.gns3a` +(`registry_version: 6`). It sets the full chassis — **35 adapters** +(`mgmt0` + `e1-1`..`e1-34`) — with matching `GNS3_INTERFACE_NAMES` and 35 +`custom_adapters` entries (`mgmt0`, `e1-1`..`e1-34`) so the canvas labels, +the kernel interface names and the `ethernet-1/N` CLI names all line up. + +Three appliance-schema fixes are required for this appliance to load (all on +the gns3-server side; the registry JSON schema is unchanged because its docker +block allows `additionalProperties`): + +1. **`DockerConsoleType`** (`schemas/controller/appliances.py`) must include + `docker_exec`, or the Pydantic appliance model rejects the file at import. +2. **`ApplianceV1_6.custom_adapters`** must be declared on the top-level + appliance model, or `GET /appliances` (response_model=`schemas.Appliance`) + strips `custom_adapters` from the API response even though the file and the + server-side template conversion handle it. (Node creation still worked + because `appliance_to_template._add_docker_config` reads it from the raw + dict; only the GET response was lossy.) +3. `extra_volumes` rides inside the `docker` block (passed through by + `new_config.update(appliance_config["docker"])`); no schema change needed. + +> **Symbol theme caveat.** An appliance `symbol` that starts with +> `:/symbols/` is forcibly rewritten at load time +> (`appliance_manager._load_appliances`) to the current theme's default for the +> appliance category — so `:/symbols/affinity/circle/blue/router_cloud.svg` (or +> `router2.svg`) becomes `:/symbols/affinity/circle/blue/router.svg`, because +> the theme maps only the canonical name `"router"`. This is intentional: it +> lets theme switching re-skin every node consistently. To use a non-default +> icon (e.g. `router_cloud`), install it as a **custom symbol** under the +> configured `symbols_path` and reference it by filename (no `:/symbols/` +> prefix) — custom symbols do not participate in re-theming. The SR Linux +> appliance uses `router.svg`. + +### Persistent state + +For SR Linux, persist `/etc/opt/srlinux` (config / AAA users / TLS certs) and +`/var/log/srlinux` (logs, optional) by adding them to the node's +`extra_volumes`. The image also declares its own `VOLUME` directories +(e.g. `/opt/srlinux/appmgr`), which GNS3 persists automatically. + +## Volume persistence with `GNS3_SKIP_INIT` + +This is the one place where skipping init.sh changes behaviour beyond boot: +`/gns3/init.sh` normally performs the volume-persistence bridge, and without it +**nothing writes through to the host** — the container writes to its overlay +filesystem and the data is lost on stop. + +init.sh (as the entrypoint) is safe because it runs **before** the +application: for each volume it seeds the host directory with the image's +original files on first start, then `mount --bind /gns3volumes ` +bridges persistent storage into place. + +`VendorDockerVM` cannot use that position (the NOS must own its entrypoint), +so the same persistence is established entirely **outside the container and +before it exists**: + +``` +create() 之前: host dir seeded from the image (docker create + docker cp, first time only) +create() 时: host ──Docker bind mount──▶ /etc/opt/srlinux (direct, at the real path) +启动: NOS native entrypoint — the persisted config is visible from the first process +``` + +1. **`_prepare_volumes()`** — host-side, at `create()` time (after the image + is present, before the container is created). For each persistent volume + whose host directory lacks the `.gns3_perms` marker, a throwaway + `docker create` container (nothing executes) is used as a `docker cp -a` + source to seed the host directory with the image's original content. The + marker is written after the copy attempt — a volume that has it (every + node that ever started, on any GNS3 version) is **never re-seeded**, so + saved configuration is never overwritten with factory content. + +2. **`_mount_binds()` override** — the volume binds target the **real + in-container paths** (`/etc/opt/srlinux`) instead of `/gns3volumes`. + With the content seeded first, the image's files are never shadowed by an + empty mount, and the NOS sees its persisted configuration from the very + first process — no post-start mount pass that could race the NOS reading + its startup config (see "History: the exec-bridge race" below). + +3. **Container-side `_fix_permissions()` override** — runs the same busybox + record/chmod/chown script **inside the container (as root) on the volume + paths**. Because the volumes are Docker bind mounts created with the + container, the in-container paths resolve to the host files for the whole + container lifetime. A stopped/exited container is **not** restarted (the + base class would, just to chown; vendor NOS images are heavy to boot): + the pass is skipped and the next start fixes ownership. It runs at start + (so the controller can read project files while the node runs) and at + stop (for files written during runtime). + +> The fix must run container-side: files written by the container are +> host-side root-owned, and an unprivileged GNS3 process cannot chown them +> from the host. Container-side root (with GNS3's `UsernsMode: host`) can. + +With `GNS3_SKIP_INIT`, GNS3's hardcoded `/etc/network` volume (see +`docker_vm.py` `_mount_binds()`) is dropped entirely by +`VendorDockerVM._mount_binds()`: it holds GNS3's own network config for +init.sh's `ifup`, which never runs for SKIP_INIT containers — the NOS +manages its own interfaces. The override removes the bind, filters the +volume out of `self._volumes`, and deletes the host-side skeleton directory +the base class just created. Without `GNS3_SKIP_INIT` the mount is kept +(behaviour matches the base class). + +### Lifecycle summary + +| Phase | Normal Docker node | `VendorDockerVM` + `GNS3_SKIP_INIT` | +|-------|--------------------|--------------------------------------| +| create | — | `_prepare_volumes()` seeds host dirs from the image (first create only); volumes bound **directly** at their real paths | +| start | init.sh seeds + bind-mounts + restores perms (in-container, before the app starts) | container-side `_fix_permissions` on the volume paths (skips dead containers, no restart) | +| stop | container-side `_fix_permissions` on in-container paths (restarts an exited container) | container-side `_fix_permissions` on the volume paths (skips dead containers, no restart) | +| volume config | `_mount_binds`: host → `/gns3volumes` | `_mount_binds` override: host → `` directly | + +### Runtime ownership safety + +The start-time fix pass chowns the volume files to the host user **while the +container is running** — a deliberate deviation from the standard model, where +init.sh restores container-native ownership at start and the container never +sees host-owned files during runtime. Verified harmless for SR Linux: + +1. **Most processes run as root** (`sr_linux`, appmgr) — root ignores file + ownership entirely. +2. **Self-healing daemons.** SR Linux's `aaamgr` rewrites its managed files + with its own ownership at boot: after the start-time pass chowned + `etc/opt/srlinux/aaamgr_local_user.json` to the host user, the daemon + re-created it as `srlinux:srlinux` (uid 1002, mode 700) within seconds. +3. **ACL-based access.** The directory carries a default ACL + (`default:group:srlinux:rwx`, `default:other::rwx`), so named group ACL + entries grant access independently of the owner uid; the observed file ACL + (`group:srlinux:rwx`, owner `srlinux`) survives chown. + +Caveat: a NOS that strictly validates ownership of its files (e.g. "SSH keys +must be root:root 600 or refuse to start") would not tolerate this. If that +ever matters, drop the start-time pass and keep only the stop-time one +(standard behaviour — the trade-off is mid-run `Permission denied` in the +file browser, identical to regular Docker nodes). + +### History: the exec-bridge race (fixed) + +The first SKIP_INIT implementation replicated init.sh's script **via +`docker exec` after the container started** instead of binding directly at +create time. That copied the mechanism but not the invariant that makes +init.sh safe — the entrypoint position, which guarantees the volume is in +place *before* the application runs. An exec-based bind runs **concurrently** +with the NOS boot, so whether the NOS reads its persisted config or the +overlay's factory copy was a timing race: + +- a single node stop/start on an idle system won it (the exec landed ~1 s + in, SR Linux reads its startup config at ~2–4 s) — which is why the + round-trip "save → stop → start → config still there" passed; +- a server restart + project reload lost it (all nodes start concurrently, + the Docker API queue delays the execs by several seconds) — SR Linux + booted factory while the persisted `config.json` sat intact on the host; +- XRd was immune either way (systemd boots for tens of seconds before any + XR process touches `/xr-storage`), which is why the race was never seen + on it. + +The direct-bind-at-create design removes the window entirely; there is no +ordering requirement left to verify when adopting a new NOS image. + +## Troubleshooting + +**1. Console shows only boot logs, no CLI** +- You are on the primary attach console. Set `console_type: "docker_exec"` and + use `GNS3_CONSOLE_CMD` to point at the vendor CLI. + +**2. `User '...' is not authorized to use CLI`** +- The exec must run as root. The implementation sets `User: "root"`; if you + fork it, keep that. + +**3. `Terminal doesn't support cursor position requests (CPR)`** +- This means the exec was started without an xterm.js client connected (the + startup probe had no one to answer). The lazy-start design avoids this; if you + see it, ensure the exec is created on first connect, not at node start. + +**4. Reconnecting the Web console shows a blank screen** +- A `Ctrl-L` is sent on each connect to force a TUI redraw. If the TUI does not + redraw, verify the `client_connected_hook` still writes `\x0c` to the pty. + +**5. `aiohttp WSServerHandshakeError: 400` on exec start** +- Do **not** use the websocket client to start an exec. Use the hijacked raw + HTTP upgrade over the unix socket (see Implementation). + +**6. SR Linux data interfaces stay down** +- SR Linux defaults its data ports to `admin-state disable`; enable them in the + CLI (`interface ethernet-1/1 admin-state enable`) and bind the interface to a + network-instance before ping works. This is SR Linux behaviour, not a GNS3 + issue. + +**7. "Session has been idle, will logout in 300 seconds" → Connection closed** +- SR Linux's own CLI idle timeout logs the CLI out, the exec pty closes, and + the console disconnects. Reopening the console recreates the exec (see + *Reconnection*) and gives a fresh login. To keep a permanent session, + disable the timeout in the CLI: `enter candidate` → + `/system cli idle-timeout disable` → `commit now`. + +**8. Controller logs `Permission denied` reading files under the node's + project directory while the node runs** +- Root-written files inside a persistent volume. The container-side + `_fix_permissions` pass runs at start (fixes the seeded files) and at stop; + files created by the container *during* runtime become readable after the + next stop. +- Concrete example: SR Linux's `aaamgr` daemon rewrites + `etc/opt/srlinux/aaamgr_local_user.json` during boot, **after** the + start-time pass, as the image's `srlinux` user (uid 1002, mode 700) — so + the host-side file stays `1002:1002` until the stop-time pass chowns it. +- The log line comes from the file-browser API chain: Web UI *Show in file + manager* → `GET /v3/projects/{pid}/nodes/{nid}/files` + (`controller/nodes.py:538`) → `project.list_node_files` + (`compute/project.py:510`), where `magic.from_file()` cannot read the + file and the `file_type` field is left empty for that entry. The MCP + `list_node_files` tool uses the same code path. Size/modified-at fields + and everything else keep working; only the type sniff and one warning + line are affected — same behaviour as any regular Docker node writing + root-owned files at runtime. + +**9. Persistent volume empty on the host after `save` + stop** +- Ensure `GNS3_SKIP_INIT=1` is set (so the direct-bind path is taken) and + the volume path is in `extra_volumes`; check that the host directory + carries the `.gns3_perms` marker (written at create-time seeding) and the + compute log for `Seeded persistent volume`. + +**10. Persisted config present on the host but not applied after restart** +- On builds since the direct-bind rework this should not happen: the volume + is in place before the first process. If you see it, confirm the server + build includes the rework (older builds established the bind via a + post-start `docker exec` that could lose the race against the NOS reading + its startup config — see *History: the exec-bridge race*). + +**11. Web console flickers (full-screen clear/redraw) on every command** +- The PTY is stuck at the tall 511×10000 default while a CPR-answering client + is attached — see + [Terminal geometry](#terminal-geometry-and-size-forwarding). Check that the + Web UI actually sends the binary size control frames on connect/resize + (F12 → the console WS should show outgoing binary frames), and that the + server is new enough to forward them (the controller used to forward text + frames only). A client that never negotiates/forwards size (old Web UI, + bare telnet without NAWS) cannot trigger the fix — but also never answers + CPR, so it doesn't flicker either. +- The much milder per-keystroke/5 s cursor toggles (`\e[?25l…\e[?25h`) from + the TUI are normal and not this bug. + +## Limitations + +1. **Shared session (broadcast).** All console clients share one CLI session + and can see each other's input — identical to GNS3's existing primary + console model. There is no per-client independent session. The PTY + geometry is shared too (last resize wins): two browsers of different sizes + disagree harmlessly, but a browser on a *paging* CLI needs + `GNS3_CONSOLE_RESIZE=0` to stop resizing on behalf of concurrent netmiko + sessions (see [Terminal geometry](#terminal-geometry-and-size-forwarding)). +2. **`reset_console` not wired.** The console-reset action only handles + `telnet`/`ssh`; it is a no-op for `docker_exec` (non-blocking; reconnect + works fine). +3. **Prototype knobs.** `GNS3_SKIP_INIT` / `GNS3_INTERFACE_NAMES` / + `GNS3_CONSOLE_CMD` / `GNS3_CONSOLE_RESIZE` are environment-driven; they are + not yet first-class node schema fields and are not declared in the + appliance (`gns3a`) schema. +4. **Rootful-Docker assumption** (`UsernsMode: host`, set for all GNS3 + Docker nodes) so the container-side chown acts on the host files' real + uid/gid (see the volume-persistence section). +5. **Docker CLI dependency.** Volume seeding shells out to the `docker` + binary (`docker create` + `docker cp` + `docker rm`) at create time — + the same dependency the permission passes already have. + +## References + +- `gns3server/compute/docker/vendor_docker_vm.py` — `VendorDockerVM`: + `_start_docker_exec_console`, `_LazyExecTelnetServer`, + `_prepare_volumes` (host-side seeding), direct volume binds in + `_mount_binds`, container-side `_fix_permissions`, `start()`. +- `gns3server/compute/docker/docker_vm.py` — `DockerVM` extension hooks + (`_prepare_init_and_interface_env`, `_start_console_server`, + `_get_container_ifname`, `_cleanup_console_resources`). +- `gns3server/compute/docker/__init__.py` — `Docker._select_node_class` / + `create_node` factory. +- `gns3server/compute/base_node.py` — console WebSocket guard; binary + terminal-size control frames → NAWS / asyncssh resize + (`start_websocket_console`). +- `gns3server/api/routes/controller/nodes.py` — console WS forwarding + (text and binary frames). +- `gns3server/schemas/common.py` — `ConsoleType.docker_exec`. +- containerlab `nodes/srl/srl.go` — reference for SR Linux launch command and + interface naming. + +## Version History + +| Version | Date | Changes | +|---------|------|---------| +| 1.7 | 2026-08-22 | SKIP_INIT volume persistence rebuilt: host-side seeding at create time (`docker create` + `docker cp -a`, marker-gated so saved config is never overwritten) and direct bind mounts at the real in-container paths replace the post-start `docker exec` bridge. Root cause: the exec bridge raced the NOS reading its startup config — SR Linux read `config.json` at ~2–4 s and booted factory whenever concurrent node starts (server restart + project reload) delayed the exec past that point, while single-node stop/start and XRd (systemd touches `/xr-storage` tens of seconds in) never lost the race. New `_prepare_volumes` hook on `DockerVM`; `_fix_permissions` now targets the volume paths directly. | +| 1.6 | 2026-08-20 | Terminal geometry and size forwarding: WS binary control frames `{"cols","rows"}` → NAWS / asyncssh resize (controller now forwards binary frames; compute intercepts them); tall 511×10000 default kept for non-NAWS clients, applied post-creation and restored on last disconnect (client size racing exec creation wins over the default); new `GNS3_CONSOLE_RESIZE=0` knob for paging CLIs (XRd) where a browser resize would break concurrent netmiko sessions on the shared exec; documented the SR Linux flicker root cause (tall rows × CPR-answering client → ~2.4× re-emitted output; rows-driven, width-independent). | +| 1.5 | 2026-08-13 | Add appliance (`gns3a`) packaging section: 35-adapter full-chassis design, the three server-side schema fixes (DockerConsoleType, ApplianceV1_6.custom_adapters, extra_volumes passthrough), and the symbol-theme caveat (any `:/symbols/` symbol is rewritten to the category default at load). | +| 1.4 | 2026-08-13 | Reconnect fix: drop the while-true wrapper (it restarted the CLI with no client to answer CPR → blank screen on reconnect); the exec is now recreated on connect when the upstream has died. `_LazyExecTelnetServer` extracted to module level and unit-tested. | +| 1.3 | 2026-08-12 | Document runtime ownership safety (root processes, self-healing daemons, ACL evidence for SR Linux), the boot-ordering caveat (bridge after boot → verify save/stop/start closed loop), and troubleshooting #10. | +| 1.2 | 2026-08-12 | `_fix_permissions` rewritten: container-side (as root) on the `/gns3volumes` bind-mount targets instead of host-side — host-side chown cannot touch root-owned files when GNS3 is unprivileged. Dead containers are skipped instead of restarted. | +| 1.1 | 2026-08-12 | Refactor: vendor logic extracted from `DockerVM` into `VendorDockerVM` subclass with 4 hook points + class-selection factory. Add SKIP_INIT volume persistence (`_setup_skip_init_volumes` + `_fix_permissions`) and lifecycle comparison. Add troubleshooting entries for idle timeout and permission-denied files. | +| 1.0 | 2026-08-12 | Initial documentation of the `docker_exec` console and vendor NOS knobs. | diff --git a/docs/features/marker-traffic-insight.md b/docs/features/marker-traffic-insight.md new file mode 100644 index 000000000..ab71cbf65 --- /dev/null +++ b/docs/features/marker-traffic-insight.md @@ -0,0 +1,380 @@ + + +> This documentation is organized by AI with reference to actual code. AI can make mistakes — please verify against the source code when in doubt. + +# Marker (Traffic Insight) + +## Overview + +A **marker** is a passive traffic-insight tap attached to a link. It runs a libpcap BPF +expression inside uBridge; on every match uBridge emits a real-time `MARK` signal and +appends the matching packet to a per-marker pcap file. Markers exist at two layers that +coexist on the same link: **per-link private markers** and **project-level definitions** +that are inherited by every capable link. + +## Architecture + +```mermaid +graph TB + UI["Web UI"] + + subgraph Controller["Controller"] + DEF["Project definitions
(inheritance templates)"] + LNK["Per-link markers"] + end + + Compute["Compute Node"] + UB["uBridge
mark filter"] + PCAP[("pcap file")] + LSTN["Marker listener
(UDP, per compute)"] + + UI -->|"REST + notifications ws"| Controller + DEF -.->|"fan-out: global-{name}"| LNK + LNK -->|"node.post /markers"| Compute + Compute --> UB + UB -->|"BPF match"| PCAP + UB -->|"UDP MARK signal"| LSTN + LSTN -->|"marker.match"| UI +``` + +Inheritance is a controller-only fan-out: a definition CRUD loops over links and reuses the +existing per-link marker operations, so the compute side sees an ordinary marker and is +unchanged. Each compute process runs one UDP listener serving every uBridge on that host; the +`node` and `link` fields in each signal together identify the source link (see +[Per-link attribution](#per-link-attribution)). + +## Business Process + +```mermaid +sequenceDiagram + participant UI as Web UI + participant C as Controller + participant L as Capable Link + participant N as Compute / uBridge + + UI->>C: POST /marker-definitions {name, bpf, ...} + C->>C: store definition + loop every capable link + C->>L: start_marker("global-{name}") + L->>N: install mark filter (BPF + pcap) + end + C-->>UI: 201 + link_ids + + Note over N: later: a packet matches the BPF + N->>N: emit MARK signal + append pcap + N-->>UI: marker.match notification (per-project ws) +``` + +Updating a definition syncs `bpf / tag / color / highlight_duration` to every inherited +copy; deleting a definition removes every inherited copy. A newly created link inherits all +existing definitions automatically. + +## Per-link attribution + +A uBridge `MARK` signal carries `node`, `filter`, `link`, `tag`, and `len` — but no bridge +name. When one node is the capture side for several links — the common case for a project-level +`global-{name}` marker on a multi-interface router — `node` + `filter` alone are identical +across those links, so they cannot tell the signals (or pcap files) apart. The `link` field +resolves this: + +1. At install time the controller stamps each filter with its link id + (`mark [tag ] link [pcap ]`). +2. uBridge treats `link` as opaque and echoes it verbatim in the signal (`link=`). +3. The listener takes the signal's `link=` as the **authoritative** `link_id` of the + `marker.match` event, falling back to its registry only for legacy signals that carry no + `link=`. + +This is also why the pcap path is keyed on link — +`/markers/__.pcap`, not on `bridge`+`filter`: a single +uBridge bridge can serve several links, and only the link id keeps their captures distinct. + +### IOU: one bridge, many interfaces + +IOU runs a single `IOL-BRIDGE` per node shared by every interface, so `bridge`+`filter` are +identical across that node's links. uBridge keeps a separate filter list **per port +(bay/unit)** within the bridge, so each interface gets its own `global-{name}` filter, its own +pcap file, and its own `link=`. The shared bridge name is irrelevant to attribution. Other +capable node types (`qemu`, `docker`, `vpcs`, `cloud`, `ethernet_switch`) already use one +bridge per link (the switch's per-port relay); `link` applies uniformly to all of them. + +## Direction + +A `MARK` signal optionally carries `dir=` — the matched packet's travel direction +**relative to the capture node** (the `node=` in the same signal, i.e. the node whose +uBridge hosts the marker): + +| `dir` | Ingress NIO | Meaning | +|-------|-------------|---------| +| `tx` | device side (`source_nio` on a generic bridge; the IOL instance on an IOU `IOL-BRIDGE`) | capture node is **sending** | +| `rx` | link side (`destination_nio` on a generic bridge; the NIO side on an IOU `IOL-BRIDGE`) | capture node is **receiving** | + +A marker is single-sided: only the chosen capture node's uBridge installs the `mark` filter, +yet both directions of the link transit that one bridge (it carries exactly two NIOs — the +device side and the link side), so that single uBridge observes and classifies both +directions. The `marker.match` event forwards `dir` through unchanged; the Web UI combines it +with the link's two endpoints and the capture `node_id` to draw an arrow: + +- `dir=tx` → `capture_node → far_node` +- `dir=rx` → `far_node → capture_node` +- `dir` absent (older uBridge) → undirected highlight (current behaviour) + +Because the listener ignores unknown keys, `dir` is **additive**: an older server silently +drops it and an older uBridge simply omits it — either way the system falls back to +undirected rendering with no error. + +### Choosing the capture node + +Since `dir` is relative to the capture node, *which* endpoint is the observer decides what +`tx`/`rx` mean. By default the server auto-picks (first started marker-capable endpoint, in +link-endpoint order). To pin it — e.g. so `dir=tx` unambiguously means "vpcs1 is sending" — +pass `capture_node_id` on marker **create**: + +```json +{ "bpf": "icmp", "direction": "tx", "capture_node_id": "" } +``` + +The value must be one of the link's two endpoints and a marker-capable type (`vpcs`, `qemu`, +`docker`, `iou`, `dynamips`, `cloud`, `ethernet_switch`); any other id is rejected with `409`. Omit it to keep +the auto-pick. The chosen id is echoed back as `capture_node_id` in the marker entry and in +each `MARK` signal's `node=`, so the Web UI always knows the observer regardless of who +picked it. + +`capture_node_id` is **create-only**: it is fixed once the marker exists (changing the +observer would silently flip the meaning of stored `direction`, so recreate the marker +instead). It is not accepted on project-level definitions — a definition is link-agnostic and +has no endpoints to choose from, so inherited markers always auto-pick per link. + +For the same reason, a definition **rejects `direction: tx|rx`** (HTTP 409): each inherited +copy auto-picks its capture node, so a fixed tx/rx would denote different session directions +on different links. A definition is `both` only; encode the direction you want in the BPF +instead — e.g. `icmp and icmp[icmptype]==8` for echo requests, a packet-intrinsic property +that is consistent on every link regardless of capture node. tx/rx remains available on +per-link markers, where the capture node is fixed. + +## Pause & resume + +Two levels of silencing, both instant (no NIO rebuild, no pcap flush): + +- **Per-marker (private)** — `PUT /v3/projects/{pid}/links/{lid}/markers/{name}` + with `{"enabled": false}` flips that one filter off in place (uBridge + `enable_packet_filter … off`): no signal, no pcap, but traffic still relays — + a paused `mark` is a no-op tap, not a drop. `{"enabled": true}` flips it back. + A change to `enabled` alone is a single command (the pcap identity and emitted + counter are preserved). Changing `bpf`, `tag`, or `direction` rebuilds just that + one filter (`delete_packet_filter` + add) — only that marker's own pcap reopens + (a new capture session for the new BPF); changing `color`/`highlight_duration` + is UI-only, nothing is pushed to uBridge. +- **Per-definition (inherited)** — `POST /v3/projects/{pid}/marker-definitions/{name}/pause` + and `/resume` toggle **every** inherited `global-{name}` copy across all links + at once (same `enable_packet_filter on|off`, fanned out per copy). Use to + pause or resume a whole rule independently of the others. The definition's + `paused` flag is persisted to the `.gns3` and echoed on the definition object, + so links created later inherit it already paused, and the Web UI renders the + per-rule button from server truth. + +| Action | signal | pcap | sink | +|--------|--------|------|------| +| per-marker `enabled: false` | stop | stop | n/a | +| per-def `pause` (all `global-{name}` copies) | stop | stop | n/a | +| per-def `resume` | resume | resume | n/a | + +## Capture files + +Each marker appends matches to `/project-files/markers/__.pcap`. +Removing a marker — per-link `DELETE .../markers/{name}` or deleting a definition (which +removes every inherited copy) — deletes that marker's pcap too, even with the capture node +stopped (the filter is removed with `delete_packet_filter`, the file is unlinked). uBridge's +`reset_packet_filters` (run on NIO/filter changes) preserves mark filters, so unrelated +changes no longer close/reopen any marker's pcap. + +## API Endpoints + +All endpoints require a JWT bearer token (`POST /v3/access/users/authenticate`). The +`Auth` column lists the required privilege. + +### Per-link markers + +| Method | Path | Description | Auth | +|--------|------|-------------|------| +| GET | `/v3/projects/{pid}/links/{lid}/markers` | List markers on a link | Link.Audit | +| POST | `/v3/projects/{pid}/links/{lid}/markers` | Attach a marker | Link.Modify | +| PUT | `/v3/projects/{pid}/links/{lid}/markers/{name}` | Update a marker | Link.Modify | +| DELETE | `/v3/projects/{pid}/links/{lid}/markers/{name}` | Remove a marker | Link.Modify | + +### Project-level definitions + +| Method | Path | Description | Auth | +|--------|------|-------------|------| +| GET | `/v3/projects/{pid}/marker-definitions` | List definitions + bound `link_ids` | Project.Audit | +| POST | `/v3/projects/{pid}/marker-definitions` | Create definition (fans out to every link) | Project.Modify | +| PUT | `/v3/projects/{pid}/marker-definitions/{name}` | Update definition (syncs all copies) | Project.Modify | +| DELETE | `/v3/projects/{pid}/marker-definitions/{name}` | Delete definition (clears all copies) | Project.Modify | +| POST | `/v3/projects/{pid}/marker-definitions/{name}/pause` | Pause every inherited copy (instant, persisted) | Project.Modify | +| POST | `/v3/projects/{pid}/marker-definitions/{name}/resume` | Resume every inherited copy | Project.Modify | + +### Aggregation + +| Method | Path | Description | Auth | +|--------|------|-------------|------| +| GET | `/v3/projects/{pid}/markers` | All markers across links, flat | Project.Audit | + +The link object returned by `GET /v3/projects/{pid}/links[/{lid}]` also carries a `markers` +field (including inherited markers), so the Web UI can render a link's markers without an +extra request. + +## Request / Response + +**Marker create body** (`MarkerCreate`, shared by per-link POST and PUT): + +```json +{ + "name": "icmp", + "bpf": "icmp", + "tag": 1, + "direction": "tx", + "capture_node_id": "a37e2235-e21f-46c9-a2ab-ba0f8c5465e6", + "color": "#ff5722", + "highlight_duration": 800, + "enabled": true +} +``` + +`direction` and `capture_node_id` are both optional and create-only (see +[Direction](#direction)). + +**Definition create body** (`MarkerDefinitionCreate`, shared by POST and PUT): + +```json +{ + "name": "arp", + "bpf": "arp", + "tag": 5, + "color": "#ff5722", + "highlight_duration": 1200 +} +``` + +**Marker entry** (returned by GET/POST/PUT, and the value of each link's `markers[name]`): + +```json +{ + "bpf": "icmp", + "tag": 1, + "enabled": true, + "color": "#ff5722", + "highlight_duration": 800, + "capture_node_id": "a37e2235-e21f-46c9-a2ab-ba0f8c5465e6", + "inherited_from": null +} +``` + +**Definition GET response** (adds `link_ids`): + +```json +{ + "arp": { + "bpf": "arp", + "tag": 5, + "color": null, + "highlight_duration": 1200, + "direction": null, + "paused": false, + "link_ids": ["656ed826-...", "6bd9d156-..."] + } +} +``` + +## Field Reference + +### Marker entry + +| Field | Type | Description | +|-------|------|-------------| +| `bpf` | string | libpcap BPF expression (required) | +| `tag` | int \| null | Correlation id echoed in `MARK` signals | +| `enabled` | bool | Whether the marker is active. Toggle is instant: `false` flips the uBridge filter off in place (no signal/pcap), `true` back on — no NIO rebuild (see [Pause & resume](#pause--resume)) | +| `color` | string \| null | Hex color render hint, e.g. `#ff5722` | +| `highlight_duration` | int \| null | UI highlight duration in ms after a match; `null` = UI default | +| `direction` | string \| null | `tx` / `rx` filter relative to the capture node; `null` = both | +| `capture_node_id` | string | Node whose uBridge hosts the marker — caller-set on create, else auto-picked | +| `inherited_from` | string | Source definition name — present on inherited markers only | + +### Definition + +| Field | Type | Description | +|-------|------|-------------| +| `bpf` | string | libpcap BPF expression (required) | +| `tag` | int \| null | Correlation id | +| `color` | string \| null | Hex color render hint | +| `highlight_duration` | int \| null | UI highlight duration in ms; `null` = UI default | +| `direction` | string \| null | `tx` / `rx` filter relative to the capture node; `null` = both | +| `paused` | bool | Per-definition mute flag — `true` mutes every inherited copy (persisted) | +| `link_ids` | string[] | Links currently carrying an inherited copy (GET only) | + +### Notifications + +| Event | Payload | Delivered to | +|-------|---------|--------------| +| `link.updated` | Link object (its `markers` field is the source of truth) | Project notification ws | +| `marker.match` | `project_id`, `node_id`, `link_id`, `filter`, `tag`, `ts`, `len`, `dir` | Project notification ws only | + +The `marker.match` `link_id` is taken from the signal's `link=` field (authoritative); see +[Per-link attribution](#per-link-attribution). The `dir` field is the matched packet's travel +direction relative to the capture node; see [Direction](#direction). + +## Error Responses + +| Status | Description | +|--------|-------------| +| 401 | Not authenticated | +| 404 | Link / marker / definition not found | +| 409 | Per-link edit or delete of an inherited marker; reserved (`global`) name or duplicate name on create | +| 422 | Validation failure (name format, `highlight_duration < 1`, missing `bpf`) | + +## Notes + +- **Marker name is immutable.** It is the identifier across the controller, the uBridge + filter, the pcap filename, and `MARK` signal routing — so rename is a delete + recreate, + not a field update. PUT ignores the body `name`; the `{name}` path parameter identifies + the target, and only `bpf / tag / color / enabled / highlight_duration` are changeable. + Names are 1–32 chars (`[A-Za-z0-9][A-Za-z0-9_.-]*`); inherited copies carry a `global-` + prefix, so their filter names reach ~39. +- **`global` prefix reserved.** User-chosen names may not start with `global`; inherited + markers are stored as `global-{definition_name}` so the two namespaces cannot collide. + Omitting `name` on create yields an auto-generated, prefix-free name. +- **Inherited markers are read-only per-link.** PUT/DELETE on an inherited marker returns + 409 — edit them through the definitions API. +- **Render hints are not enforced.** `color` and `highlight_duration` (milliseconds, `>= 1`) + are stored on the link and never sent to uBridge; `null` lets the UI apply its own + default. A partial PUT (e.g. changing only `bpf`) leaves them untouched. +- **BPF is validated once per source.** A private per-link marker validates its BPF inline + on create/update. A definition validates its BPF once at create/update (and once per + definition on project load, dropping any whose BPF has gone invalid); the inherited + fan-out to every link then skips re-validation, so creating a definition over *N* links + runs one `tcpdump -d` rather than *N*. (uBridge still runs `pcap_compile` itself at + install time, so an invalid expression can never slip through.) +- **Supported node types.** A marker needs a uBridge bridge: `vpcs`, `qemu`, `docker`, + `iou`, `dynamips`, `cloud`, `ethernet_switch` (one capable endpoint suffices). The + `ethernet_switch` hosts markers on its per-port uBridge relays (brctl backend); the + `ethernet_hub` is still Dynamips-hosted and has no uBridge. Types without a uBridge are + silently skipped by the inheritance fan-out. IOU uses one shared `IOL-BRIDGE` per node but + keeps filters, pcap files, and `link=` ids per port, so multi-interface nodes are handled + (see [Per-link attribution](#per-link-attribution)). +- **Shared capture-side node.** When one node hosts markers for several links (typical for + `global-*` definitions on a router), each filter is stamped with its `link_id` so signals + and pcap files stay link-distinct; the controller never collapses them to a single link. +- **Persistence.** Definitions and private markers persist in the topology; inherited + markers are re-created from definitions on project load, so reopening a project restores + the same configuration and stale inherited copies cannot survive on disk. +- **Log interpretation across node types.** Each node type logs its startup and link + operations differently — do not mistake sparse logs from one type for inactivity. + QEMU prints `set_link gns3- on` via its QEMU monitor, which is the most visible + startup log among all types. VPCS, Docker, IOU, Dynamips, and Cloud each have their own + startup paths (fork + ubridge, container veth, iouyap, Dynamips hypervisor, and TAP + device respectively) and none of them emit QEMU-monitor-style logs. To verify marker + operations (toggle, pause, resume) on non-QEMU types, either inspect uBridge's + own log for `enable_packet_filter` / `marker pause` / `marker resume` commands, or + watch the gns3server log for the corresponding compute-route calls at INFO level. diff --git a/docs/features/mcp-service.md b/docs/features/mcp-service.md new file mode 100644 index 000000000..a6d5eb837 --- /dev/null +++ b/docs/features/mcp-service.md @@ -0,0 +1,474 @@ +# MCP (Model Context Protocol) Service + +## Overview + +GNS3 Server provides a standard [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) interface, allowing AI assistants like Claude to interact with GNS3 network simulations through SSE (Server-Sent Events) transport. + +The MCP service exposes GNS3 project management operations as MCP tools that can be discovered and called by MCP clients. + +## Endpoints + +| Path | Method | Description | +|------|--------|-------------| +| `/v3/mcp/` | GET | MCP service metadata | +| `/v3/mcp/transport/sse` | GET | SSE stream (MCP connection) | +| `/v3/mcp/transport/messages/` | POST | JSON-RPC messages | + +## Authentication + +The SSE endpoint supports two types of credentials, passed the same way. + +1. **Authorization header** (recommended): + ``` + Authorization: Bearer + ``` + +2. **Query parameter** (for clients that don't support custom headers): + ``` + GET /v3/mcp/transport/sse?token= + ``` + +### Option 1: JWT Token (24h expiry) + +```bash +curl -X POST http://localhost:3080/v3/access/users/authenticate \ + -H "Content-Type: application/json" \ + -d '{"username": "admin", "password": "admin"}' +``` + +Default lifetime is **1440 minutes (24 hours)**. Configurable in `gns3_server.conf`: +```ini +jwt_access_token_expire_minutes = 1440 ; 24 hours +``` + +### Option 2: API Key (permanent, revocable) — Recommended for MCP + +API keys never expire and can be revoked individually. Format: `gns3__` — the embedded UUID enables O(1) lookup without scanning all keys. + +Create one via the REST API: + +```bash +# Create an API key (requires a JWT to authenticate) +curl -X POST http://localhost:3080/v3/access/api-keys \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{"name": "MCP Production"}' +# Response: {"api_key": "gns3_550e8400-e29b-41d4-a716-446655440000_a1b2c3d4...", ...} +# ⚠️ The key is only shown once — save it immediately. +``` + +API key management endpoints: + +| Endpoint | Description | +|----------|-------------| +| `POST /v3/access/api-keys` | Create a new key (returns plaintext once) | +| `GET /v3/access/api-keys` | List all your keys | +| `POST /v3/access/api-keys/{id}/revoke` | Revoke a key (can be restored) | +| `POST /v3/access/api-keys/{id}/restore` | Restore a revoked key | +| `DELETE /v3/access/api-keys/{id}` | Permanently delete a key | + +Both JWT tokens and API keys work for MCP and REST API endpoints interchangeably. + +### Authentication Flow + +When connecting with an API key: + +``` +SSE connect → Authorization: Bearer gns3__ + ↓ +MCP auth wrapper extracts UUID → single DB query → 1 bcrypt (thread pool) + ↓ +Generates a fresh short-lived JWT → stored in ContextVar for the session + ↓ +All subsequent tool handler REST API calls use this JWT → zero extra bcrypt +``` + +### Concurrency + +| Setting | Value | +|---------|-------| +| MCP batch workers | 100 (`BATCH_MAX_WORKERS`) | +| MCP HTTP client timeout | 30s | +| HTTP connection pool (`pool_connections`/`pool_maxsize`) | 500 / 1000 | +| REST API node/link creation pool | 100 (`Pool(concurrency=100)`) | + +## Available Tools + +**76 tools** across 11 categories: + +### Project (14) + +| Tool | Description | +|------|-------------| +| `project_list` | List all projects | +| `project_get` | Get project details | +| `project_create` | Create a project | +| `project_delete` | Delete a project | +| `project_open` | Open a closed project | +| `project_close` | Close an open project | +| `project_stats` | Get project statistics | +| `project_update` | Update project properties | +| `project_duplicate` | Duplicate a project | +| `project_readme_get` | Get project README content | +| `project_readme_update` | Update project README | +| `project_lock` | Lock project (prevent edits) | +| `project_unlock` | Unlock project | +| `project_locked` | Check if project is locked | + +### Node (22) + +| Tool | Description | +|------|-------------| +| `node_list` | List all nodes (`fields` to filter columns, e.g. `["name","status"]`) | +| `node_get` | Get node details (`fields` to filter columns) | +| `node_create` | Create node(s) — single via `template_id` or batch via `nodes` array. Supports `fields` to filter response. Top-level `template_id` applies as default in batch mode. Pass `name` to override template naming. Coordinates: left-handed Cartesian (origin at canvas center, X right-positive, Y down-positive). | +| `node_delete` | Delete a node | +| `node_update` | Update node properties | +| `node_start` | Start node(s) — `node_id` or `node_ids` array | +| `node_stop` | Stop node(s) — `node_id` or `node_ids` array | +| `node_suspend` | Suspend node(s) — `node_id` or `node_ids` array | +| `node_console` | Get WebSocket console URL | +| `node_file_list` | List files in node directory | +| `node_file_get` | Read a file (with offset/limit) | +| `node_file_write` | Write a file | +| `node_file_delete` | Delete a file | +| `node_start_all` | Start all nodes | +| `node_stop_all` | Stop all nodes | +| `node_suspend_all` | Suspend all nodes | +| `node_duplicate` | Duplicate a node | +| `node_isolate` | Isolate a node (suspend links) | +| `node_unisolate` | Un-isolate a node (resume links) | +| `node_links` | List links connected to a node | + +### Link (9) + +| Tool | Description | +|------|-------------| +| `link_list` | List all links (`fields` to filter columns) | +| `link_get` | Get link details | +| `link_create` | Create link(s) — single via `nodes` or batch via `links` array. Nodes support compact `[id, ad, pt, id, ad, pt]` format. Supports `fields` to filter response. | +| `link_delete` | Delete link(s) — `link_id` or `link_ids` array | +| `link_update` | Update link (suspend, filters) | +| `link_reset` | Reset link(s) — `link_id` or `link_ids` array | +| `link_capture_start` | Start capture(s) — `link_id` or `link_ids` array | +| `link_capture_stop` | Stop capture(s) — `link_id` or `link_ids` array | +| `link_capture_download` | Get PCAP download URL(s) — `link_id` or `link_ids` array | + +### Template (5) + +| Tool | Description | +|------|-------------| +| `template_list` | List all templates. Supports `fields` to filter response columns. | +| `template_get` | Get template details | +| `template_create` | Create a template (Docker needs `image`) | +| `template_update` | Update a template | +| `template_delete` | Delete a template | + +### Compute (3) + +| Tool | Description | +|------|-------------| +| `compute_list` | List registered remote computes | +| `compute_get` | Get compute details (requires UUID) | +| `compute_images` | List emulator images on a compute | + +### Snapshot (4) + +| Tool | Description | +|------|-------------| +| `snapshot_list` | List snapshots | +| `snapshot_create` | Create a snapshot | +| `snapshot_delete` | Delete a snapshot | +| `snapshot_restore` | Restore a snapshot | + +### Drawing (5) + +| Tool | Description | +|------|-------------| +| `drawing_list` | List drawings on canvas | +| `drawing_get` | Get drawing details | +| `drawing_create` | Create drawing (SVG label/shape/image) | +| `drawing_update` | Update drawing (position, rotation, SVG) | +| `drawing_delete` | Delete a drawing | + + + +### Appliance (3) + +| Tool | Description | +|------|-------------| +| `appliance_list` | List appliances (`fields` to filter, e.g. `["name","category"]`) | +| `appliance_get` | Get appliance details | +| `appliance_install` | Create template from appliance (images must exist locally) | + +### Image (5) + +| Tool | Description | +|------|-------------| +| `image_list` | List all images | +| `image_get` | Get image details | +| `image_delete` | Delete an image | +| `image_prune` | Remove images not referenced by any template | +| `image_install` | Auto-create templates from uploaded images by checksum | + +### Server (2) + +| Tool | Description | +|------|-------------| +| `server_version` | Get GNS3 server version | +| `server_statistics` | Get server statistics (computes, projects, nodes) | + +### Device Config (3) + +| Tool | Description | +|------|-------------| +| `device_config_send` | Push config commands to devices via console (Nornir + Netmiko). Supports Jinja2 `template` + `vars` | +| `device_show_run` | Run read-only show commands on devices. Supports Jinja2 `template` + `vars` | +| `vpcs_config_set` | Configure VPCS devices (IP, gateway, etc.) | + +The tool connects to each device's console via telnet/SSH. Nodes must be in the `started` state (use `node_start` or `node_start_all`). Device type is auto-detected from the node's `device_type:` tag in GNS3. + +#### Jinja2 Template Mode + +Both `device_config_send` and `device_show_run` support an optional `template` parameter. When provided, each device's `vars` dict is rendered against the template to produce commands. Entries with the same `device_name` are merged into a single device session. + +```python +# Direct commands (single/batch) +device_config_send(project_id, device_configs=[ + {"device_name": "R1", "config_commands": ["int lo0", "ip add 1.1.1.1 255.255.255.255"]}, +]) + +# Jinja2 template (reduces token usage for batch) +device_config_send(project_id, + template="interface lo{{ n }}\nip address {{ ip }} 255.255.255.255", + device_configs=[ + {"device_name": "R1", "vars": {"n": 0, "ip": "1.1.1.1"}}, + {"device_name": "R2", "vars": {"n": 0, "ip": "2.2.2.2"}}, + ]) + +# Show commands with template +device_show_run(project_id, + template="show ip route {{ protocol }}", + device_configs=[ + {"device_name": "R1", "vars": {"protocol": "ospf"}}, + {"device_name": "R2", "vars": {"protocol": "bgp"}}, + ]) +``` + +### Best Practices + +**Prefer template over direct commands for batch.** When ≥2 nodes share the same config structure with different values, use `template`+`vars` instead of writing `config_commands` per node. This reduces token usage and transcription errors. + +**Batch merging.** Multiple entries with the same `device_name` are merged into a single Nornir session. The output contains all commands' results in one block. Match results by `device_name`, not list index. + +**Don't rely on `status: success` alone.** It only means commands entered config mode. IOS errors (`% Invalid input`, `% overlaps`, `% Incomplete command`) appear inside `output` text — always scan for `%` lines. + +**Pilot before full rollout.** Test template + vars on 1–2 devices first to verify rendering and syntax, then expand to all nodes. + +**Config backup via file operations.** IOU and Dynamips nodes save startup config as a plain text file (`startup-config.cfg`) in the node directory after `write memory`. These can be backed up and restored via `node_file_get`/`node_file_write`. + +```python +# Save config on device +device_show_run(project_id, device_configs=[ + {"device_name": "R1", "commands": ["write memory"]}, +]) +# Backup +config = node_file_get(project_id, node_id, "startup-config.cfg") +# Restore if config breaks +node_file_write(project_id, node_id, "startup-config.cfg", config) +node_stop(project_id, node_id) +node_start(project_id, node_id) +``` + +### Device Config Workflow + +```mermaid +sequenceDiagram + participant AI as AI Agent + participant MCP as MCP Handler + participant TM as Template Renderer + participant DP as Device Discovery + participant NR as Nornir + participant NM as Netmiko + participant D as Device Console + + Note over AI: Decide: template or direct commands? + + alt Direct commands + AI->>MCP: device_config_send(config_commands=[...]) + else Jinja2 template + AI->>MCP: device_config_send(template + vars) + MCP->>TM: Render template per device + TM->>TM: Jinja2.render(**vars) + TM-->>MCP: device_configs with rendered commands + end + + MCP->>DP: get_device_ports_from_topology() + DP-->>MCP: hosts_data (console port, device_type) + + Note over MCP: Prepare Nornir inventory + + MCP->>NR: InitNornir(hosts, threaded runner) + par Device 1 to N (parallel, max 10) + NR->>NM: netmiko_send_config(commands) + NM->>D: telnet/SSH console session + D-->>NM: command output + NM-->>NR: execution result + end + NR-->>MCP: aggregated results + MCP-->>AI: per-device results with output +``` + +## Configuration + +### Claude Code (CLI) + +```bash +# Option A: Using API key (recommended — never expires) +claude mcp add --transport sse My_GNS3_Server \ + http://localhost:3080/v3/mcp/transport/sse \ + -H "Authorization: Bearer gns3_a1b2c3d4..." + +# Option B: Using JWT token (expires after 24h) +TOKEN=$(curl -s -X POST http://localhost:3080/v3/access/users/authenticate \ + -H "Content-Type: application/json" \ + -d '{"username": "admin", "password": "admin"}' | python3 -c \ + "import sys,json; print(json.load(sys.stdin)['access_token'])") + +claude mcp add --transport sse My_GNS3_Server \ + http://localhost:3080/v3/mcp/transport/sse \ + -H "Authorization: Bearer $TOKEN" +``` + +## Transport Security + +MCP server uses FastMCP's DNS rebinding protection to prevent attackers from +exploiting DNS resolution to access the MCP endpoint through unauthorized domains. + +### Default Behaviour + +DNS rebinding protection is **disabled by default**, allowing connections from +any host. This aligns with GNS3 server's default `host = 0.0.0.0` binding policy, +which is designed for VM distribution scenarios where users access the server +from various network locations. + +### Enabling Protection + +Add to `gns3_server.conf` under the `[Server]` section: + +```ini +; Enable DNS rebinding protection for MCP server +mcp_enable_dns_rebinding_protection = True + +; Allowed hosts (comma-separated, "host:*" port wildcard patterns only) +mcp_allowed_hosts = 127.0.0.1:*,localhost:*,192.168.1.3:* + +; Allowed origins (comma-separated) +mcp_allowed_origins = http://127.0.0.1:*,http://localhost:*,http://192.168.1.3:* +``` + +> **Note**: The MCP library only supports `"host:*"` port wildcard patterns +> (e.g., `"192.168.1.3:*"`). Standalone `"*"` wildcards are not supported. + +### Protection Mechanism + +When protection is enabled, the MCP server validates the `Host` header of +incoming SSE connection requests: + +```python +# Verify the request's Host header matches allowed patterns +validate_request → check Host header → 421 Misdirected Request if invalid +``` + +This prevents DNS rebinding attacks: +1. Attacker registers `evil.com` pointing to your server's IP +2. User's browser makes requests to `evil.com:3080` +3. MCP server checks Host header = `"evil.com:3080"` +4. `"evil.com:3080"` is not in `allowed_hosts` → connection rejected + +### Behaviour Summary + +| `mcp_enable_dns_rebinding_protection` | Result | +|:---|:---| +| `False` (default) | All hosts allowed | +| `True` + correct hosts configured | Only configured hosts allowed | +| `True` + missing/wrong hosts | Connections rejected with 421 | + +For public-facing MCP servers, set `allowed_hosts` to your server's domain name. + +## Architecture + +```mermaid +sequenceDiagram + participant Client as Claude Code + participant MCP as MCP Service + participant Auth as Auth + participant GNS3 as GNS3 REST API + + Note over Client: 1. Connect with API Key or JWT + Client->>MCP: GET /sse (Authorization: Bearer ) + + alt API Key (gns3_<uuid>_<secret>) + MCP->>Auth: Extract UUID → DB lookup → 1 bcrypt (thread pool) + Auth-->>MCP: Generate fresh JWT + else JWT + MCP->>Auth: Decode JWT + Auth-->>MCP: Token valid + end + + MCP-->>Client: event: endpoint /messages/?session_id=xxx + + Note over Client: 2. Initialize & Call Tools + Client->>MCP: POST /messages/ (tools/call ...) + MCP->>GNS3: HTTP request (with JWT from step 1) + GNS3-->>MCP: Response + MCP-->>Client: event: message (tool result) +``` + +## Internal Implementation + +- **FastMCP** (Anthropic MCP SDK) is used for tool registration and SSE transport +- The SSE app is mounted as a Starlette sub-application under `/v3/mcp/transport` +- **Auth:** JWT validation via `auth_service`. API key (`gns3__`) extracts UUID for O(1) DB lookup, runs bcrypt in thread pool, returns a fresh JWT — subsequent calls use the JWT with zero extra bcrypt. +- Tool handlers use `Gns3Connector` (from `gns3_copilot.gns3_client.connector`) via the shared handler layer (`gns3_copilot.gns3_client.api_handlers`), keeping the MCP layer decoupled +- The JWT token is stored in a `contextvars.ContextVar` so it is available within tool handler threads (Python ≥ 3.9 propagates contextvars through `asyncio.to_thread`) + +### Console WebSocket + +The `node_console` tool returns a WebSocket URL for connecting to a node's console. The URL includes a short-lived **access ticket** (10 min) — a short random string (`gns3t_…`, 22 chars) minted server-side and bound to that node's console endpoints. `link_capture_download` uses the same kind of ticket bound to one exact resource path instead of embedding a Bearer JWT in the returned curl command. Tickets replaced the long JWTs previously embedded in these URLs/commands: LLM clients retyping them into shell commands reliably corrupted a ~200-char JWT, while a short ticket survives copying. Re-request the URL when the ticket expires (logging out also invalidates outstanding tickets). This endpoint is protocol-agnostic — it works for **telnet**, **ssh**, and **vnc** console types alike. The WebSocket simply proxies raw byte streams between the client and the compute node; protocol negotiation (e.g. SSH key exchange) happens on the compute side. + +The WebSocket URL is constructed using the server's `_server_url()`, which resolves the host as follows: + +| `Server.host` value | Resolved host in URL | +|:---|:---| +| Specific IP or hostname (e.g. `192.168.1.3`) | Used as-is | +| `0.0.0.0` (IPv4 any, default) | Detected via **default route interface IP** | +| `::` (IPv6 any) | Detected via default route interface IP | +| Detection failure | Fallback to `127.0.0.1` | + +When `Server.host` is `0.0.0.0` (listen on all interfaces), the MCP server discovers the default route interface IP using a UDP socket connect to `8.8.8.8:80` — no network data is sent, the operating system simply selects the interface that would be used for the default route. This ensures the returned WebSocket URL uses a reachable address (e.g. `192.168.1.3` instead of `127.0.0.1`). + +If the configured host is already a specific IP or hostname (not `0.0.0.0`), it is used directly in the URL without modification. + +Use `websocat` to connect from the command line (use the `command` returned by `node_console` verbatim — never reconstruct the URL by hand): + +```bash +# The host in the URL is automatically resolved to a reachable address +websocat ws://192.168.1.3:3080/v3/projects/{project_id}/nodes/{node_id}/console/ws?token= +``` + +### Source Files + +| File | Purpose | +|------|---------| +| `gns3server/agent/mcp/__init__.py` | FastMCP server, tool decorators, SSE transport, JWT auth wrapper | +| `gns3server/agent/mcp/projects.py` | Project tool handlers | +| `gns3server/agent/mcp/nodes.py` | Node tool handlers | +| `gns3server/agent/mcp/links.py` | Link tool handlers | +| `gns3server/agent/mcp/templates.py` | Template tool handlers | +| `gns3server/agent/mcp/computes.py` | Compute tool handlers | +| `gns3server/api/server.py` | Mounts MCP routes via `register_starlette_routes()` | diff --git a/docs/features/project-open-performance.md b/docs/features/project-open-performance.md new file mode 100644 index 000000000..38cd4334c --- /dev/null +++ b/docs/features/project-open-performance.md @@ -0,0 +1,148 @@ +# Project Open Performance + +## Overview + +Optimizations to accelerate project opening (`POST /projects/{id}/open`) and node creation for topologies with many nodes and links. The main bottlenecks were sequential link creation, redundant subprocess calls, and SQLite write contention. + +## Before vs After + +| Scenario | Before | After | +|----------|--------|-------| +| 20 IOU nodes + 20 links (project open) | ~2s | ~1s | +| 40 QEMU nodes creation (MCP batch) | ~40s | ~1-2s | + +## Optimizations + +### 1. Parallel Link Creation + +**File:** `gns3server/controller/project.py` + +Links were created sequentially during project loading, each requiring up to 5 HTTP round-trips to the compute. Now uses `Pool(concurrency=100)` for parallel creation. + +```python +# Before: sequential loop +for link_data in topology.get("links", []): + link = await self.add_link(...) + await link.add_node(...) + +# After: parallel Pool +pool = Pool(concurrency=100) +for link_data in topology.get("links", []): + pool.append(self._create_link_from_topology_data, link_data) +await pool.join() +``` + +### 2. Batch UDP Port Allocation + +**Files:** `gns3server/api/routes/compute/compute.py`, `gns3server/controller/project.py`, `gns3server/controller/udp_link.py` + +During project loading, all required UDP ports are pre-allocated per compute in a single batch call before link creation begins. `UDPLink.create()` checks the pre-allocated pool first, falling back to individual allocation if unavailable. + +```python +# New batch endpoint +POST /projects/{id}/ports/udp/batch → {"count": N} → {"udp_ports": [...]} +``` + +### 3. IOU Image Subprocess Cache + +**File:** `gns3server/compute/iou/iou_vm.py` + +Each IOU VM creation spawned `ld-linux --verify` and `iou-image -h` subprocesses. With 20 nodes using the same image, this ran 40 redundant subprocesses. Now results are cached per image path at the class level. + +```python +# Class-level caches shared across all instances +IOUVM._loader_cache = {} # image path → loader command +IOUVM._default_values_cache = {} # image path → (ram, nvram) +``` + +Only the first node with a given image runs the subprocesses; subsequent nodes reuse cached values. + +### 4. SQLite WAL Mode + +**File:** `gns3server/db/tasks.py` + +Write-Ahead Logging allows concurrent reads without blocking on writes. The PRAGMA is registered on `engine.sync_engine` instead of the `Engine` class to correctly fire for async engine connections. + +```python +@event.listens_for(engine.sync_engine, "connect") +def set_sqlite_pragma(dbapi_connection, connection_record): + cursor = dbapi_connection.cursor() + cursor.execute("PRAGMA journal_mode=WAL") + cursor.execute("PRAGMA foreign_keys=ON") + cursor.close() +``` + +Without WAL mode, concurrent API requests caused `sqlite3.OperationalError: database is locked`. + +### 5. API Key Authentication O(1) Lookup + +**Files:** `gns3server/api/routes/controller/api_keys.py`, `gns3server/api/routes/controller/dependencies/authentication.py` + +**Old format:** `gns3_` — required scanning ALL keys and running bcrypt on each (O(n)). +**New format:** `gns3__` — extract UUID from token, single DB query (O(1)), single bcrypt. + +```python +# New auth flow +parts = token.split("_", 2) +key_id = UUID(parts[1]) +secret = parts[2] +db_key = await api_keys_repo.get_api_key(key_id) # O(1) lookup +if await asyncio.to_thread(bcrypt.checkpw, secret.encode(), db_key.key_hash.encode()): + # Authenticated — 1 query + 1 bcrypt regardless of total key count +``` + +### 6. bcrypt in Thread Pool + +**File:** `gns3server/api/routes/controller/dependencies/authentication.py` + +`bcrypt.checkpw()` is CPU-bound (~1.3s per call) and was blocking the async event loop. With 5 API keys and 10 concurrent requests, this caused ~13s delay before any handler could start. + +```python +# Before: blocking the event loop +if bcrypt.checkpw(token.encode(), db_key.key_hash.encode()): + ... + +# After: offloaded to thread pool +if await asyncio.to_thread(bcrypt.checkpw, secret.encode(), db_key.key_hash.encode()): + ... +``` + +### 7. Concurrency Settings + +| Setting | Before | After | File | +|---------|--------|-------|------| +| Node creation Pool | 5 | 100 | `controller/project.py` | +| Link creation Pool | 5 | 100 | `controller/project.py` | +| MCP BATCH_MAX_WORKERS | 10 | 100 | `agent/mcp/nodes.py` | +| MCP HTTP timeout | 10s | 30s | `agent/gns3_copilot/gns3_client/connector.py` | +| HTTP connection pool | 10 (default) | 500/1000 | `agent/gns3_copilot/gns3_client/connector.py` | +| Start nodes Pool | 3 | 3 (unchanged) | `controller/project.py` | + +### 8. MCP Auth Returns JWT + +**File:** `gns3server/agent/mcp/__init__.py` + +When an MCP client connects with an API key, the `_resolve_token` function validates the key then returns a fresh short-lived JWT instead of the raw API key. The JWT is stored in a `ContextVar` and reused for all subsequent tool calls within the same SSE session — zero extra bcrypt. + +```python +if user: + fresh_token = auth_service.create_access_token(user.username) + return fresh_token # JWT for subsequent REST API calls +``` + +## Related Files + +| File | Changes | +|------|---------| +| `gns3server/controller/project.py` | Parallel link creation, batch UDP, Pool(100) | +| `gns3server/compute/iou/iou_vm.py` | Image subprocess cache | +| `gns3server/db/tasks.py` | WAL mode + sync_engine event listener | +| `gns3server/api/routes/compute/compute.py` | Batch UDP endpoint | +| `gns3server/controller/udp_link.py` | Pre-allocated port consumption | +| `gns3server/api/routes/controller/api_keys.py` | O(1) key format | +| `gns3server/api/routes/controller/dependencies/authentication.py` | O(1) auth + thread pool bcrypt | +| `gns3server/agent/mcp/__init__.py` | Auth returns JWT, tool enhancements | +| `gns3server/agent/mcp/nodes.py` | fields filter, inherited template_id, name passthrough | +| `gns3server/agent/mcp/links.py` | fields filter, compact array format | +| `gns3server/agent/gns3_copilot/gns3_client/connector.py` | Timeout 30s, connection pool 500/1000 | +| `gns3server/utils/images.py` | md5sum cache error → warning | diff --git a/docs/features/refresh-token-mechanism.md b/docs/features/refresh-token-mechanism.md new file mode 100644 index 000000000..e82e393d6 --- /dev/null +++ b/docs/features/refresh-token-mechanism.md @@ -0,0 +1,154 @@ +# Stateless JWT Refresh Token Mechanism + +## Overview + +GNS3 server now supports a stateless JWT refresh token mechanism for interactive sessions (e.g., Web UI). This allows clients to stay authenticated across page reloads without repeated username/password prompts, while keeping access tokens short-lived. + +No new database table or migration is required — refresh tokens are signed JWTs using the same secret and algorithm as access tokens. + +## Architecture + +```mermaid +graph TD + Client -->|login / authenticate| API[Controller API] + API -->|access_token + refresh_token| Client + Client -->|POST /refresh| Refresh[Refresh Endpoint] + Refresh -->|new access_token + new refresh_token| Client + Client -->|Bearer access_token| Protected[Protected Endpoints] + Protected -->|401| Client + Client -->|refresh_token in body| Refresh + Refresh -->|401 if invalid/expired/revoked| Client + Refresh -->|verify type, exp, ver| AuthService[AuthService] + AuthService -->|check token_version| DB[(users table)] +``` + +## Business Process + +### Login / Authenticate Flow + +```mermaid +sequenceDiagram + participant C as Client + participant API as Controller API + participant AS as AuthService + participant DB as Database + + C->>API: POST /login or /authenticate (username + password) + API->>DB: authenticate_user() + DB-->>API: user (with token_version) + API->>AS: create_access_token(user, ver) + API->>AS: create_refresh_token(user, ver) + AS-->>API: access_token (type: access, exp: 15min) + AS-->>API: refresh_token (type: refresh, exp: 30d) + API-->>C: { access_token, token_type, refresh_token } +``` + +### Refresh Flow (Silent Renewal) + +```mermaid +sequenceDiagram + participant C as Client + participant API as Controller API + participant AS as AuthService + participant DB as Database + + Note over C: access_token expired + C->>API: POST /refresh { refresh_token } + API->>AS: get_token_data(refresh_token) + AS-->>API: { username, ver, token_use: "refresh" } + API->>DB: get_user_by_username() + DB-->>API: user (with current token_version) + Note over API,DB: rejects if user not found, inactive, or token_version mismatch + API->>AS: create_access_token(user, ver) + API->>AS: create_refresh_token(user, ver) + AS-->>API: new access_token (sliding window) + AS-->>API: new refresh_token (sliding window) + API-->>C: { access_token, token_type, refresh_token } + C->>API: Retry original request with new access_token +``` + +### Logout — Token Revocation + +```mermaid +sequenceDiagram + participant C as Client + participant API as Controller API + participant DB as Database + + C->>API: POST /logout (Bearer access_token) + API->>DB: logout_user(user_id) → token_version += 1 + DB-->>API: done + API-->>C: 204 No Content + Note over C, DB: All existing access and refresh tokens with old ver are now invalid +``` + +## API Endpoints + +| Method | Path | Description | Authentication | +|--------|------|-------------|---------------| +| POST | `/v3/access/users/login` | Login with form data, returns access + refresh tokens | Public | +| POST | `/v3/access/users/authenticate` | Login with JSON, returns access + refresh tokens | Public | +| POST | `/v3/access/users/refresh` | Exchange a refresh token for a new access token + refresh token | Public (token itself proves identity) | +| POST | `/v3/access/users/logout` | Revoke all tokens for the current user | Bearer token required | + +### POST /v3/access/users/refresh + +**Request:** +```json +{ + "refresh_token": "" +} +``` + +**Response 200:** +```json +{ + "access_token": "", + "token_type": "bearer", + "refresh_token": "" +} +``` + +**Error Responses:** +- `401` — Invalid, expired, or revoked refresh token +- `422` — Missing `refresh_token` field in request body + +## Security Design + +### Token Claims + +| Claim | Access Token | Refresh Token | +|-------|-------------|---------------| +| `sub` | username | username | +| `exp` | 24h (configurable) | 30d (configurable) | +| `ver` | user's `token_version` | user's `token_version` | +| `type` | `"access"` | `"refresh"` | + +### Key Security Properties + +- **Type-based isolation**: Access tokens (`type: access`) are rejected by `/refresh`. Refresh tokens (`type: refresh`) are rejected by HTTP and WebSocket authentication paths. This prevents a stolen long-lived refresh token from being used directly for API access. +- **Token version integration**: Both token types carry the user's `token_version`. `logout` increments `token_version` in the database, immediately invalidating all outstanding access and refresh tokens. +- **Stateless (no replay detection)**: Since there is no `refresh_tokens` database table, a stolen refresh token remains valid until its `exp` or until the user logs out. This is an accepted trade-off for avoiding a new table and migration. +- **Sliding window**: Each `/refresh` call issues a new refresh token with a fresh expiry, keeping active sessions alive indefinitely until logout or inactivity. + +### Implementation Files + +- `gns3server/services/authentication.py` — `_create_token`, `create_access_token`, `create_refresh_token`, `get_token_data` +- `gns3server/api/routes/controller/users.py` — `refresh_access_token` endpoint handler +- `gns3server/api/routes/controller/dependencies/authentication.py` — `_reject_refresh_token` guard in HTTP and WebSocket paths +- `gns3server/schemas/controller/tokens.py` — `Token`, `TokenData`, `RefreshTokenRequest` models +- `gns3server/schemas/config.py` — `jwt_refresh_token_expire_minutes` configuration + +## Configuration + +| Setting | Default | Description | +|---------|---------|-------------| +| `Controller.jwt_access_token_expire_minutes` | 1440 (24h) | Access token TTL. Web UI recommends 15 min. | +| `Controller.jwt_refresh_token_expire_minutes` | 43200 (30d) | Refresh token TTL. | +| `Controller.jwt_secret_key` | (random) | HMAC signing key for all JWT tokens. | + +## Notes + +- **Web UI integration**: The client should implement a response interceptor that catches 401, silently calls `/refresh`, and retries the original request. Multiple concurrent 401s should be queued with a single refresh request. +- **No per-session revocation**: All tokens for a user share the same `token_version`. Logout revokes everything. Per-session granularity would require adding a `refresh_tokens` table. +- **Rate limiting**: `/refresh` is a public endpoint with a valid credential (the refresh token). Rate limiting is recommended if brute-force attacks are a concern. diff --git a/docs/features/vendor-nos-xrd.md b/docs/features/vendor-nos-xrd.md new file mode 100644 index 000000000..13ed3960f --- /dev/null +++ b/docs/features/vendor-nos-xrd.md @@ -0,0 +1,221 @@ + + +> This documentation is organized by AI with reference to actual code. AI can make mistakes — please verify against the source code when in doubt. + + +# Cisco XRd Control Plane (Vendor NOS Adaptation) + +## Overview + +Cisco XRd Control Plane runs as a first-class GNS3 Docker router node by +combining the existing vendor NOS path (`console_type: "docker_exec"` + +`GNS3_SKIP_INIT=1`, see [docker-exec-console.md](./docker-exec-console.md)) +with four generic server mechanisms added for heavy/systemd NOS containers: +`/dev/shm` and host-device injection, config-file injection (`extra_configs`), +and udev masking. XRd itself is pure appliance configuration — no image +rebuild, no source patching. + +## Why XRd must take the vendor path + +XRd boots `/usr/sbin/init` (systemd) as PID 1. GNS3's generic init.sh +wrapper chain (`/gns3/init.sh → su → run-cmd.sh → /usr/sbin/init`) crashes +XRd's glibc loader with `Fatal glibc error: dl-call-libc-early-init.c:37 +(sym != NULL)` (SIGABRT loop). With `GNS3_SKIP_INIT=1` the container runs +its native entrypoint directly and boots cleanly — same arrangement as +SR Linux. + +## Architecture + +```mermaid +graph TB + subgraph Appliance["XRd appliance (.gns3a) — pure configuration"] + ENV["environment: GNS3_SKIP_INIT / GNS3_CONSOLE_CMD / GNS3_MASK_UDEV / GNS3_SHM_SIZE / GNS3_DEVICES + XR_*"] + XC["extra_configs: /firstboot.cfg"] + XV["extra_volumes: /xr-storage + /xr-storage-shadow"] + end + subgraph Server["gns3-server (generic mechanisms)"] + CREATE["DockerVM.create() HostConfig"] + MASK["GNS3_MASK_UDEV → /dev/null binds"] + HOSTCFG["ShmSize / Devices"] + CFGINJ["extra_configs → RO single-file bind"] + VBRIDGE["VendorDockerVM volume seeding + direct binds"] + HOSTCHK["host-readiness check (read-only)"] + end + subgraph Container["XRd container"] + SYSTEMD["systemd (/usr/sbin/init)"] + XR["XR control plane"] + XRS["/xr-storage (persisted, live data layer)"] + end + ENV --> CREATE --> SYSTEMD + ENV --> MASK & HOSTCFG + XC --> CFGINJ + XV --> VBRIDGE --> XRS + HOSTCHK -.->|"warn: inotify/file-max/fuse"| Server +``` + +## Mechanisms added (all generic, XRd is just the first consumer) + +| Mechanism | Interface | Effect | Where | +|-----------|-----------|--------|-------| +| shm size | `GNS3_SHM_SIZE=1024` (MB) in `environment` | native `HostConfig.ShmSize` at create time — works with or without init.sh | `docker_vm.py` `create()` | +| host devices | `GNS3_DEVICES=/dev/fuse` (`docker run --device` syntax, space-separated) | native `HostConfig.Devices` | `docker_vm.py` `_format_devices()` | +| config injection | `extra_configs: [{target, content}]` (template/node/appliance schema field) | content written under the node dir, bind-mounted **read-only** at `target` — seeds NOS startup configs without rebuilding the image | `docker_vm.py` `_mount_binds()`; persisted in `docker_templates.extra_configs` (Alembic migration) | +| udev masking | `GNS3_MASK_UDEV=1` | `/dev/null` over the 5 udev systemd units **and** `/bin|/sbin|/usr/bin/udevadm` | `docker_vm.py` `create()` | +| generic unit mask | `GNS3_MASK_SYSTEMD=u1,u2` | `/dev/null` over arbitrary `/etc/systemd/system/` | `docker_vm.py` `create()` | +| graceful stop | `GNS3_STOP_TIMEOUT=60` (seconds; default 60, max 210) | explicit user stop sends SIGTERM + a grace period (Docker SIGKILLs once it expires) instead of the base class' immediate kill — systemd NOS images require a graceful shutdown; internal paths (delete/update/close) keep the immediate kill since the container is force-deleted right after. Max 210 keeps the +30 s HTTP margin inside the controller's 240 s stop budget | `vendor_docker_vm.py` `_terminate_container()` | +| host check | automatic at Docker connect | read-only `/proc` check of inotify/file-max/FUSE; warns with exact fix commands (server is unprivileged — it can only check) | `compute/docker/__init__.py` `_check_host_readiness()` | + +`GNS3_*` variables are consumed host-side only and never forwarded into the +container (existing GNS3 behaviour); `XR_*` variables pass through normally. + +## Host-disturbance root causes (all fixed) + +A privileged systemd container can disturb the *host* desktop. Three +independent causes were isolated with plain-`docker run` A/B/C experiments +(udevd coldplug / busybox chown crash / direct `udevadm trigger`): + +| Host symptom | Root cause | Fix | +|---|---|---| +| Audio muted on every node start | container `systemd-udevd` coldplug replays **all** devices it can see (privileged → host `/sys`) | `GNS3_MASK_UDEV=1` (unit masks) | +| USB reconnects (mouse notification), journal noise | XRd's own `xr_startup.sh` calls `udevadm trigger --action=add --parent-match=` (USB license-dongle probing) — a direct binary call, unit masks don't stop it | `GNS3_MASK_UDEV=1` (udevadm null-bind) | +| Same USB/journal noise + broken persistence | static busybox `chown` dlopens container NSS modules → glibc abort → per-file coredump storm → host `systemd-coredump` rescans devices | vendor volume path prefers the container's own `chown` (`vendor_docker_vm.py`) | + +Diagnostics: `udevadm monitor --kernel --udev` (uevent stream), +`docker exec grep -n udevadm /opt/cisco/install-iosxr/base/etc/xr_startup.sh`. +Note: "journal corrupted" messages with varying machine-IDs come from the +*container's* journald (random machine-id per start), not the host journal. + +## XRd appliance recipe + +| Field | Value | +|-------|-------| +| `image` | official `ios-xr/xrd-control-plane:` — no wrapper image needed | +| `console_type` | `docker_exec` | +| `extra_volumes` | `["/xr-storage", "/xr-storage-shadow"]` | +| `extra_configs` | `{target: /firstboot.cfg, content: }` | + +``` +GNS3_SKIP_INIT=1 +GNS3_CONSOLE_CMD=/pkg/bin/xr_cli.sh +GNS3_CONSOLE_RESIZE=0 +GNS3_MASK_UDEV=1 +GNS3_SHM_SIZE=1024 +GNS3_DEVICES=/dev/fuse +GNS3_STOP_TIMEOUT=40 +XR_FIRST_BOOT_CONFIG=/firstboot.cfg +XR_MGMT_INTERFACES=linux:eth0,xr_name=Mg0/RP0/CPU0/0,chksum,snoop_v4,snoop_v6 +XR_INTERFACES=linux:eth1,xr_name=Gi0/0/0/0;linux:eth2,xr_name=Gi0/0/0/1;... +``` + +`GNS3_CONSOLE_RESIZE=0` (console geometry lock): the XR pager pages on the +PTY window size and ignores `terminal length 0`, so the exec PTY must stay at +the tall no-paging default for **every** client. The docker_exec console is a +single shared exec — one browser's terminal-size resize (WS control frames → +NAWS) would change the geometry concurrent netmiko/copilot sessions see and +bring `--More--` back. XRd's CLI is line-oriented, so browsers lose nothing +by not resizing. See +[docker-exec-console.md](./docker-exec-console.md#terminal-geometry-and-size-forwarding). + +XRd-specific gotchas (image-side, not GNS3): + +- Management interface xr_name is **`Mg0/RP0/CPU0/0`** (short prefix, `CPU0` + without slash). `MgmtEth0/RP0/CPU/0` is rejected: "not a valid + rack/slot/instance/port combination". +- `XR_INTERFACES` must list exactly `adapters − 1` data interfaces (eth0 is + management). Changing the adapter count requires regenerating the string. +- **Persistence layout**: in the *image*, `/xr-storage/{config,disk1,log, + scratch}` are symlinks into `/xr-storage-shadow` (a pristine spare copy of + the initial state). At boot the bootstrap replaces the symlinks with real + directories, and XR writes everything — committed config (`commitdb`, + `running`) included — into **`/xr-storage`**, never touching the shadow + again. This mirrors containerlab, which bind-mounts `/xr-storage` + (`nodes/xrd/xrd.go`: "persist data by mounting /xr-storage"). The + appliance persists **both** paths so writes land on host regardless of + whether they happen before or after the symlink→directory transition. +- `XR_FIRST_BOOT_CONFIG` only applies when XR's config storage is empty + (first boot). To re-seed, delete and recreate the node. +- The official image ships no default login; the first-boot config must + create one (e.g. `username admin / group root-lr / secret ...`). +- Docker mounts are fixed at container *create* time: after changing a + template's `extra_volumes`, existing nodes must be deleted and recreated + (a stop/start keeps the old mounts). +- Host sysctls (XRd's own requirements, same for containerlab): + `fs.inotify.max_user_instances=64000`, `max_user_watches=524288`, + `fs.file-max=1000000`, FUSE module loaded. GNS3 warns about these at + Docker connect; the admin raises them once. XRd also warns (non-fatal) + about `net.core.*` socket buffer sizes. + +## Business process + +```mermaid +sequenceDiagram + participant U as User + participant S as gns3-server + participant D as Docker daemon + participant X as XRd container + U->>S: create node from template + S->>S: parse GNS3_* env host-side + S->>D: seed volume host dirs from image (docker create + cp, first time only) + S->>D: container create (ShmSize, Devices, /dev/null binds, firstboot.cfg RO bind, volumes bound directly at /xr-storage*) + U->>S: start + S->>X: container start (native entrypoint /usr/sbin/init) + Note over X: systemd boots; udevd + udevadm masked → host untouched + S->>X: docker exec permission fix (container's own chown) + U->>S: open console + S->>X: docker exec pty: /pkg/bin/xr_cli.sh + X-->>U: IOS XR CLI (first boot: apply /firstboot.cfg, save to /xr-storage-shadow) +``` + +## Troubleshooting + +| Symptom | Cause / fix | +|---|---| +| Node exits 139, `Fatal glibc error ... sym != NULL` in logs | init.sh wrapper path — set `GNS3_SKIP_INIT=1` **and** `console_type: docker_exec` (the flag is only honoured on the vendor class) | +| `XR_FIRST_BOOT_CONFIG ... File not found` | env path and `extra_configs` target disagree (e.g. `/firstboot.cfg` vs `/first_boot.cfg`), or entry missing | +| Console stuck at `Username:` with no credentials | image has no default user; provide a first-boot config creating one, then **recreate** the node (first-boot only runs on empty config storage) | +| `Invalid interface entries ... XR_MGMT_INTERFACES` | use `xr_name=Mg0/RP0/CPU0/0` | +| Host audio muted / USB reconnects when the node starts | set `GNS3_MASK_UDEV=1` | +| Config lost across stop/start | `extra_volumes` must include `/xr-storage` (XR's live data layer; the shadow alone is only a pristine spare). Changing `extra_volumes` requires deleting and recreating the node — Docker mounts are fixed at create time | +| Two nodes can't ping, only one side ARPs | GNS3 link wiring bug (UDP self-loop on one end), not XRd — fixed (port-allocation race); on older builds delete and re-create the link; see [link-udp-self-loop](../bugs/link-udp-self-loop.md) | +| Compute log: busybox coredump storm | fixed by the container-chown change; verify gns3-server is current | + +## Notes + +- All four mechanisms are opt-in: nodes that don't set the variables or the + field get byte-identical container configuration. +- `extra_configs` is a schema field (unlike the env knobs) because the + `environment` field is line-delimited and cannot carry multi-line file + content. +- Template fields live in three places (pydantic schema, DB column, Alembic + migration) — see the `extra_configs` DB migration when adding new ones. +- `net.core.*` socket-buffer requirements are not yet part of the + host-readiness check (XRd warns about them itself, non-fatally). + +## References + +- `gns3server/compute/docker/docker_vm.py` — HostConfig env injection, + `_UDEV_UNITS`/`_UDEVADM_PATHS`, `extra_configs` binds, `_format_devices()` +- `gns3server/compute/docker/vendor_docker_vm.py` — vendor path, volume + seeding + direct binds, container-chown +- `gns3server/compute/docker/__init__.py` — `_check_host_readiness()` +- `gns3server/schemas/common.py` — `ExtraConfig` +- `gns3server/db/models/templates.py` + `db_migrations/` — persistence +- [docker-exec-console.md](./docker-exec-console.md) — the vendor NOS base + (docker_exec console, SKIP_INIT volume persistence) +- containerlab `nodes/xrd/xrd.go` — reference for XRd env defaults and + `/xr-storage` persistence + +## Version History + +| Version | Date | Changes | +|---------|------|---------| +| 1.6 | 2026-08-21 | SKIP_INIT volume persistence rebuilt: host-side seeding at create time (`docker create` + `docker cp`) and direct bind mounts at the real in-container paths replace the post-start `docker exec` bridge, which raced the NOS reading its startup config (visible on SR Linux: factory boot after a server restart + project reload; XRd was immune only because systemd touches `/xr-storage` tens of seconds in). No behaviour change for XRd beyond the race removal. | +| 1.5 | 2026-08-20 | Appliance env gains `GNS3_CONSOLE_RESIZE=0`: client-driven console resizes are ignored so the shared exec PTY stays at the tall no-paging geometry for concurrent netmiko/copilot sessions (browsers included). | +| 1.4 | 2026-08-15 | Code-review hardening: stop-query HTTP timeout scales with `GNS3_STOP_TIMEOUT` (values >300 s no longer abort); overlapping mask/config bind targets deduplicated (Docker "Duplicate mount point"); `ExtraConfig.target` validated at save time and directory forms rejected; host-readiness check no longer aborts on one unreadable `/proc/sys` key; base env parser strips trailing commas; vendor env knobs re-parsed on create (PUT environment takes effect); graceful stop limited to explicit user stop (delete/update/close keep the immediate kill); extra_configs under a persisted volume warns. | +| 1.3 | 2026-08-14 | Persistence corrected: XR's live data layer is `/xr-storage` (the image's symlink farm is materialized into real directories at bootstrap; `/xr-storage-shadow` is a pristine spare) — the appliance persists both. Vendor containers now stop gracefully (SIGTERM + `GNS3_STOP_TIMEOUT` grace, default 60 s) instead of being SIGKILLed on the spot. | +| 1.2 | 2026-08-14 | Link UDP self-loop root-caused to a port-allocation race and fixed — see [link-udp-self-loop](../bugs/link-udp-self-loop.md) (now marked Fixed). | +| 1.1 | 2026-08-14 | Datapath validated end-to-end (XRd brings its own interfaces up; ARP/ICMP bidirectional). Add troubleshooting entry for the one-way-link symptom (GNS3 link UDP self-loop bug, see bugs/link-udp-self-loop.md). | +| 1.0 | 2026-08-14 | Initial documentation of the XRd control-plane adaptation: vendor path requirement, shm/devices/extra_configs/udev-mask mechanisms, host-disturbance root causes, appliance recipe. | diff --git a/docs/features/vnc-websocket-console.md b/docs/features/vnc-websocket-console.md index 41b2a2ec2..b117ad152 100644 --- a/docs/features/vnc-websocket-console.md +++ b/docs/features/vnc-websocket-console.md @@ -58,7 +58,7 @@ graph LR **URL**: `ws://{controller_host}:{port}/v3/projects/{project_id}/nodes/{node_id}/console/vnc?token={jwt_token}` **Authentication**: -- JWT token via query parameter +- JWT token via query parameter, **or** a short-lived console ticket (`gns3t_…`, minted per node by the `node_console` MCP tool, valid 10 min, bound to this node's console endpoints) - User must have `Node.Console` privilege **WebSocket Subprotocols**: @@ -179,7 +179,7 @@ sequenceDiagram 1. **Authentication**: - JWT token validation via `has_privilege_on_websocket("Node.Console")` dependency - - Token passed as query parameter: `?token={jwt}` + - Token passed as query parameter: `?token={jwt}`, or a console ticket (`gns3t_…`) redeemable only on the node it was minted for 2. **Authorization**: - RBAC privilege check: `Node.Console` diff --git a/docs/gns3-copilot/implemented/ai-assistant-overview.en.md b/docs/gns3-copilot/implemented/ai-assistant-overview.en.md new file mode 100644 index 000000000..b01c1d20f --- /dev/null +++ b/docs/gns3-copilot/implemented/ai-assistant-overview.en.md @@ -0,0 +1,161 @@ + + +# GNS3-Copilot AI Assistant Overview + +## Overall Architecture + +```mermaid +flowchart TB + subgraph "Client" + A["Web UI"] --> B["SSE Streaming"] + end + + subgraph "FastAPI Route Layer" + B --> C["POST /chat/stream\nPOST /chat/inject"] + C --> D["Auth + LLM Config Loading\nSet ContextVars"] + end + + subgraph "AgentService (Project-level)" + D --> E["LangGraph Agent\nStateGraph"] + E --> F["SQLite Checkpointer\ncopilot_checkpoints.db"] + end + + subgraph "LangGraph Workflow" + E --> G["llm_call node\nmodel invocation"] + E --> H["tool_node\ntool execution"] + E --> I["title_generator_node\nauto title"] + E --> J["abort_handler_node\ninterrupt handling"] + end + + subgraph "Three Copilot Modes" + G --> K["teaching_assistant\ndiagnostic read-only"] + G --> L["lab_automation_assistant\nfull control"] + G --> M["troubleshooting_injection\nfault injection"] + end + + subgraph "LLM Config System" + D --> N["User configs\nGroup config inheritance\nAPI key encryption"] + end +``` + +## API Endpoints + +| Endpoint | Function | +|---|---| +| `POST /v3/projects/{pid}/chat/stream` | Streaming conversation (SSE), supports three copilot modes | +| `POST /v3/projects/{pid}/chat/inject` | Fault injection entry, auto-switches to `troubleshooting_injection` mode | +| `GET /v3/projects/{pid}/chat/sessions` | List sessions (supports filtering, pagination) | +| `DELETE /v3/projects/{pid}/chat/sessions/{sid}` | Delete session | +| `PATCH /v3/projects/{pid}/chat/sessions/{sid}` | Update session (rename, pin) | +| `POST /v3/projects/{pid}/chat/sessions/{sid}/abort` | Abort an active session | + +## LangGraph Agent Workflow + +```mermaid +sequenceDiagram + participant U as User + participant API as FastAPI + participant AS as AgentService + participant LLM as LLM Node + participant Tool as Tool Node + participant TGen as Title Node + + U->>API: send message + API->>AS: stream_chat() + AS->>AS: set ContextVars
(jwt_token, llm_config) + + Note over AS,LLM: llm_call node + AS->>LLM: invoke pre-compiled model + LLM->>LLM: pre_model_hook
inject topology + trim context + LLM-->>AS: AI reply (may include tool_calls) + + opt has tool calls + AS->>Tool: execute tools + Tool-->>AS: tool results + AS->>LLM: continue LLM call + end + + opt first turn and no title + AS->>TGen: auto-generate title + TGen-->>AS: session title + end + + AS-->>API: SSE streaming response + API-->>U: stream output +``` + +## Three Copilot Modes + +### Mode Comparison + +| Mode | Tool Scope | Use Case | +|---|---|---| +| `teaching_assistant` (default) | Diagnostic read-only + packet analysis + node management | Teaching demos, troubleshooting guidance | +| `lab_automation_assistant` | All tools (including config changes) | Lab automation, device configuration | +| `troubleshooting_injection` | Fault injection tool set | Troubleshooting practice, fault simulation | + +### Tool Binding Details + +| Tool | teaching_assistant | lab_automation_assistant | troubleshooting_injection | +|---|---|---|---| +| `GNS3TemplateTool` get templates | ✓ | ✓ | | +| `GNS3CreateNodeTool` create nodes | ✓ | ✓ | | +| `GNS3LinkTool` create links | ✓ | ✓ | | +| `GNS3StartNodeTool` start nodes | ✓ | ✓ | | +| `GNS3UpdateNodeNameTool` rename | ✓ | ✓ | | +| `GNS3StopNodeTool` stop nodes | | ✓ | | +| `GNS3SuspendNodeTool` suspend nodes | | ✓ | | +| `ExecuteMultipleDeviceCommands` read-only commands | ✓ | ✓ | ✓ | +| `ExecuteMultipleDeviceConfigCommands` config commands | | ✓ | ✓ | +| `VPCSCommands` VPCS commands | | ✓ | | +| `PacketAnalysisTool` live packet analysis | ✓ | ✓ | | +| `PacketAnalysisSkillsTool` protocol knowledge | ✓ | ✓ | | +| `DeviceSkillsTool` device skills | ✓ | ✓ | | +| `GNS3PacketFilterTool` link filters | | | ✓ | +| `InjectionSkillsTool` fault injection skills | | | ✓ | +| `GNS3TopologyTool` topology info | | | ✓ | + +The mode is selected in the `llm_call` node via `copilot_mode`, which picks the corresponding tool list and binds it to the LLM model instance through `create_base_model_with_tools(mode_tools, llm_config)`. + +## Context Window Management + +```mermaid +flowchart LR + A["LLM call triggered"] --> B["pre_model_hook"] + B --> C["Inject topology\ninto System Prompt"] + B --> D["Estimate tool definition\ntoken cost"] + B --> E["trim_messages\nby strategy"] + E --> F["conservative 60%\nbalanced 75%\naggressive 85%"] + F --> G["Invoke LLM"] +``` + +- Accurate token counting via tiktoken (`cl100k_base`) +- Three trimming strategies: conservative / balanced / aggressive +- Auto-injects `{{topology_info}}` into System Prompt + +## Session Management + +- Per-project independent SQLite database (`gns3-copilot/copilot_checkpoints.db`) +- Supports pin, rename, delete, history query +- Auto-records token usage, message count, LLM call count + +## LLM Config System + +| Feature | Description | +|---|---| +| User-level configs | Each user can independently configure provider / model / api_key | +| Group inheritance | Users auto-inherit group config when no personal config is set | +| API key encryption | Auto-encrypted at database storage | +| Optimistic locking | `version` field prevents concurrent modification conflicts | + +## Key Design Points + +1. **Project-level Isolation** — Each GNS3 project has its own Agent instance and SQLite storage +2. **ContextVars Safe Passing** — JWT token, API key exist only in memory, auto-cleared when request ends +3. **LangGraph StateGraph** — Custom nodes + conditional edges, supports ReAct loop and recursion limits +4. **SSE Streaming** — Real-time push of content / tool_call / tool_start / tool_end / error / done events +5. **Hot Reload** — System Prompt, Skills, Protocols all support runtime reload +6. **Mode-based Tool Sets** — Three copilot modes bind different tools, safely isolated by scenario diff --git a/docs/gns3-copilot/implemented/command-security.md b/docs/gns3-copilot/implemented/command-security.md index c1f646ad2..1e84b2f7a 100644 --- a/docs/gns3-copilot/implemented/command-security.md +++ b/docs/gns3-copilot/implemented/command-security.md @@ -86,7 +86,7 @@ flowchart TD ### Forbidden Commands Configuration -The forbidden commands list is loaded from the external [GNS3-Skills](https://github.com/yueguobin/GNS3-Skills) repository at `config/forbidden_commands.txt`. +The forbidden commands list is loaded from the external [GNS3-Skills](https://github.com/gns3/gns3-skills) repository at `config/forbidden_commands.txt`. **Format:** - One command pattern per line @@ -168,7 +168,7 @@ Applies to any command with embedded newlines: `banner`, multi-line ACLs, route- ### Customizing Forbidden Commands -Edit `config/forbidden_commands.txt` in the [GNS3-Skills repository](https://github.com/yueguobin/GNS3-Skills) and push the changes, then call `POST /copilot/reload/skills` to apply them without restarting the server. +Edit `config/forbidden_commands.txt` in the [GNS3-Skills repository](https://github.com/gns3/gns3-skills) and push the changes, then call `POST /copilot/reload/skills` to apply them without restarting the server. ## Implementation Verification diff --git a/docs/gns3-copilot/implemented/fault-injection-overview.en.md b/docs/gns3-copilot/implemented/fault-injection-overview.en.md new file mode 100644 index 000000000..00a0acfac --- /dev/null +++ b/docs/gns3-copilot/implemented/fault-injection-overview.en.md @@ -0,0 +1,104 @@ + + +# GNS3-Copilot Fault Injection Overview + +## Core Flow + +```mermaid +flowchart TB + subgraph "① API Trigger & Mode Switch" + A["POST /chat/inject\nUser requests fault injection"] --> B["Verify project is opened"] + B --> C["Set copilot_mode =\ntroubleshooting_injection"] + C --> D["Start Agent\nwith fault injection tool set"] + end + + subgraph "② Topology Analysis & Fault Selection" + D --> E["GNS3TopologyTool\nget topology info"] + E --> F["ExecuteMultipleDeviceCommands\nget device configs"] + F --> G["InjectionSkillsTool\nquery available fault types"] + G --> H{"Injection Skills Repository\ngns3/gns3-skills"} + H --> I["Return matching fault definitions\nwith config injection commands"] + end + + subgraph "③ Fault Injection" + I --> J["Choose injection method"] + J --> K["ExecuteMultipleDeviceConfigCommands\ninject config changes"] + J --> L["GNS3PacketFilterTool\ninject link-layer faults"] + end + + subgraph "④ Result Confirmation" + K --> M["Verify fault is active"] + L --> M + M --> N["Document fault details\nincluding restore commands"] + end +``` + +## Tool Overview + +| Tool | Source File | Purpose | Available Modes | +|---|---|---|---| +| `InjectionSkillsTool` | `registry.py` (skills module) | Query protocol-level fault definitions (config change commands) | troubleshooting_injection | +| `GNS3PacketFilterTool` | `gns3_packet_filter.py` | Link-layer fault injection (delay, loss, corruption, BPF) | troubleshooting_injection | +| `ExecuteMultipleDeviceConfigCommands` | `config_tools_nornir.py` | Batch device config changes | troubleshooting_injection | +| `ExecuteMultipleDeviceCommands` | `display_tools_nornir.py` | Read device configurations (read-only) | troubleshooting_injection | +| `GNS3TopologyTool` | `gns3_client` | Get project topology information | troubleshooting_injection | + +## Fault Injection API + +| Endpoint | Function | +|---|---| +| `POST /v3/projects/{pid}/chat/inject` | Trigger fault injection, sets `troubleshooting_injection` mode then starts Agent | + +**Prerequisite**: Project must be in `opened` status, otherwise returns 403. + +## GNS3PacketFilterTool Link Filters + +| Filter Type | Function | Parameters | +|---|---|---| +| `delay` | Latency + jitter | `[latency(0-32767), jitter(0-32767)]` | +| `packet_loss` | Packet loss percentage | `[chance(0-100)]` | +| `corrupt` | Packet corruption percentage | `[chance(0-100)]` | +| `frequency_drop` | Drop every Nth packet | `[frequency(-1~32767)]` | +| `bpf` | Berkeley Packet Filter | expression text | + +## Agent Workflow (LangGraph) + +```mermaid +sequenceDiagram + participant U as User + participant API as POST /chat/inject + participant LLM as LLM Node + participant Topo as GNS3TopologyTool + participant DC as ExecuteMultipleDeviceCommands + participant CC as ExecuteMultipleDeviceConfigCommands + participant Skill as InjectionSkillsTool + participant Filter as GNS3PacketFilterTool + + U->>API: Inject an OSPF fault + API->>LLM: set mode=troubleshooting_injection + LLM->>Topo: get topology + Topo-->>LLM: topology info + LLM->>DC: read device configs + DC-->>LLM: running configs + LLM->>Skill: list context=["ospf"] + Skill-->>LLM: matching fault types + LLM->>Skill: get device_type=injection_ospf + Skill-->>LLM: fault definition + injection commands + LLM->>CC: execute config injection + CC-->>LLM: injection result + LLM->>Filter: set filters={delay:[200,50]} + Filter-->>LLM: link delay injected successfully + LLM-->>U: Fault injected, restore commands included +``` + +## Key Design Points + +1. **Dedicated API Endpoint** — `POST /chat/inject` is the dedicated entry point, automatically switching to `troubleshooting_injection` mode +2. **LLM-driven Fault Selection** — The LLM analyzes the topology then queries matching faults via `InjectionSkillsTool`; no hardcoded fault scenarios +3. **Dual-Layer Injection** — Device-level config changes + link-level network impairment, covering complete troubleshooting scenarios +4. **Fully Reversible** — Every injection includes restore commands; link filters can be cleared with `action: clear` +5. **Safety First** — BPF syntax is pre-validated via tshark; config commands are restricted by `command_filter` +6. **Context Filtering** — `InjectionSkillsTool` requires a `context` parameter, returning only faults matching the topology protocols diff --git a/docs/gns3-copilot/implemented/fault-injection.md b/docs/gns3-copilot/implemented/fault-injection.md index 73cc4d165..43774b991 100644 --- a/docs/gns3-copilot/implemented/fault-injection.md +++ b/docs/gns3-copilot/implemented/fault-injection.md @@ -121,7 +121,7 @@ sequenceDiagram ## Injection Skills Repository -Skills are organized by protocol/category in the external [GNS3-Skills](https://github.com/yueguobin/GNS3-Skills) repository: +Skills are organized by protocol/category in the external [GNS3-Skills](https://github.com/gns3/gns3-skills) repository: | Category | File | Example Issues | |----------|------|----------------| diff --git a/docs/gns3-copilot/implemented/node-control-tools.md b/docs/gns3-copilot/implemented/node-control-tools.md index 6d7d105f0..b09083f08 100644 --- a/docs/gns3-copilot/implemented/node-control-tools.md +++ b/docs/gns3-copilot/implemented/node-control-tools.md @@ -516,29 +516,39 @@ gns3server/agent/gns3_copilot/tools_v2/ ### API Integration -The tools use the `Node` and `Link` classes from `custom_gns3fy`: +The tools call the shared REST handler layer (`gns3_copilot.gns3_client.api_handlers`), the same functions the MCP service exposes as MCP tools: ```python -from gns3server.agent.gns3_copilot.gns3_client import Node, Link, get_gns3_connector +from gns3server.agent.gns3_copilot.gns3_client.api_handlers import ( + build_gns3_ctx, create_node_handler, create_link_handler, + get_nodes_handler, start_node_handler, stop_node_handler, + suspend_node_handler, update_node_handler, +) -# Get templates -templates = get_gns3_connector().get_templates() +gns3_ctx = build_gns3_ctx() # JWT + server URL from the request context -# Create node -node = Node(project_id=project_id, template_id=template_id, x=x, y=y, connector=gns3_server) -node.create() +# Create node (single POST, batch mode is parallel) +created = create_node_handler( + {"project_id": project_id, "template_id": template_id, "x": x, "y": y, "name": name}, + gns3_ctx, +) # Create link -link = Link(project_id=project_id, connector=gns3_server, nodes=[...]) -link.create() +link = create_link_handler( + {"project_id": project_id, "nodes": [{"node_id": nid, "adapter_number": 0, "port_number": 0}, ...]}, + gns3_ctx, +) -# Update node name -node = Node(project_id=project_id, node_id=node_id, connector=gns3_server) -node.update(name=new_name) +# Update node name — the PUT response is the updated node +updated = update_node_handler({"project_id": project_id, "node_id": node_id, "name": new_name}, gns3_ctx) -# Start/stop/suspend node -node = Node(project_id=project_id, node_id=node_id, connector=gns3_server) -node.start() # or node.stop() / node.suspend() +# Start/stop/suspend nodes (node_ids batch runs in parallel) +start_node_handler({"project_id": project_id, "node_ids": [nid1, nid2]}, gns3_ctx) +stop_node_handler({"project_id": project_id, "node_ids": [nid1, nid2]}, gns3_ctx) +suspend_node_handler({"project_id": project_id, "node_ids": [nid1, nid2]}, gns3_ctx) + +# Node listing/status (single call for the whole project) +listing = get_nodes_handler({"project_id": project_id}, gns3_ctx) ``` ### Progress Tracking diff --git a/docs/gns3-copilot/implemented/packet-analysis-overview.en.md b/docs/gns3-copilot/implemented/packet-analysis-overview.en.md new file mode 100644 index 000000000..ba11a7aa3 --- /dev/null +++ b/docs/gns3-copilot/implemented/packet-analysis-overview.en.md @@ -0,0 +1,74 @@ + + +# GNS3-Copilot Real-time Packet AI Analysis Overview + +## Core Flow + +```mermaid +flowchart TB + subgraph "① Analysis Trigger & Knowledge Query" + A["User asks\n'e.g. Analyze OSPF neighbor state'"] --> B["LLM calls\nPacketAnalysisSkillsTool"] + B --> C{"Protocol Knowledge Repository\ngns3/gns3-skills"} + C --> D["Returns protocol definition\nfields/base_filter/check_rules"] + B --> E["LLM calls\nsearch_fields mode"] + E --> F["tshark -G fields\nfield name search"] + F --> G["Returns valid field names"] + end + + subgraph "② Live Capture & Analysis" + D --> H["LLM constructs tshark_args"] + G --> H + H --> I["PacketAnalysisTool\ncapture analysis mode"] + I --> J["GET /capture/file\ndownload live PCAP"] + J --> K["Pre-validate -e field names"] + K --> L["tshark -r pcap\nrun analysis"] + L --> M["Return analysis results"] + end +``` + +## Tool Overview + +| Tool | Source File | Purpose | Available Modes | +|---|---|---|---| +| `PacketAnalysisTool` | `packet_analysis_tool.py` | Download live PCAP + tshark analysis | teaching / lab_automation | +| `PacketAnalysisSkillsTool` | `registry.py` (skills module) | Query protocol-level analysis knowledge (fields, filters) | teaching / lab_automation | + +## Agent Workflow (LangGraph) + +```mermaid +sequenceDiagram + participant U as User + participant LLM as LLM Node + participant Skills as PacketAnalysisSkillsTool + participant Pcap as PacketAnalysisTool + + U->>LLM: OSPF neighbors can't establish, analyze + LLM->>Skills: get protocol=ospf + Skills-->>LLM: OSPF fields, filter definitions + LLM->>Pcap: search_fields query=ospf.hello + Pcap-->>LLM: valid -e field names + LLM->>Pcap: download PCAP + tshark_args + Pcap-->>LLM: tshark output results + LLM->>LLM: analysis reveals Dead interval mismatch + LLM-->>U: OSPF Dead interval mismatch detected +``` + +## Server Capture API + +| Endpoint | Function | +|---|---| +| `POST /v3/projects/{pid}/links/{lid}/capture/start` | Start packet capture on a link | +| `POST /v3/projects/{pid}/links/{lid}/capture/stop` | Stop packet capture | +| `GET /v3/projects/{pid}/links/{lid}/capture/file` | Download PCAP file (available even while capture is active) | +| `GET /v3/projects/{pid}/links/{lid}/capture/stream` | Stream PCAP data | +| `WS /v3/projects/{pid}/links/{lid}/capture/web-wireshark` | Web Wireshark WebSocket proxy | + +## Key Design Points + +1. **LLM-driven Analysis** — The LLM constructs tshark parameters itself; the framework does not hardcode protocol logic, only performs safety validation +2. **Live PCAP** — Captures can be downloaded and analyzed while running, no need to stop capturing +3. **Dual Knowledge Sources** — External repository provides protocol-specific knowledge; local tshark field registry provides exact field names +4. **Safety First** — Pre-validation of tshark field names prevents execution failures from invalid fields diff --git a/docs/gns3-copilot/implemented/packet-analysis.md b/docs/gns3-copilot/implemented/packet-analysis.md new file mode 100644 index 000000000..d77ed3404 --- /dev/null +++ b/docs/gns3-copilot/implemented/packet-analysis.md @@ -0,0 +1,182 @@ + + +> This documentation is organized by AI with reference to actual code. AI can make mistakes — please verify against the source code when in doubt. + + +# Protocol-Oriented Packet Analysis + +## Overview + +GNS3 Copilot provides protocol-oriented packet analysis that allows the AI assistant to diagnose network issues from live GNS3 captures. Protocol definitions (tshark fields, display filters, check rules) are stored as YAML files in the external GNS3-Skills repository and loaded into memory at startup. + +The system exposes two LangChain tools: `PacketAnalysisSkillsTool` queries protocol definitions, and `PacketAnalysisTool` runs tshark against downloaded pcap files using the LLM-constructed arguments. + +## Architecture + +```mermaid +graph TD + subgraph "GNS3-Skills Repository" + YAML[packet_analysis/*.yaml
40+ protocol files] + end + + subgraph "GNS3 Server" + SM[SkillsManager] + SL[SkillsLoader] + REG[PACKET_ANALYSIS_REGISTRY
in-memory dict] + SKILL[PacketAnalysisSkillsTool
query protocol definitions] + TOOL[PacketAnalysisTool
run tshark on captures] + end + + subgraph "tshark" + FIELDS["tshark -G fields"
live field registry] + CAP["tshark -r pcap"
packet capture analysis] + end + + YAML -->|load at startup / reload| SL + SL --> REG + SM --> SL + + SKILL -->|read protocol fields/filters| REG + TOOL -->|validate -e fields| FIELDS + TOOL -->|download pcap + run tshark| CAP +``` + +## Supported Protocols + +Definitions are loaded from `GNS3-Skills/packet_analysis/*.yaml`. The following protocol families are covered: + +| Category | Protocols | +|----------|-----------| +| Network Layer | ip, ipv6, arp, icmp, icmpv6 | +| Transport Layer | tcp, udp | +| Data Link | ethernet, ppp, hdlc, frame_relay, vlan, isl, llc | +| Routing | ospf, eigrp, rip, bgp, isis, pim, dvmrp | +| Link Protocols | l2tp, lacp, pagp, udld | +| Infrastructure | cdp, lldp, stp, vtp, dtp | +| Management | snmp, telnet, ssh, radius, tacacs | +| Application | dns, http, bootp, dhcp | +| Tunneling / Security | gre, esp, ah, mpls, eapol, ssl, isakmp | +| Miscellaneous | nbns, slarp, ocsp, wccp, auto_rp, loop | + +Each protocol YAML contains: + +```yaml +name: "OSPF Packet Analysis" +description: "Analyze OSPF routing protocol packets" +protocol_key: "ospf" +display_filter: "ospf" +fields: + - label: "Source IP" + tshark_field: "ip.src" + description: "Source IPv4 address" + - label: "OSPF Message Type" + tshark_field: "ospf.msg" + description: "1=Hello, 2=DBD, 3=LSR, 4=LSU, 5=LSAck" +filter_examples: + - description: "Show OSPF Hello packets" + filter: "ospf.msg == 1" +checks: + - name: hello_dead_mismatch + severity: critical + message: "Hello/Dead Interval mismatch between neighbors" +``` + +## Analysis Flow + +```mermaid +sequenceDiagram + participant LLM as LLM Agent + participant SKILL as PacketAnalysisSkillsTool + participant TOOL as PacketAnalysisTool + participant tshark as tshark + participant GNS3 as GNS3 Server + + LLM->>SKILL: {"action": "list"} + SKILL-->>LLM: Available protocols + + LLM->>SKILL: {"action": "get", "protocol": "ospf"} + SKILL-->>LLM: Fields, filters, check rules + + Note over LLM: LLM constructs tshark_args
using protocol knowledge + + LLM->>TOOL: {project_id, link_id,
tshark_args: "-Y ospf -T fields -e ip.src -e ospf.msg"} + TOOL->>TOOL: Validate -e field names
against tshark -G fields + TOOL->>GNS3: Download pcap for link_id + GNS3-->>TOOL: Capture file + TOOL->>tshark: tshark -r pcap -Y ospf -T fields -e ip.src -e ospf.msg + tshark-->>TOOL: Tab-separated output + TOOL-->>LLM: Raw tshark output +``` + +## Tool Registration + +The packet analysis tools are available in two copilot modes: + +| Tool | Teaching Assistant | Lab Automation | Troubleshooting Injection | +|------|:------------------:|:--------------:|:-------------------------:| +| `PacketAnalysisTool` | Yes | Yes | No | +| `PacketAnalysisSkillsTool` | Yes | Yes | No | + +## Tool Interface + +### PacketAnalysisSkillsTool (`packet_analysis_skills`) + +Queries protocol definitions from the `PACKET_ANALYSIS_REGISTRY`. Used before running tshark to look up valid field names, display filters, and anomaly checks. + +| Action | Input | Output | +|--------|-------|--------| +| List protocols | `{"action": "list"}` | `{count, protocols: [{protocol, name, description}]}` | +| Get protocol | `{"action": "get", "protocol": "ospf"}` | Protocol definition with fields, filters, checks | + +### PacketAnalysisTool (`packet_analysis`) + +Runs tshark against a downloaded GNS3 capture file. Supports two modes: + +**Capture analysis mode:** + +| Parameter | Required | Description | +|-----------|----------|-------------| +| `project_id` | Yes | UUID of the GNS3 project | +| `link_id` | Yes | UUID of the link to analyze | +| `tshark_args` | Yes | tshark arguments (after `-r `) | + +**Field search mode:** + +| Parameter | Required | Description | +|-----------|----------|-------------| +| `action` | Yes | `"search_fields"` | +| `query` | Yes | Single keyword (e.g., `"ospf.lsa"`, `"bgp.open"`) | + +### Validation and Error Handling + +Before downloading the capture, the tool validates `-e` field names against the live tshark field registry (`tshark -G fields`). Invalid field names are rejected early with a hint to use `search_fields`. + +| Scenario | Response | +|----------|----------| +| Invalid `-e` field name | `{error, hint, invalid_fields}` | +| tshark filter/field error | `{error: "tshark argument error", hints}` | +| Empty capture file | `{error: "Capture file is empty"}` | +| No matching packets | `{result: "No matching packets found", hints}` | +| tshark timeout (30s) | `{error: "tshark timeout after 30 seconds"}` | +| tshark not installed | `{error: "tshark not installed"}` | + +When `-c` is used and no results are found, the tool hints that `-c` limits total packets read (not matched count), and suggests removing it or piping to `head`. + +## Hot Reload + +Packet analysis protocols can be reloaded without restarting the server via the existing reload API: + +``` +POST /v3/copilot/reload/skills +``` + +This triggers `SkillsManager.reload_packet_analysis_protocols()`, which re-reads all YAML files from the `packet_analysis/` directory and updates `PACKET_ANALYSIS_REGISTRY` in place. + +## Related Documentation + +- [External Skills Repository](skills-repository.md) +- [Fault Injection](fault-injection.md) +- [Chat API](chat-api.md) diff --git a/docs/gns3-copilot/implemented/server-settings-api.md b/docs/gns3-copilot/implemented/server-settings-api.md new file mode 100644 index 000000000..a99b34074 --- /dev/null +++ b/docs/gns3-copilot/implemented/server-settings-api.md @@ -0,0 +1,105 @@ + + +# Server Settings API + +## Overview + +REST API for reading and updating `gns3_server.conf` at runtime, enabling a server settings page in the Web UI. Updates are persisted with a read-modify-write strategy (unknown options in the file are preserved), validated before anything touches disk, and hot-reloaded into the running server; the response tells the caller which changes require a restart. + +## Architecture + +```mermaid +flowchart LR + U["Web UI / Client"] -->|"GET /v3/settings"| R["routes/controller/settings.py"] + U -->|"PUT /v3/settings"| R + R -->|"Server.Audit / Server.Modify"| RBAC["privilege check"] + R --> RS["SettingsResponse / SettingsUpdate schemas
(extra=forbid, secret masking)"] + RS --> UC["Config.update_config()
read-modify-write"] + UC --> FILE["gns3_server.conf
(atomic replace, mode 0600)"] + UC --> RN["reload_and_notify()"] + RN --> CB["file-watch callbacks
(runtime hot reload)"] + R --> NOTIF["notification stream:
settings.updated"] +``` + +- **Exposure** — all sections except the deprecated `VirtualBox`/`VMware`: `Server`, `Controller`, `VPCS`, `Dynamips`, `IOU`, `Qemu`, `WebWireshark`. `Controller.jwt_secret_key` is excluded entirely: it is loaded from `/gns3_jwt_secret_key` and writing it to the configuration file is a no-op. +- **Schema metadata** — every field carries a pydantic `description`, default value and validation bounds. They flow into `/openapi.json` (the `SettingsResponse` component), so clients can render the settings form — labels, tooltips, initial values, input validation — from the OpenAPI schema alone, without maintaining a field table. The human-readable annotated reference is `gns3server/config_samples/gns3_server.conf`, kept in sync with the schema. +- **Write strategy** — `Config.update_config()` re-reads the configuration files with `configparser`, applies only the submitted options, and atomically rewrites the main configuration file. Comments and formatting are lost (accepted trade-off); options unknown to the schema are preserved. +- **Validate before write** — the merged view of all configuration files is validated as `ServerConfig` *before* any disk write. A validation error must never reach disk: the `FileWatcher` reload callback would raise and permanently stop polling that file. + +## Business Process (PUT) + +```mermaid +sequenceDiagram + participant C as Client + participant R as PUT /v3/settings + participant U as Config.update_config() + participant D as gns3_server.conf + C->>R: PUT {"Section": {"option": value | null}} + R->>R: schema validation (unknown key/section → 422) + R->>U: changes (masked/empty secrets skipped) + U->>U: locate owning file per option (later file wins) + alt option owned by a non-main configuration file + U-->>R: ConfigConflictError → 409 (nothing written) + else + U->>U: set / remove option (null removes, value falls back to default) + U->>U: validate merged ServerConfig (failure → 400, file untouched) + U->>D: atomic write (.tmp + os.replace, mode 0600) + U->>U: reload_and_notify() → runtime hot reload + R-->>C: 200 — new values + restart_required + R->>C: notification settings.updated (option names only, no values) + end +``` + +## API Endpoints + +| Method | Path | Description | Privilege | +|--------|------|-------------|-----------| +| GET | `/v3/settings` | Return all current server settings | `Server.Audit` | +| PUT | `/v3/settings` | Update and persist server settings | `Server.Modify` | + +Request example: + +```json +{ + "Server": { + "report_errors": true, + "allowed_interfaces": ["eth0", "lo"], + "compute_password": "**********" + }, + "Qemu": { + "enable_monitor": false + } +} +``` + +Response example (abbreviated): + +```json +{ + "Server": { "report_errors": true, "allowed_interfaces": ["eth0", "lo"], "...": "..." }, + "Qemu": { "enable_monitor": false }, + "restart_required": ["Server.port"] +} +``` + +## Notes + +- **Secrets** — `SecretStr` fields are masked in responses. An empty secret (e.g. `compute_password` before the server generates one) serializes as `""` rather than the mask. On PUT, the mask or an empty string means "leave unchanged"; only an explicit new value is written, in clear text like a hand-edited file. +- **`restart_required`** — options that only take effect after a server restart (bind host/port, protocol, TLS and certificates, port ranges, image/symbol/config paths, GNS3 VM credentials, skills paths, …). Everything else hot-reloads via `Config.instance().settings`. +- **GET reflects runtime values** — in-memory settings may differ from disk (e.g. the generated `compute_password`, the resolved `secrets_dir`); the mask/empty skip rule guarantees PUT never writes echoed values back. +- **Privileges** — `Server.Audit`/`Server.Modify` are seeded into the `Administrator` role at table creation only; existing databases need a manual grant. Superadmins bypass RBAC. +- **Hardening** — the file watcher callback is exception-guarded (`utils/file_watcher.py`): a callback failure is logged instead of silently killing the polling loop. + +### Related Files + +| File | Role | +|------|------| +| `gns3server/config.py` | `Config.update_config()` (read-modify-write, validate-before-write), `reload_and_notify()` | +| `gns3server/utils/file_watcher.py` | callback exception hardening | +| `gns3server/schemas/controller/settings.py` | response/update models, `SECRET_MASK` | +| `gns3server/api/routes/controller/settings.py` | GET/PUT endpoints, `restart_required`, notification | +| `gns3server/db/models/privileges.py` | `Server.Audit` / `Server.Modify` privilege seeds | +| `gns3server/config_samples/gns3_server.conf` | annotated sample configuration, human-readable reference kept in sync with the schema | diff --git a/docs/gns3-copilot/implemented/skills-repository.md b/docs/gns3-copilot/implemented/skills-repository.md index 78c295cce..167941465 100644 --- a/docs/gns3-copilot/implemented/skills-repository.md +++ b/docs/gns3-copilot/implemented/skills-repository.md @@ -10,11 +10,11 @@ See LICENSE file for licensing information. ## Overview -GNS3 Copilot loads all skills, prompts, and security configurations from an external Git repository at [github.com/yueguobin/GNS3-Skills](https://github.com/yueguobin/GNS3-Skills). This enables dynamic updates without server redeployment. +GNS3 Copilot loads all skills, prompts, and security configurations from an external Git repository at [github.com/gns3/gns3-skills](https://github.com/gns3/gns3-skills). This enables dynamic updates without server redeployment. The repository provides: - **Injection skills** (39 categories): Network fault scenarios for troubleshooting practice -- **Device skills**: Device-specific command knowledge (VPCS, etc.) +- **Device skills**: Device-specific command knowledge (VPCS, etc.) — large devices split into per-protocol **topics** - **Feature skills**: Topology planning, network design - **System prompts**: Agent personality and behavior definitions - **Forbidden commands**: Security rules for command filtering @@ -24,7 +24,7 @@ The repository provides: ```mermaid graph TD subgraph "GNS3-Skills Repository" - YAML[injection/*.yaml
device/*.yaml
feature/*.yaml] + YAML[injection/*.yaml
device/*.yaml + device/*/*.yaml
feature/*.yaml] MD[prompts/*.md] CFG[config/forbidden_commands.txt] end @@ -56,7 +56,11 @@ GNS3-Skills/ │ ├── vlan_issues.yaml │ └── ... ├── device/ # Device-specific skills -│ └── vpcs.yaml +│ ├── vpcs.yaml # small devices: one single file +│ └── frr/ # large devices: split per protocol topic +│ ├── _base.yaml # device-level skill (console model, notes, aliases) +│ ├── ospf.yaml # topic file (merged under "topics" at load time) +│ └── bgp.yaml ├── feature/ # Feature skills │ └── topology_planner.yaml ├── prompts/ # System prompts (Markdown) @@ -68,20 +72,40 @@ GNS3-Skills/ └── forbidden_commands.txt ``` +## Device Topics + +A device with knowledge for many protocols would grow one YAML file indefinitely. Such devices use a split layout instead: `device//_base.yaml` holds the device-level skill, and one file per protocol topic (`ospf.yaml`, `bgp.yaml`, ...) holds its commands and troubleshooting entries. The loader merges them into a single `SKILLS_REGISTRY` entry: + +``` +SKILLS_REGISTRY["frr_vtysh"] = { ..._base.yaml..., "topics": { "ospf": {...}, "bgp": {...} } } +``` + +Topic files must declare `device_type` (matching their `_base.yaml`), `topic` and `name`; the CI validator in the skills repository enforces this. + +The `device_skills` tool exposes a three-step drill-down (mirroring `injection_skills`'s list → index → issue pattern): + +```json +{"action": "list"} +{"device_type": "frr_vtysh", "detail": "index"} +{"device_type": "frr_vtysh", "topic": "bgp"} +``` + +Topic bodies are only served on an explicit `topic` request — every other detail level returns a topic index — so adding topics to a device does not grow the token cost of device-level lookups. + ## Configuration Skills repository settings are configured in `gns3_server.conf` under the `[Server]` section: ```ini [Server] -skills_repo_url = https://github.com/yueguobin/GNS3-Skills.git +skills_repo_url = https://github.com/gns3/gns3-skills.git skills_repo_branch = main skills_auto_update = true ``` | Setting | Default | Description | |---------|---------|-------------| -| `skills_repo_url` | `https://github.com/yueguobin/GNS3-Skills.git` | Git repository URL | +| `skills_repo_url` | `https://github.com/gns3/gns3-skills.git` | Git repository URL | | `skills_repo_branch` | `main` | Git branch to track | | `skills_auto_update` | `true` | Automatically pull on reload | diff --git a/docs/gns3-copilot/packet-analysis-roadmap.md b/docs/gns3-copilot/packet-analysis-roadmap.md deleted file mode 100644 index f6b83991c..000000000 --- a/docs/gns3-copilot/packet-analysis-roadmap.md +++ /dev/null @@ -1,92 +0,0 @@ - - -> This document is a roadmap/planning document. The described features have not been implemented yet. - - -# Protocol-Oriented Packet Analysis — Roadmap - -## Problem - -The current `PacketCaptureTool` (`analyze_packets`) only accepts a single `packet_number` parameter and runs `tshark -V` on that one frame. This approach: - -- Forces the LLM to guess packet numbers without any visibility into the capture -- Returns raw verbose output instead of structured data -- Has no protocol awareness — every protocol looks the same to the tool -- Provides no built-in anomaly detection; the LLM must infer issues from raw output each time -- Downloads the same pcap from the server on every call (no caching) - -## Proposed Architecture - -Move from number-based to **protocol-oriented** packet analysis. Protocol definitions (fields, display filters, anomaly checks) are stored as YAML files in the GNS3-Skills repository. The tool only needs `link_id` + `protocol` — no complex parameters for the LLM to get wrong. - -### Data Flow - -``` -GNS3-Skills/packet_analysis/.yaml - │ - ▼ loaded at startup / reload -PACKET_ANALYSIS_REGISTRY (in-memory dict) - │ - ▼ -PacketAnalysisTool(link_id, protocol) - │ - ├── download pcap (with link_id caching) - ├── tshark -T fields -e - ├── run anomaly checks from YAML - └── return structured JSON + check results - │ - ▼ - LLM produces natural language explanation -``` - -### YAML Format - -```yaml -name: "OSPF Packet Analysis" -protocol: "ospf" -display_filter: "ospf" -fields: - - label: "Source IP" - field: "ip.src" - description: "Source IPv4 address" - - label: "OSPF Message Type" - field: "ospf.msg" - description: "1=Hello, 2=DBD, 3=LSR, 4=LSU, 5=LSAck" -checks: - - name: hello_dead_mismatch - severity: critical - message: "Hello/Dead Interval mismatch between {src} and {dst}" - condition: "Same broadcast domain has inconsistent hello/dead intervals" -``` - -### Planned Protocols - -| File | Protocols | Key Checks | -|------|-----------|------------| -| `arp.yaml` | ARP, NDP (ICMPv6 NS/NA) | Duplicate IP, no ARP reply, ARP flooding | -| `icmp.yaml` | ICMPv4, ICMPv6 | Unreachable classification, ping loss, PMTUD issues | -| `ospf.yaml` | OSPFv2, OSPFv3 | Hello/Dead mismatch, Area ID mismatch, Router ID conflict | -| `bgp.yaml` | BGPv4, BGP+ | Hold timer mismatch, Notification analysis, AS_PATH loop | - -### Tool Interface - -```json -{ - "link_id": "uuid (required)", - "protocol": "arp | icmp | ospf | bgp (required)", - "summary_only": "bool (optional, default: false)" -} -``` - -## Status - -- [ ] GNS3-Skills: create `packet_analysis/` directory and YAML definitions -- [ ] gns3-server: add `PACKET_ANALYSIS_REGISTRY` loading from skills repo -- [ ] gns3-server: implement `PacketAnalysisTool` with tshark field extraction -- [ ] gns3-server: implement protocol-specific anomaly checks -- [ ] gns3-server: add pcap caching by `link_id` -- [ ] gns3-server: register tool in teaching assistant and lab automation modes -- [ ] gns3-server: deprecate and remove old `PacketCaptureTool` diff --git a/docs/gns3-copilot/roadmap/aiops-fault-injection-testing-pipeline-roadmap.md b/docs/gns3-copilot/roadmap/aiops-fault-injection-testing-pipeline-roadmap.md new file mode 100644 index 000000000..760785efd --- /dev/null +++ b/docs/gns3-copilot/roadmap/aiops-fault-injection-testing-pipeline-roadmap.md @@ -0,0 +1,280 @@ + + +> This document is a roadmap/planning document. The described features have not been implemented yet. + +# AIOps Fault Injection Testing Pipeline — Roadmap + +## Overview + +Build a realistic testing pipeline that duplicates the company's production network architecture into GNS3, then systematically injects network faults using the AI Copilot's fault injection capabilities to validate and train the AIOps module before production deployment. + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ AIOps Testing Pipeline │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ +│ │ Network │ │ Fault │ │ AIOps │ │ +│ │ Duplication │───▶│ Injection │───▶│ Validation │ │ +│ │ (Phase 1) │ │ (Phase 2) │ │ (Phase 3) │ │ +│ └──────────────┘ └──────────────┘ └──────────────────┘ │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ +│ │ GNS3 Network │ │ Fault │ │ Results & │ │ +│ │ Replica │ │ Scenarios │ │ Reporting │ │ +│ └──────────────┘ └──────────────┘ └──────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +## Core Concept + +1. **Duplicate** the company's production network architecture into a GNS3 simulation environment +2. **Select** a set of fault types to test (OSPF, BGP, VxLAN, STP, packet filter, etc.) +3. **Inject** faults automatically using the AI Copilot's fault injection capabilities +4. **Validate** whether the AIOps module correctly identifies and reports each fault +5. **Loop** through all selected fault scenarios, building a comprehensive test matrix +6. **Train** the AIOps module on results to improve accuracy before production deployment + +## Phase 1: Network Architecture Duplication + +### Goal + +Create a high-fidelity replica of the company production network in GNS3. + +### Key Tasks + +- [ ] **Topology Mapping**: Document production network topology (devices, links, protocols) +- [ ] **Device Selection**: Map production devices to GNS3-compatible images (Cisco IOSv, XRv, Juniper vSRX, etc.) +- [ ] **Configuration Extraction**: Export sanitized production configs (remove passwords, public IPs, sensitive data) +- [ ] **GNS3 Deployment**: Build the network in GNS3 with accurate device placement and links +- [ ] **Config Replication**: Apply adapted configurations to GNS3 devices +- [ ] **Connectivity Validation**: Verify OSPF/BGP adjacencies, VLANs, VRFs, and end-to-end reachability +- [ ] **Baseline Capture**: Record normal operation metrics (CPU, memory, interface counters, routing tables) + +### Considerations + +- Sanitize all production configurations before importing into GNS3 +- Use environment-specific IP addressing where necessary (loopbacks, management) +- Document all deviations from production for traceability + +## Phase 2: Fault Injection Pipeline + +### Goal + +Systematically inject network faults and validate AIOps detection using the existing AI Copilot fault injection infrastructure. + +### Components + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ Fault Injection Pipeline │ +├──────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ +│ │ Scenario │ │ Inject │ │ AIOps │ │ Record │ │ +│ │ Selector │──▶│ Fault │──▶│ Validate │──▶│ & Report │ │ +│ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │ +│ │ │ │ │ │ +│ │ │ │ │ │ +│ └──────────────┴──────────────┴───────────────┘ │ +│ │ │ +│ ▼ │ +│ Loop until all scenarios tested │ +└──────────────────────────────────────────────────────────────────┘ +``` + +### 2.1 Scenario Selector + +- Read fault scenarios from the GNS3-Skills repository +- Support filtering by: + - Protocol (OSPF, BGP, VxLAN, STP, VLAN, etc.) + - Severity (critical, high, medium, low) + - Difficulty (beginner, intermediate, advanced) +- Track which scenarios have been tested +- Randomize selection order to avoid bias +- Exclude previously tested scenarios + +### 2.2 Fault Injection + +- Use existing `manage_gns3_packet_filter` tool for network-level faults +- Use existing `execute_multiple_device_config_commands` for configuration faults +- Use existing `InjectionSkillsTool` to query and select appropriate faults +- Support combined faults (multiple simultaneous issues) +- Auto-recovery between scenarios (restore baseline state) + +### 2.3 AIOps Validation + +- Feed network state (after fault injection) to the AIOps module +- Record AIOps diagnosis output +- Compare AIOps results against expected fault definition: + - **Correct identification**: AIOps names the exact fault + - **Partial identification**: AIOps identifies related symptoms but not root cause + - **Missed**: AIOps fails to detect any issue + - **False positive**: AIOps reports a fault that doesn't exist + +### 2.4 Test Execution Flow + +``` +1. Reset network to clean baseline state +2. Select next untested fault scenario +3. Inject the fault into the GNS3 network +4. Wait for convergence (configurable delay) +5. Query AIOps module for diagnosis +6. Compare AIOps output with expected fault definition +7. Record result (pass/fail/partial) +8. Restore network to baseline +9. Repeat from step 2 until all scenarios completed +``` + +## Phase 3: Traffic Injection (Enhanced Realism) + +### Goal + +Add realistic network traffic to the GNS3 simulation so that AIOps has real telemetry data to analyze, rather than a static network. + +### Approaches + +#### 3.1 Traffic Generators in GNS3 + +- Deploy traffic generator appliances in GNS3 (e.g., TRex, Ostinato, Scapy on Linux nodes) +- Generate realistic traffic patterns: + - VoIP/RTP streams + - HTTP/HTTPS web traffic + - Database replication + - Routing protocol updates (OSPF hellos, BGP keepalives) + - ICMP monitoring traffic + +#### 3.2 tcpreplay with Captured Traffic + +- Capture real production traffic (sanitized) +- Use `tcpreplay` to replay traffic through the GNS3 network +- More realistic than synthetic traffic generators + +#### 3.3 Integration with Network Monitoring + +- Feed simulated device telemetry (SNMP, syslog, NetFlow) to the AIOps module +- Enable AIOps to analyze real-time telemetry during fault conditions +- Validate that AIOps can distinguish between traffic anomalies and actual faults + +## Success Metrics + +| Metric | Target | Measurement Method | +|--------|--------|-------------------| +| Fault detection rate | >95% | AIOps correctly identifies injected faults | +| False positive rate | <5% | AIOps reports fault when none exists | +| Time to detection | <30s | Duration from injection to AIOps alert | +| Coverage | >80% of defined scenarios | Percentage of scenarios tested | +| Accuracy improvement | Measurable per cycle | Compare pass rates across test cycles | + +## Technical Architecture + +``` +┌────────────────────────────────────────────────────────────────────┐ +│ Test Orchestrator │ +│ ┌──────────────────────────────────────────────────────────────┐ │ +│ │ Test Runner (Python) │ │ +│ │ - Scenario selection & scheduling │ │ +│ │ - Fault injection coordination │ │ +│ │ - AIOps query & result collection │ │ +│ │ - Report generation │ │ +│ └──────────┬────────────────────────────────────────────┬───────┘ │ +│ │ │ │ +│ ▼ ▼ │ +│ ┌────────────────────┐ ┌────────────────────┐ │ +│ │ GNS3 Controller │ │ AIOps Module │ │ +│ │ (gns3-server) │ │ │ │ +│ │ - Network mgmt │ │ - Fault diagnosis │ │ +│ │ - Fault injection │ │ - Alert detection │ │ +│ │ - State queries │ │ - Root cause │ │ +│ └────────────────────┘ └────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌────────────────────┐ │ +│ │ GNS3 Network │ │ +│ │ Replica │ │ +│ │ - Devices │ │ +│ │ - Traffic │ │ +│ │ - Telemetry │ │ +│ └────────────────────┘ │ +└────────────────────────────────────────────────────────────────────┘ +``` + +## Reporting + +Each test cycle produces: + +- **Summary report**: Pass/fail rates, coverage, trends +- **Detailed per-scenario report**: Injection details, AIOps response, comparison +- **Regression tracker**: Which scenarios regressed since last cycle +- **Accuracy trend**: Improvement or degradation over time + +### Example Report Entry + +```yaml +test_cycle: 7 +date: "2026-06-01" +scenarios_planned: 20 +scenarios_completed: 18 +failed_injections: 1 +skipped: 1 +results: + - scenario: ospf_hello_dead_mismatch + protocol: ospf + severity: major + injection_method: device_config + target_device: R1 + aiops_detection: true + aiops_diagnosis: "OSPF Hello/Dead interval mismatch between R1 and R2" + detection_latency_ms: 12000 + match: exact + - scenario: packet_loss_heavy + protocol: performance + severity: high + injection_method: packet_filter + target_link: "R1 ↔ R2 (ethernet)" + aiops_detection: true + aiops_diagnosis: "High packet loss detected on link R1-R2" + detection_latency_ms: 45000 + match: partial +``` + +## Dependencies + +- [ ] GNS3 network replica ready and validated +- [ ] AI Copilot fault injection tools operational +- [ ] AIOps module query interface available +- [ ] Traffic generation tools deployed (for Phase 3) +- [ ] Test orchestrator framework (to be built) +- [ ] Result database and reporting system + +## Timeline + +| Phase | Duration | Deliverable | +|-------|----------|-------------| +| P1: Network Duplication | 2-4 weeks | GNS3 replica of production network | +| P2: Fault Injection Pipeline | 2-3 weeks | Automated test runner + first results | +| P3: Traffic Injection | 2-4 weeks | Realistic traffic simulation integrated | + +## Status + +- [ ] P1: Network architecture duplication + - [ ] Topology mapping documented + - [ ] Device configurations sanitized and adapted + - [ ] GNS3 replica deployed + - [ ] Baseline connectivity verified +- [ ] P2: Fault injection pipeline + - [ ] Scenario selection framework + - [ ] Automated fault injection + - [ ] AIOps validation interface + - [ ] Report generation + - [ ] Loop/retry mechanism +- [ ] P3: Traffic injection + - [ ] Traffic generator deployment + - [ ] Traffic pattern library + - [ ] AIOps telemetry integration + diff --git a/docs/gns3-copilot/injection-fault-tracking-roadmap.md b/docs/gns3-copilot/roadmap/injection-fault-tracking-roadmap.md similarity index 100% rename from docs/gns3-copilot/injection-fault-tracking-roadmap.md rename to docs/gns3-copilot/roadmap/injection-fault-tracking-roadmap.md diff --git a/docs/gns3-copilot/roadmap/rbac-user-isolation-roadmap.md b/docs/gns3-copilot/roadmap/rbac-user-isolation-roadmap.md new file mode 100644 index 000000000..447279d07 --- /dev/null +++ b/docs/gns3-copilot/roadmap/rbac-user-isolation-roadmap.md @@ -0,0 +1,621 @@ +# RBAC User Isolation Roadmap + +## Overview + +GNS3 3.0 ships with a complete RBAC framework (ACE + Role + Privilege models), but the user isolation layer is incomplete. Users can see resources they should not have access to because: + +- Resource creation does not auto-grant the creator an ACE +- List endpoints return unfiltered results +- Two GET endpoints have RBAC checks bypassed via FIXME + +**This document has been updated to reflect the implemented solution in `feature/simple-user-isolation` branch.** + +## Current State + +| Resource | Route Check | List Filtering | Auto-ACE on Create | ACE Cleanup on Delete | +|---|---|---|---|---| +| Project | `Project.Audit/Modify/Allocate` | **Implemented** — Three-step filtering | **Not needed** | Done | +| Template | `Template.Audit` **FIXME** | None — all templates returned | **Missing** | Done | +| Node | `Node.Audit/Modify/Allocate` | N/A (inherits project) | Inherits project | N/A | +| Link | `Link.Audit/Modify/Allocate` | N/A (inherits project) | Inherits project | N/A | +| Drawing | `Drawing.Audit/Modify/Allocate` | N/A (inherits project) | Inherits project | N/A | +| Snapshot | `Snapshot.Audit/Allocate/Restore` | N/A (inherits project) | Inherits project | N/A | +| Image | `Image.Audit/Allocate` | None — all images returned | **Missing** | Missing | +| Compute | `Compute.Audit` **FIXME** | None — all computes returned | N/A (shared infra) | Done | +| Appliance | `Appliance.Audit/Allocate` | None — all appliances returned | N/A (builtin) | N/A | +| Symbol | `Symbol.Audit/Allocate` | None — all symbols returned | N/A (builtin) | N/A | + +## Implemented Solution: Project Isolation + +### Three-Step Permission Check Logic + +**Status**: ✅ Implemented in `feature/simple-user-isolation` branch + +```python +# Step 1: ACE check - basic access permission +# Get projects user has ACE for +ace_projects = [] +for project in controller.projects.values(): + project_path = f"/projects/{project.id}" + if await rbac_repo.check_user_has_privilege(current_user.user_id, project_path, "Project.Audit"): + ace_projects.append(project) + +# Step 2: Filter ace_projects by created_by - user's own projects +# Project sharing is only available through resource pools +user_projects = [p.asdict() for p in ace_projects if p.created_by == current_user.username] +projects.extend(user_projects) + +# Step 3: Resource pool projects +# Projects shared through resource pools +user_pool_resources = await rbac_repo.get_user_pool_resources(current_user.user_id, "Project.Audit") +project_ids_in_pools = [str(r.resource_id) for r in user_pool_resources if r.resource_type == "project"] +pool_projects = [p.asdict() for p in controller.projects.values() if p.id in project_ids_in_pools] +projects.extend(pool_projects) +``` + +### Key Design Principles + +1. **ACE for basic access control**: Controls whether user can access the system +2. **created_by for user isolation**: Controls which specific resources user can access +3. **Resource pools for project sharing**: The only mechanism for sharing projects between users +4. **No direct ACE sharing**: Users cannot configure ACE directly on specific projects to share them + +### Advantages of This Approach + +- **Fault tolerance**: Even with broad ACE configuration (`path: "/" + propagate: true`), user isolation remains effective +- **Clear separation**: Basic access, data ownership, and team sharing are clearly separated +- **Simple mechanism**: No complex auto-ACE or seen_project_ids tracking required +- **Performance**: Leverages existing created_by field, no schema changes needed + +## Updated Architecture + +```mermaid +graph TD + subgraph Client + WebUI + CLI + end + + subgraph "Controller API" + Auth[get_current_active_user] + Routes[Resource Routes] + ACE_Check[Step 1: ACE Check] + Owner_Filter[Step 2: Filter by created_by] + Pool_Check[Step 3: Resource Pools] + end + + subgraph "RBAC Engine" + ACE[(ACE table)] + Role[(Role table)] + Privilege[(Privilege table)] + Checker[check_user_has_privilege] + end + + WebUI --> Auth + CLI --> Auth + Auth --> Routes + Routes --> ACE_Check + ACE_Check --> Checker + ACE_Check --> Owner_Filter + Owner_Filter --> Pool_Check + Pool_Check --> Checker + Checker --> ACE + Checker --> Role + Role --> Privilege +``` + +## Business Process + +### Project listing with three-step filtering + +```mermaid +sequenceDiagram + actor U as User + participant API as GET /projects + participant Controller as Controller + participant RBAC as RBAC Engine + + U->>API: List projects + API->>API: get_current_active_user (not superadmin) + + API->>RBAC: Step 1: ACE check on each project + loop Each project + RBAC-->>API: ACE results + end + + API->>API: Step 2: Filter by created_by + API-->>U: User's own projects + + API->>RBAC: Step 3: Resource pool projects + RBAC-->>API: Pool projects + + API-->>U: Combined list (own + pool) +``` + +## Updated Phased Plan + +### ✅ Phase 1 — MVP: Project isolation (COMPLETED) + +**Goal**: Users only see projects they created or were granted access to through resource pools. + +| Task | Files | Status | Detail | +|---|---|---|---| +| Fix project list filtering | `projects.py` — `get_projects()` | ✅ **Implemented** | Three-step filtering: ACE check → created_by filter → resource pools | + +**Result**: +- ✅ Users can only see projects they created +- ✅ Team collaboration through resource pools works +- ✅ Fault tolerance: Works correctly even with broad ACE configurations +- ✅ No schema changes required +- ✅ ~30 lines changed + +### Phase 2 — Template isolation + +**Goal**: Users see their own templates + builtin templates only. + +| Task | Files | Detail | +|---|---|---| +| Apply same pattern to templates | `templates.py` — `get_templates()` | Use same three-step filtering as projects | +| Uncomment `Template.Audit` | `templates.py` | Restore `has_privilege("Template.Audit")` checks | + +**Dependency**: Web UI must handle 403 from `GET /templates/{id}`. Mitigation: keep builtin templates unconditionally visible so the UI always has data. + +### Phase 3 — Image isolation (optional) + +**Goal**: Users see only images they uploaded. + +| Task | Files | +|---|---| +| Apply same pattern to images | `images.py` — `get_images()` | +| Fix image list filtering | `images.py` | +| ACE cleanup on delete | `images.py` — `delete_image()` | + +### Phase 4 — Default ACE for "Users" group (optional) + +**Goal**: Users in "Users" group can create/list resources without admin ACE intervention. + +| Task | Detail | +|---|---| +| Default ACE on `/projects` | Grant "Users" group → User role → `/projects` (propagate=False) | +| Default ACE on `/templates` | Grant "Users" group → User role → `/templates` (propagate=False) | + +## API Endpoints Changed + +### Phase 1 (Implemented) + +| Method | Path | Change | +|---|---|---| +| `GET` | `/v3/projects` | Three-step filtering: ACE → created_by → resource pools | + +### Phase 2 (Planned) + +| Method | Path | Change | +|---|---|---| +| `GET` | `/v3/templates` | Apply same three-step filtering | +| `GET` | `/v3/templates/{id}` | Restore `Template.Audit` check | + +## Key Design Decisions + +1. **Project sharing through resource pools only**: Users cannot configure ACE directly to share specific projects. All sharing must go through resource pools. This prevents permission configuration chaos and maintains clear ownership semantics. + +2. **No auto-ACE required**: The three-step filtering logic works without needing automatic ACE creation on project creation. The created_by field provides sufficient ownership information. + +3. **Fault-tolerant to ACE configuration**: Even if administrators configure broad ACE permissions (like `path: "/" + propagate: true`), user isolation remains effective because Step 2 filters by created_by. + +4. **No DB migration required**: Uses existing ACE/role/privilege tables and created_by field. No schema changes needed. + +5. **Performance**: Project list filtering is O(n) where n is the total number of projects. Each project requires one ACE check. Acceptable for < 500 projects. Can optimize later with batch ACE queries if needed. + +## Updated References + +- **Implementation**: `gns3server/api/routes/controller/projects.py` (feature/simple-user-isolation branch) +- **Discussion**: https://github.com/GNS3/gns3-server/discussions/1949 +- **RBAC models**: `gns3server/db/models/acl.py`, `roles.py`, `privileges.py` +- **RBAC repository**: `gns3server/db/repositories/rbac.py` +- **Auth dependency**: `gns3server/api/routes/controller/dependencies/authentication.py` +- **RBAC dependency**: `gns3server/api/routes/controller/dependencies/rbac.py` +- **Resource pools**: `gns3server/db/models/pools.py` and `gns3server/db/repositories/pools.py` + +## Implementation Notes + +### What Was Implemented + +The `feature/simple-user-isolation` branch implements a robust user isolation mechanism that: + +1. **Integrates with existing RBAC framework** without breaking changes +2. **Leverages the created_by field** that already exists in the Project model +3. **Uses three-step pipeline filtering** to avoid complex seen_project_ids tracking +4. **Supports team collaboration** through existing resource pool functionality +5. **Is fault-tolerant to ACE misconfiguration** - broad ACE permissions don't break user isolation + +### What Was Not Implemented + +The original roadmap's Phase 1 included auto-ACE creation on project creation. This was determined to be unnecessary because: + +- The three-step filtering logic achieves user isolation without auto-ACE +- Auto-ACE would add complexity without significant benefit +- Project sharing through resource pools is cleaner than direct ACE configuration + +### Future Work + +The same three-step filtering pattern can be applied to: +- **Templates**: Replace the FIXME comment with proper filtering logic +- **Images**: Apply the same pattern for user image isolation +- **Other resources**: Extend the pattern as needed + +This implementation provides a solid foundation for user isolation in GNS3 3.0+ while maintaining compatibility with the existing RBAC framework. + +## Phase 5 — ACE Architecture Refactoring (Future) + +**Goal**: Improve ACE manageability by supporting multiple paths and resource pools in a single ACE entry. + +### Current Problem + +With the current design where one ACE = one path: +- **ACE explosion**: 5 user groups × 10 resource pools = 50 ACE entries +- **Management complexity**: Difficult to maintain and understand ACE purpose +- **Performance impact**: Permission checking must iterate through many ACE entries + +### Proposed Solution + +Redesign ACE structure to support multiple paths and resource pools in a single ACE entry: + +```json +{ + "name": "Development Team Access", + "description": "Full access for development team", + "ace_type": "group", + "group_id": "...", + "role_id": "...", + "paths": ["/projects", "/templates", "/images"], + "resource_pools": ["pool-id-1", "pool-id-2"], + "propagate": true, + "allowed": true +} +``` + +### Database Changes Required + +1. **Add name and description to ACE table**: +```sql +ALTER TABLE acl ADD COLUMN name VARCHAR; +ALTER TABLE acl ADD COLUMN description TEXT; +``` + +2. **Create association tables**: +```sql +CREATE TABLE ace_paths ( + ace_id UUID REFERENCES acl(ace_id), + path VARCHAR, + PRIMARY KEY (ace_id, path) +); + +CREATE TABLE ace_pools ( + ace_id UUID REFERENCES acl(ace_id), + resource_pool_id UUID REFERENCES resource_pools(resource_pool_id), + PRIMARY KEY (ace_id, resource_pool_id) +); +``` + +3. **Update permission checking logic** to check both paths and resource_pools tables + +### Benefits + +- ✅ **Reduced ACE entries**: One ACE covers multiple related paths/pools +- ✅ **Better organization**: Logical grouping with clear names and descriptions +- ✅ **Easier management**: Edit one ACE instead of multiple related entries +- ✅ **Improved performance**: Fewer ACE entries to check during permission validation + +### Implementation Considerations + +- **Migration path**: Need to migrate existing single-path ACEs to new structure +- **Backward compatibility**: API should support both old and new formats during transition +- **UI updates**: ACE management interface needs to support multi-path/pool selection +- **Permission checking**: Update `check_user_has_privilege` to check association tables + +## Phase 6 — Frontend Permission Query API (Future) + +**Goal**: Provide an API endpoint for the Web UI to query the current user's permissions, enabling dynamic UI rendering based on role and ACE configuration. + +### Problem + +Currently the Web UI cannot determine what the authenticated user is allowed to see or do: + +- ❌ Users see menu items and buttons they don't have permission to use +- ❌ Clicking a forbidden action results in a 403 error (unexpected UX) +- ❌ No way to hide/show UI elements based on actual permissions + +### Proposed Solution + +Create a `GET /v3/me/permissions` endpoint that returns the current user's effective permissions: + +```json +{ + "user_id": "uuid", + "is_superadmin": false, + "permissions": [ + {"path": "/projects", "privileges": ["Project.Audit", "Project.Allocate"]}, + {"path": "/projects/{id}", "privileges": ["Project.Audit", "Project.Modify"]}, + {"path": "/templates", "privileges": ["Template.Audit"]} + ], + "pools": [ + {"path": "/pools/{id}", "name": "Team Projects", "privileges": ["Pool.Audit"]} + ] +} +``` + +### Benefits + +- ✅ **Dynamic UI**: Frontend can hide inaccessible menus/buttons +- ✅ **Better UX**: Users only see what they can actually use +- ✅ **Reduced errors**: Fewer 403 responses from hidden operations +- ✅ **Faster feedback**: Permission checks happen at render time, not request time + +### Dependencies + +- **Phase 5 (ACE refactoring)** may change how permissions are stored, which would affect this API's implementation + +## Phase 7 — Resource Pool Renaming (Future) + +**Goal**: Rename "Resource Pool" to a more descriptive name that better reflects its actual purpose. + +### Problem + +The current name "Resource Pool" is too generic and doesn't clearly convey its actual function: + +- ❌ **Ambiguous name**: "Resource Pool" could refer to compute pools, connection pools, etc. +- ❌ **Unclear purpose**: Users don't understand it's primarily for sharing projects +- ❌ **Discoverability**: Hard to find the right feature when looking for project sharing + +### Actual Function + +Resource pools in GNS3 are used for: +- **Project sharing**: Allow users to access projects created by other users +- **Team collaboration**: Enable team members to work on shared projects +- **Access control**: Provide fine-grained permissions for project access through three-step filtering (ACE check → created_by filter → resource pools) + +### Proposed Name Options + +| Option | Pros | Cons | +|--------|-------|-------| +| **Project Pool** | More explicit, indicates it contains projects | Still uses "pool" terminology | +| **Shared Projects** | Directly describes the function | Loses the "collection" concept | +| **Team Projects** | Emphasizes collaboration use case | Doesn't cover non-team sharing scenarios | + +**Recommended**: **Project Pool** - strikes a balance between clarity and consistency with existing terminology. + +### Implementation Scope + +Renaming would require changes to: +- Database tables: `resource_pools` → `project_pools` +- API routes: `/v3/pools` → `/v3/project_pools` +- Schema classes and field names throughout the codebase +- All documentation and help text +- Migration script to preserve existing data + +### Benefits + +- ✅ **Improved discoverability**: Users can easily find the project sharing feature +- ✅ **Better onboarding**: New users understand the purpose without confusion +- ✅ **Clearer API**: API endpoints and schemas more self-documenting + +### Implementation Considerations + +- **Breaking change**: Requires API version bump or backward compatibility layer +- **Data migration**: Existing resource pools must be preserved during table rename +- **Documentation updates**: All references in docs, tutorials, and API specs need updating +- **UI changes**: Frontend labels and navigation menus need to match new terminology + +## Phase 8 — Per-User Project Namespace (Future) + +**Goal**: Allow project names to be unique per user instead of globally unique, enabling better user experience. + +### Current Problem + +Although Phase 1 implements user isolation (users only see projects they created or have access to through resource pools), project names remain globally unique: + +- ❌ **Naming conflicts**: Alice and Bob cannot both create a project named "My Project" +- ❌ **Unnecessary restrictions**: Even though projects are isolated, users must coordinate globally unique names +- ❌ **Poor user experience**: Users get confusing error messages when trying to use common names like "Test Project" + +### Proposed Solution + +Change project uniqueness from global to per-user: + +**Current:** +```sql +UNIQUE(name) -- Project names must be globally unique +``` + +**Proposed:** +```sql +UNIQUE(user_id, name) -- Project names unique per user +``` + +### Benefits + +- ✅ **Better UX**: Users can name projects whatever they want without worrying about global conflicts +- ✅ **Natural naming**: Common names like "Test Project" or "Demo" can coexist between users +- ✅ **No coordination needed**: Teams don't need to maintain a shared project naming registry +- ✅ **Consistent isolation**: Projects are isolated both in visibility AND naming + +### Implementation Scope + +Changes required: +- **Database schema**: Modify Project table unique constraint from `(name)` to `(user_id, name)` +- **Migration script**: Handle existing projects with conflicting names +- **API validation**: Update project creation validation logic +- **Frontend**: Remove global name uniqueness checks from UI + +### Migration Considerations + +**Handling existing name conflicts:** +If the database already has projects with the same name but different users: +- Option 1: Keep existing names, only enforce uniqueness for new projects +- Option 2: Append suffixes to duplicates (e.g., "My Project (alice)", "My Project (bob)") +- Option 3: Require admin resolution for conflicts before enabling new constraint + +**Recommended**: Option 1 (grandfather existing projects) for minimal disruption. + +### Dependencies + +- **Phase 1 (User isolation)**: Must be completed first +- Database migration required to modify unique constraint + +## Phase 9 — User Self-Registration (Future) + +**Goal**: Allow users to register their own accounts without requiring manual admin creation. + +### Current State + +Currently, user accounts can only be created by administrators or through direct database operations: +- ❌ **Admin burden**: Every new user requires manual account creation +- ❌ **Poor scalability**: Not suitable for public deployments or large organizations +- ❌ **Friction**: Users cannot immediately start using the system + +### Proposed Features + +**Self-Registration Flow:** +1. User provides email, username, and password +2. System validates input and creates account +3. Optional email verification to confirm email address +4. Account created with default role (typically "User" role) +5. User can immediately log in and start creating projects + +**Email Verification (Optional):** +- Send verification email with confirmation link/code +- Verify email address before granting full access +- Prevent spam account creation +- Require SMTP server configuration + +### Implementation Components + +1. **New API endpoint**: `POST /v3/access/register` (public, no authentication required) +2. **Email service**: Integration with SMTP server for sending emails +3. **Configuration**: SMTP settings (host, port, credentials, encryption) +4. **Rate limiting**: Prevent abuse of self-registration +5. **Captcha integration**: Optional bot protection + +### Configuration + +```yaml +Server: + email: + enabled: true + smtp_host: smtp.example.com + smtp_port: 587 + smtp_username: noreply@example.com + smtp_password: secret + use_tls: true + registration: + require_email_verification: true + default_role: "User" + allow_public_registration: true +``` + +### Security Considerations + +- ✅ **Rate limiting**: Prevent spam account creation +- ✅ **Email verification**: Confirm email ownership +- ✅ **Default permissions**: New users get limited default role +- ✅ **Admin approval** (optional): Require admin approval before account activation +- ❌ **Superadmin creation**: Never allow self-registration as superadmin + +### Dependencies + +- **Phase 10 (Email service)**: SMTP integration required for email verification + +## Phase 10 — Email Service Integration (Future) + +**Goal**: Implement email sending capability for notifications, verification, and alerts. + +### Use Cases + +1. **User registration**: Email verification links/codes +2. **Password reset**: Secure password reset emails +3. **System alerts**: Error notifications, system updates +4. **Project sharing**: Notify users when projects are shared via resource pools +5. **Usage reports**: Periodic usage summaries or quota alerts + +### Proposed Implementation + +**Email Service Architecture:** +- Abstract email service interface +- Support multiple email providers (SMTP, SendGrid, AWS SES, etc.) +- Email templates with Jinja2 for customization +- Async email sending to avoid blocking API responses +- Email queue for retry logic on failures + +**Email Templates:** +- Registration verification +- Password reset +- Project shared notification +- System alerts +- Usage reports + +**API Endpoints:** +```python +# Configuration (admin only) +POST /v3/access/config/email +GET /v3/access/config/email +PUT /v3/access/config/email + +# Test email (admin only) +POST /v3/access/config/email/test + +# Password reset (public) +POST /v3/access/users/password/reset/request +POST /v3/access/users/password/reset/confirm + +# Email verification (public) +POST /v3/access/users/verify/email +POST /v3/access/users/verify/confirm +``` + +### Database Schema + +New table for email tracking: +```sql +CREATE TABLE email_verification_tokens ( + token_id UUID PRIMARY KEY, + user_id UUID REFERENCES users(user_id) ON DELETE CASCADE, + token VARCHAR(255), # Verification code + purpose VARCHAR(50), # 'registration', 'password_reset', etc. + expires_at DATETIME, + created_at DATETIME, + used BOOLEAN DEFAULT FALSE +); +``` + +### Configuration + +```yaml +Server: + email: + enabled: true + provider: "smtp" # or "sendgrid", "aws_ses", etc. + from_address: "noreply@example.com" + from_name: "GNS3 Server" + reply_to: "support@example.com" + smtp: + host: smtp.example.com + port: 587 + username: noreply@example.com + password: encrypted_password + use_tls: true + templates_dir: /etc/gns3/email_templates +``` + +### Security Considerations + +- ✅ **Encrypted credentials**: SMTP passwords stored encrypted in database +- ✅ **Token expiration**: Verification tokens expire after configurable time +- ✅ **Rate limiting**: Prevent email spamming +- ✅ **Async sending**: Don't block API responses on email operations +- ✅ **Retry logic**: Handle temporary email service failures +- ✅ **Privacy**: Don't expose user information in error messages + +### Dependencies + +- **Phase 9 (Self-registration)**: User self-registration requires email verification +- Encryption utilities for storing SMTP credentials securely diff --git a/docs/gns3-copilot/roadmap/skills-editor-api-roadmap.md b/docs/gns3-copilot/roadmap/skills-editor-api-roadmap.md new file mode 100644 index 000000000..534265116 --- /dev/null +++ b/docs/gns3-copilot/roadmap/skills-editor-api-roadmap.md @@ -0,0 +1,318 @@ + + +> This documentation is organized by AI with reference to actual code. AI can make mistakes — please verify against the source code when in doubt. + + +# Skills Editor API + +## Overview + +A management API that allows the Web UI to browse, edit, save, and contribute skill files (prompts, fault injection, device skills, packet analysis protocols) back to the upstream GNS3-Skills repository via Pull Requests. + +Today, skills are read-only from the server's perspective — the only management endpoint is `POST /copilot/reload/skills` for hot-reloading. This API adds full CRUD operations on the local skills repository plus Git commit/push/PR workflows. + +## Architecture + +```mermaid +graph TD + subgraph "Web UI" + UI[Skills Editor Page] + end + + subgraph "GNS3 Server API" + API[skills_editor.py
/copilot/skills/*] + FM[SkillsFileManager] + SM[SkillsManager] + end + + subgraph "Local Git Repo" + INJ[injection/*.yaml] + DEV[device/*.yaml] + PA[packet_analysis/*.yaml] + PRM[prompts/*.md] + CFG[config/*.txt] + end + + subgraph "Remote" + GH[GitHub API
Pull Requests] + REPO[gns3/gns3-skills] + end + + UI -->|CRUD + PR| API + API --> FM + FM -->|file read/write| INJ + FM -->|file read/write| DEV + FM -->|file read/write| PA + FM -->|file read/write| PRM + FM -->|file read/write| CFG + FM -->|git commit/push| REPO + FM -->|create PR| GH + SM -->|hot reload| INJ + SM -->|hot reload| DEV + SM -->|hot reload| PA + SM -->|hot reload| PRM +``` + +## Business Process + +### Edit and Contribute Flow + +```mermaid +sequenceDiagram + participant UI as Web UI + participant API as Skills Editor API + participant FM as SkillsFileManager + participant Git as Local Git Repo + participant GH as GitHub + + UI->>API: GET /copilot/skills + API-->>UI: List categories + file counts + + UI->>API: GET /copilot/skills/injection + API-->>UI: List YAML files + + UI->>API: GET /copilot/skills/injection/ospf_issues + API->>FM: read_file("injection", "ospf_issues") + FM-->>API: YAML content + API-->>UI: File content + + UI->>API: PUT /copilot/skills/injection/ospf_issues
{content: "..."} + API->>FM: write_file("injection", "ospf_issues", content) + FM->>Git: Write file to disk + FM-->>API: Success + API-->>UI: Updated + + UI->>API: POST /copilot/skills/commit
{message: "fix: update OSPF fault"} + API->>FM: commit_changes(message, files) + FM->>Git: git add + git commit + FM-->>API: Commit hash + API-->>UI: Committed + + UI->>API: POST /copilot/skills/pull-request
{title, body, branch} + API->>FM: create_pull_request(...) + FM->>Git: git push origin + FM->>GH: POST /repos/{owner}/{repo}/pulls + GH-->>FM: PR URL + FM-->>API: PR created + API-->>UI: PR URL + + UI->>API: POST /copilot/skills/reload + API->>FM: Hot reload all registries + API-->>UI: Reloaded +``` + +## Valid Categories + +| Category | Directory | File Extension | Content | +|----------|-----------|---------------|---------| +| `prompts` | `prompts/` | `.md` | System prompts (teaching_assistant, lab_automation_assistant, etc.) | +| `injection` | `injection/` | `.yaml` | Fault injection skill definitions (OSPF, BGP, VLAN, etc.) | +| `device` | `device/` | `.yaml` | Device-specific command knowledge | +| `packet_analysis` | `packet_analysis/` | `.yaml` | Protocol definitions for tshark-based analysis | +| `config` | `config/` | `.txt` | Security and configuration files (forbidden_commands, etc.) | + +## API Endpoints + +All endpoints require **superadmin** authentication. Prefix: `/v3/copilot/skills`. + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/copilot/skills` | List all categories with file counts | +| GET | `/copilot/skills/{category}` | List files in a category | +| GET | `/copilot/skills/{category}/{filename}` | Read file content (without extension) | +| POST | `/copilot/skills/{category}` | Create a new skill file | +| PUT | `/copilot/skills/{category}/{filename}` | Update existing file content | +| DELETE | `/copilot/skills/{category}/{filename}` | Delete a skill file | +| GET | `/copilot/skills/status` | Git status (modified/untracked/deleted files) + repo info | +| POST | `/copilot/skills/commit` | Stage and commit changes | +| POST | `/copilot/skills/push` | Push a branch to remote | +| POST | `/copilot/skills/pull-request` | Create a Pull Request (direct or via fork) | +| POST | `/copilot/skills/reload` | Hot reload all skills into memory (replaces `/reload/skills`) | +| POST | `/copilot/skills/rollback/{commit_hash}` | Rollback repository to a specific commit | + +### Response Examples + +**GET /copilot/skills** — List categories + +```json +{ + "categories": [ + {"category": "injection", "file_count": 39, "path": "injection/"}, + {"category": "device", "file_count": 2, "path": "device/"}, + {"category": "packet_analysis", "file_count": 8, "path": "packet_analysis/"}, + {"category": "prompts", "file_count": 4, "path": "prompts/"}, + {"category": "config", "file_count": 1, "path": "config/"} + ], + "repository": { + "repo_url": "https://github.com/gns3/gns3-skills.git", + "branch": "main", + "current_version": "abc123def456...", + "is_dirty": false + } +} +``` + +**GET /copilot/skills/injection** — List files in category + +```json +{ + "category": "injection", + "files": [ + {"filename": "ospf_issues", "extension": ".yaml", "size": 4521, "last_modified": "2026-05-10T08:30:00Z"}, + {"filename": "bgp_issues", "extension": ".yaml", "size": 3820, "last_modified": "2026-05-09T14:00:00Z"} + ] +} +``` + +**GET /copilot/skills/injection/ospf_issues** — Read file content + +```json +{ + "category": "injection", + "filename": "ospf_issues", + "extension": ".yaml", + "content": "name: OSPF Fault Injection\n...\n", + "size": 4521, + "last_modified": "2026-05-10T08:30:00Z" +} +``` + +**PUT /copilot/skills/injection/ospf_issues** — Update file + +```json +{ + "content": "name: OSPF Fault Injection\n..." +} +``` + +Response: + +```json +{ + "category": "injection", + "filename": "ospf_issues", + "size": 4600, + "last_modified": "2026-05-12T10:00:00Z", + "status": "modified" +} +``` + +**POST /copilot/skills/commit** — Commit changes + +Request: + +```json +{ + "message": "fix: update OSPF hello/dead interval fault descriptions", + "files": ["injection/ospf_issues.yaml"] +} +``` + +Response: + +```json +{ + "success": true, + "commit_hash": "def456abc789...", + "message": "fix: update OSPF hello/dead interval fault descriptions", + "files_committed": 1 +} +``` + +**POST /copilot/skills/pull-request** — Create PR + +Request: + +```json +{ + "title": "Fix OSPF fault injection descriptions", + "body": "Updated OSPF hello/dead interval fault descriptions for clarity.", + "branch": "fix/ospf-descriptions", + "target_branch": "main", + "fork_url": "https://github.com/user/GNS3-Skills.git" +} +``` + +Response: + +```json +{ + "success": true, + "pr_url": "https://github.com/gns3/gns3-skills/pull/42", + "pr_number": 42, + "branch": "fix/ospf-descriptions" +} +``` + +**GET /copilot/skills/status** — Git status + +```json +{ + "current_version": "abc123def456...", + "branch": "main", + "is_dirty": true, + "modified": ["injection/ospf_issues.yaml"], + "untracked": [], + "deleted": [], + "staged": [], + "available_versions": [ + {"hash": "abc123def456", "message": "Add MPLS fault scenarios", "author": "dev", "date": "2026-05-10T08:00:00Z"} + ] +} +``` + +## Implementation Plan + +### New Files + +| File | Purpose | +|------|---------| +| `gns3server/agent/gns3_copilot/skills/file_manager.py` | `SkillsFileManager` class — file CRUD, git commit/push, GitHub PR API | +| `gns3server/api/routes/controller/skills_editor.py` | FastAPI router with all endpoints above | + +### Modified Files + +| File | Change | +|------|--------| +| `gns3server/agent/gns3_copilot/skills/manager.py` | Add `get_file_manager()` method returning a `SkillsFileManager` | +| `gns3server/api/routes/controller/__init__.py` | Register `skills_editor` router under `/copilot/skills` prefix | +| `gns3server/api/routes/controller/copilot.py` | Deprecate `/reload/skills` in favor of `/skills/reload` | + +### SkillsFileManager Key Methods + +| Method | Returns | +|--------|---------| +| `list_categories()` | `list[{category, file_count, path}]` | +| `list_files(category)` | `list[{filename, size, last_modified}]` | +| `read_file(category, filename)` | File content as string | +| `write_file(category, filename, content)` | Write (create or update) | +| `delete_file(category, filename)` | Delete file | +| `get_git_status()` | Modified/untracked/deleted file lists | +| `commit_changes(message, files)` | git add + commit | +| `push_to_remote(branch)` | git push | +| `create_pull_request(title, body, branch, target, fork_url)` | Push + GitHub PR API | + +## Security + +- **Authentication**: All endpoints require superadmin (`current_user.is_superadmin` check) +- **Path traversal prevention**: Category validated against whitelist; filename sanitized (no `/`, `..`, or absolute paths) +- **File extension enforcement**: `.yaml` for injection/device/packet_analysis, `.md` for prompts, `.txt` for config +- **File size limit**: Reject files > 1MB +- **YAML validation**: Validate with `yaml.safe_load` before saving +- **GitHub token**: Required for PR creation, stored in GNS3 server config + +### PR Creation Modes + +1. **Direct push**: Push to a new branch on the main repo, create PR via GitHub API +2. **Fork**: Push to user's fork, create PR against upstream + +The `fork_url` parameter selects the mode. When omitted, the API pushes to the same repository and creates a PR directly. + +## Related Documentation + +- [External Skills Repository](../implemented/skills-repository.md) +- [Fault Injection](../implemented/fault-injection.md) +- [Chat API](../implemented/chat-api.md) diff --git a/docs/gns3-copilot/template-based-configuration-roadmap.md b/docs/gns3-copilot/roadmap/template-based-configuration-roadmap.md similarity index 100% rename from docs/gns3-copilot/template-based-configuration-roadmap.md rename to docs/gns3-copilot/roadmap/template-based-configuration-roadmap.md diff --git a/docs/gns3-copilot/roadmap/user-node-limit-roadmap.md b/docs/gns3-copilot/roadmap/user-node-limit-roadmap.md new file mode 100644 index 000000000..1d59ad006 --- /dev/null +++ b/docs/gns3-copilot/roadmap/user-node-limit-roadmap.md @@ -0,0 +1,311 @@ +# User Node Limit Roadmap + +## Overview + +Implement a user-level node startup limit feature for GNS3 server to prevent single users from consuming excessive system resources. This feature is **disabled by default** and can be enabled through configuration files, supporting a three-tier configuration priority system. + +## Problem Statement + +Currently, GNS3 server has no mechanism to limit the number of nodes a user can start across all their projects. This can lead to: + +- **Resource exhaustion**: A single user can consume all available system resources +- **Unfair usage**: Some users may prevent others from using the system +- **System instability**: Too many running nodes can degrade overall performance +- **Cost issues**: In cloud environments, this can lead to unexpected costs + +## Solution Design + +### Core Principles + +1. **Default to no limits**: System maintains backward compatibility by defaulting to unrestricted usage +2. **Configuration-driven**: All limits can be controlled through configuration files +3. **Multi-tier priority**: User-specific > User group-specific > Global configuration +4. **Intelligent filtering**: Only count nodes that actually consume resources +5. **Clear error messages**: Users receive actionable feedback when limits are reached + +### Technical Architecture + +#### 1. Database Layer Extension + +**Files**: `gns3server/db/models/users.py` + +Add `max_nodes` field to both `User` and `UserGroup` models: + +```python +# In User model +max_nodes = Column(Integer, nullable=True) # NULL = no limit + +# In UserGroup model +max_nodes = Column(Integer, nullable=True) # NULL = no limit +``` + +#### 2. Configuration System Extension + +**File**: `gns3server/schemas/config.py` + +Add node limit configuration to `ControllerSettings`: + +```python +class NodeLimitSettings(BaseModel): + enabled: bool = False # Feature toggle (default disabled) + default_max_nodes: int = 5 # Default limit when enabled + excluded_node_types: List[str] = Field(default_factory=lambda: [ + "ethernet_switch", "ethernet_hub", "cloud", "nat" + ]) +``` + +#### 3. Core Service Implementation + +**New File**: `gns3server/services/node_limit_service.py` + +Implement the `NodeLimitService` class with key methods: + +```python +class NodeLimitService: + async def get_user_active_node_count(self, username: str, excluded_types: List[str]) -> int: + """Count user's active nodes across all projects""" + + async def get_user_node_limit(self, user: User) -> Optional[int]: + """Get user's node limit with priority logic""" + + async def check_user_node_limit(self, user: User, project: Project) -> Tuple[bool, str]: + """Check if user can start more nodes""" +``` + +#### 4. API Integration + +**File**: `gns3server/api/routes/controller/nodes.py` + +Add limit checking to node startup endpoint: + +```python +@router.post("/{node_id}/start") +async def start_node( + node: Node = Depends(dep_node), + current_user: User = Depends(get_current_active_user), + node_limit_service: NodeLimitService = Depends(get_node_limit_service) +): + # Node limit check + can_start, error_msg = await node_limit_service.check_user_node_limit( + current_user, node.project + ) + if not can_start: + raise HTTPException(status_code=403, detail=error_msg) + + # Original startup logic + await node.start() +``` + +### Node Counting Logic + +#### What Counts Toward the Limit + +- **Status**: Only nodes in `started` or `suspended` state +- **Ownership**: Only projects where `project.created_by == current_user.username` +- **Node types**: All node types except those explicitly excluded + +#### What's Excluded from the Limit + +- **Always-running nodes**: Ethernet switches, hubs (nodes where `is_always_running()` returns true) +- **Infrastructure nodes**: Cloud nodes and NAT nodes +- **Stopped nodes**: Nodes in `stopped` state +- **Other users' projects**: Nodes in projects created by other users + +#### Configuration Priority + +``` +User-specific limit (highest priority) + ↓ not set +User group limit + ↓ not set +Global configuration (if enabled) + ↓ disabled +No limit (default) +``` + +### Configuration Examples + +#### Scenario 1: Default No Limits (System Default) + +```ini +[Controller] +node_limits_enabled = false +``` + +**Result**: All users have no node limits + +#### Scenario 2: Enable Global Limits + +```ini +[Controller] +node_limits_enabled = true +node_limits_default_max_nodes = 5 +node_limits_excluded_types = ethernet_switch,ethernet_hub,cloud,nat +``` + +**Result**: All users limited to 5 active nodes (excluding infrastructure nodes) + +#### Scenario 3: User-Specific Limits + +Configuration file: `node_limits_enabled = false` + +Database: +- User A: `max_nodes = 10` (limited to 10 nodes) +- User B: `max_nodes = NULL` (no limit) +- Other users: no limit + +#### Scenario 4: User Group Limits + +Configuration file: `node_limits_enabled = false` + +Database: +- "Users" group: `max_nodes = 5` +- "Premium Users" group: `max_nodes = 20` +- "Administrators" group: `max_nodes = NULL` + +**Result**: Members inherit limits from their groups + +## Implementation Files + +| File Path | Type | Description | +|-----------|------|-------------| +| `gns3server/db/models/users.py` | Modify | Add `max_nodes` field to User and UserGroup | +| `gns3server/db_migrations/versions/xxx_add_node_limits.py` | New | Database migration script | +| `gns3server/schemas/config.py` | Modify | Add NodeLimitSettings configuration class | +| `gns3server/schemas/controller/users.py` | Modify | Add `max_nodes` to API schemas | +| `gns3server/services/node_limit_service.py` | New | Core node limit service | +| `gns3server/api/routes/controller/nodes.py` | Modify | Add limit check to node startup | +| `gns3server/api/routes/controller/users.py` | Modify | Add user limit configuration API | +| `gns3server/api/routes/controller/groups.py` | Modify | Add group limit configuration API | + +## Implementation Steps + +### Phase 1: Database Layer +1. Add `max_nodes` field to `User` and `UserGroup` models +2. Create database migration file +3. Test database migration and rollback + +### Phase 2: Configuration System +1. Add `NodeLimitSettings` to configuration schema +2. Update configuration file loading logic +3. Test configuration parsing and validation + +### Phase 3: Core Service +1. Implement `NodeLimitService` class +2. Implement node counting logic +3. Implement limit checking logic +4. Add unit tests for service methods + +### Phase 4: API Integration +1. Modify node startup endpoint to add limit check +2. Add user limit configuration endpoints +3. Add group limit configuration endpoints +4. Add user node usage statistics endpoint + +### Phase 5: Testing +1. Unit tests for all core functions +2. Integration tests for API endpoints +3. End-to-end tests for complete workflows +4. Performance tests for node counting operations + +## Testing Strategy + +### Functional Tests +- [ ] Default state verification (no limits) +- [ ] Global limit enablement +- [ ] User-specific limits +- [ ] User group limits +- [ ] Configuration priority verification + +### Boundary Tests +- [ ] Exactly at limit (can start last node) +- [ ] One over limit (startup rejected) +- [ ] Stop node then restart (should work) +- [ ] Special node type exclusion + +### Integration Tests +- [ ] Multi-user concurrent startups +- [ ] Node state transitions +- [ ] Dynamic configuration changes +- [ ] Project ownership filtering + +### Performance Tests +- [ ] Node counting performance with many projects +- [ ] Concurrent startup request handling +- [ ] Memory usage monitoring + +## Error Messages + +### User-Friendly Error Response + +When a user hits their node limit: + +```json +{ + "detail": "节点启动限制:您当前有 5 个活跃节点,限制为 5 个。请停止一些节点后再试,或联系管理员调整限制。" +} +``` + +Alternative formats: +- Show current usage vs limit +- Provide action suggestions +- Include contact information for administrators + +## Migration Path + +### For Existing Systems + +1. **Database migration**: Add new nullable fields (safe, no data loss) +2. **Configuration update**: Add new optional settings (backward compatible) +3. **API changes**: Add new optional dependency injection (no breaking changes) +4. **Behavior**: No changes to existing functionality when disabled + +### Rollback Plan + +If issues occur: +1. Set `node_limits_enabled = false` in configuration +2. Service automatically disables limit checking +3. System returns to pre-feature behavior + +## Benefits + +1. **Resource Management**: Prevent resource exhaustion +2. **Fair Usage**: Ensure equitable resource distribution +3. **Cost Control**: Manage cloud resource costs +4. **System Stability**: Maintain performance under load +5. **Flexibility**: Support different usage patterns and tiers +6. **Backward Compatible**: No impact on existing deployments + +## Future Enhancements + +- Per-project limits (in addition to global user limits) +- Time-based limits (different limits for different times) +- Burst limits (temporary allowance for peak usage) +- Usage quotas with reset periods (daily/weekly/monthly) +- Monitoring and alerting for limit approaching +- Administrative override capabilities +- Usage history and analytics + +## Documentation Updates + +- [ ] Update API documentation with new endpoints +- [ ] Add configuration guide to admin documentation +- [ ] Update user guide with limit information +- [ ] Add troubleshooting section for limit issues +- [ ] Provide migration guide for existing deployments + +## Status + +**Current Status**: Design Phase + +**Next Steps**: +1. Review and approve this roadmap +2. Begin Phase 1 implementation (Database Layer) +3. Create detailed technical specification +4. Set up development and testing environment + +--- + +**Document Version**: 1.0 +**Last Updated**: 2026-05-27 +**Author**: GNS3 Development Team \ No newline at end of file diff --git a/docs/gns3-copilot/user-preferences-api-roadmap.md b/docs/gns3-copilot/roadmap/user-preferences-api-roadmap.md similarity index 100% rename from docs/gns3-copilot/user-preferences-api-roadmap.md rename to docs/gns3-copilot/roadmap/user-preferences-api-roadmap.md diff --git a/docs/gns3-copilot/roadmap/web-wireshark-docker-optimization-roadmap.md b/docs/gns3-copilot/roadmap/web-wireshark-docker-optimization-roadmap.md new file mode 100644 index 000000000..de74d850b --- /dev/null +++ b/docs/gns3-copilot/roadmap/web-wireshark-docker-optimization-roadmap.md @@ -0,0 +1,372 @@ + + +> This document is a roadmap/planning document. The described features have not been implemented yet. + + +# Web Wireshark Docker Image Size Optimization — Roadmap + +## Problem + +The `gns3/web-wireshark` Docker image currently occupies **~2GB** of disk space, which impacts: +- Initial pull/download time for users +- Storage requirements on Docker Hub +- Deployment flexibility in resource-constrained environments + +### Current Size Breakdown + +Based on `docker history gns3/web-wireshark:latest`: + +| Component | Size | Percentage | +|-----------|------|------------| +| `debian:trixie` base image | 120 MB | 6% | +| xpra + dependencies | 65.1 MB | 3.3% | +| Wireshark + GUI stack (Qt, GTK, X11) | 1.82 GB | 91% | +| **Total** | **~2 GB** | **100%** | + +### Detailed File System Analysis + +Analysis of container file system reveals significant cleanup opportunities: + +| Directory | Size | Cleanup Potential | +|-----------|------|-------------------| +| `/usr/lib` | 1.3 GB | ~50MB (static libraries) | +| `/usr/share/locale` | 151 MB | **~145MB** (192 locales → 1) | +| `/usr/share/ibus` | 130 MB | **~130MB** (input framework) | +| `/usr/share/doc` | 76 MB | **~76MB** (documentation) | +| `/usr/share/backgrounds` | 37 MB | **~37MB** (desktop backgrounds) | +| `/usr/share/man` | 27 MB | **~27MB** (man pages) | +| `/usr/share/icons` | 16 MB | ~5MB (keep essential) | +| Development packages | ~50 MB | **~50MB** (21 `-dev` packages) | +| Static libraries (`*.a`, `*.la`) | 16 MB | **~16MB** | +| Sounds/help/perl | ~20 MB | **~20MB** | +| **Total Cleanable** | **~570 MB** | | + +**Key Findings**: +- 192 locale languages installed (only need en_US) +- ibus input framework installed (not needed in headless container) +- 21 development packages with headers/headers +- 161 static library files (`.a`, `.la`) +- Complete documentation and man pages +- Desktop environment components (backgrounds, sounds) + +## Proposed Optimizations + +### Phase 1: Safe Optimizations (Estimated: -300~400MB) + +Low-risk changes that maintain full compatibility. + +#### 1. Use Slim Base Image (-50MB) + +```dockerfile +FROM debian:trixie-slim # Instead of debian:trixie +``` + +**Impact**: Reduces base from 120MB to ~70MB +**Risk**: Low - slim variant contains all essential runtime libraries +**Testing Required**: Verify xpra and Wireshark launch without errors + +#### 2. Install Without Recommended Packages (-150~200MB) + +```dockerfile +RUN apt-get install -y --no-install-recommends \ + wireshark-common \ + wireshark \ + xpra=6.4.3* \ + xpra-x11 \ + xvfb \ + curl \ + x11-utils +``` + +**Impact**: Prevents installation of non-essential recommended packages +**Risk**: Low - only excludes recommended packages, not required dependencies +**Testing Required**: Full functionality test (capture, analysis, WebSocket) + +#### 3. Cleanup Unnecessary Files (-400~500MB) + +```dockerfile +RUN apt-get install -y --no-install-recommends \ + wireshark-common wireshark xpra=6.4.3* xpra-x11 xvfb curl x11-utils \ + # Remove documentation and man pages + && rm -rf /var/lib/apt/lists/* \ + && rm -rf /usr/share/doc/* /usr/share/man/* /usr/share/help/* \ + # Remove unnecessary locales (keep only en_US) + && rm -rf /usr/share/locale/* \ + && localedef -i en_US -f UTF-8 en_US.UTF-8 \ + # Remove desktop environment components + && rm -rf /usr/share/backgrounds/* /usr/share/sounds/* \ + # Remove static libraries + && find /usr/lib -name '*.a' -delete \ + && find /usr/lib -name '*.la' -delete \ + # Remove unnecessary packages + && apt-get purge -y -y \ + ibus ibus-data ibus-gtk* python3-ibus-1.0 \ + gnome-backgrounds \ + && apt-get autoremove -y \ + && apt-get clean +``` + +**Impact**: Removes documentation, locales, desktop components, input framework +**Risk**: Low - all removed components are unnecessary in headless container +**Testing Required**: Verify Wireshark GUI renders correctly without icons/themes + +### Phase 2: Experimental (Requires Testing, -200~500MB) + +Higher-risk optimizations that need extensive validation. + +#### 4. Alpine Linux Alternative (-500MB~1GB) + +```dockerfile +FROM alpine:3.19 +RUN apk add --no-cache wireshark xpra xvfb curl ... +``` + +**Impact**: Could reduce image to ~500MB-1GB +**Risk**: **High** - Wireshark and xpra have complex Qt/GTK dependencies +**Challenges**: +- Wireshark Qt dependencies may not be available in Alpine repos +- xpra package availability and compatibility +- X11 library differences +- May require building dependencies from source + +**Testing Required**: +- [ ] Verify Wireshark package availability in Alpine +- [ ] Test xpra compilation/installation on Alpine +- [ ] Validate all GUI libraries work correctly +- [ ] Full integration testing + +**Recommendation**: Do not pursue unless Phase 1 insufficient + +## Compression Feasibility Analysis + +### Question: Can we reduce image size through compression? + +**Short Answer**: **Not recommended** - limited benefit with performance trade-offs + +### Current Compression Status + +Docker images already use compression: + +| Format | Size | Compression Rate | Use Case | +|--------|------|------------------|----------| +| Runtime size | 2.0 GB | - | Container running | +| docker save (raw) | 1.9 GB | 5% | Docker internal compression | +| docker save + gzip | 718 MB | **64%** | Standard transfer | +| docker save + xz | 563 MB | **72%** | Maximum compression | +| docker save + zstd | ~600 MB | **70%** | Fast compression | + +### Binary Compression Analysis + +#### File Already Stripped +All binaries already have debug symbols removed: +```bash +wireshark: ELF 64-bit... stripped +python3.13: ELF 64-bit... stripped +libc.so.6: ELF 64-bit... stripped +``` + +**No further stripping possible** + +#### UPX Executable Compression (Limited Benefit) + +Test results compressing major executables: + +| Binary | Original | UPX Compressed | Savings | Startup Impact | +|--------|----------|----------------|---------|----------------| +| wireshark (11MB) | 11.0 MB | 4.2 MB | 62% | +0.3s | +| Xvfb (2.1MB) | 2.1 MB | 0.9 MB | 57% | +0.1s | +| python3.13 (6.6MB) | 6.6 MB | 2.8 MB | 58% | +0.2s | +| **Total** | **19.7 MB** | **7.9 MB** | **60%** | **+0.6s** | + +**Overall Impact**: Only ~20MB savings (1%) with 0.6s startup penalty + +### Why Compression Has Limited Benefit + +1. **Small Executable Footprint**: Binaries are only 74MB (3.7% of image) +2. **Libraries Are Data Files**: `/usr/lib` contains mostly data, not code +3. **Already Compressed**: Docker storage drivers compress layers automatically +4. **Resource Files Dominate**: Fonts, icons, themes don't compress well + +### Compression Trade-offs + +| Method | Potential Savings | Performance Impact | Complexity | Risk | +|--------|-------------------|-------------------|------------|------| +| **File cleanup** | 400-500MB (20-25%) | None | Low | Low | +| UPX compression | 50-100MB (2.5-5%) | +0.6s startup | Medium | Medium | +| Layer squashing | 10-50MB (0.5-2.5%) | None | Low | Low | +| Transfer compression | 1.4GB (70%) | None (transfer only) | None | None | + +### Recommendation + +**Do not pursue binary compression** because: +- ✅ File cleanup is **4-10x more effective** +- ✅ No performance penalty +- ✅ Simpler build process +- ✅ Better compatibility + +**For transfer/storage optimization**, use standard tools: +```bash +# For archiving (use zstd for best speed/ratio) +docker save gns3/web-wireshark:latest | \ + zstd -19 -o web-wireshark.tar.zst + +# For maximum compression (slow) +docker save gns3/web-wireshark:latest | \ + xz -9 -T 0 > web-wireshark.tar.xz +``` + +## Implementation Plan + +### Step 1: Create Optimized Dockerfile + +Create `gns3server/agent/web_wireshark/docker/Dockerfile.optimized` with Phase 1 changes. + +### Step 2: Local Testing + +```bash +# Build optimized image +cd gns3server/agent/web_wireshark/docker +docker build -f Dockerfile.optimized -t gns3/web-wireshark:optimized . + +# Verify size reduction +docker images | grep web-wireshark + +# Test Wireshark functionality +docker run --rm gns3/web-wireshark:optimized wireshark --version + +# Test xpra functionality +docker run --rm gns3/web-wireshark:optimized xpra --version +``` + +### Step 3: Integration Testing + +```bash +# Test with actual GNS3 server +pip install . && gns3server-web-wireshark-setup + +# Start a test session +python3 gns3server/agent/web_wireshark/manage_wireshark.py \ + --verbose start \ + --project-id "test-optimized" \ + --link-id "test-link-1" \ + --jwt-token "test-token" \ + --image "gns3/web-wireshark:optimized" + +# Verify WebSocket connectivity and packet capture +``` + +### Step 4: Production Rollout + +1. Tag optimized image: `gns3/web-wireshark:v1.6-optimized` +2. Deploy to staging environment +3. Monitor for 1 week with real workloads +4. If stable, promote to `latest` tag +5. Keep old image available for rollback + +## Testing Checklist + +Before marking as complete: + +- [ ] Wireshark launches without errors +- [ ] xpra HTML5 client connects successfully +- [ ] Packet capture works end-to-end +- [ ] Packet analysis and filtering functional +- [ ] WebSocket proxy integration works +- [ ] Multi-session handling tested (3+ simultaneous captures) +- [ ] Image size measured and documented +- [ ] Tested on Docker 20.10, 24.x, 29.x +- [ ] Startup time not negatively impacted +- [ ] Memory/CPU usage unchanged +- [ ] All existing tests pass + +## Expected Results + +### Phase 1: Safe Optimizations (Recommended) + +| Metric | Current | Target | Improvement | +|--------|---------|--------|-------------| +| Image Size | 2GB | ~1.5GB | **-25% (500MB)** | +| Pull Time | 3-5 min | 2-3 min | **-40%** | +| Startup Time | 5-6s | 5-6s | No change | +| Functionality | Full | Full | No regression | +| Compatibility | All | All | No change | + +### Phase 2: Experimental (If Needed) + +| Metric | Current | Target | Improvement | +|--------|---------|--------|-------------| +| Image Size | 2GB | ~1GB | **-50% (1GB)** | +| Pull Time | 3-5 min | 1-2 min | **-60%** | +| Startup Time | 5-6s | 5-7s | Slight increase | +| Functionality | Full | Full | Risk of regressions | +| Compatibility | All | Alpine only | Significant testing required | + +### Compression Comparison (Not Recommended) + +| Method | Size | Savings | Trade-offs | +|--------|------|---------|------------| +| **File cleanup** | ~1.5GB | 500MB | None | +| UPX compression | ~1.9GB | 100MB | +0.6s startup, compatibility risk | +| Transfer compression | 600MB | 1.4GB | Transfer only, no runtime benefit | + +## Related Files + +| File | Current State | Changes Needed | +|------|---------------|----------------| +| `gns3server/agent/web_wireshark/docker/Dockerfile` | Current 2GB image | Create optimized variant | +| `gns3server/agent/web_wireshark/setup_wireshark_image.py` | Pulls/builds current image | Support optimized image option | +| `gns3server/schemas/config.py` | WebWiresharkSettings | Add image variant config | +| `gns3server/agent/web_wireshark/WEB_WIRESHARK.md` | Documents current image | Update with optimization notes | + +## Status + +### Phase 1: Safe Optimizations + +- [ ] Create `Dockerfile.optimized` with slim base + --no-install-recommends + cleanup +- [ ] Local build and size verification +- [ ] Functional testing (Wireshark, xpra, WebSocket) +- [ ] Integration testing with GNS3 server +- [ ] Document actual size reduction achieved +- [ ] Deploy to staging for 1-week observation +- [ ] Promote to production if stable + +### Phase 2: Experimental (Only if Phase 1 insufficient) + +- [ ] Research Alpine Wireshark/xpra package availability +- [ ] Prototype Alpine build if feasible +- [ ] Extensive compatibility testing +- [ ] Performance benchmarking vs. Phase 1 + +## Notes + +- All size estimates based on actual `docker history` and container filesystem analysis +- Phase 1 optimizations are conservative and should be safe +- Compression techniques (UPX, layer squashing) provide minimal benefit (1-5%) +- File cleanup is **10x more effective** than compression (25% vs 2.5%) +- Phase 2 requires significant research and testing effort +- Backward compatibility must be maintained during transition +- Consider maintaining both `latest` and `optimized` tags during migration period + +### Key Findings from Analysis + +1. **Major space waste**: 570MB of cleanable files (28.5% of image) + - Locale files: 151MB → 6MB (keep only en_US) + - ibus input framework: 130MB → 0MB (not needed in headless container) + - Documentation: 76MB → 0MB (docs, man pages, help) + - Desktop components: 37MB → 0MB (backgrounds, sounds) + - Development packages: 50MB → 0MB (21 -dev packages) + +2. **Compression not viable**: + - Binaries already stripped (no debug symbols) + - UPX only saves 20MB (1%) with 0.6s startup penalty + - Docker already compresses layers internally + - Resource files (fonts, themes) don't compress well + +3. **Best approach**: Clean up unnecessary files rather than compress + - 25x better compression ratio than UPX + - No performance impact + - Simpler build process + - Better compatibility diff --git a/docs/gns3-copilot/server-settings-api-roadmap.md b/docs/gns3-copilot/server-settings-api-roadmap.md deleted file mode 100644 index 887814374..000000000 --- a/docs/gns3-copilot/server-settings-api-roadmap.md +++ /dev/null @@ -1,67 +0,0 @@ - - -> This document is a roadmap/planning document. The described features have not been implemented yet. - - -# Server Settings REST API — Roadmap - -## Problem - -Currently, `gns3_server.conf` can only be modified by directly editing the file on disk. There is no REST API endpoint to read or write server configuration, which prevents the Web UI from offering a settings page for server parameters. - -## Proposed API - -``` -GET /v3/settings → Return all current server settings -PUT /v3/settings → Update and persist server settings -``` - -### Implementation Plan - -**1. Add `save_config()` to `Config` class** (`gns3server/config.py`) - -The `Config` class currently only reads configuration (via `read_config()` / `reload()`). A `save_config()` method is needed to serialize the in-memory `ServerConfig` pydantic model back to INI format and write it to disk. - -Serialization details: -- `bool` → `"True"` / `"False"` (configparser convention) -- `SecretStr` → `get_secret_value()` -- `Enum` → `.value` -- `List[str]` → semi-colon for `additional_images_paths`, comma for `allowed_interfaces` -- `None` → skip - -**2. Add a settings getter/setter** to `Config` to allow programmatic updates to the in-memory settings. - -**3. New route file** (`gns3server/api/routes/controller/settings.py`): - -- `GET /v3/settings` — returns the full `ServerConfig` as JSON (pydantic automatically masks `SecretStr` fields as `"********"`) -- `PUT /v3/settings` — accepts `ServerConfig`, merges existing secrets when placeholder values (`"********"`) are submitted, calls `save_config()`, and triggers runtime config update callbacks - -Both endpoints require `get_current_active_user` for authentication. - -**4. Register the new router** in `gns3server/api/routes/controller/__init__.py` under the `/settings` prefix. - -### Security - -- All settings endpoints require admin authentication (`get_current_active_user`) -- `SecretStr` fields (`compute_password`, `default_admin_password`, `jwt_secret_key`) are masked in responses -- On write, unchanged secrets are preserved via placeholder detection - -### Related Files - -| File | Role | -|------|------| -| `gns3server/config.py` | Config singleton with `read_config()` / `reload()` | -| `gns3server/schemas/config.py` | `ServerConfig` pydantic model with all 9 sub-models | -| `gns3server/api/routes/controller/__init__.py` | Controller router mounting | -| `gns3server/controller/__init__.py` | `Controller._update_config()` for runtime credential sync | - -## Status - -- [ ] gns3server/config.py: add `save_config()` method -- [ ] gns3server/config.py: add settings getter/setter -- [ ] gns3server/api/routes/controller/settings.py: new route file with GET and PUT endpoints -- [ ] gns3server/api/routes/controller/__init__.py: register settings router under `/settings` -- [ ] gns3server/api/routes/controller/controller.py: add notification emission on config change diff --git a/docs/openapi.json b/docs/openapi.json index 8ab09ade4..20fcf083e 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -1 +1 @@ -{"openapi": "3.1.0", "info": {"title": "GNS3 controller API", "description": "This page describes the public controller API for GNS3", "version": "3.0.0"}, "paths": {"/v3/version": {"get": {"tags": ["Controller"], "summary": "Get Version", "description": "Return the server version number.", "operationId": "get_version_v3_version_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Version"}}}}}}, "post": {"tags": ["Controller"], "summary": "Check Version", "description": "Check if version is the same as the server.", "operationId": "check_version_v3_version_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Version"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Version"}}}}, "409": {"description": "Invalid version", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/reload": {"post": {"tags": ["Controller"], "summary": "Reload", "description": "Reload the controller", "operationId": "reload_v3_reload_post", "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/shutdown": {"post": {"tags": ["Controller"], "summary": "Shutdown", "description": "Shutdown the server", "operationId": "shutdown_v3_shutdown_post", "responses": {"204": {"description": "Successful Response"}, "403": {"description": "Server shutdown not allowed", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/iou_license": {"get": {"tags": ["Controller"], "summary": "Get Iou License", "description": "Return the IOU license settings", "operationId": "get_iou_license_v3_iou_license_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IOULicense"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "put": {"tags": ["Controller"], "summary": "Update Iou License", "description": "Update the IOU license settings.", "operationId": "update_iou_license_v3_iou_license_put", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/IOULicense"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IOULicense"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/statistics": {"get": {"tags": ["Controller"], "summary": "Statistics", "description": "Return server statistics including compute resources, projects, and nodes.", "operationId": "statistics_v3_statistics_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"additionalProperties": true, "type": "object", "title": "Response Statistics V3 Statistics Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/notifications": {"get": {"tags": ["Controller"], "summary": "Controller Http Notifications", "description": "Receive controller notifications about the controller from HTTP stream.", "operationId": "controller_http_notifications_v3_notifications_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/access/users/login": {"post": {"tags": ["Users"], "summary": "Login", "description": "Default user login method using forms (x-www-form-urlencoded).\nExample: curl -X POST http://host:port/v3/access/users/login -H \"Content-Type: application/x-www-form-urlencoded\" -d \"username=admin&password=admin\"", "operationId": "login_v3_access_users_login_post", "requestBody": {"content": {"application/x-www-form-urlencoded": {"schema": {"$ref": "#/components/schemas/Body_login_v3_access_users_login_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Token"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/users/authenticate": {"post": {"tags": ["Users"], "summary": "Authenticate", "description": "Alternative authentication method using json.\nExample: curl -X POST http://host:port/v3/access/users/authenticate -d '{\"username\": \"admin\", \"password\": \"admin\"}' -H \"Content-Type: application/json\"", "operationId": "authenticate_v3_access_users_authenticate_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Credentials"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Token"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/users/logout": {"post": {"tags": ["Users"], "summary": "Logout", "description": "Logout the current user by revoking all existing tokens.", "operationId": "logout_v3_access_users_logout_post", "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/access/users/me": {"get": {"tags": ["Users"], "summary": "Get Logged In User", "description": "Get the current active user.", "operationId": "get_logged_in_user_v3_access_users_me_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/User"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "put": {"tags": ["Users"], "summary": "Update Logged In User", "description": "Update the current active user.", "operationId": "update_logged_in_user_v3_access_users_me_put", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/LoggedInUserUpdate"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/User"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/access/users": {"get": {"tags": ["Users"], "summary": "Get Users", "description": "Get all users.\n\nRequired privilege: User.Audit", "operationId": "get_users_v3_access_users_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/User"}, "type": "array", "title": "Response Get Users V3 Access Users Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["Users"], "summary": "Create User", "description": "Create a new user.\n\nRequired privilege: User.Allocate", "operationId": "create_user_v3_access_users_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserCreate"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/User"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/access/users/{user_id}": {"get": {"tags": ["Users"], "summary": "Get User", "description": "Get a user.\n\nRequired privilege: User.Audit", "operationId": "get_user_v3_access_users__user_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/User"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Users"], "summary": "Update User", "description": "Update a user.\n\nRequired privilege: User.Modify", "operationId": "update_user_v3_access_users__user_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/User"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Users"], "summary": "Delete User", "description": "Delete a user.\n\nRequired privilege: User.Allocate", "operationId": "delete_user_v3_access_users__user_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/users/{user_id}/groups": {"get": {"tags": ["Users"], "summary": "Get User Memberships", "description": "Get user memberships.\n\nRequired privilege: Group.Audit", "operationId": "get_user_memberships_v3_access_users__user_id__groups_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/UserGroup"}, "title": "Response Get User Memberships V3 Access Users User Id Groups Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/groups": {"get": {"tags": ["Users groups"], "summary": "Get User Groups", "description": "Get all user groups.\n\nRequired privilege: Group.Audit", "operationId": "get_user_groups_v3_access_groups_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/UserGroup"}, "type": "array", "title": "Response Get User Groups V3 Access Groups Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["Users groups"], "summary": "Create User Group", "description": "Create a new user group.\n\nRequired privilege: Group.Allocate", "operationId": "create_user_group_v3_access_groups_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserGroupCreate"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserGroup"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/access/groups/{user_group_id}": {"get": {"tags": ["Users groups"], "summary": "Get User Group", "description": "Get a user group.\n\nRequired privilege: Group.Audit", "operationId": "get_user_group_v3_access_groups__user_group_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_group_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Group Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserGroup"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Users groups"], "summary": "Update User Group", "description": "Update a user group.\n\nRequired privilege: Group.Modify", "operationId": "update_user_group_v3_access_groups__user_group_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_group_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Group Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserGroupUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserGroup"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Users groups"], "summary": "Delete User Group", "description": "Delete a user group.\n\nRequired privilege: Group.Allocate", "operationId": "delete_user_group_v3_access_groups__user_group_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_group_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Group Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/groups/{user_group_id}/members": {"get": {"tags": ["Users groups"], "summary": "Get User Group Members", "description": "Get all user group members.\n\nRequired privilege: Group.Audit", "operationId": "get_user_group_members_v3_access_groups__user_group_id__members_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_group_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Group Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/User"}, "title": "Response Get User Group Members V3 Access Groups User Group Id Members Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/groups/{user_group_id}/members/{user_id}": {"put": {"tags": ["Users groups"], "summary": "Add Member To Group", "description": "Add member to a user group.\n\nRequired privilege: Group.Modify", "operationId": "add_member_to_group_v3_access_groups__user_group_id__members__user_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_group_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Group Id"}}, {"name": "user_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Users groups"], "summary": "Remove Member From Group", "description": "Remove member from a user group.\n\nRequired privilege: Group.Modify", "operationId": "remove_member_from_group_v3_access_groups__user_group_id__members__user_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_group_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Group Id"}}, {"name": "user_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/roles": {"get": {"tags": ["Roles"], "summary": "Get Roles", "description": "Get all roles.\n\nRequired privilege: Role.Audit", "operationId": "get_roles_v3_access_roles_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/Role"}, "type": "array", "title": "Response Get Roles V3 Access Roles Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["Roles"], "summary": "Create Role", "description": "Create a new role.\n\nRequired privilege: Role.Allocate", "operationId": "create_role_v3_access_roles_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/RoleCreate"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Role"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/access/roles/{role_id}": {"get": {"tags": ["Roles"], "summary": "Get Role", "description": "Get a role.\n\nRequired privilege: Role.Audit", "operationId": "get_role_v3_access_roles__role_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "role_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Role Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Role"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Roles"], "summary": "Update Role", "description": "Update a role.\n\nRequired privilege: Role.Modify", "operationId": "update_role_v3_access_roles__role_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "role_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Role Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RoleUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Role"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Roles"], "summary": "Delete Role", "description": "Delete a role.\n\nRequired privilege: Role.Allocate", "operationId": "delete_role_v3_access_roles__role_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "role_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Role Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/roles/{role_id}/privileges": {"get": {"tags": ["Roles"], "summary": "Get Role Privileges", "description": "Get all role privileges.\n\nRequired privilege: Role.Audit", "operationId": "get_role_privileges_v3_access_roles__role_id__privileges_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "role_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Role Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Privilege"}, "title": "Response Get Role Privileges V3 Access Roles Role Id Privileges Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/roles/{role_id}/privileges/{privilege_id}": {"put": {"tags": ["Roles"], "summary": "Add Privilege To Role", "description": "Add a privilege to a role.\n\nRequired privilege: Role.Modify", "operationId": "add_privilege_to_role_v3_access_roles__role_id__privileges__privilege_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "role_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Role Id"}}, {"name": "privilege_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Privilege Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Roles"], "summary": "Remove Privilege From Role", "description": "Remove privilege from a role.\n\nRequired privilege: Role.Modify", "operationId": "remove_privilege_from_role_v3_access_roles__role_id__privileges__privilege_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "role_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Role Id"}}, {"name": "privilege_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Privilege Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/privileges": {"get": {"tags": ["Privileges"], "summary": "Get Privileges", "description": "Get all privileges.\n\nRequired privilege: None", "operationId": "get_privileges_v3_access_privileges_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/Privilege"}, "type": "array", "title": "Response Get Privileges V3 Access Privileges Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/access/acl/endpoints": {"get": {"tags": ["ACL"], "summary": "Endpoints", "description": "List all endpoints to be used in ACL entries.", "operationId": "endpoints_v3_access_acl_endpoints_get", "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"additionalProperties": true, "type": "object"}, "type": "array", "title": "Response Endpoints V3 Access Acl Endpoints Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/access/acl": {"get": {"tags": ["ACL"], "summary": "Get Aces", "description": "Get all ACL entries.\n\nRequired privilege: ACE.Audit", "operationId": "get_aces_v3_access_acl_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/ACE"}, "type": "array", "title": "Response Get Aces V3 Access Acl Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["ACL"], "summary": "Create Ace", "description": "Create a new ACL entry.\n\nRequired privilege: ACE.Allocate", "operationId": "create_ace_v3_access_acl_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ACECreate"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ACE"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/access/acl/{ace_id}": {"get": {"tags": ["ACL"], "summary": "Get Ace", "description": "Get an ACL entry.\n\nRequired privilege: ACE.Audit", "operationId": "get_ace_v3_access_acl__ace_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "ace_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Ace Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ACE"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["ACL"], "summary": "Update Ace", "description": "Update an ACL entry.\n\nRequired privilege: ACE.Modify", "operationId": "update_ace_v3_access_acl__ace_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "ace_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Ace Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ACEUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ACE"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["ACL"], "summary": "Delete Ace", "description": "Delete an ACL entry.\n\nRequired privilege: ACE.Allocate", "operationId": "delete_ace_v3_access_acl__ace_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "ace_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Ace Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/images/qemu/{image_path}": {"post": {"tags": ["Images"], "summary": "Create Qemu Image", "description": "Create a new blank Qemu image.\n\nRequired privilege: Image.Allocate", "operationId": "create_qemu_image_v3_images_qemu__image_path__post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "image_path", "in": "path", "required": true, "schema": {"type": "string", "title": "Image Path"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/QemuDiskImageCreate"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Image"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/images": {"get": {"tags": ["Images"], "summary": "Get Images", "description": "Return all images.\n\nRequired privilege: Image.Audit", "operationId": "get_images_v3_images_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "image_type", "in": "query", "required": false, "schema": {"anyOf": [{"$ref": "#/components/schemas/ImageType"}, {"type": "null"}], "title": "Image Type"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Image"}, "title": "Response Get Images V3 Images Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/images/upload/{image_path}": {"post": {"tags": ["Images"], "summary": "Upload Image", "description": "Upload an image.\n\nExample: curl -X POST http://host:port/v3/images/upload/my_image_name.qcow2 -H 'Authorization: Bearer ' --data-binary @\"/path/to/image.qcow2\"\n\nRequired privilege: Image.Allocate", "operationId": "upload_image_v3_images_upload__image_path__post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "image_path", "in": "path", "required": true, "schema": {"type": "string", "title": "Image Path"}}, {"name": "install_appliances", "in": "query", "required": false, "schema": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": false, "title": "Install Appliances"}}], "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Image"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/images/prune": {"delete": {"tags": ["Images"], "summary": "Prune Images", "description": "Prune images not attached to any template.\n\nRequired privilege: Image.Allocate", "operationId": "prune_images_v3_images_prune_delete", "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/images/install": {"post": {"tags": ["Images"], "summary": "Install Images", "description": "Attempt to automatically create templates based on image checksums.\n\nRequired privilege: Image.Allocate", "operationId": "install_images_v3_images_install_post", "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/images/{image_path}": {"get": {"tags": ["Images"], "summary": "Get Image", "description": "Return an image.\n\nRequired privilege: Image.Audit", "operationId": "get_image_v3_images__image_path__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "image_path", "in": "path", "required": true, "schema": {"type": "string", "title": "Image Path"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Image"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Images"], "summary": "Delete Image", "description": "Delete an image.\n\nRequired privilege: Image.Allocate", "operationId": "delete_image_v3_images__image_path__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "image_path", "in": "path", "required": true, "schema": {"type": "string", "title": "Image Path"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/templates": {"post": {"tags": ["Templates"], "summary": "Create Template", "description": "Create a new template.\n\nRequired privilege: Template.Allocate", "operationId": "create_template_v3_templates_post", "security": [{"OAuth2PasswordBearer": []}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/TemplateCreate"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Template"}}}}, "404": {"description": "Could not find template", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "get": {"tags": ["Templates"], "summary": "Get Templates", "description": "Return all templates.\n\nRequired privilege: Template.Audit\n\nQuery Parameters:\n- tags: Filter by tags. Multiple tags are ANDed together.\n Example: ?tags=vendor:cisco&tags=model:7200", "operationId": "get_templates_v3_templates_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "tags", "in": "query", "required": false, "schema": {"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}], "description": "Filter by tags (e.g. tags=vendor:cisco&tags=model:7200)", "title": "Tags"}, "description": "Filter by tags (e.g. tags=vendor:cisco&tags=model:7200)"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Template"}, "title": "Response Get Templates V3 Templates Get"}}}}, "404": {"description": "Could not find template", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/templates/{template_id}": {"get": {"tags": ["Templates"], "summary": "Get Template", "description": "Return a template.\n\nRequired privilege: Template.Audit", "operationId": "get_template_v3_templates__template_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "template_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Template Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Template"}}}}, "404": {"description": "Could not find template", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Templates"], "summary": "Update Template", "description": "Update a template.\n\nRequired privilege: Template.Modify", "operationId": "update_template_v3_templates__template_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "template_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Template Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/TemplateUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Template"}}}}, "404": {"description": "Could not find template", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Templates"], "summary": "Delete Template", "description": "Delete a template.\n\nRequired privilege: Template.Allocate", "operationId": "delete_template_v3_templates__template_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "template_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Template Id"}}, {"name": "prune_images", "in": "query", "required": false, "schema": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": false, "title": "Prune Images"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find template", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/templates/{template_id}/duplicate": {"post": {"tags": ["Templates"], "summary": "Duplicate Template", "description": "Duplicate a template.\n\nRequired privilege: Template.Allocate", "operationId": "duplicate_template_v3_templates__template_id__duplicate_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "template_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Template Id"}}], "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Template"}}}}, "404": {"description": "Could not find template", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects": {"get": {"tags": ["Projects"], "summary": "Get Projects", "description": "Return all projects.\n\nRequired privilege: Project.Audit", "operationId": "get_projects_v3_projects_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/Project"}, "type": "array", "title": "Response Get Projects V3 Projects Get"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["Projects"], "summary": "Create Project", "description": "Create a new project.\n\nRequired privilege: Project.Allocate", "operationId": "create_project_v3_projects_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ProjectCreate"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Project"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "409": {"description": "Could not create project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/projects/{project_id}": {"get": {"tags": ["Projects"], "summary": "Get Project", "description": "Return a project.\n\nRequired privilege: Project.Audit", "operationId": "get_project_v3_projects__project_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Project"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Projects"], "summary": "Update Project", "description": "Update a project.\n\nRequired privilege: Project.Modify", "operationId": "update_project_v3_projects__project_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ProjectUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Project"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Projects"], "summary": "Delete Project", "description": "Delete a project.\n\nRequired privilege: Project.Allocate", "operationId": "delete_project_v3_projects__project_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/stats": {"get": {"tags": ["Projects"], "summary": "Get Project Stats", "description": "Return a project statistics.\n\nRequired privilege: Project.Audit", "operationId": "get_project_stats_v3_projects__project_id__stats_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Response Get Project Stats V3 Projects Project Id Stats Get"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/close": {"post": {"tags": ["Projects"], "summary": "Close Project", "description": "Close a project.\n\nRequired privilege: Project.Allocate", "operationId": "close_project_v3_projects__project_id__close_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "409": {"description": "Could not close project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/open": {"post": {"tags": ["Projects"], "summary": "Open Project", "description": "Open a project.\n\nRequired privilege: Project.Allocate", "operationId": "open_project_v3_projects__project_id__open_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Project"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "409": {"description": "Could not open project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/load": {"post": {"tags": ["Projects"], "summary": "Load Project", "description": "Load a project (local server only).\n\nRequired privilege: Project.Allocate", "operationId": "load_project_v3_projects_load_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Body_load_project_v3_projects_load_post"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Project"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "409": {"description": "Could not load project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/projects/{project_id}/notifications": {"get": {"tags": ["Projects"], "summary": "Project Http Notifications", "description": "Receive project notifications about the controller from HTTP stream.\n\nRequired privilege: Project.Audit", "operationId": "project_http_notifications_v3_projects__project_id__notifications_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/export": {"get": {"tags": ["Projects"], "summary": "Export Project", "description": "Export a project as a portable archive.\n\nRequired privilege: Project.Audit", "operationId": "export_project_v3_projects__project_id__export_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "include_snapshots", "in": "query", "required": false, "schema": {"type": "boolean", "default": false, "title": "Include Snapshots"}}, {"name": "include_images", "in": "query", "required": false, "schema": {"type": "boolean", "default": false, "title": "Include Images"}}, {"name": "reset_mac_addresses", "in": "query", "required": false, "schema": {"type": "boolean", "default": false, "title": "Reset Mac Addresses"}}, {"name": "keep_compute_ids", "in": "query", "required": false, "schema": {"type": "boolean", "default": false, "title": "Keep Compute Ids"}}, {"name": "compression", "in": "query", "required": false, "schema": {"$ref": "#/components/schemas/ProjectCompression", "default": "zstd"}}, {"name": "compression_level", "in": "query", "required": false, "schema": {"type": "integer", "title": "Compression Level"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/import": {"post": {"tags": ["Projects"], "summary": "Import Project", "description": "Import a project from a portable archive.\n\nRequired privilege: Project.Allocate", "operationId": "import_project_v3_projects__project_id__import_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "name", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}}], "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Project"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/duplicate": {"post": {"tags": ["Projects"], "summary": "Duplicate Project", "description": "Duplicate a project.\n\nRequired privilege: Project.Audit", "operationId": "duplicate_project_v3_projects__project_id__duplicate_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ProjectDuplicate"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Project"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "409": {"description": "Could not duplicate project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/locked": {"get": {"tags": ["Projects"], "summary": "Locked Project", "description": "Returns whether a project is locked or not.\n\nRequired privilege: Project.Audit", "operationId": "locked_project_v3_projects__project_id__locked_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "boolean", "title": "Response Locked Project V3 Projects Project Id Locked Get"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/lock": {"post": {"tags": ["Projects"], "summary": "Lock Project", "description": "Lock all drawings and nodes in a given project.\n\nRequired privilege: Project.Audit", "operationId": "lock_project_v3_projects__project_id__lock_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/unlock": {"post": {"tags": ["Projects"], "summary": "Unlock Project", "description": "Unlock all drawings and nodes in a given project.\n\nRequired privilege: Project.Modify", "operationId": "unlock_project_v3_projects__project_id__unlock_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/files/{file_path}": {"get": {"tags": ["Projects"], "summary": "Get File", "description": "Return a file from a project.\n\nRequired privilege: Project.Audit", "operationId": "get_file_v3_projects__project_id__files__file_path__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "file_path", "in": "path", "required": true, "schema": {"type": "string", "title": "File Path"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["Projects"], "summary": "Write File", "description": "Write a file to a project.\n\nRequired privilege: Project.Modify", "operationId": "write_file_v3_projects__project_id__files__file_path__post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "file_path", "in": "path", "required": true, "schema": {"type": "string", "title": "File Path"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/templates/{template_id}": {"post": {"tags": ["Projects"], "summary": "Create Node From Template", "description": "Create a new node from a template.\n\nRequired privilege: Node.Allocate", "operationId": "create_node_from_template_v3_projects__project_id__templates__template_id__post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "template_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Template Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/TemplateUsage"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Node"}}}}, "404": {"description": "Could not find project or template", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes": {"post": {"tags": ["Nodes"], "summary": "Create Node", "description": "Create a new node.\n\nRequired privilege: Node.Allocate", "operationId": "create_node_v3_projects__project_id__nodes_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/NodeCreate"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Node"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "409": {"description": "Could not create node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "get": {"tags": ["Nodes"], "summary": "Get Nodes", "description": "Return all nodes belonging to a given project.\n\nRequired privilege: Node.Audit\n\nQuery Parameters:\n- tags: Filter by tags. Multiple tags are ANDed together.\n Example: ?tags=vendor:cisco&tags=model:7200", "operationId": "get_nodes_v3_projects__project_id__nodes_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "tags", "in": "query", "required": false, "schema": {"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}], "description": "Filter by tags (e.g. tags=vendor:cisco&tags=model:7200)", "title": "Tags"}, "description": "Filter by tags (e.g. tags=vendor:cisco&tags=model:7200)"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Node"}, "title": "Response Get Nodes V3 Projects Project Id Nodes Get"}}}}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/start": {"post": {"tags": ["Nodes"], "summary": "Start All Nodes", "description": "Start all nodes belonging to a given project.\n\nRequired privilege: Node.PowerMgmt", "operationId": "start_all_nodes_v3_projects__project_id__nodes_start_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/stop": {"post": {"tags": ["Nodes"], "summary": "Stop All Nodes", "description": "Stop all nodes belonging to a given project.\n\nRequired privilege: Node.PowerMgmt", "operationId": "stop_all_nodes_v3_projects__project_id__nodes_stop_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/suspend": {"post": {"tags": ["Nodes"], "summary": "Suspend All Nodes", "description": "Suspend all nodes belonging to a given project.\n\nRequired privilege: Node.PowerMgmt", "operationId": "suspend_all_nodes_v3_projects__project_id__nodes_suspend_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/reload": {"post": {"tags": ["Nodes"], "summary": "Reload All Nodes", "description": "Reload all nodes belonging to a given project.\n\nRequired privilege: Node.PowerMgmt", "operationId": "reload_all_nodes_v3_projects__project_id__nodes_reload_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}": {"get": {"tags": ["Nodes"], "summary": "Get Node", "description": "Return a node from a given project.\n\nRequired privilege: Node.Audit", "operationId": "get_node_v3_projects__project_id__nodes__node_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Node"}}}}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Nodes"], "summary": "Update Node", "description": "Update a node.\n\nRequired privilege: Node.Modify", "operationId": "update_node_v3_projects__project_id__nodes__node_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/NodeUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Node"}}}}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Nodes"], "summary": "Delete Node", "description": "Delete a node from a project.\n\nRequired privilege: Node.Allocate", "operationId": "delete_node_v3_projects__project_id__nodes__node_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "409": {"description": "Cannot delete node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/duplicate": {"post": {"tags": ["Nodes"], "summary": "Duplicate Node", "description": "Duplicate a node.\n\nRequired privilege: Node.Allocate", "operationId": "duplicate_node_v3_projects__project_id__nodes__node_id__duplicate_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/NodeDuplicate"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Node"}}}}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/start": {"post": {"tags": ["Nodes"], "summary": "Start Node", "description": "Start a node.\n\nRequired privilege: Node.PowerMgmt", "operationId": "start_node_v3_projects__project_id__nodes__node_id__start_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"content": {"application/json": {"schema": {"anyOf": [{"type": "object", "additionalProperties": true}, {"type": "null"}], "title": "Start Data"}}}}, "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/stop": {"post": {"tags": ["Nodes"], "summary": "Stop Node", "description": "Stop a node.\n\nRequired privilege: Node.PowerMgmt", "operationId": "stop_node_v3_projects__project_id__nodes__node_id__stop_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/suspend": {"post": {"tags": ["Nodes"], "summary": "Suspend Node", "description": "Suspend a node.\n\nRequired privilege: Node.PowerMgmt", "operationId": "suspend_node_v3_projects__project_id__nodes__node_id__suspend_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/reload": {"post": {"tags": ["Nodes"], "summary": "Reload Node", "description": "Reload a node.\n\nRequired privilege: Node.PowerMgmt", "operationId": "reload_node_v3_projects__project_id__nodes__node_id__reload_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/isolate": {"post": {"tags": ["Nodes"], "summary": "Isolate Node", "description": "Isolate a node (suspend all attached links).\n\nRequired privilege: Link.Modify", "operationId": "isolate_node_v3_projects__project_id__nodes__node_id__isolate_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/unisolate": {"post": {"tags": ["Nodes"], "summary": "Unisolate Node", "description": "Un-isolate a node (resume all attached suspended links).\n\nRequired privilege: Link.Modify", "operationId": "unisolate_node_v3_projects__project_id__nodes__node_id__unisolate_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/links": {"get": {"tags": ["Nodes"], "summary": "Get Node Links", "description": "Return all the links connected to a node.\n\nRequired privilege: Link.Audit", "operationId": "get_node_links_v3_projects__project_id__nodes__node_id__links_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Link"}, "title": "Response Get Node Links V3 Projects Project Id Nodes Node Id Links Get"}}}}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/dynamips/auto_idlepc": {"get": {"tags": ["Nodes"], "summary": "Auto Idlepc", "description": "Compute an Idle-PC value for a Dynamips node\n\nRequired privilege: Node.Audit", "operationId": "auto_idlepc_v3_projects__project_id__nodes__node_id__dynamips_auto_idlepc_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Response Auto Idlepc V3 Projects Project Id Nodes Node Id Dynamips Auto Idlepc Get"}}}}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/dynamips/idlepc_proposals": {"get": {"tags": ["Nodes"], "summary": "Idlepc Proposals", "description": "Compute a list of potential idle-pc values for a Dynamips node\n\nRequired privilege: Node.Audit", "operationId": "idlepc_proposals_v3_projects__project_id__nodes__node_id__dynamips_idlepc_proposals_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"type": "string"}, "title": "Response Idlepc Proposals V3 Projects Project Id Nodes Node Id Dynamips Idlepc Proposals Get"}}}}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/qemu/disk_image/{disk_name}": {"post": {"tags": ["Nodes"], "summary": "Create Disk Image", "description": "Create a Qemu disk image.\n\nRequired privilege: Node.Allocate", "operationId": "create_disk_image_v3_projects__project_id__nodes__node_id__qemu_disk_image__disk_name__post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "disk_name", "in": "path", "required": true, "schema": {"type": "string", "title": "Disk Name"}}, {"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/QemuDiskImageCreate"}}}}, "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Nodes"], "summary": "Update Disk Image", "description": "Update a Qemu disk image.\n\nRequired privilege: Node.Allocate", "operationId": "update_disk_image_v3_projects__project_id__nodes__node_id__qemu_disk_image__disk_name__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "disk_name", "in": "path", "required": true, "schema": {"type": "string", "title": "Disk Name"}}, {"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/QemuDiskImageUpdate"}}}}, "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Nodes"], "summary": "Delete Disk Image", "description": "Delete a Qemu disk image.\n\nRequired privilege: Node.Allocate", "operationId": "delete_disk_image_v3_projects__project_id__nodes__node_id__qemu_disk_image__disk_name__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "disk_name", "in": "path", "required": true, "schema": {"type": "string", "title": "Disk Name"}}, {"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/files": {"get": {"tags": ["Nodes"], "summary": "List Node Files", "description": "List files in a node directory with detailed metadata.\n\nRequired privilege: Node.Audit", "operationId": "list_node_files_v3_projects__project_id__nodes__node_id__files_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/NodeFile"}, "title": "Response List Node Files V3 Projects Project Id Nodes Node Id Files Get"}}}}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/files/{file_path}": {"get": {"tags": ["Nodes"], "summary": "Get File", "description": "Return a file from the node directory.\n\nRequired privilege: Node.Audit", "operationId": "get_file_v3_projects__project_id__nodes__node_id__files__file_path__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "file_path", "in": "path", "required": true, "schema": {"type": "string", "title": "File Path"}}, {"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["Nodes"], "summary": "Post File", "description": "Write a file in the node directory.\n\nRequired privilege: Node.Modify", "operationId": "post_file_v3_projects__project_id__nodes__node_id__files__file_path__post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "file_path", "in": "path", "required": true, "schema": {"type": "string", "title": "File Path"}}, {"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/console/reset": {"post": {"tags": ["Nodes"], "summary": "Reset Console All Nodes", "description": "Reset console for all nodes belonging to the project.\n\nRequired privilege: Node.Console", "operationId": "reset_console_all_nodes_v3_projects__project_id__nodes_console_reset_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/console/reset": {"post": {"tags": ["Nodes"], "summary": "Console Reset", "description": "Reset a console for a given node.\n\nRequired privilege: Node.Console", "operationId": "console_reset_v3_projects__project_id__nodes__node_id__console_reset_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/links": {"get": {"tags": ["Links"], "summary": "Get Links", "description": "Return all links for a given project.\n\nRequired privilege: Link.Audit", "operationId": "get_links_v3_projects__project_id__links_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Link"}, "title": "Response Get Links V3 Projects Project Id Links Get"}}}}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["Links"], "summary": "Create Link", "description": "Create a new link.\n\nRequired privilege: Link.Allocate", "operationId": "create_link_v3_projects__project_id__links_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LinkCreate"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Link"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "409": {"description": "Could not create link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/links/{link_id}/available_filters": {"get": {"tags": ["Links"], "summary": "Get Filters", "description": "Return all filters available for a given link.\n\nRequired privilege: Link.Audit", "operationId": "get_filters_v3_projects__project_id__links__link_id__available_filters_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"type": "object", "additionalProperties": true}, "title": "Response Get Filters V3 Projects Project Id Links Link Id Available Filters Get"}}}}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/links/{link_id}": {"get": {"tags": ["Links"], "summary": "Get Link", "description": "Return a link.\n\nRequired privilege: Link.Audit", "operationId": "get_link_v3_projects__project_id__links__link_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Link"}}}}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Links"], "summary": "Update Link", "description": "Update a link.\n\nRequired privilege: Link.Modify", "operationId": "update_link_v3_projects__project_id__links__link_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LinkUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Link"}}}}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Links"], "summary": "Delete Link", "description": "Delete a link.\n\nRequired privilege: Link.Allocate", "operationId": "delete_link_v3_projects__project_id__links__link_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/links/{link_id}/reset": {"post": {"tags": ["Links"], "summary": "Reset Link", "description": "Reset a link.\n\nRequired privilege: Link.Modify", "operationId": "reset_link_v3_projects__project_id__links__link_id__reset_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Link"}}}}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/links/{link_id}/capture/start": {"post": {"tags": ["Links"], "summary": "Start Capture", "description": "Start packet capture on the link.\n\nRequired privilege: Link.Capture", "operationId": "start_capture_v3_projects__project_id__links__link_id__capture_start_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LinkCapture"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Link"}}}}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/links/{link_id}/capture/stop": {"post": {"tags": ["Links"], "summary": "Stop Capture", "description": "Stop packet capture on the link.\n\nRequired privilege: Link.Capture", "operationId": "stop_capture_v3_projects__project_id__links__link_id__capture_stop_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/links/{link_id}/capture/wireshark/restart": {"post": {"tags": ["Links"], "summary": "Restart Wireshark", "description": "Restart Wireshark window without stopping the capture.\n\nThis allows recovery after accidentally closing the Wireshark window.\n\nRequired privilege: Link.Capture", "operationId": "restart_wireshark_v3_projects__project_id__links__link_id__capture_wireshark_restart_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Response Restart Wireshark V3 Projects Project Id Links Link Id Capture Wireshark Restart Post"}}}}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/links/{link_id}/capture/stream": {"get": {"tags": ["Links"], "summary": "Stream Pcap", "description": "Stream the PCAP capture file from compute.\n\nRequired privilege: Link.Capture", "operationId": "stream_pcap_v3_projects__project_id__links__link_id__capture_stream_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/links/{link_id}/capture/file": {"get": {"tags": ["Links"], "summary": "Download Capture File", "description": "Download the PCAP capture file.\n\nThis endpoint allows downloading the capture file even while capture is active.\nThe file is streamed directly, so partial data may be received if capture is still running.\n\nRequired privilege: Link.Capture", "operationId": "download_capture_file_v3_projects__project_id__links__link_id__capture_file_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "responses": {"200": {"description": "Successful Response"}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/links/{link_id}/iface": {"get": {"tags": ["Links"], "summary": "Get Iface", "description": "Return iface info for links to Cloud or NAT devices.\n\nRequired privilege: Link.Audit", "operationId": "get_iface_v3_projects__project_id__links__link_id__iface_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"anyOf": [{"$ref": "#/components/schemas/UDPPortInfo"}, {"$ref": "#/components/schemas/EthernetPortInfo"}], "title": "Response Get Iface V3 Projects Project Id Links Link Id Iface Get"}}}}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/drawings": {"get": {"tags": ["Drawings"], "summary": "Get Drawings", "description": "Return the list of all drawings for a given project.\n\nRequired privilege: Drawing.Audit", "operationId": "get_drawings_v3_projects__project_id__drawings_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Drawing"}, "title": "Response Get Drawings V3 Projects Project Id Drawings Get"}}}}, "404": {"description": "Project or drawing not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["Drawings"], "summary": "Create Drawing", "description": "Create a new drawing.\n\nRequired privilege: Drawing.Allocate", "operationId": "create_drawing_v3_projects__project_id__drawings_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Drawing"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Drawing"}}}}, "404": {"description": "Project or drawing not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/drawings/{drawing_id}": {"get": {"tags": ["Drawings"], "summary": "Get Drawing", "description": "Return a drawing.\n\nRequired privilege: Drawing.Audit", "operationId": "get_drawing_v3_projects__project_id__drawings__drawing_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "drawing_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Drawing Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Drawing"}}}}, "404": {"description": "Project or drawing not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Drawings"], "summary": "Update Drawing", "description": "Update a drawing.\n\nRequired privilege: Drawing.Modify", "operationId": "update_drawing_v3_projects__project_id__drawings__drawing_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "drawing_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Drawing Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Drawing"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Drawing"}}}}, "404": {"description": "Project or drawing not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Drawings"], "summary": "Delete Drawing", "description": "Delete a drawing.\n\nRequired privilege: Drawing.Allocate", "operationId": "delete_drawing_v3_projects__project_id__drawings__drawing_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "drawing_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Drawing Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Project or drawing not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/symbols": {"get": {"tags": ["Symbols"], "summary": "Get Symbols", "description": "Return all symbols.\n\nRequired privilege: Symbol.Audit", "operationId": "get_symbols_v3_symbols_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"additionalProperties": true, "type": "object"}, "type": "array", "title": "Response Get Symbols V3 Symbols Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/symbols/{symbol_id}/raw": {"get": {"tags": ["Symbols"], "summary": "Get Symbol", "description": "Download a symbol file.\n\nRequired privilege: Symbol.Audit", "operationId": "get_symbol_v3_symbols__symbol_id__raw_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "symbol_id", "in": "path", "required": true, "schema": {"type": "string", "title": "Symbol Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Could not find symbol", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["Symbols"], "summary": "Upload Symbol", "description": "Upload a symbol file.\n\nRequired privilege: Symbol.Allocate", "operationId": "upload_symbol_v3_symbols__symbol_id__raw_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "symbol_id", "in": "path", "required": true, "schema": {"type": "string", "title": "Symbol Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/symbols/{symbol_id}/dimensions": {"get": {"tags": ["Symbols"], "summary": "Get Symbol Dimensions", "description": "Get a symbol dimensions.\n\nRequired privilege: Symbol.Audit", "operationId": "get_symbol_dimensions_v3_symbols__symbol_id__dimensions_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "symbol_id", "in": "path", "required": true, "schema": {"type": "string", "title": "Symbol Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Response Get Symbol Dimensions V3 Symbols Symbol Id Dimensions Get"}}}}, "404": {"description": "Could not find symbol", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/symbols/default_symbols": {"get": {"tags": ["Symbols"], "summary": "Get Default Symbols", "description": "Return all default symbols.\n\nRequired privilege: Symbol.Audit", "operationId": "get_default_symbols_v3_symbols_default_symbols_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"additionalProperties": true, "type": "object", "title": "Response Get Default Symbols V3 Symbols Default Symbols Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/symbols/{symbol_id}": {"delete": {"tags": ["Symbols"], "summary": "Delete Symbol", "description": "Delete a custom symbol file.\n\nRequired privilege: Symbol.Allocate", "operationId": "delete_symbol_v3_symbols__symbol_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "symbol_id", "in": "path", "required": true, "schema": {"type": "string", "title": "Symbol Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/snapshots": {"post": {"tags": ["Snapshots"], "summary": "Create Snapshot", "description": "Create a new snapshot of a project.\n\nRequired privilege: Snapshot.Allocate", "operationId": "create_snapshot_v3_projects__project_id__snapshots_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SnapshotCreate"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Snapshot"}}}}, "404": {"description": "Could not find project or snapshot", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "get": {"tags": ["Snapshots"], "summary": "Get Snapshots", "description": "Return all snapshots belonging to a given project.\n\nRequired privilege: Snapshot.Audit", "operationId": "get_snapshots_v3_projects__project_id__snapshots_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Snapshot"}, "title": "Response Get Snapshots V3 Projects Project Id Snapshots Get"}}}}, "404": {"description": "Could not find project or snapshot", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/snapshots/{snapshot_id}": {"delete": {"tags": ["Snapshots"], "summary": "Delete Snapshot", "description": "Delete a snapshot.\n\nRequired privilege: Snapshot.Allocate", "operationId": "delete_snapshot_v3_projects__project_id__snapshots__snapshot_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "snapshot_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Snapshot Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or snapshot", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/snapshots/{snapshot_id}/restore": {"post": {"tags": ["Snapshots"], "summary": "Restore Snapshot", "description": "Restore a snapshot.\n\nRequired privilege: Snapshot.Restore", "operationId": "restore_snapshot_v3_projects__project_id__snapshots__snapshot_id__restore_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "snapshot_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Snapshot Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Project"}}}}, "404": {"description": "Could not find project or snapshot", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/computes": {"post": {"tags": ["Computes"], "summary": "Create Compute", "description": "Create a new compute on the controller.\n\nRequired privilege: Compute.Allocate", "operationId": "create_compute_v3_computes_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "connect", "in": "query", "required": false, "schema": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": false, "title": "Connect"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ComputeCreate"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Compute"}}}}, "404": {"description": "Could not connect to compute", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "409": {"description": "Could not create compute", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "401": {"description": "Invalid authentication for compute", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "get": {"tags": ["Computes"], "summary": "Get Computes", "description": "Return all computes known by the controller.\n\nRequired privilege: Compute.Audit", "operationId": "get_computes_v3_computes_get", "security": [{"OAuth2PasswordBearer": []}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Compute"}, "title": "Response Get Computes V3 Computes Get"}}}}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/computes/{compute_id}/connect": {"post": {"tags": ["Computes"], "summary": "Connect Compute", "description": "Connect to compute on the controller.\n\nRequired privilege: Compute.Audit", "operationId": "connect_compute_v3_computes__compute_id__connect_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/computes/{compute_id}": {"get": {"tags": ["Computes"], "summary": "Get Compute", "description": "Return a compute from the controller.\n\nRequired privilege: Compute.Audit", "operationId": "get_compute_v3_computes__compute_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Compute"}}}}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Computes"], "summary": "Update Compute", "description": "Update a compute on the controller.\n\nRequired privilege: Compute.Modify", "operationId": "update_compute_v3_computes__compute_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ComputeUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Compute"}}}}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Computes"], "summary": "Delete Compute", "description": "Delete a compute from the controller.\n\nRequired privilege: Compute.Allocate", "operationId": "delete_compute_v3_computes__compute_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/computes/{compute_id}/docker/images": {"get": {"tags": ["Computes"], "summary": "Docker Get Images", "description": "Get Docker images from a compute.", "operationId": "docker_get_images_v3_computes__compute_id__docker_images_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/ComputeDockerImage"}, "title": "Response Docker Get Images V3 Computes Compute Id Docker Images Get"}}}}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/computes/{compute_id}/virtualbox/vms": {"get": {"tags": ["Computes"], "summary": "Virtualbox Vms", "description": "Get VirtualBox VMs from a compute.", "operationId": "virtualbox_vms_v3_computes__compute_id__virtualbox_vms_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/ComputeVirtualBoxVM"}, "title": "Response Virtualbox Vms V3 Computes Compute Id Virtualbox Vms Get"}}}}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/computes/{compute_id}/vmware/vms": {"get": {"tags": ["Computes"], "summary": "Vmware Vms", "description": "Get VMware VMs from a compute.", "operationId": "vmware_vms_v3_computes__compute_id__vmware_vms_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/ComputeVMwareVM"}, "title": "Response Vmware Vms V3 Computes Compute Id Vmware Vms Get"}}}}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/computes/{compute_id}/dynamips/auto_idlepc": {"post": {"tags": ["Computes"], "summary": "Dynamips Autoidlepc", "description": "Find a suitable Idle-PC value for a given IOS image. This may take a few minutes.", "operationId": "dynamips_autoidlepc_v3_computes__compute_id__dynamips_auto_idlepc_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AutoIdlePC"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/computes/{compute_id}/{emulator}/{endpoint_path}": {"get": {"tags": ["Computes"], "summary": "Forward Get", "description": "Forward a GET request to a compute.\nRead the full compute API documentation for available routes.", "operationId": "forward_get_v3_computes__compute_id___emulator___endpoint_path__get", "deprecated": true, "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}, {"name": "emulator", "in": "path", "required": true, "schema": {"type": "string", "title": "Emulator"}}, {"name": "endpoint_path", "in": "path", "required": true, "schema": {"type": "string", "title": "Endpoint Path"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Forward Get V3 Computes Compute Id Emulator Endpoint Path Get"}}}}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["Computes"], "summary": "Forward Post", "description": "Forward a POST request to a compute.\nRead the full compute API documentation for available routes.", "operationId": "forward_post_v3_computes__compute_id___emulator___endpoint_path__post", "deprecated": true, "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}, {"name": "emulator", "in": "path", "required": true, "schema": {"type": "string", "title": "Emulator"}}, {"name": "endpoint_path", "in": "path", "required": true, "schema": {"type": "string", "title": "Endpoint Path"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Compute Data"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Forward Post V3 Computes Compute Id Emulator Endpoint Path Post"}}}}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Computes"], "summary": "Forward Put", "description": "Forward a PUT request to a compute.\nRead the full compute API documentation for available routes.", "operationId": "forward_put_v3_computes__compute_id___emulator___endpoint_path__put", "deprecated": true, "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}, {"name": "emulator", "in": "path", "required": true, "schema": {"type": "string", "title": "Emulator"}}, {"name": "endpoint_path", "in": "path", "required": true, "schema": {"type": "string", "title": "Endpoint Path"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Compute Data"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Forward Put V3 Computes Compute Id Emulator Endpoint Path Put"}}}}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/appliances": {"get": {"tags": ["Appliances"], "summary": "Get Appliances", "description": "Return all appliances known by the controller.\n\nRequired privilege: Appliance.Audit", "operationId": "get_appliances_v3_appliances_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "update", "in": "query", "required": false, "schema": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": false, "title": "Update"}}, {"name": "symbol_theme", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Symbol Theme"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"oneOf": [{"$ref": "#/components/schemas/ApplianceV1_6"}, {"$ref": "#/components/schemas/ApplianceV8"}], "discriminator": {"propertyName": "registry_version", "mapping": {"1": "#/components/schemas/ApplianceV1_6", "2": "#/components/schemas/ApplianceV1_6", "3": "#/components/schemas/ApplianceV1_6", "4": "#/components/schemas/ApplianceV1_6", "5": "#/components/schemas/ApplianceV1_6", "6": "#/components/schemas/ApplianceV1_6", "8": "#/components/schemas/ApplianceV8"}}}, "title": "Response Get Appliances V3 Appliances Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/appliances/{appliance_id}": {"get": {"tags": ["Appliances"], "summary": "Get Appliance", "description": "Get an appliance file.\n\nRequired privilege: Appliance.Audit", "operationId": "get_appliance_v3_appliances__appliance_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "appliance_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Appliance Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"oneOf": [{"$ref": "#/components/schemas/ApplianceV1_6"}, {"$ref": "#/components/schemas/ApplianceV8"}], "discriminator": {"propertyName": "registry_version", "mapping": {"1": "#/components/schemas/ApplianceV1_6", "2": "#/components/schemas/ApplianceV1_6", "3": "#/components/schemas/ApplianceV1_6", "4": "#/components/schemas/ApplianceV1_6", "5": "#/components/schemas/ApplianceV1_6", "6": "#/components/schemas/ApplianceV1_6", "8": "#/components/schemas/ApplianceV8"}}, "title": "Response Get Appliance V3 Appliances Appliance Id Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/appliances/{appliance_id}/version": {"post": {"tags": ["Appliances"], "summary": "Add Appliance Version", "description": "Add a version to an appliance.\n\nRequired privilege: Appliance.Allocate", "operationId": "add_appliance_version_v3_appliances__appliance_id__version_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "appliance_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Appliance Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"anyOf": [{"$ref": "#/components/schemas/ApplianceVersion"}, {"$ref": "#/components/schemas/ApplianceVersionV8"}], "title": "Appliance Version"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Response Add Appliance Version V3 Appliances Appliance Id Version Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/appliances/{appliance_id}/install": {"post": {"tags": ["Appliances"], "summary": "Install Appliance", "description": "Install an appliance.\n\nRequired privilege: Appliance.Allocate", "operationId": "install_appliance_v3_appliances__appliance_id__install_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "appliance_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Appliance Id"}}, {"name": "version", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Version"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/pools": {"get": {"tags": ["Resource pools"], "summary": "Get Resource Pools", "description": "Get all resource pools.\n\nRequired privilege: Pool.Audit", "operationId": "get_resource_pools_v3_pools_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/ResourcePool"}, "type": "array", "title": "Response Get Resource Pools V3 Pools Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["Resource pools"], "summary": "Create Resource Pool", "description": "Create a new resource pool\n\nRequired privilege: Pool.Allocate", "operationId": "create_resource_pool_v3_pools_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResourcePoolCreate"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResourcePool"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/pools/{resource_pool_id}": {"get": {"tags": ["Resource pools"], "summary": "Get Resource Pool", "description": "Get a resource pool.\n\nRequired privilege: Pool.Audit", "operationId": "get_resource_pool_v3_pools__resource_pool_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "resource_pool_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Resource Pool Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResourcePool"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Resource pools"], "summary": "Update Resource Pool", "description": "Update a resource pool.\n\nRequired privilege: Pool.Modify", "operationId": "update_resource_pool_v3_pools__resource_pool_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "resource_pool_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Resource Pool Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResourcePoolUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResourcePool"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Resource pools"], "summary": "Delete Resource Pool", "description": "Delete a resource pool.\n\nRequired privilege: Pool.Allocate", "operationId": "delete_resource_pool_v3_pools__resource_pool_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "resource_pool_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Resource Pool Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/pools/{resource_pool_id}/resources": {"get": {"tags": ["Resource pools"], "summary": "Get Pool Resources", "description": "Get all resource in a pool.\n\nRequired privilege: Pool.Audit", "operationId": "get_pool_resources_v3_pools__resource_pool_id__resources_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "resource_pool_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Resource Pool Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Resource"}, "title": "Response Get Pool Resources V3 Pools Resource Pool Id Resources Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/pools/{resource_pool_id}/resources/{resource_id}": {"put": {"tags": ["Resource pools"], "summary": "Add Resource To Pool", "description": "Add resource to a resource pool.\n\nRequired privilege: Pool.Modify", "operationId": "add_resource_to_pool_v3_pools__resource_pool_id__resources__resource_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "resource_pool_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Resource Pool Id"}}, {"name": "resource_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Resource Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Resource pools"], "summary": "Remove Resource From Pool", "description": "Remove resource from a resource pool.\n\nRequired privilege: Pool.Modify", "operationId": "remove_resource_from_pool_v3_pools__resource_pool_id__resources__resource_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "resource_pool_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Resource Pool Id"}}, {"name": "resource_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Resource Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/gns3vm/engines": {"get": {"tags": ["GNS3 VM"], "summary": "Get Engines", "description": "Return the list of supported engines for the GNS3VM.", "operationId": "get_engines_v3_gns3vm_engines_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"additionalProperties": true, "type": "object"}, "type": "array", "title": "Response Get Engines V3 Gns3Vm Engines Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "deprecated": true, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/gns3vm/engines/{engine}/vms": {"get": {"tags": ["GNS3 VM"], "summary": "Get Vms", "description": "Return all the available VMs for a specific virtualization engine.", "operationId": "get_vms_v3_gns3vm_engines__engine__vms_get", "deprecated": true, "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "engine", "in": "path", "required": true, "schema": {"type": "string", "title": "Engine"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"type": "object", "additionalProperties": true}, "title": "Response Get Vms V3 Gns3Vm Engines Engine Vms Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/gns3vm": {"get": {"tags": ["GNS3 VM"], "summary": "Get Gns3Vm Settings", "description": "Return the GNS3 VM settings.", "operationId": "get_gns3vm_settings_v3_gns3vm_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/GNS3VM"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "deprecated": true, "security": [{"OAuth2PasswordBearer": []}]}, "put": {"tags": ["GNS3 VM"], "summary": "Update Gns3Vm Settings", "description": "Update the GNS3 VM settings.", "operationId": "update_gns3vm_settings_v3_gns3vm_put", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/GNS3VM"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/GNS3VM"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "deprecated": true, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/access/{path}": {"delete": {"tags": ["LLM Model Configurations"], "summary": "Ai Not Available", "operationId": "ai_not_available_v3_access__path__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "path", "in": "path", "required": true, "schema": {"type": "string", "title": "Path"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["LLM Model Configurations"], "summary": "Ai Not Available", "operationId": "ai_not_available_v3_access__path__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "path", "in": "path", "required": true, "schema": {"type": "string", "title": "Path"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "patch": {"tags": ["LLM Model Configurations"], "summary": "Ai Not Available", "operationId": "ai_not_available_v3_access__path__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "path", "in": "path", "required": true, "schema": {"type": "string", "title": "Path"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["LLM Model Configurations"], "summary": "Ai Not Available", "operationId": "ai_not_available_v3_access__path__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "path", "in": "path", "required": true, "schema": {"type": "string", "title": "Path"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "get": {"tags": ["LLM Model Configurations"], "summary": "Ai Not Available", "operationId": "ai_not_available_v3_access__path__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "path", "in": "path", "required": true, "schema": {"type": "string", "title": "Path"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/copilot/{path}": {"delete": {"tags": ["GNS3 Copilot"], "summary": "Ai Not Available", "operationId": "ai_not_available_v3_copilot__path__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "path", "in": "path", "required": true, "schema": {"type": "string", "title": "Path"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["GNS3 Copilot"], "summary": "Ai Not Available", "operationId": "ai_not_available_v3_copilot__path__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "path", "in": "path", "required": true, "schema": {"type": "string", "title": "Path"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "patch": {"tags": ["GNS3 Copilot"], "summary": "Ai Not Available", "operationId": "ai_not_available_v3_copilot__path__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "path", "in": "path", "required": true, "schema": {"type": "string", "title": "Path"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["GNS3 Copilot"], "summary": "Ai Not Available", "operationId": "ai_not_available_v3_copilot__path__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "path", "in": "path", "required": true, "schema": {"type": "string", "title": "Path"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "get": {"tags": ["GNS3 Copilot"], "summary": "Ai Not Available", "operationId": "ai_not_available_v3_copilot__path__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "path", "in": "path", "required": true, "schema": {"type": "string", "title": "Path"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/copilot/projects/{project_id}/chat/{path}": {"delete": {"tags": ["GNS3 Copilot"], "summary": "Ai Not Available", "operationId": "ai_not_available_v3_copilot_projects__project_id__chat__path__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "path", "in": "path", "required": true, "schema": {"type": "string", "title": "Path"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["GNS3 Copilot"], "summary": "Ai Not Available", "operationId": "ai_not_available_v3_copilot_projects__project_id__chat__path__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "path", "in": "path", "required": true, "schema": {"type": "string", "title": "Path"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "patch": {"tags": ["GNS3 Copilot"], "summary": "Ai Not Available", "operationId": "ai_not_available_v3_copilot_projects__project_id__chat__path__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "path", "in": "path", "required": true, "schema": {"type": "string", "title": "Path"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["GNS3 Copilot"], "summary": "Ai Not Available", "operationId": "ai_not_available_v3_copilot_projects__project_id__chat__path__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "path", "in": "path", "required": true, "schema": {"type": "string", "title": "Path"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "get": {"tags": ["GNS3 Copilot"], "summary": "Ai Not Available", "operationId": "ai_not_available_v3_copilot_projects__project_id__chat__path__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "path", "in": "path", "required": true, "schema": {"type": "string", "title": "Path"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}}, "components": {"schemas": {"ACE": {"properties": {"ace_type": {"$ref": "#/components/schemas/ACEType", "description": "Type of the ACE"}, "path": {"type": "string", "title": "Path"}, "propagate": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Propagate", "default": true}, "allowed": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Allowed", "default": true}, "user_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "User Id"}, "group_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Group Id"}, "role_id": {"type": "string", "format": "uuid", "title": "Role Id"}, "created_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Created At"}, "updated_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Updated At"}, "ace_id": {"type": "string", "format": "uuid", "title": "Ace Id"}}, "type": "object", "required": ["ace_type", "path", "role_id", "ace_id"], "title": "ACE"}, "ACECreate": {"properties": {"ace_type": {"$ref": "#/components/schemas/ACEType", "description": "Type of the ACE"}, "path": {"type": "string", "title": "Path"}, "propagate": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Propagate", "default": true}, "allowed": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Allowed", "default": true}, "user_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "User Id"}, "group_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Group Id"}, "role_id": {"type": "string", "format": "uuid", "title": "Role Id"}}, "type": "object", "required": ["ace_type", "path", "role_id"], "title": "ACECreate", "description": "Properties to create an ACE."}, "ACEType": {"type": "string", "enum": ["user", "group"], "title": "ACEType"}, "ACEUpdate": {"properties": {"ace_type": {"$ref": "#/components/schemas/ACEType", "description": "Type of the ACE"}, "path": {"type": "string", "title": "Path"}, "propagate": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Propagate", "default": true}, "allowed": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Allowed", "default": true}, "user_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "User Id"}, "group_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Group Id"}, "role_id": {"type": "string", "format": "uuid", "title": "Role Id"}}, "type": "object", "required": ["ace_type", "path", "role_id"], "title": "ACEUpdate", "description": "Properties to update an ACE."}, "ApplianceImage": {"properties": {"filename": {"type": "string", "title": "Filename"}, "version": {"type": "string", "title": "Version of the file"}, "md5sum": {"anyOf": [{"type": "string", "pattern": "^[a-f0-9]{32}$"}, {"type": "null"}], "title": "md5sum of the file"}, "filesize": {"type": "integer", "title": "File size in bytes"}, "download_url": {"anyOf": [{"type": "string", "minLength": 1, "format": "uri"}, {"type": "string", "maxLength": 0}, {"type": "null"}], "title": "Download url where you can download the appliance from a browser"}, "direct_download_url": {"anyOf": [{"type": "string", "minLength": 1, "format": "uri"}, {"type": "string", "maxLength": 0}, {"type": "null"}], "title": "Optional. Non authenticated url to the image file where you can download the image."}, "compression": {"anyOf": [{"$ref": "#/components/schemas/Compression"}, {"type": "null"}], "title": "Optional, compression type of direct download url image."}, "checksum": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "checksum of the image file"}, "checksum_type": {"anyOf": [{"$ref": "#/components/schemas/ChecksumType"}, {"type": "null"}], "title": "checksum type of the image file"}, "compression_target": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Optional, file name of the image file inside the compressed file."}}, "type": "object", "required": ["filename", "version", "filesize"], "title": "ApplianceImage", "description": "Appliance image definition - compatible with both versions"}, "ApplianceV1_6": {"properties": {"registry_version": {"type": "integer", "enum": [1, 2, 3, 4, 5, 6], "title": "Version of the registry compatible with this appliance"}, "appliance_id": {"type": "string", "format": "uuid", "title": "Appliance ID"}, "name": {"type": "string", "title": "Appliance name"}, "builtin": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Whether the appliance is builtin or not"}, "category": {"$ref": "#/components/schemas/gns3server__schemas__controller__appliances__Category", "title": "Category of the appliance"}, "description": {"type": "string", "title": "Description of the appliance. Could be a marketing description"}, "vendor_name": {"type": "string", "title": "Name of the vendor"}, "vendor_url": {"anyOf": [{"type": "string", "minLength": 1, "format": "uri"}, {"type": "string", "maxLength": 0}, {"type": "null"}], "title": "Website of the vendor"}, "documentation_url": {"anyOf": [{"type": "string", "minLength": 1, "format": "uri"}, {"type": "string", "maxLength": 0}, {"type": "null"}], "title": "An optional documentation for using the appliance on vendor website"}, "product_name": {"type": "string", "title": "Product name"}, "product_url": {"anyOf": [{"type": "string", "minLength": 1, "format": "uri"}, {"type": "string", "maxLength": 0}, {"type": "null"}], "title": "An optional product url on vendor website"}, "status": {"$ref": "#/components/schemas/Status", "title": "Document if the appliance is working or not"}, "availability": {"anyOf": [{"$ref": "#/components/schemas/Availability"}, {"type": "null"}], "title": "About image availability: can be downloaded directly; download requires a free registration; paid but a trial version (time or feature limited) is available; not available publicly"}, "maintainer": {"type": "string", "title": "Maintainer name"}, "maintainer_email": {"anyOf": [{"type": "string", "format": "email"}, {"type": "string", "maxLength": 0}, {"type": "null"}], "title": "Maintainer email"}, "usage": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "How to use the appliance"}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "An optional symbol for the appliance"}, "first_port_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Optional name of the first networking port example: eth0"}, "port_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Optional formating of the networking port example: eth{0}"}, "port_segment_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Optional port segment size. A port segment is a block of port. For example Ethernet0/0 Ethernet0/1 is the module 0 with a port segment size of 2"}, "linked_clone": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "False if you don't want to use a single image for all nodes"}, "docker": {"anyOf": [{"$ref": "#/components/schemas/Docker"}, {"type": "null"}], "title": "Docker specific options"}, "iou": {"anyOf": [{"$ref": "#/components/schemas/Iou"}, {"type": "null"}], "title": "IOU specific options"}, "dynamips": {"anyOf": [{"$ref": "#/components/schemas/Dynamips"}, {"type": "null"}], "title": "Dynamips specific options"}, "qemu": {"anyOf": [{"$ref": "#/components/schemas/Qemu"}, {"type": "null"}], "title": "Qemu specific options"}, "tags": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "User-defined metadata tags for the appliance"}, "images": {"anyOf": [{"items": {"$ref": "#/components/schemas/ApplianceImage"}, "type": "array"}, {"type": "null"}], "title": "Images for this appliance"}, "versions": {"anyOf": [{"items": {"$ref": "#/components/schemas/ApplianceVersion"}, "type": "array"}, {"type": "null"}], "title": "Versions of the appliance"}}, "type": "object", "required": ["registry_version", "appliance_id", "name", "category", "description", "vendor_name", "product_name", "status", "maintainer"], "title": "ApplianceV1_6", "description": "GNS3 Appliance model for registry versions 1-6"}, "ApplianceV8": {"properties": {"registry_version": {"type": "integer", "const": 8, "title": "Version of the registry compatible with this appliance (version >=8 introduced breaking changes)"}, "appliance_id": {"type": "string", "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$", "title": "Appliance ID"}, "name": {"type": "string", "title": "Appliance name"}, "builtin": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Whether the appliance is builtin or not"}, "category": {"$ref": "#/components/schemas/gns3server__schemas__controller__appliances__Category", "title": "Category of the appliance"}, "description": {"type": "string", "title": "Description of the appliance. Could be a marketing description"}, "vendor_name": {"type": "string", "title": "Name of the vendor"}, "vendor_url": {"type": "string", "minLength": 1, "format": "uri", "title": "Website of the vendor"}, "vendor_logo_url": {"anyOf": [{"type": "string", "minLength": 1, "format": "uri"}, {"type": "null"}], "title": "Link to the vendor logo (used by the GNS3 marketplace)"}, "documentation_url": {"anyOf": [{"type": "string", "minLength": 1, "format": "uri"}, {"type": "null"}], "title": "An optional documentation for using the appliance on vendor website"}, "product_name": {"type": "string", "title": "Product name"}, "product_url": {"anyOf": [{"type": "string", "minLength": 1, "format": "uri"}, {"type": "null"}], "title": "An optional product url on vendor website"}, "status": {"$ref": "#/components/schemas/Status", "title": "Document if the appliance is working or not"}, "availability": {"anyOf": [{"$ref": "#/components/schemas/Availability"}, {"type": "null"}], "title": "About image availability: can be downloaded directly; download requires a free registration; paid but a trial version (time or feature limited) is available; not available publicly"}, "maintainer": {"type": "string", "title": "Maintainer name"}, "maintainer_email": {"type": "string", "format": "email", "title": "Maintainer email"}, "installation_instructions": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Optional installation instructions"}, "usage": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "How to use the appliance"}, "default_username": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default username for the appliance"}, "default_password": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default password for the appliance"}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "An optional symbol for the appliance"}, "tags": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "User-defined metadata tags for the appliance"}, "settings": {"items": {"$ref": "#/components/schemas/TemplateSetting"}, "type": "array", "title": "Settings for running the appliance"}, "images": {"anyOf": [{"items": {"$ref": "#/components/schemas/ApplianceImage"}, "type": "array"}, {"type": "null"}], "title": "Images for this appliance"}, "versions": {"anyOf": [{"items": {"$ref": "#/components/schemas/ApplianceVersionV8"}, "type": "array"}, {"type": "null"}], "title": "Versions of the appliance"}}, "type": "object", "required": ["registry_version", "appliance_id", "name", "category", "description", "vendor_name", "vendor_url", "product_name", "status", "maintainer", "maintainer_email", "settings"], "title": "ApplianceV8", "description": "GNS3 Appliance model for registry version 8"}, "ApplianceVersion": {"properties": {"name": {"type": "string", "title": "Name of the version"}, "idlepc": {"anyOf": [{"type": "string", "pattern": "^0x[0-9a-f]{8}"}, {"type": "null"}], "title": "Idlepc"}, "images": {"anyOf": [{"$ref": "#/components/schemas/ApplianceVersionImages"}, {"type": "null"}], "title": "Images used for this version"}}, "type": "object", "required": ["name"], "title": "ApplianceVersion", "description": "Appliance version definition for v1-6"}, "ApplianceVersionImages": {"properties": {"kernel_image": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Kernel image"}, "initrd": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Initrd disk image"}, "image": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "OS image"}, "bios_image": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Bios image"}, "hda_disk_image": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Hda disk image"}, "hdb_disk_image": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Hdc disk image"}, "hdc_disk_image": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Hdd disk image"}, "hdd_disk_image": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Hdd diskimage"}, "cdrom_image": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "cdrom image"}}, "type": "object", "title": "ApplianceVersionImages", "description": "Appliance version images configuration for v1-6"}, "ApplianceVersionV8": {"properties": {"name": {"type": "string", "title": "Name of the version"}, "settings": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Template settings to use to run the version"}, "category": {"anyOf": [{"$ref": "#/components/schemas/gns3server__schemas__controller__appliances__Category"}, {"type": "null"}], "title": "Category of the version"}, "installation_instructions": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Optional installation instructions for the version"}, "usage": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Optional instructions about using the version"}, "default_username": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default username for the version"}, "default_password": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default password for the version"}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "An optional symbol for the version"}, "images": {"anyOf": [{"$ref": "#/components/schemas/ApplianceVersionImages"}, {"type": "null"}], "title": "Images used for this version"}}, "type": "object", "required": ["name"], "title": "ApplianceVersionV8", "description": "Appliance version definition (v8)"}, "AutoIdlePC": {"properties": {"platform": {"type": "string", "title": "Platform", "description": "Cisco platform"}, "image": {"type": "string", "title": "Image", "description": "Image path"}, "ram": {"type": "integer", "title": "Ram", "description": "Amount of RAM in MB"}}, "type": "object", "required": ["platform", "image", "ram"], "title": "AutoIdlePC", "description": "Data for auto Idle-PC request.", "example": {"image": "/path/to/c7200_image.bin", "platform": "c7200", "ram": 256}}, "Availability": {"type": "string", "enum": ["free", "with-registration", "free-to-try", "service-contract"], "title": "Availability", "description": "Image availability enum"}, "Body_load_project_v3_projects_load_post": {"properties": {"path": {"type": "string", "title": "Path"}}, "type": "object", "required": ["path"], "title": "Body_load_project_v3_projects_load_post"}, "Body_login_v3_access_users_login_post": {"properties": {"grant_type": {"anyOf": [{"type": "string", "pattern": "^password$"}, {"type": "null"}], "title": "Grant Type"}, "username": {"type": "string", "title": "Username"}, "password": {"type": "string", "format": "password", "title": "Password"}, "scope": {"type": "string", "title": "Scope", "default": ""}, "client_id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Client Id"}, "client_secret": {"anyOf": [{"type": "string"}, {"type": "null"}], "format": "password", "title": "Client Secret"}}, "type": "object", "required": ["username", "password"], "title": "Body_login_v3_access_users_login_post"}, "Capabilities": {"properties": {"version": {"type": "string", "title": "Version", "description": "Compute version number"}, "node_types": {"items": {"$ref": "#/components/schemas/NodeType"}, "type": "array", "title": "Node Types", "description": "Node types supported by the compute"}, "platform": {"type": "string", "title": "Platform", "description": "Platform where the compute is running (Linux, Windows or macOS)"}, "cpus": {"type": "integer", "title": "Cpus", "description": "Number of CPUs on this compute"}, "memory": {"type": "integer", "title": "Memory", "description": "Amount of memory on this compute"}, "disk_size": {"type": "integer", "title": "Disk Size", "description": "Disk size on this compute"}}, "type": "object", "required": ["version", "node_types", "platform", "cpus", "memory", "disk_size"], "title": "Capabilities", "description": "Capabilities supported by a compute."}, "ChecksumType": {"type": "string", "enum": ["md5"], "title": "ChecksumType", "description": "Checksum type enum"}, "Compression": {"type": "string", "enum": ["bzip2", "gzip", "lzma", "xz", "rar", "zip", "7z"], "title": "Compression", "description": "Compression type enum"}, "Compute": {"properties": {"protocol": {"$ref": "#/components/schemas/Protocol"}, "host": {"type": "string", "title": "Host"}, "port": {"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0, "title": "Port"}, "user": {"type": "string", "title": "User"}, "password": {"anyOf": [{"type": "string", "format": "password", "writeOnly": true}, {"type": "null"}], "title": "Password"}, "name": {"type": "string", "title": "Name"}, "created_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Created At"}, "updated_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Updated At"}, "compute_id": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}, "connected": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Connected", "description": "Whether the controller is connected to the compute or not"}, "cpu_usage_percent": {"anyOf": [{"type": "number", "maximum": 100.0, "minimum": 0.0}, {"type": "null"}], "title": "Cpu Usage Percent", "description": "CPU usage of the compute"}, "memory_usage_percent": {"anyOf": [{"type": "number", "maximum": 100.0, "minimum": 0.0}, {"type": "null"}], "title": "Memory Usage Percent", "description": "Memory usage of the compute"}, "disk_usage_percent": {"anyOf": [{"type": "number", "maximum": 100.0, "minimum": 0.0}, {"type": "null"}], "title": "Disk Usage Percent", "description": "Disk usage of the compute"}, "last_error": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Last Error", "description": "Last error found on the compute"}, "capabilities": {"anyOf": [{"$ref": "#/components/schemas/Capabilities"}, {"type": "null"}]}}, "type": "object", "required": ["protocol", "host", "port", "name", "compute_id"], "title": "Compute", "description": "Data returned for a compute."}, "ComputeCreate": {"properties": {"protocol": {"$ref": "#/components/schemas/Protocol"}, "host": {"type": "string", "title": "Host"}, "port": {"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0, "title": "Port"}, "user": {"type": "string", "title": "User"}, "password": {"anyOf": [{"type": "string", "format": "password", "writeOnly": true}, {"type": "null"}], "title": "Password"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "compute_id": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}, "type": "object", "required": ["protocol", "host", "port"], "title": "ComputeCreate", "description": "Data to create a compute.", "example": {"host": "127.0.0.1", "name": "My compute", "password": "password", "port": 3080, "user": "user"}}, "ComputeDockerImage": {"properties": {"image": {"type": "string", "title": "Image", "description": "Docker image name"}}, "type": "object", "required": ["image"], "title": "ComputeDockerImage", "description": "Docker image from compute."}, "ComputeUpdate": {"properties": {"protocol": {"anyOf": [{"$ref": "#/components/schemas/Protocol"}, {"type": "null"}]}, "host": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Host"}, "port": {"anyOf": [{"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Port"}, "user": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "User"}, "password": {"anyOf": [{"type": "string", "format": "password", "writeOnly": true}, {"type": "null"}], "title": "Password"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}}, "type": "object", "title": "ComputeUpdate", "description": "Data to update a compute.", "example": {"host": "10.0.0.1", "port": 8080}}, "ComputeVMwareVM": {"properties": {"vmname": {"type": "string", "title": "Vmname", "description": "VMware VM name"}, "vmx_path": {"type": "string", "title": "Vmx Path", "description": "Path to the vmx file"}}, "type": "object", "required": ["vmname", "vmx_path"], "title": "ComputeVMwareVM", "description": "VMware VM from compute."}, "ComputeVirtualBoxVM": {"properties": {"vmname": {"type": "string", "title": "Vmname", "description": "VirtualBox VM name"}, "ram": {"type": "integer", "title": "Ram", "description": "VirtualBox VM memory"}}, "type": "object", "required": ["vmname", "ram"], "title": "ComputeVirtualBoxVM", "description": "VirtualBox VM from compute."}, "ConsoleType": {"type": "string", "enum": ["vnc", "telnet", "ssh", "http", "https", "spice", "spice+agent", "none"], "title": "ConsoleType", "description": "Supported console types."}, "Credentials": {"properties": {"username": {"type": "string", "title": "Username"}, "password": {"type": "string", "title": "Password"}}, "type": "object", "required": ["username", "password"], "title": "Credentials"}, "CustomAdapter": {"properties": {"adapter_number": {"type": "integer", "title": "Adapter Number"}, "port_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Port Name"}, "adapter_type": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Adapter Type"}, "mac_address": {"anyOf": [{"type": "string", "pattern": "^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$"}, {"type": "null"}], "title": "Mac Address"}}, "type": "object", "required": ["adapter_number"], "title": "CustomAdapter", "description": "Custom adapter data."}, "CustomAdapterItem": {"properties": {"adapter_number": {"type": "integer", "title": "Adapter number"}, "port_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Custom port name"}, "adapter_type": {"anyOf": [{"$ref": "#/components/schemas/QemuAdapterType"}, {"type": "null"}], "title": "Custom adapter type"}, "mac_address": {"anyOf": [{"type": "string", "pattern": "^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$"}, {"type": "null"}], "title": "Custom MAC address"}}, "type": "object", "required": ["adapter_number"], "title": "CustomAdapterItem", "description": "Custom adapter configuration (v8)"}, "Docker": {"properties": {"adapters": {"type": "integer", "title": "Number of Ethernet adapters"}, "image": {"type": "string", "title": "Docker image in the Docker Hub"}, "start_command": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Command executed when the container start. Empty will use the default"}, "environment": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "One KEY=VAR environment by line"}, "console_type": {"anyOf": [{"$ref": "#/components/schemas/DockerConsoleType"}, {"type": "null"}], "title": "Type of console connection for the administration of the appliance"}, "console_http_port": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Console Http Port", "description": "Internal port in the container of the HTTP server"}, "console_http_path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Console Http Path", "description": "Path of the web interface"}, "extra_hosts": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Extra Hosts", "description": "Hosts which will be written to /etc/hosts into container"}, "extra_volumes": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "Extra Volumes", "description": "Additional directories to make persistent that are not included in the images VOLUME directive"}}, "type": "object", "required": ["adapters", "image"], "title": "Docker", "description": "Docker configuration for v1-6"}, "DockerConsoleType": {"type": "string", "enum": ["telnet", "ssh", "vnc", "http", "https", "none"], "title": "DockerConsoleType", "description": "Docker console type enum"}, "DockerPropertiesV8": {"properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name of the template"}, "category": {"anyOf": [{"$ref": "#/components/schemas/gns3server__schemas__controller__appliances__Category"}, {"type": "null"}], "title": "Category of the template"}, "default_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default name format"}, "usage": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "How to use the template"}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Symbol of the template"}, "image": {"type": "string", "title": "Docker image"}, "adapters": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Number of ethernet adapters"}, "start_command": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Command executed when the container start. Empty will use the default"}, "environment": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "One KEY=VAR environment by line"}, "console_type": {"anyOf": [{"$ref": "#/components/schemas/DockerConsoleType"}, {"type": "null"}], "title": "Type of console"}, "console_http_port": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Internal port in the container of the HTTP server"}, "console_http_path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Path of the web interface"}, "console_resolution": {"anyOf": [{"type": "string", "pattern": "^[0-9]+x[0-9]+$"}, {"type": "null"}], "title": "Console resolution for VNC, for example 1024x768"}, "extra_hosts": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Docker extra hosts (added to /etc/hosts)"}, "extra_volumes": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "Additional directories to make persistent"}}, "type": "object", "required": ["image"], "title": "DockerPropertiesV8", "description": "Docker template properties (v8)"}, "Drawing": {"properties": {"drawing_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Drawing Id"}, "project_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Project Id"}, "x": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "X"}, "y": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Y"}, "z": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Z"}, "locked": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Locked"}, "rotation": {"anyOf": [{"type": "integer", "maximum": 360.0, "minimum": -359.0}, {"type": "null"}], "title": "Rotation"}, "svg": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Svg"}}, "type": "object", "title": "Drawing", "description": "Drawing data."}, "Dynamips": {"properties": {"chassis": {"anyOf": [{"$ref": "#/components/schemas/DynamipsChassis"}, {"type": "null"}], "title": "Chassis type"}, "platform": {"$ref": "#/components/schemas/DynamipsPlatform", "title": "Platform type"}, "ram": {"type": "integer", "minimum": 1.0, "title": "Amount of ram"}, "nvram": {"type": "integer", "minimum": 1.0, "title": "Amount of nvram"}, "startup_config": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Config loaded at startup"}, "wic0": {"anyOf": [{"$ref": "#/components/schemas/DynamipsWic"}, {"type": "null"}]}, "wic1": {"anyOf": [{"$ref": "#/components/schemas/DynamipsWic"}, {"type": "null"}]}, "wic2": {"anyOf": [{"$ref": "#/components/schemas/DynamipsWic"}, {"type": "null"}]}, "slot0": {"anyOf": [{"$ref": "#/components/schemas/DynamipsSlot"}, {"type": "null"}]}, "slot1": {"anyOf": [{"$ref": "#/components/schemas/DynamipsSlot"}, {"type": "null"}]}, "slot2": {"anyOf": [{"$ref": "#/components/schemas/DynamipsSlot"}, {"type": "null"}]}, "slot3": {"anyOf": [{"$ref": "#/components/schemas/DynamipsSlot"}, {"type": "null"}]}, "slot4": {"anyOf": [{"$ref": "#/components/schemas/DynamipsSlot"}, {"type": "null"}]}, "slot5": {"anyOf": [{"$ref": "#/components/schemas/DynamipsSlot"}, {"type": "null"}]}, "slot6": {"anyOf": [{"$ref": "#/components/schemas/DynamipsSlot"}, {"type": "null"}]}, "midplane": {"anyOf": [{"$ref": "#/components/schemas/DynamipsMidplane"}, {"type": "null"}]}, "npe": {"anyOf": [{"$ref": "#/components/schemas/DynamipsNpe"}, {"type": "null"}]}}, "type": "object", "required": ["platform", "ram", "nvram"], "title": "Dynamips", "description": "Dynamips configuration for v1-6"}, "DynamipsChassis": {"type": "string", "enum": ["1720", "1721", "1750", "1751", "1760", "2610", "2620", "2610XM", "2620XM", "2650XM", "2621", "2611XM", "2621XM", "2651XM", "3620", "3640", "3660", ""], "title": "DynamipsChassis", "description": "Dynamips chassis enum"}, "DynamipsMidplane": {"type": "string", "enum": ["std", "vxr"], "title": "DynamipsMidplane", "description": "Dynamips midplane enum"}, "DynamipsNpe": {"type": "string", "enum": ["npe-100", "npe-150", "npe-175", "npe-200", "npe-225", "npe-300", "npe-400", "npe-g2"], "title": "DynamipsNpe", "description": "Dynamips NPE enum"}, "DynamipsPlatform": {"type": "string", "enum": ["c1700", "c2600", "c2691", "c3725", "c3745", "c3600", "c7200"], "title": "DynamipsPlatform", "description": "Dynamips platform enum"}, "DynamipsPropertiesV8": {"properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name of the template"}, "category": {"anyOf": [{"$ref": "#/components/schemas/gns3server__schemas__controller__appliances__Category"}, {"type": "null"}], "title": "Category of the template"}, "default_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default name format"}, "usage": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "How to use the template"}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Symbol of the template"}, "chassis": {"anyOf": [{"$ref": "#/components/schemas/DynamipsChassis"}, {"type": "null"}], "title": "Chassis type"}, "platform": {"anyOf": [{"$ref": "#/components/schemas/DynamipsPlatform"}, {"type": "null"}], "title": "Platform type"}, "ram": {"anyOf": [{"type": "integer", "minimum": 1.0}, {"type": "null"}], "title": "Amount of ram"}, "nvram": {"anyOf": [{"type": "integer", "minimum": 1.0}, {"type": "null"}], "title": "Amount of nvram"}, "idlepc": {"anyOf": [{"type": "string", "pattern": "^0x[0-9a-f]{8}"}, {"type": "null"}], "title": "Idlepc"}, "startup_config": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Config loaded at startup"}, "wic0": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Wic0"}, "wic1": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Wic1"}, "wic2": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Wic2"}, "slot0": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Slot0"}, "slot1": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Slot1"}, "slot2": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Slot2"}, "slot3": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Slot3"}, "slot4": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Slot4"}, "slot5": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Slot5"}, "slot6": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Slot6"}, "midplane": {"anyOf": [{"$ref": "#/components/schemas/DynamipsMidplane"}, {"type": "null"}]}, "npe": {"anyOf": [{"$ref": "#/components/schemas/DynamipsNpe"}, {"type": "null"}]}}, "type": "object", "title": "DynamipsPropertiesV8", "description": "Dynamips template properties (v8)"}, "DynamipsSlot": {"type": "string", "enum": ["C7200-IO-2FE", "C7200-IO-FE", "C7200-IO-GE-E", "NM-16ESW", "NM-1E", "NM-1FE-TX", "NM-4E", "NM-4T", "PA-2FE-TX", "PA-4E", "PA-4T+", "PA-8E", "PA-8T", "PA-A1", "PA-FE-TX", "PA-GE", "PA-POS-OC3", "C2600-MB-2FE", "C2600-MB-1E", "C1700-MB-1FE", "C2600-MB-2E", "C2600-MB-1FE", "C1700-MB-WIC1", "GT96100-FE", "Leopard-2FE", ""], "title": "DynamipsSlot", "description": "Dynamips slot enum"}, "DynamipsWic": {"type": "string", "enum": ["WIC-1ENET", "WIC-1T", "WIC-2T", ""], "title": "DynamipsWic", "description": "Dynamips WIC enum"}, "Engine": {"type": "string", "enum": ["vmware", "virtualbox", "hyper-v", "none"], "title": "Engine", "description": "\"The engine to use for the GNS3 VM."}, "ErrorMessage": {"properties": {"message": {"type": "string", "title": "Message"}}, "type": "object", "required": ["message"], "title": "ErrorMessage", "description": "Error message."}, "EthernetPortInfo": {"properties": {"node_id": {"type": "string", "format": "uuid", "title": "Node Id"}, "interface": {"type": "string", "title": "Interface"}, "type": {"type": "string", "title": "Type"}}, "type": "object", "required": ["node_id", "interface", "type"], "title": "EthernetPortInfo", "description": "Ethernet port information."}, "GNS3VM": {"properties": {"enable": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Enable", "description": "Enable/disable the GNS3 VM"}, "vmname": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Vmname", "description": "GNS3 VM name"}, "when_exit": {"anyOf": [{"$ref": "#/components/schemas/WhenExit"}, {"type": "null"}], "description": "Action when the GNS3 VM exits"}, "headless": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Headless", "description": "Start the GNS3 VM GUI or not"}, "engine": {"anyOf": [{"$ref": "#/components/schemas/Engine"}, {"type": "null"}], "description": "The engine to use for the GNS3 VM"}, "allocate_vcpus_ram": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Allocate Vcpus Ram", "description": "Allocate vCPUS and RAM settings"}, "vcpus": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Vcpus", "description": "Number of CPUs to allocate for the GNS3 VM"}, "ram": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Ram", "description": "Amount of memory to allocate for the GNS3 VM"}, "port": {"anyOf": [{"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Port"}}, "type": "object", "title": "GNS3VM", "description": "GNS3 VM data."}, "HTTPValidationError": {"properties": {"detail": {"items": {"$ref": "#/components/schemas/ValidationError"}, "type": "array", "title": "Detail"}}, "type": "object", "title": "HTTPValidationError"}, "IOULicense": {"properties": {"iourc_content": {"type": "string", "title": "Iourc Content", "description": "Content of iourc file"}, "license_check": {"type": "boolean", "title": "License Check", "description": "Whether the license must be checked or not"}}, "type": "object", "required": ["iourc_content", "license_check"], "title": "IOULicense"}, "Image": {"properties": {"filename": {"type": "string", "title": "Filename", "description": "Image filename"}, "path": {"type": "string", "title": "Path", "description": "Image path"}, "image_type": {"$ref": "#/components/schemas/ImageType", "description": "Image type"}, "image_size": {"type": "integer", "title": "Image Size", "description": "Image size in bytes"}, "checksum": {"type": "string", "title": "Checksum", "description": "Checksum value"}, "checksum_algorithm": {"type": "string", "title": "Checksum Algorithm", "description": "Checksum algorithm"}, "created_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Created At"}, "updated_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Updated At"}}, "type": "object", "required": ["filename", "path", "image_type", "image_size", "checksum", "checksum_algorithm"], "title": "Image"}, "ImageType": {"type": "string", "enum": ["qemu", "ios", "iou"], "title": "ImageType"}, "Iou": {"properties": {"ethernet_adapters": {"type": "integer", "title": "Number of Ethernet adapters"}, "serial_adapters": {"type": "integer", "title": "Number of serial adapters"}, "nvram": {"type": "integer", "title": "Host NVRAM"}, "ram": {"type": "integer", "title": "Host RAM"}, "startup_config": {"type": "string", "title": "Config loaded at startup"}}, "type": "object", "required": ["ethernet_adapters", "serial_adapters", "nvram", "ram", "startup_config"], "title": "Iou", "description": "IOU configuration for v1-6"}, "IouPropertiesV8": {"properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name of the template"}, "category": {"anyOf": [{"$ref": "#/components/schemas/gns3server__schemas__controller__appliances__Category"}, {"type": "null"}], "title": "Category of the template"}, "default_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default name format"}, "usage": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "How to use the template"}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Symbol of the template"}, "ethernet_adapters": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Number of ethernet adapters"}, "serial_adapters": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Number of serial adapters"}, "ram": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Host RAM"}, "nvram": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Host NVRAM"}, "startup_config": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Config loaded at startup"}}, "type": "object", "title": "IouPropertiesV8", "description": "IOU template properties (v8)"}, "Kvm": {"type": "string", "enum": ["require", "allow", "disable"], "title": "Kvm", "description": "KVM requirements enum"}, "Label": {"properties": {"text": {"type": "string", "title": "Text"}, "style": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Style", "description": "SVG style attribute. Apply default style if null"}, "x": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "X", "description": "Relative X position of the label. Center it if null"}, "y": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Y", "description": "Relative Y position of the label"}, "rotation": {"anyOf": [{"type": "integer", "maximum": 360.0, "minimum": -359.0}, {"type": "null"}], "title": "Rotation", "description": "Rotation of the label"}}, "type": "object", "required": ["text"], "title": "Label", "description": "Label data."}, "Link": {"properties": {"nodes": {"anyOf": [{"items": {"$ref": "#/components/schemas/LinkNode"}, "type": "array", "maxItems": 2, "minItems": 0}, {"type": "null"}], "title": "Nodes"}, "suspend": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Suspend"}, "link_style": {"anyOf": [{"$ref": "#/components/schemas/LinkStyle"}, {"type": "null"}]}, "filters": {"anyOf": [{"additionalProperties": true, "type": "object"}, {"type": "null"}], "title": "Filters"}, "link_id": {"type": "string", "format": "uuid", "title": "Link Id"}, "project_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Project Id"}, "link_type": {"anyOf": [{"$ref": "#/components/schemas/gns3server__schemas__controller__links__LinkType"}, {"type": "null"}]}, "capturing": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Capturing", "description": "Read only property. True if a capture running on the link"}, "capture_file_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Capture File Name", "description": "Read only property. The name of the capture file if a capture is running"}, "capture_file_path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Capture File Path", "description": "Read only property. The full path of the capture file if a capture is running"}, "capture_compute_id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Capture Compute Id", "description": "Read only property. The compute identifier where a capture is running"}, "wireshark": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Wireshark", "description": "Read only property. True if a Web Wireshark session is active on the link", "default": false}}, "type": "object", "required": ["link_id"], "title": "Link"}, "LinkCapture": {"properties": {"data_link_type": {"type": "string", "title": "Data Link Type", "default": "DLT_EN10MB"}, "capture_file_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Capture File Name"}, "wireshark": {"type": "boolean", "title": "Wireshark", "default": false}}, "type": "object", "title": "LinkCapture", "description": "Link capture data."}, "LinkCreate": {"properties": {"nodes": {"items": {"$ref": "#/components/schemas/LinkNode"}, "type": "array", "maxItems": 2, "minItems": 2, "title": "Nodes"}, "suspend": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Suspend"}, "link_style": {"anyOf": [{"$ref": "#/components/schemas/LinkStyle"}, {"type": "null"}]}, "filters": {"anyOf": [{"additionalProperties": true, "type": "object"}, {"type": "null"}], "title": "Filters"}, "link_id": {"type": "string", "format": "uuid", "title": "Link Id"}}, "type": "object", "required": ["nodes"], "title": "LinkCreate"}, "LinkNode": {"properties": {"node_id": {"type": "string", "format": "uuid", "title": "Node Id"}, "adapter_number": {"type": "integer", "title": "Adapter Number"}, "port_number": {"type": "integer", "title": "Port Number"}, "label": {"anyOf": [{"$ref": "#/components/schemas/Label"}, {"type": "null"}]}}, "type": "object", "required": ["node_id", "adapter_number", "port_number"], "title": "LinkNode", "description": "Link node data."}, "LinkStyle": {"properties": {"color": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Color"}, "width": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Width"}, "type": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Type"}, "link_type": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Link Type"}, "bezier_curviness": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Bezier Curviness"}, "flowchart_roundness": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Flowchart Roundness"}, "control_offset": {"anyOf": [{"prefixItems": [{"type": "number"}, {"type": "number"}], "type": "array", "maxItems": 2, "minItems": 2}, {"type": "null"}], "title": "Control Offset"}}, "type": "object", "title": "LinkStyle"}, "LinkUpdate": {"properties": {"nodes": {"anyOf": [{"items": {"$ref": "#/components/schemas/LinkNode"}, "type": "array", "maxItems": 2, "minItems": 0}, {"type": "null"}], "title": "Nodes"}, "suspend": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Suspend"}, "link_style": {"anyOf": [{"$ref": "#/components/schemas/LinkStyle"}, {"type": "null"}]}, "filters": {"anyOf": [{"additionalProperties": true, "type": "object"}, {"type": "null"}], "title": "Filters"}}, "type": "object", "title": "LinkUpdate"}, "LoggedInUserUpdate": {"properties": {"password": {"anyOf": [{"type": "string", "maxLength": 100, "minLength": 8, "format": "password", "writeOnly": true}, {"type": "null"}], "title": "Password"}, "email": {"anyOf": [{"type": "string", "format": "email"}, {"type": "null"}], "title": "Email"}, "full_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Full Name"}}, "type": "object", "title": "LoggedInUserUpdate", "description": "Properties to update a logged-in user."}, "Node": {"properties": {"compute_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "string"}], "title": "Compute Id"}, "name": {"type": "string", "title": "Name"}, "node_type": {"$ref": "#/components/schemas/NodeType"}, "node_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Node Id"}, "console": {"anyOf": [{"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Console", "description": "Console TCP port"}, "console_type": {"anyOf": [{"$ref": "#/components/schemas/ConsoleType"}, {"type": "null"}]}, "console_auto_start": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Console Auto Start", "description": "Automatically start the console when the node has started", "default": false}, "aux": {"anyOf": [{"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Aux", "description": "Auxiliary console TCP port"}, "aux_type": {"anyOf": [{"$ref": "#/components/schemas/ConsoleType"}, {"type": "null"}]}, "properties": {"anyOf": [{"additionalProperties": true, "type": "object"}, {"type": "null"}], "title": "Properties", "description": "Properties specific to an emulator"}, "label": {"anyOf": [{"$ref": "#/components/schemas/Label"}, {"type": "null"}]}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Symbol"}, "x": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "X", "default": 0}, "y": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Y", "default": 0}, "z": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Z", "default": 1}, "locked": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Locked", "description": "Whether the element locked or not", "default": false}, "port_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Port Name Format", "description": "Formatting for port name {0} will be replace by port number"}, "port_segment_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Port Segment Size", "description": "Size of the port segment"}, "first_port_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "First Port Name", "description": "Name of the first port"}, "custom_adapters": {"anyOf": [{"items": {"$ref": "#/components/schemas/CustomAdapter"}, "type": "array"}, {"type": "null"}], "title": "Custom Adapters"}, "tags": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "Tags", "description": "User-defined metadata tags (e.g. 'vendor:cisco' or 'model:7200')"}, "template_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Template Id", "description": "Template UUID from which the node has been created. Read only"}, "project_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Project Id"}, "node_directory": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Node Directory", "description": "Working directory of the node. Read only"}, "status": {"anyOf": [{"$ref": "#/components/schemas/NodeStatus"}, {"type": "null"}], "description": "Node status. Read only"}, "command_line": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Command Line", "description": "Command line use to start the node. Read only"}, "width": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Width", "description": "Width of the node. Read only"}, "height": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Height", "description": "Height of the node. Read only"}, "ports": {"anyOf": [{"items": {"$ref": "#/components/schemas/NodePort"}, "type": "array"}, {"type": "null"}], "title": "Ports", "description": "List of node ports. Read only"}, "console_host": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Console Host", "description": "Console host. Warning if the host is 0.0.0.0 or :: (listen on all interfaces) you need to use the same address you use to connect to the controller"}}, "type": "object", "required": ["compute_id", "name", "node_type"], "title": "Node"}, "NodeCreate": {"properties": {"compute_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "string"}], "title": "Compute Id"}, "name": {"type": "string", "title": "Name"}, "node_type": {"$ref": "#/components/schemas/NodeType"}, "node_id": {"type": "string", "format": "uuid", "title": "Node Id"}, "console": {"anyOf": [{"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Console", "description": "Console TCP port"}, "console_type": {"anyOf": [{"$ref": "#/components/schemas/ConsoleType"}, {"type": "null"}]}, "console_auto_start": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Console Auto Start", "description": "Automatically start the console when the node has started", "default": false}, "aux": {"anyOf": [{"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Aux", "description": "Auxiliary console TCP port"}, "aux_type": {"anyOf": [{"$ref": "#/components/schemas/ConsoleType"}, {"type": "null"}]}, "properties": {"anyOf": [{"additionalProperties": true, "type": "object"}, {"type": "null"}], "title": "Properties", "description": "Properties specific to an emulator"}, "label": {"anyOf": [{"$ref": "#/components/schemas/Label"}, {"type": "null"}]}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Symbol"}, "x": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "X", "default": 0}, "y": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Y", "default": 0}, "z": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Z", "default": 1}, "locked": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Locked", "description": "Whether the element locked or not", "default": false}, "port_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Port Name Format", "description": "Formatting for port name {0} will be replace by port number"}, "port_segment_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Port Segment Size", "description": "Size of the port segment"}, "first_port_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "First Port Name", "description": "Name of the first port"}, "custom_adapters": {"anyOf": [{"items": {"$ref": "#/components/schemas/CustomAdapter"}, "type": "array"}, {"type": "null"}], "title": "Custom Adapters"}, "tags": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "Tags", "description": "User-defined metadata tags (e.g. 'vendor:cisco' or 'model:7200')"}}, "type": "object", "required": ["compute_id", "name", "node_type"], "title": "NodeCreate"}, "NodeDuplicate": {"properties": {"x": {"type": "integer", "title": "X"}, "y": {"type": "integer", "title": "Y"}, "z": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Z", "default": 0}}, "type": "object", "required": ["x", "y"], "title": "NodeDuplicate", "description": "Data to duplicate a node."}, "NodeFile": {"properties": {"path": {"type": "string", "title": "Path", "description": "File name"}, "size": {"type": "integer", "title": "Size", "description": "File size in bytes"}, "created_at": {"type": "string", "title": "Created At", "description": "File creation time (ISO 8601)"}, "modified_at": {"type": "string", "title": "Modified At", "description": "File modification time (ISO 8601)"}, "extension": {"type": "string", "title": "Extension", "description": "File extension"}}, "type": "object", "required": ["path", "size", "created_at", "modified_at", "extension"], "title": "NodeFile", "description": "Detailed file information for node files."}, "NodePort": {"properties": {"name": {"type": "string", "title": "Name", "description": "Port name"}, "short_name": {"type": "string", "title": "Short Name", "description": "Port name"}, "adapter_number": {"type": "integer", "title": "Adapter Number", "description": "Adapter slot"}, "adapter_type": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Adapter Type", "description": "Adapter type"}, "port_number": {"type": "integer", "title": "Port Number", "description": "Port slot"}, "link_type": {"$ref": "#/components/schemas/gns3server__schemas__controller__nodes__LinkType", "description": "Type of link"}, "data_link_types": {"additionalProperties": true, "type": "object", "title": "Data Link Types", "description": "Available PCAP types for capture"}, "mac_address": {"anyOf": [{"type": "string", "pattern": "^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$"}, {"type": "null"}], "title": "Mac Address"}}, "type": "object", "required": ["name", "short_name", "adapter_number", "port_number", "link_type", "data_link_types"], "title": "NodePort", "description": "Node port data."}, "NodeStatus": {"type": "string", "enum": ["stopped", "started", "suspended"], "title": "NodeStatus", "description": "Supported node statuses."}, "NodeType": {"type": "string", "enum": ["cloud", "nat", "ethernet_hub", "ethernet_switch", "frame_relay_switch", "atm_switch", "docker", "dynamips", "vpcs", "virtualbox", "vmware", "iou", "qemu"], "title": "NodeType", "description": "Supported node types."}, "NodeUpdate": {"properties": {"compute_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "string"}, {"type": "null"}], "title": "Compute Id"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "node_type": {"anyOf": [{"$ref": "#/components/schemas/NodeType"}, {"type": "null"}]}, "node_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Node Id"}, "console": {"anyOf": [{"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Console", "description": "Console TCP port"}, "console_type": {"anyOf": [{"$ref": "#/components/schemas/ConsoleType"}, {"type": "null"}]}, "console_auto_start": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Console Auto Start", "description": "Automatically start the console when the node has started", "default": false}, "aux": {"anyOf": [{"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Aux", "description": "Auxiliary console TCP port"}, "aux_type": {"anyOf": [{"$ref": "#/components/schemas/ConsoleType"}, {"type": "null"}]}, "properties": {"anyOf": [{"additionalProperties": true, "type": "object"}, {"type": "null"}], "title": "Properties", "description": "Properties specific to an emulator"}, "label": {"anyOf": [{"$ref": "#/components/schemas/Label"}, {"type": "null"}]}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Symbol"}, "x": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "X", "default": 0}, "y": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Y", "default": 0}, "z": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Z", "default": 1}, "locked": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Locked", "description": "Whether the element locked or not", "default": false}, "port_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Port Name Format", "description": "Formatting for port name {0} will be replace by port number"}, "port_segment_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Port Segment Size", "description": "Size of the port segment"}, "first_port_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "First Port Name", "description": "Name of the first port"}, "custom_adapters": {"anyOf": [{"items": {"$ref": "#/components/schemas/CustomAdapter"}, "type": "array"}, {"type": "null"}], "title": "Custom Adapters"}, "tags": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "Tags", "description": "User-defined metadata tags (e.g. 'vendor:cisco' or 'model:7200')"}}, "type": "object", "title": "NodeUpdate", "description": "Data to update a node."}, "Privilege": {"properties": {"name": {"type": "string", "title": "Name"}, "description": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Description"}, "created_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Created At"}, "updated_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Updated At"}, "privilege_id": {"type": "string", "format": "uuid", "title": "Privilege Id"}}, "type": "object", "required": ["name", "privilege_id"], "title": "Privilege"}, "Project": {"properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "project_id": {"type": "string", "format": "uuid", "title": "Project Id"}, "path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Path", "description": "Project directory"}, "auto_close": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Close", "description": "Close project when last client leaves"}, "auto_open": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Open", "description": "Project opens when GNS3 starts"}, "auto_start": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Start", "description": "Project starts when opened"}, "scene_height": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Scene Height", "description": "Height of the drawing area"}, "scene_width": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Scene Width", "description": "Width of the drawing area"}, "zoom": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Zoom", "description": "Zoom of the drawing area"}, "show_layers": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Layers", "description": "Show layers on the drawing area"}, "snap_to_grid": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Snap To Grid", "description": "Snap to grid on the drawing area"}, "show_grid": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Grid", "description": "Show the grid on the drawing area"}, "grid_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Grid Size", "description": "Grid size for the drawing area for nodes"}, "drawing_grid_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Drawing Grid Size", "description": "Grid size for the drawing area for drawings"}, "show_interface_labels": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Interface Labels", "description": "Show interface labels on the drawing area"}, "supplier": {"anyOf": [{"$ref": "#/components/schemas/Supplier"}, {"type": "null"}], "description": "Supplier of the project"}, "variables": {"anyOf": [{"items": {"$ref": "#/components/schemas/Variable"}, "type": "array"}, {"type": "null"}], "title": "Variables", "description": "Variables required to run the project"}, "status": {"anyOf": [{"$ref": "#/components/schemas/ProjectStatus"}, {"type": "null"}]}, "filename": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Filename"}, "created_by": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Created By", "description": "Username of the user who created the project"}}, "type": "object", "required": ["project_id"], "title": "Project"}, "ProjectCompression": {"type": "string", "enum": ["none", "zip", "bzip2", "lzma", "zstd"], "title": "ProjectCompression", "description": "Supported project compression."}, "ProjectCreate": {"properties": {"name": {"type": "string", "title": "Name"}, "project_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Project Id"}, "path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Path", "description": "Project directory"}, "auto_close": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Close", "description": "Close project when last client leaves"}, "auto_open": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Open", "description": "Project opens when GNS3 starts"}, "auto_start": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Start", "description": "Project starts when opened"}, "scene_height": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Scene Height", "description": "Height of the drawing area"}, "scene_width": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Scene Width", "description": "Width of the drawing area"}, "zoom": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Zoom", "description": "Zoom of the drawing area"}, "show_layers": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Layers", "description": "Show layers on the drawing area"}, "snap_to_grid": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Snap To Grid", "description": "Snap to grid on the drawing area"}, "show_grid": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Grid", "description": "Show the grid on the drawing area"}, "grid_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Grid Size", "description": "Grid size for the drawing area for nodes"}, "drawing_grid_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Drawing Grid Size", "description": "Grid size for the drawing area for drawings"}, "show_interface_labels": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Interface Labels", "description": "Show interface labels on the drawing area"}, "supplier": {"anyOf": [{"$ref": "#/components/schemas/Supplier"}, {"type": "null"}], "description": "Supplier of the project"}, "variables": {"anyOf": [{"items": {"$ref": "#/components/schemas/Variable"}, "type": "array"}, {"type": "null"}], "title": "Variables", "description": "Variables required to run the project"}}, "type": "object", "required": ["name"], "title": "ProjectCreate", "description": "Properties for project creation."}, "ProjectDuplicate": {"properties": {"name": {"type": "string", "title": "Name"}, "project_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Project Id"}, "path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Path", "description": "Project directory"}, "auto_close": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Close", "description": "Close project when last client leaves"}, "auto_open": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Open", "description": "Project opens when GNS3 starts"}, "auto_start": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Start", "description": "Project starts when opened"}, "scene_height": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Scene Height", "description": "Height of the drawing area"}, "scene_width": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Scene Width", "description": "Width of the drawing area"}, "zoom": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Zoom", "description": "Zoom of the drawing area"}, "show_layers": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Layers", "description": "Show layers on the drawing area"}, "snap_to_grid": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Snap To Grid", "description": "Snap to grid on the drawing area"}, "show_grid": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Grid", "description": "Show the grid on the drawing area"}, "grid_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Grid Size", "description": "Grid size for the drawing area for nodes"}, "drawing_grid_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Drawing Grid Size", "description": "Grid size for the drawing area for drawings"}, "show_interface_labels": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Interface Labels", "description": "Show interface labels on the drawing area"}, "supplier": {"anyOf": [{"$ref": "#/components/schemas/Supplier"}, {"type": "null"}], "description": "Supplier of the project"}, "variables": {"anyOf": [{"items": {"$ref": "#/components/schemas/Variable"}, "type": "array"}, {"type": "null"}], "title": "Variables", "description": "Variables required to run the project"}, "reset_mac_addresses": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Reset Mac Addresses", "description": "Reset MAC addresses for this project", "default": false}}, "type": "object", "required": ["name"], "title": "ProjectDuplicate", "description": "Properties for project duplication."}, "ProjectStatus": {"type": "string", "enum": ["opened", "closed"], "title": "ProjectStatus", "description": "Supported project statuses."}, "ProjectUpdate": {"properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "project_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Project Id"}, "path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Path", "description": "Project directory"}, "auto_close": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Close", "description": "Close project when last client leaves"}, "auto_open": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Open", "description": "Project opens when GNS3 starts"}, "auto_start": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Start", "description": "Project starts when opened"}, "scene_height": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Scene Height", "description": "Height of the drawing area"}, "scene_width": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Scene Width", "description": "Width of the drawing area"}, "zoom": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Zoom", "description": "Zoom of the drawing area"}, "show_layers": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Layers", "description": "Show layers on the drawing area"}, "snap_to_grid": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Snap To Grid", "description": "Snap to grid on the drawing area"}, "show_grid": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Grid", "description": "Show the grid on the drawing area"}, "grid_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Grid Size", "description": "Grid size for the drawing area for nodes"}, "drawing_grid_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Drawing Grid Size", "description": "Grid size for the drawing area for drawings"}, "show_interface_labels": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Interface Labels", "description": "Show interface labels on the drawing area"}, "supplier": {"anyOf": [{"$ref": "#/components/schemas/Supplier"}, {"type": "null"}], "description": "Supplier of the project"}, "variables": {"anyOf": [{"items": {"$ref": "#/components/schemas/Variable"}, "type": "array"}, {"type": "null"}], "title": "Variables", "description": "Variables required to run the project"}}, "type": "object", "title": "ProjectUpdate", "description": "Properties for project update."}, "Protocol": {"type": "string", "enum": ["http", "https"], "title": "Protocol", "description": "Protocol supported to communicate with a compute."}, "Qemu": {"properties": {"adapter_type": {"$ref": "#/components/schemas/QemuAdapterType", "title": "Type of network adapter"}, "adapters": {"type": "integer", "title": "Number of adapters"}, "ram": {"type": "integer", "title": "RAM allocated to the appliance (MB)"}, "cpus": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Number of Virtual CPU"}, "hda_disk_interface": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskInterface"}, {"type": "null"}], "title": "Disk interface for the installed hda_disk_image"}, "hdb_disk_interface": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskInterface"}, {"type": "null"}], "title": "Disk interface for the installed hdb_disk_image"}, "hdc_disk_interface": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskInterface"}, {"type": "null"}], "title": "Disk interface for the installed hdc_disk_image"}, "hdd_disk_interface": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskInterface"}, {"type": "null"}], "title": "Disk interface for the installed hdd_disk_image"}, "arch": {"$ref": "#/components/schemas/QemuPlatform", "title": "Architecture emulated"}, "console_type": {"$ref": "#/components/schemas/QemuConsoleType", "title": "Type of console connection for the administration of the appliance"}, "boot_priority": {"anyOf": [{"$ref": "#/components/schemas/QemuBootPriority"}, {"type": "null"}], "title": "Disk boot priority"}, "kernel_command_line": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Command line parameters sent to the kernel"}, "kvm": {"$ref": "#/components/schemas/Kvm", "title": "KVM requirements"}, "options": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Optional additional qemu command line options"}, "cpu_throttling": {"anyOf": [{"type": "number", "maximum": 100.0, "minimum": 0.0}, {"type": "null"}], "title": "Throttle the CPU"}, "on_close": {"anyOf": [{"$ref": "#/components/schemas/QemuOnClose"}, {"type": "null"}], "title": "Action to execute on the VM is closed"}, "process_priority": {"anyOf": [{"$ref": "#/components/schemas/QemuProcessPriority"}, {"type": "null"}], "title": "Process priority for QEMU"}}, "type": "object", "required": ["adapter_type", "adapters", "ram", "arch", "console_type", "kvm"], "title": "Qemu", "description": "QEMU configuration for v1-6"}, "QemuAdapterType": {"type": "string", "enum": ["e1000", "i82550", "i82551", "i82557a", "i82557b", "i82557c", "i82558a", "i82558b", "i82559a", "i82559b", "i82559c", "i82559er", "i82562", "i82801", "igb", "ne2k_pci", "pcnet", "rtl8139", "virtio", "virtio-net-pci", "vmxnet3"], "title": "QemuAdapterType", "description": "Qemu adapter type enum"}, "QemuBootPriority": {"type": "string", "enum": ["c", "d", "n", "cn", "cd", "dn", "dc", "nc", "nd"], "title": "QemuBootPriority", "description": "Boot priority enum"}, "QemuConsoleType": {"type": "string", "enum": ["telnet", "ssh", "vnc", "spice", "spice+agent", "none"], "title": "QemuConsoleType", "description": "Qemu console type enum"}, "QemuDiskImageAdapterType": {"type": "string", "enum": ["ide", "lsilogic", "buslogic", "legacyESX"], "title": "QemuDiskImageAdapterType", "description": "Supported Qemu disk image on/off options."}, "QemuDiskImageCreate": {"properties": {"format": {"$ref": "#/components/schemas/QemuDiskImageFormat", "description": "Image format type"}, "size": {"type": "integer", "title": "Size", "description": "Image size in Megabytes"}, "preallocation": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImagePreallocation"}, {"type": "null"}]}, "cluster_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Cluster Size"}, "refcount_bits": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Refcount Bits"}, "lazy_refcounts": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImageOnOff"}, {"type": "null"}]}, "subformat": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImageSubformat"}, {"type": "null"}]}, "static": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImageOnOff"}, {"type": "null"}]}, "zeroed_grain": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImageOnOff"}, {"type": "null"}]}, "adapter_type": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImageAdapterType"}, {"type": "null"}]}}, "type": "object", "required": ["format", "size"], "title": "QemuDiskImageCreate"}, "QemuDiskImageFormat": {"type": "string", "enum": ["qcow2", "qcow", "vpc", "vdi", "vdmk", "raw"], "title": "QemuDiskImageFormat", "description": "Supported Qemu disk image formats."}, "QemuDiskImageOnOff": {"type": "string", "enum": ["on", "off"], "title": "QemuDiskImageOnOff", "description": "Supported Qemu image on/off options."}, "QemuDiskImagePreallocation": {"type": "string", "enum": ["off", "metadata", "falloc", "full"], "title": "QemuDiskImagePreallocation", "description": "Supported Qemu disk image pre-allocation options."}, "QemuDiskImageSubformat": {"type": "string", "enum": ["dynamic", "fixed", "streamOptimized", "twoGbMaxExtentSparse", "twoGbMaxExtentFlat", "monolithicSparse", "monolithicFlat"], "title": "QemuDiskImageSubformat", "description": "Supported Qemu disk image sub-format options."}, "QemuDiskImageUpdate": {"properties": {"format": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImageFormat"}, {"type": "null"}], "description": "Image format type"}, "size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Size", "description": "Image size in Megabytes"}, "preallocation": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImagePreallocation"}, {"type": "null"}]}, "cluster_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Cluster Size"}, "refcount_bits": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Refcount Bits"}, "lazy_refcounts": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImageOnOff"}, {"type": "null"}]}, "subformat": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImageSubformat"}, {"type": "null"}]}, "static": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImageOnOff"}, {"type": "null"}]}, "zeroed_grain": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImageOnOff"}, {"type": "null"}]}, "adapter_type": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImageAdapterType"}, {"type": "null"}]}, "extend": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Extend", "description": "Number of Megabytes to extend the image"}}, "type": "object", "title": "QemuDiskImageUpdate"}, "QemuDiskInterface": {"type": "string", "enum": ["ide", "sata", "nvme", "scsi", "sd", "mtd", "floppy", "pflash", "virtio", "none"], "title": "QemuDiskInterface", "description": "Disk interface enum"}, "QemuOnClose": {"type": "string", "enum": ["power_off", "shutdown_signal", "save_vm_state"], "title": "QemuOnClose", "description": "Qemu on_close action enum"}, "QemuPlatform": {"type": "string", "enum": ["aarch64", "alpha", "arm", "cris", "i386", "lm32", "m68k", "microblaze", "microblazeel", "mips", "mips64", "mips64el", "mipsel", "moxie", "or32", "ppc", "ppc64", "ppcemb", "s390x", "sh4", "sh4eb", "sparc", "sparc64", "tricore", "unicore32", "x86_64", "xtensa", "xtensaeb"], "title": "QemuPlatform", "description": "Qemu platform enum"}, "QemuProcessPriority": {"type": "string", "enum": ["realtime", "very high", "high", "normal", "low", "very low"], "title": "QemuProcessPriority", "description": "Qemu process priority enum"}, "QemuPropertiesV8": {"properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name of the template"}, "category": {"anyOf": [{"$ref": "#/components/schemas/gns3server__schemas__controller__appliances__Category"}, {"type": "null"}], "title": "Category of the template"}, "default_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default name format"}, "usage": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "How to use the template"}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Symbol of the template"}, "adapter_type": {"anyOf": [{"$ref": "#/components/schemas/QemuAdapterType"}, {"type": "null"}], "title": "Type of network adapter"}, "adapters": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Number of adapters"}, "custom_adapters": {"anyOf": [{"items": {"$ref": "#/components/schemas/CustomAdapterItem"}, "type": "array"}, {"type": "null"}], "title": "Custom adapters"}, "first_port_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Optional name of the first networking port example: eth0"}, "port_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Optional formating of the networking port example: eth{0}"}, "port_segment_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Optional port segment size. A port segment is a block of port. For example Ethernet0/0 Ethernet0/1 is the module 0 with a port segment size of 2"}, "linked_clone": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "False if you don't want to use a single image for all nodes"}, "ram": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Ram allocated to the appliance (MB)"}, "cpus": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Number of Virtual CPU"}, "hda_disk_interface": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskInterface"}, {"type": "null"}], "title": "Disk interface for the installed hda_disk_image"}, "hdb_disk_interface": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskInterface"}, {"type": "null"}], "title": "Disk interface for the installed hdb_disk_image"}, "hdc_disk_interface": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskInterface"}, {"type": "null"}], "title": "Disk interface for the installed hdc_disk_image"}, "hdd_disk_interface": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskInterface"}, {"type": "null"}], "title": "Disk interface for the installed hdd_disk_image"}, "platform": {"anyOf": [{"$ref": "#/components/schemas/QemuPlatform"}, {"type": "null"}], "title": "Platform to emulate"}, "console_type": {"anyOf": [{"$ref": "#/components/schemas/QemuConsoleType"}, {"type": "null"}], "title": "Type of console connection for the administration of the appliance"}, "boot_priority": {"anyOf": [{"$ref": "#/components/schemas/QemuBootPriority"}, {"type": "null"}], "title": "Optional define the disk boot priory. Refer to -boot option in qemu manual for more details."}, "kernel_command_line": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Command line parameters send to the kernel"}, "options": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Optional additional qemu command line options"}, "cpu_throttling": {"anyOf": [{"type": "number", "maximum": 100.0, "minimum": 0.0}, {"type": "null"}], "title": "Throttle the CPU"}, "tpm": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Enable the Trusted Platform Module (TPM)"}, "uefi": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Enable the UEFI boot mode"}, "on_close": {"anyOf": [{"$ref": "#/components/schemas/QemuOnClose"}, {"type": "null"}], "title": "Action to execute on the VM is closed"}, "process_priority": {"anyOf": [{"$ref": "#/components/schemas/QemuProcessPriority"}, {"type": "null"}], "title": "Process priority for QEMU"}}, "type": "object", "title": "QemuPropertiesV8", "description": "Qemu template properties (v8)"}, "Resource": {"properties": {"resource_id": {"type": "string", "format": "uuid", "title": "Resource Id"}, "resource_type": {"$ref": "#/components/schemas/ResourceType", "description": "Type of the resource"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "created_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Created At"}, "updated_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Updated At"}}, "type": "object", "required": ["resource_id", "resource_type"], "title": "Resource"}, "ResourcePool": {"properties": {"name": {"type": "string", "title": "Name"}, "created_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Created At"}, "updated_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Updated At"}, "resource_pool_id": {"type": "string", "format": "uuid", "title": "Resource Pool Id"}}, "type": "object", "required": ["name", "resource_pool_id"], "title": "ResourcePool"}, "ResourcePoolCreate": {"properties": {"name": {"type": "string", "title": "Name"}}, "type": "object", "required": ["name"], "title": "ResourcePoolCreate", "description": "Properties to create a resource pool."}, "ResourcePoolUpdate": {"properties": {"name": {"type": "string", "title": "Name"}}, "type": "object", "required": ["name"], "title": "ResourcePoolUpdate", "description": "Properties to update a resource pool."}, "ResourceType": {"type": "string", "enum": ["project"], "title": "ResourceType"}, "Role": {"properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "description": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Description"}, "created_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Created At"}, "updated_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Updated At"}, "role_id": {"type": "string", "format": "uuid", "title": "Role Id"}, "is_builtin": {"type": "boolean", "title": "Is Builtin"}, "privileges": {"items": {"$ref": "#/components/schemas/Privilege"}, "type": "array", "title": "Privileges"}}, "type": "object", "required": ["role_id", "is_builtin", "privileges"], "title": "Role"}, "RoleCreate": {"properties": {"name": {"type": "string", "title": "Name"}, "description": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Description"}}, "type": "object", "required": ["name"], "title": "RoleCreate", "description": "Properties to create a role."}, "RoleUpdate": {"properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "description": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Description"}}, "type": "object", "title": "RoleUpdate", "description": "Properties to update a role."}, "Snapshot": {"properties": {"name": {"type": "string", "title": "Name", "description": "Name of the snapshot"}, "description": {"type": "string", "title": "Description", "description": "Description of the snapshot"}, "snapshot_id": {"type": "string", "format": "uuid", "title": "Snapshot Id"}, "project_id": {"type": "string", "format": "uuid", "title": "Project Id"}, "filename": {"type": "string", "title": "Filename", "description": "Filename of the snapshot"}, "created_at": {"type": "integer", "title": "Created At", "description": "Date of the snapshot (UTC timestamp)"}}, "type": "object", "required": ["name", "description", "snapshot_id", "project_id", "filename", "created_at"], "title": "Snapshot"}, "SnapshotCreate": {"properties": {"name": {"type": "string", "title": "Name", "description": "Name of the snapshot"}, "description": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Description", "description": "Description of the snapshot"}}, "type": "object", "required": ["name"], "title": "SnapshotCreate", "description": "Properties for snapshot creation."}, "Status": {"type": "string", "enum": ["stable", "experimental", "broken"], "title": "Status", "description": "Appliance status enum"}, "Supplier": {"properties": {"logo": {"type": "string", "title": "Logo", "description": "Path to the project supplier logo"}, "url": {"anyOf": [{"type": "string", "maxLength": 2083, "minLength": 1, "format": "uri"}, {"type": "null"}], "title": "Url", "description": "URL to the project supplier site"}}, "type": "object", "required": ["logo"], "title": "Supplier"}, "Template": {"properties": {"template_id": {"type": "string", "format": "uuid", "title": "Template Id"}, "name": {"type": "string", "title": "Name"}, "version": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Version"}, "category": {"$ref": "#/components/schemas/gns3server__schemas__controller__templates__Category"}, "default_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default Name Format"}, "symbol": {"type": "string", "title": "Symbol"}, "template_type": {"$ref": "#/components/schemas/NodeType"}, "compute_id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Compute Id"}, "usage": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Usage", "default": ""}, "tags": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "Tags", "description": "User-defined metadata tags (e.g. 'vendor:cisco' or 'model:7200')"}, "created_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Created At"}, "updated_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Updated At"}, "builtin": {"type": "boolean", "title": "Builtin"}}, "additionalProperties": true, "type": "object", "required": ["template_id", "name", "category", "symbol", "template_type", "builtin"], "title": "Template"}, "TemplateCreate": {"properties": {"template_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Template Id"}, "name": {"type": "string", "title": "Name"}, "version": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Version"}, "category": {"anyOf": [{"$ref": "#/components/schemas/gns3server__schemas__controller__templates__Category"}, {"type": "null"}]}, "default_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default Name Format"}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Symbol"}, "template_type": {"$ref": "#/components/schemas/NodeType"}, "compute_id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Compute Id"}, "usage": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Usage", "default": ""}, "tags": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "Tags", "description": "User-defined metadata tags (e.g. 'vendor:cisco' or 'model:7200')"}}, "additionalProperties": true, "type": "object", "required": ["name", "template_type"], "title": "TemplateCreate", "description": "Properties to create a template."}, "TemplateSetting": {"properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name of the settings set"}, "default": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Whether these are the default settings"}, "inherit_default_properties": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Whether the default properties should be used", "default": true}, "template_type": {"$ref": "#/components/schemas/TemplateType", "title": "Type of emulator properties"}, "template_properties": {"anyOf": [{"$ref": "#/components/schemas/QemuPropertiesV8"}, {"$ref": "#/components/schemas/DynamipsPropertiesV8"}, {"$ref": "#/components/schemas/IouPropertiesV8"}, {"$ref": "#/components/schemas/DockerPropertiesV8"}], "title": "Properties for the template"}}, "type": "object", "required": ["template_type", "template_properties"], "title": "TemplateSetting", "description": "Emulator settings configuration (v8)"}, "TemplateType": {"type": "string", "enum": ["docker", "iou", "dynamips", "qemu"], "title": "TemplateType", "description": "Template type enum"}, "TemplateUpdate": {"properties": {"template_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Template Id"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "version": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Version"}, "category": {"anyOf": [{"$ref": "#/components/schemas/gns3server__schemas__controller__templates__Category"}, {"type": "null"}]}, "default_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default Name Format"}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Symbol"}, "template_type": {"anyOf": [{"$ref": "#/components/schemas/NodeType"}, {"type": "null"}]}, "compute_id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Compute Id"}, "usage": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Usage", "default": ""}, "tags": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "Tags", "description": "User-defined metadata tags (e.g. 'vendor:cisco' or 'model:7200')"}}, "additionalProperties": true, "type": "object", "title": "TemplateUpdate"}, "TemplateUsage": {"properties": {"x": {"type": "integer", "title": "X"}, "y": {"type": "integer", "title": "Y"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name", "description": "Use this name to create a new node"}, "compute_id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Compute Id", "description": "Used if the template doesn't have a default compute"}}, "type": "object", "required": ["x", "y"], "title": "TemplateUsage"}, "Token": {"properties": {"access_token": {"type": "string", "title": "Access Token"}, "token_type": {"type": "string", "title": "Token Type"}}, "type": "object", "required": ["access_token", "token_type"], "title": "Token"}, "UDPPortInfo": {"properties": {"node_id": {"type": "string", "format": "uuid", "title": "Node Id"}, "lport": {"type": "integer", "title": "Lport"}, "rhost": {"type": "string", "title": "Rhost"}, "rport": {"type": "integer", "title": "Rport"}, "type": {"type": "string", "title": "Type"}}, "type": "object", "required": ["node_id", "lport", "rhost", "rport", "type"], "title": "UDPPortInfo", "description": "UDP port information."}, "User": {"properties": {"username": {"anyOf": [{"type": "string", "minLength": 3, "pattern": "[a-zA-Z0-9_-]+$"}, {"type": "null"}], "title": "Username"}, "is_active": {"type": "boolean", "title": "Is Active", "default": true}, "email": {"anyOf": [{"type": "string", "format": "email"}, {"type": "null"}], "title": "Email"}, "full_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Full Name"}, "created_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Created At"}, "updated_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Updated At"}, "user_id": {"type": "string", "format": "uuid", "title": "User Id"}, "last_login": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Last Login"}, "is_superadmin": {"type": "boolean", "title": "Is Superadmin", "default": false}}, "type": "object", "required": ["user_id"], "title": "User"}, "UserCreate": {"properties": {"username": {"type": "string", "minLength": 3, "pattern": "[a-zA-Z0-9_-]+$", "title": "Username"}, "is_active": {"type": "boolean", "title": "Is Active", "default": true}, "email": {"anyOf": [{"type": "string", "format": "email"}, {"type": "null"}], "title": "Email"}, "full_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Full Name"}, "password": {"type": "string", "maxLength": 100, "minLength": 8, "format": "password", "title": "Password", "writeOnly": true}}, "type": "object", "required": ["username", "password"], "title": "UserCreate", "description": "Properties to create a user."}, "UserGroup": {"properties": {"name": {"anyOf": [{"type": "string", "minLength": 3, "pattern": "[a-zA-Z0-9_-]+$"}, {"type": "null"}], "title": "Name"}, "created_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Created At"}, "updated_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Updated At"}, "user_group_id": {"type": "string", "format": "uuid", "title": "User Group Id"}, "is_builtin": {"type": "boolean", "title": "Is Builtin"}}, "type": "object", "required": ["user_group_id", "is_builtin"], "title": "UserGroup"}, "UserGroupCreate": {"properties": {"name": {"anyOf": [{"type": "string", "minLength": 3, "pattern": "[a-zA-Z0-9_-]+$"}, {"type": "null"}], "title": "Name"}}, "type": "object", "required": ["name"], "title": "UserGroupCreate", "description": "Properties to create a user group."}, "UserGroupUpdate": {"properties": {"name": {"anyOf": [{"type": "string", "minLength": 3, "pattern": "[a-zA-Z0-9_-]+$"}, {"type": "null"}], "title": "Name"}}, "type": "object", "title": "UserGroupUpdate", "description": "Properties to update a user group."}, "UserUpdate": {"properties": {"username": {"anyOf": [{"type": "string", "minLength": 3, "pattern": "[a-zA-Z0-9_-]+$"}, {"type": "null"}], "title": "Username"}, "is_active": {"type": "boolean", "title": "Is Active", "default": true}, "email": {"anyOf": [{"type": "string", "format": "email"}, {"type": "null"}], "title": "Email"}, "full_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Full Name"}, "password": {"anyOf": [{"type": "string", "maxLength": 100, "minLength": 8, "format": "password", "writeOnly": true}, {"type": "null"}], "title": "Password"}}, "type": "object", "title": "UserUpdate", "description": "Properties to update a user."}, "ValidationError": {"properties": {"loc": {"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]}, "type": "array", "title": "Location"}, "msg": {"type": "string", "title": "Message"}, "type": {"type": "string", "title": "Error Type"}, "input": {"title": "Input"}, "ctx": {"type": "object", "title": "Context"}}, "type": "object", "required": ["loc", "msg", "type"], "title": "ValidationError"}, "Variable": {"properties": {"name": {"type": "string", "title": "Name", "description": "Variable name"}, "value": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Value", "description": "Variable value"}}, "type": "object", "required": ["name"], "title": "Variable"}, "Version": {"properties": {"controller_host": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Controller Host", "description": "Controller hostname or IP address"}, "version": {"type": "string", "title": "Version", "description": "Version number"}, "local": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Local", "description": "Whether this is a local server or not"}}, "type": "object", "required": ["version"], "title": "Version"}, "WhenExit": {"type": "string", "enum": ["stop", "suspend", "keep"], "title": "WhenExit", "description": "What to do with the VM when GNS3 VM exits."}, "gns3server__schemas__controller__appliances__Category": {"type": "string", "enum": ["router", "multilayer_switch", "switch", "firewall", "guest"], "title": "Category", "description": "Appliance category enum"}, "gns3server__schemas__controller__links__LinkType": {"type": "string", "enum": ["ethernet", "serial"], "title": "LinkType", "description": "Link type."}, "gns3server__schemas__controller__nodes__LinkType": {"type": "string", "enum": ["ethernet", "serial"], "title": "LinkType", "description": "Supported link types."}, "gns3server__schemas__controller__templates__Category": {"type": "string", "enum": ["router", "switch", "guest", "firewall"], "title": "Category", "description": "Supported categories"}}, "securitySchemes": {"OAuth2PasswordBearer": {"type": "oauth2", "flows": {"password": {"scopes": {}, "tokenUrl": "/v3/access/users/login"}}}}}} \ No newline at end of file +{"openapi": "3.1.0", "info": {"title": "GNS3 controller API", "description": "This page describes the public controller API for GNS3", "version": "3.0.0"}, "paths": {"/v3/version": {"get": {"tags": ["Controller"], "summary": "Get Version", "description": "Return the server version number.", "operationId": "get_version_v3_version_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Version"}}}}}}, "post": {"tags": ["Controller"], "summary": "Check Version", "description": "Check if version is the same as the server.", "operationId": "check_version_v3_version_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Version"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Version"}}}}, "409": {"description": "Invalid version", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/reload": {"post": {"tags": ["Controller"], "summary": "Reload", "description": "Reload the controller", "operationId": "reload_v3_reload_post", "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/shutdown": {"post": {"tags": ["Controller"], "summary": "Shutdown", "description": "Shutdown the server", "operationId": "shutdown_v3_shutdown_post", "responses": {"204": {"description": "Successful Response"}, "403": {"description": "Server shutdown not allowed", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/iou_license": {"get": {"tags": ["Controller"], "summary": "Get Iou License", "description": "Return the IOU license settings", "operationId": "get_iou_license_v3_iou_license_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IOULicense"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "put": {"tags": ["Controller"], "summary": "Update Iou License", "description": "Update the IOU license settings.", "operationId": "update_iou_license_v3_iou_license_put", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/IOULicense"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IOULicense"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/statistics": {"get": {"tags": ["Controller"], "summary": "Statistics", "description": "Return server statistics including compute resources, projects, and nodes.", "operationId": "statistics_v3_statistics_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"additionalProperties": true, "type": "object", "title": "Response Statistics V3 Statistics Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/notifications": {"get": {"tags": ["Controller"], "summary": "Controller Http Notifications", "description": "Receive controller notifications about the controller from HTTP stream.", "operationId": "controller_http_notifications_v3_notifications_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/settings": {"get": {"tags": ["Server settings"], "summary": "Get Server Settings", "description": "Return the server settings.\n\nThe values reflect the running configuration (which may include command\nline overrides). Secret fields are masked.", "operationId": "get_server_settings_v3_settings_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SettingsResponse"}}}}, "401": {"description": "Unauthorized", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "403": {"description": "Forbidden", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "put": {"tags": ["Server settings"], "summary": "Update Server Settings", "description": "Update the server settings and persist them to the configuration file.\n\nOnly the submitted options are modified. A JSON null removes an option\nfrom the configuration file (restoring its default). Secret fields set\nto an empty string or left at their masked value are considered unchanged.", "operationId": "update_server_settings_v3_settings_put", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/SettingsUpdate"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SettingsUpdateResponse"}}}}, "400": {"description": "Bad Request", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "401": {"description": "Unauthorized", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "403": {"description": "Forbidden", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "409": {"description": "Conflict", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Unprocessable Content", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/access/users/login": {"post": {"tags": ["Users"], "summary": "Login", "description": "Default user login method using forms (x-www-form-urlencoded).\nExample: curl -X POST http://host:port/v3/access/users/login -H \"Content-Type: application/x-www-form-urlencoded\" -d \"username=admin&password=admin\"", "operationId": "login_v3_access_users_login_post", "requestBody": {"content": {"application/x-www-form-urlencoded": {"schema": {"$ref": "#/components/schemas/Body_login_v3_access_users_login_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Token"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/users/authenticate": {"post": {"tags": ["Users"], "summary": "Authenticate", "description": "Alternative authentication method using json.\nExample: curl -X POST http://host:port/v3/access/users/authenticate -d '{\"username\": \"admin\", \"password\": \"admin\"}' -H \"Content-Type: application/json\"", "operationId": "authenticate_v3_access_users_authenticate_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Credentials"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Token"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/users/refresh": {"post": {"tags": ["Users"], "summary": "Refresh Access Token", "description": "Exchange a refresh token for a new access token.\n\nPublic endpoint \u2014 the refresh token itself proves identity. Respects the\nuser's token_version, so logout (which increments it) invalidates all\noutstanding refresh tokens. Refresh tokens are stateless JWTs with a\nlonger expiry (default 30 days). Stolen tokens remain valid until their\n`exp` or until logout \u2014 no replay protection without a server-side table.", "operationId": "refresh_access_token_v3_access_users_refresh_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/RefreshTokenRequest"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Token"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/users/logout": {"post": {"tags": ["Users"], "summary": "Logout", "description": "Logout the current user by revoking all existing tokens.", "operationId": "logout_v3_access_users_logout_post", "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/access/users/me": {"get": {"tags": ["Users"], "summary": "Get Logged In User", "description": "Get the current active user.", "operationId": "get_logged_in_user_v3_access_users_me_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/User"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "put": {"tags": ["Users"], "summary": "Update Logged In User", "description": "Update the current active user.", "operationId": "update_logged_in_user_v3_access_users_me_put", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/LoggedInUserUpdate"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/User"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/access/users": {"get": {"tags": ["Users"], "summary": "Get Users", "description": "Get all users.\n\nRequired privilege: User.Audit", "operationId": "get_users_v3_access_users_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/User"}, "type": "array", "title": "Response Get Users V3 Access Users Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["Users"], "summary": "Create User", "description": "Create a new user.\n\nRequired privilege: User.Allocate", "operationId": "create_user_v3_access_users_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserCreate"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/User"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/access/users/{user_id}": {"get": {"tags": ["Users"], "summary": "Get User", "description": "Get a user.\n\nRequired privilege: User.Audit", "operationId": "get_user_v3_access_users__user_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/User"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Users"], "summary": "Update User", "description": "Update a user.\n\nRequired privilege: User.Modify", "operationId": "update_user_v3_access_users__user_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/User"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Users"], "summary": "Delete User", "description": "Delete a user.\n\nRequired privilege: User.Allocate", "operationId": "delete_user_v3_access_users__user_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/users/{user_id}/groups": {"get": {"tags": ["Users"], "summary": "Get User Memberships", "description": "Get user memberships.\n\nRequired privilege: Group.Audit", "operationId": "get_user_memberships_v3_access_users__user_id__groups_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/UserGroup"}, "title": "Response Get User Memberships V3 Access Users User Id Groups Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/groups": {"get": {"tags": ["Users groups"], "summary": "Get User Groups", "description": "Get all user groups.\n\nRequired privilege: Group.Audit", "operationId": "get_user_groups_v3_access_groups_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/UserGroup"}, "type": "array", "title": "Response Get User Groups V3 Access Groups Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["Users groups"], "summary": "Create User Group", "description": "Create a new user group.\n\nRequired privilege: Group.Allocate", "operationId": "create_user_group_v3_access_groups_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserGroupCreate"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserGroup"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/access/groups/{user_group_id}": {"get": {"tags": ["Users groups"], "summary": "Get User Group", "description": "Get a user group.\n\nRequired privilege: Group.Audit", "operationId": "get_user_group_v3_access_groups__user_group_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_group_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Group Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserGroup"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Users groups"], "summary": "Update User Group", "description": "Update a user group.\n\nRequired privilege: Group.Modify", "operationId": "update_user_group_v3_access_groups__user_group_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_group_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Group Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserGroupUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserGroup"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Users groups"], "summary": "Delete User Group", "description": "Delete a user group.\n\nRequired privilege: Group.Allocate", "operationId": "delete_user_group_v3_access_groups__user_group_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_group_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Group Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/groups/{user_group_id}/members": {"get": {"tags": ["Users groups"], "summary": "Get User Group Members", "description": "Get all user group members.\n\nRequired privilege: Group.Audit", "operationId": "get_user_group_members_v3_access_groups__user_group_id__members_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_group_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Group Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/User"}, "title": "Response Get User Group Members V3 Access Groups User Group Id Members Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/groups/{user_group_id}/members/{user_id}": {"put": {"tags": ["Users groups"], "summary": "Add Member To Group", "description": "Add member to a user group.\n\nRequired privilege: Group.Modify", "operationId": "add_member_to_group_v3_access_groups__user_group_id__members__user_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_group_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Group Id"}}, {"name": "user_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Users groups"], "summary": "Remove Member From Group", "description": "Remove member from a user group.\n\nRequired privilege: Group.Modify", "operationId": "remove_member_from_group_v3_access_groups__user_group_id__members__user_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_group_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Group Id"}}, {"name": "user_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/roles": {"get": {"tags": ["Roles"], "summary": "Get Roles", "description": "Get all roles.\n\nRequired privilege: Role.Audit", "operationId": "get_roles_v3_access_roles_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/Role"}, "type": "array", "title": "Response Get Roles V3 Access Roles Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["Roles"], "summary": "Create Role", "description": "Create a new role.\n\nRequired privilege: Role.Allocate", "operationId": "create_role_v3_access_roles_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/RoleCreate"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Role"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/access/roles/{role_id}": {"get": {"tags": ["Roles"], "summary": "Get Role", "description": "Get a role.\n\nRequired privilege: Role.Audit", "operationId": "get_role_v3_access_roles__role_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "role_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Role Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Role"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Roles"], "summary": "Update Role", "description": "Update a role.\n\nRequired privilege: Role.Modify", "operationId": "update_role_v3_access_roles__role_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "role_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Role Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RoleUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Role"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Roles"], "summary": "Delete Role", "description": "Delete a role.\n\nRequired privilege: Role.Allocate", "operationId": "delete_role_v3_access_roles__role_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "role_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Role Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/roles/{role_id}/privileges": {"get": {"tags": ["Roles"], "summary": "Get Role Privileges", "description": "Get all role privileges.\n\nRequired privilege: Role.Audit", "operationId": "get_role_privileges_v3_access_roles__role_id__privileges_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "role_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Role Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Privilege"}, "title": "Response Get Role Privileges V3 Access Roles Role Id Privileges Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/roles/{role_id}/privileges/{privilege_id}": {"put": {"tags": ["Roles"], "summary": "Add Privilege To Role", "description": "Add a privilege to a role.\n\nRequired privilege: Role.Modify", "operationId": "add_privilege_to_role_v3_access_roles__role_id__privileges__privilege_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "role_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Role Id"}}, {"name": "privilege_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Privilege Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Roles"], "summary": "Remove Privilege From Role", "description": "Remove privilege from a role.\n\nRequired privilege: Role.Modify", "operationId": "remove_privilege_from_role_v3_access_roles__role_id__privileges__privilege_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "role_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Role Id"}}, {"name": "privilege_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Privilege Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/privileges": {"get": {"tags": ["Privileges"], "summary": "Get Privileges", "description": "Get all privileges.\n\nRequired privilege: None", "operationId": "get_privileges_v3_access_privileges_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/Privilege"}, "type": "array", "title": "Response Get Privileges V3 Access Privileges Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/access/acl/endpoints": {"get": {"tags": ["ACL"], "summary": "Endpoints", "description": "List all endpoints to be used in ACL entries.", "operationId": "endpoints_v3_access_acl_endpoints_get", "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"additionalProperties": true, "type": "object"}, "type": "array", "title": "Response Endpoints V3 Access Acl Endpoints Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/access/acl": {"get": {"tags": ["ACL"], "summary": "Get Aces", "description": "Get all ACL entries.\n\nRequired privilege: ACE.Audit", "operationId": "get_aces_v3_access_acl_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/ACE"}, "type": "array", "title": "Response Get Aces V3 Access Acl Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["ACL"], "summary": "Create Ace", "description": "Create a new ACL entry.\n\nRequired privilege: ACE.Allocate", "operationId": "create_ace_v3_access_acl_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ACECreate"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ACE"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/access/acl/{ace_id}": {"get": {"tags": ["ACL"], "summary": "Get Ace", "description": "Get an ACL entry.\n\nRequired privilege: ACE.Audit", "operationId": "get_ace_v3_access_acl__ace_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "ace_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Ace Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ACE"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["ACL"], "summary": "Update Ace", "description": "Update an ACL entry.\n\nRequired privilege: ACE.Modify", "operationId": "update_ace_v3_access_acl__ace_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "ace_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Ace Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ACEUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ACE"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["ACL"], "summary": "Delete Ace", "description": "Delete an ACL entry.\n\nRequired privilege: ACE.Allocate", "operationId": "delete_ace_v3_access_acl__ace_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "ace_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Ace Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/images/qemu/{image_path}": {"post": {"tags": ["Images"], "summary": "Create Qemu Image", "description": "Create a new blank Qemu image.\n\nRequired privilege: Image.Allocate", "operationId": "create_qemu_image_v3_images_qemu__image_path__post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "image_path", "in": "path", "required": true, "schema": {"type": "string", "title": "Image Path"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/QemuDiskImageCreate"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Image"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/images": {"get": {"tags": ["Images"], "summary": "Get Images", "description": "Return all images.\n\nRequired privilege: Image.Audit", "operationId": "get_images_v3_images_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "image_type", "in": "query", "required": false, "schema": {"anyOf": [{"$ref": "#/components/schemas/ImageType"}, {"type": "null"}], "title": "Image Type"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Image"}, "title": "Response Get Images V3 Images Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/images/upload/{image_path}": {"post": {"tags": ["Images"], "summary": "Upload Image", "description": "Upload an image.\n\nExample: curl -X POST http://host:port/v3/images/upload/my_image_name.qcow2 -H 'Authorization: Bearer ' --data-binary @\"/path/to/image.qcow2\"\n\nRequired privilege: Image.Allocate", "operationId": "upload_image_v3_images_upload__image_path__post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "image_path", "in": "path", "required": true, "schema": {"type": "string", "title": "Image Path"}}, {"name": "install_appliances", "in": "query", "required": false, "schema": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": false, "title": "Install Appliances"}}], "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Image"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/images/prune": {"delete": {"tags": ["Images"], "summary": "Prune Images", "description": "Prune images not attached to any template.\n\nRequired privilege: Image.Allocate", "operationId": "prune_images_v3_images_prune_delete", "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/images/install": {"post": {"tags": ["Images"], "summary": "Install Images", "description": "Attempt to automatically create templates based on image checksums.\n\nRequired privilege: Image.Allocate", "operationId": "install_images_v3_images_install_post", "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/images/{image_path}": {"get": {"tags": ["Images"], "summary": "Get Image", "description": "Return an image.\n\nRequired privilege: Image.Audit", "operationId": "get_image_v3_images__image_path__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "image_path", "in": "path", "required": true, "schema": {"type": "string", "title": "Image Path"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Image"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Images"], "summary": "Delete Image", "description": "Delete an image.\n\nRequired privilege: Image.Allocate", "operationId": "delete_image_v3_images__image_path__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "image_path", "in": "path", "required": true, "schema": {"type": "string", "title": "Image Path"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/templates": {"post": {"tags": ["Templates"], "summary": "Create Template", "description": "Create a new template.\n\nRequired privilege: Template.Allocate", "operationId": "create_template_v3_templates_post", "security": [{"OAuth2PasswordBearer": []}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/TemplateCreate"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Template"}}}}, "404": {"description": "Could not find template", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "get": {"tags": ["Templates"], "summary": "Get Templates", "description": "Return all templates.\n\nRequired privilege: Template.Audit\n\nQuery Parameters:\n- tags: Filter by tags. Multiple tags are ANDed together.\n Example: ?tags=vendor:cisco&tags=model:7200", "operationId": "get_templates_v3_templates_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "tags", "in": "query", "required": false, "schema": {"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}], "description": "Filter by tags (e.g. tags=vendor:cisco&tags=model:7200)", "title": "Tags"}, "description": "Filter by tags (e.g. tags=vendor:cisco&tags=model:7200)"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Template"}, "title": "Response Get Templates V3 Templates Get"}}}}, "404": {"description": "Could not find template", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/templates/{template_id}": {"get": {"tags": ["Templates"], "summary": "Get Template", "description": "Return a template.\n\nRequired privilege: Template.Audit", "operationId": "get_template_v3_templates__template_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "template_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Template Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Template"}}}}, "404": {"description": "Could not find template", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Templates"], "summary": "Update Template", "description": "Update a template.\n\nRequired privilege: Template.Modify", "operationId": "update_template_v3_templates__template_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "template_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Template Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/TemplateUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Template"}}}}, "404": {"description": "Could not find template", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Templates"], "summary": "Delete Template", "description": "Delete a template.\n\nRequired privilege: Template.Allocate", "operationId": "delete_template_v3_templates__template_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "template_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Template Id"}}, {"name": "prune_images", "in": "query", "required": false, "schema": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": false, "title": "Prune Images"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find template", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/templates/{template_id}/duplicate": {"post": {"tags": ["Templates"], "summary": "Duplicate Template", "description": "Duplicate a template.\n\nRequired privilege: Template.Allocate", "operationId": "duplicate_template_v3_templates__template_id__duplicate_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "template_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Template Id"}}], "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Template"}}}}, "404": {"description": "Could not find template", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/templates/{template_id}/base-config/{filename}": {"get": {"tags": ["Templates"], "summary": "Get Base Config", "operationId": "get_base_config_v3_templates__template_id__base_config__filename__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "template_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Template Id"}}, {"name": "filename", "in": "path", "required": true, "schema": {"type": "string", "title": "Filename"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Could not find template", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Templates"], "summary": "Update Base Config", "operationId": "update_base_config_v3_templates__template_id__base_config__filename__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "template_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Template Id"}}, {"name": "filename", "in": "path", "required": true, "schema": {"type": "string", "title": "Filename"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Body"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Could not find template", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/templates/{template_id}/base-configs": {"get": {"tags": ["Templates"], "summary": "List Base Configs", "operationId": "list_base_configs_v3_templates__template_id__base_configs_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "template_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Template Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Could not find template", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects": {"get": {"tags": ["Projects"], "summary": "Get Projects", "description": "Return all projects.\n\nRequired privilege: Project.Audit", "operationId": "get_projects_v3_projects_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/Project"}, "type": "array", "title": "Response Get Projects V3 Projects Get"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["Projects"], "summary": "Create Project", "description": "Create a new project.\n\nRequired privilege: Project.Allocate", "operationId": "create_project_v3_projects_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ProjectCreate"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Project"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "409": {"description": "Could not create project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/projects/{project_id}": {"get": {"tags": ["Projects"], "summary": "Get Project", "description": "Return a project.\n\nRequired privilege: Project.Audit", "operationId": "get_project_v3_projects__project_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Project"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Projects"], "summary": "Update Project", "description": "Update a project.\n\nRequired privilege: Project.Modify", "operationId": "update_project_v3_projects__project_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ProjectUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Project"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Projects"], "summary": "Delete Project", "description": "Delete a project.\n\nRequired privilege: Project.Allocate", "operationId": "delete_project_v3_projects__project_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/stats": {"get": {"tags": ["Projects"], "summary": "Get Project Stats", "description": "Return a project statistics.\n\nRequired privilege: Project.Audit", "operationId": "get_project_stats_v3_projects__project_id__stats_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Response Get Project Stats V3 Projects Project Id Stats Get"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/markers": {"get": {"tags": ["Projects"], "summary": "Get Project Markers", "description": "Return all traffic-insight markers across every link in the project.\n\nEach entry is keyed ``\"{link_id}/{marker_name}\"`` and carries the\nmarker's BPF, tag, color, enabled flag, plus its parent ``link_id``\nand capture-side ``node_id`` for frontend filtering / grouping.\n\nRequired privilege: Project.Audit", "operationId": "get_project_markers_v3_projects__project_id__markers_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Response Get Project Markers V3 Projects Project Id Markers Get"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/marker-definitions": {"get": {"tags": ["Projects"], "summary": "Get Marker Definitions", "description": "Return all project-level marker definitions with their bound link IDs.\n\nRequired privilege: Project.Audit", "operationId": "get_marker_definitions_v3_projects__project_id__marker_definitions_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Response Get Marker Definitions V3 Projects Project Id Marker Definitions Get"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["Projects"], "summary": "Create Marker Definition", "description": "Create a project-level marker definition and fan out to every link.\n\nRequired privilege: Project.Modify", "operationId": "create_marker_definition_v3_projects__project_id__marker_definitions_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MarkerDefinitionCreate"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Response Create Marker Definition V3 Projects Project Id Marker Definitions Post"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/marker-definitions/{def_name}": {"put": {"tags": ["Projects"], "summary": "Update Marker Definition", "description": "Update a marker definition and sync all inherited copies on every link.\n\nRequired privilege: Project.Modify", "operationId": "update_marker_definition_v3_projects__project_id__marker_definitions__def_name__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "def_name", "in": "path", "required": true, "schema": {"type": "string", "title": "Def Name"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MarkerDefinitionCreate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Response Update Marker Definition V3 Projects Project Id Marker Definitions Def Name Put"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Projects"], "summary": "Delete Marker Definition", "description": "Delete a marker definition and remove all inherited copies from every link.\n\nRequired privilege: Project.Modify", "operationId": "delete_marker_definition_v3_projects__project_id__marker_definitions__def_name__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "def_name", "in": "path", "required": true, "schema": {"type": "string", "title": "Def Name"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/marker-definitions/{def_name}/pause": {"post": {"tags": ["Projects"], "summary": "Pause Marker Definition", "description": "Pause a definition: toggle off every inherited ``global-{def_name}`` copy\non every link (uBridge ``enable_packet_filter off``, instant \u2014 no NIO\nrebuild). The definition's ``paused`` flag is persisted, so links created\nlater inherit it already paused.\n\nRequired privilege: Project.Modify", "operationId": "pause_marker_definition_v3_projects__project_id__marker_definitions__def_name__pause_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "def_name", "in": "path", "required": true, "schema": {"type": "string", "title": "Def Name"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/marker-definitions/{def_name}/resume": {"post": {"tags": ["Projects"], "summary": "Resume Marker Definition", "description": "Resume a paused definition (toggle on every inherited copy).\n\nRequired privilege: Project.Modify", "operationId": "resume_marker_definition_v3_projects__project_id__marker_definitions__def_name__resume_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "def_name", "in": "path", "required": true, "schema": {"type": "string", "title": "Def Name"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/close": {"post": {"tags": ["Projects"], "summary": "Close Project", "description": "Close a project.\n\nRequired privilege: Project.Allocate", "operationId": "close_project_v3_projects__project_id__close_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "409": {"description": "Could not close project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/open": {"post": {"tags": ["Projects"], "summary": "Open Project", "description": "Open a project.\n\nRequired privilege: Project.Allocate", "operationId": "open_project_v3_projects__project_id__open_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Project"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "409": {"description": "Could not open project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/load": {"post": {"tags": ["Projects"], "summary": "Load Project", "description": "Load a project (local server only).\n\nRequired privilege: Project.Allocate", "operationId": "load_project_v3_projects_load_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Body_load_project_v3_projects_load_post"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Project"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "409": {"description": "Could not load project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/projects/{project_id}/notifications": {"get": {"tags": ["Projects"], "summary": "Project Http Notifications", "description": "Receive project notifications about the controller from HTTP stream.\n\nRequired privilege: Project.Audit", "operationId": "project_http_notifications_v3_projects__project_id__notifications_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/export": {"get": {"tags": ["Projects"], "summary": "Export Project", "description": "Export a project as a portable archive.\n\nRequired privilege: Project.Audit", "operationId": "export_project_v3_projects__project_id__export_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "include_snapshots", "in": "query", "required": false, "schema": {"type": "boolean", "default": false, "title": "Include Snapshots"}}, {"name": "include_images", "in": "query", "required": false, "schema": {"type": "boolean", "default": false, "title": "Include Images"}}, {"name": "reset_mac_addresses", "in": "query", "required": false, "schema": {"type": "boolean", "default": false, "title": "Reset Mac Addresses"}}, {"name": "keep_compute_ids", "in": "query", "required": false, "schema": {"type": "boolean", "default": false, "title": "Keep Compute Ids"}}, {"name": "compression", "in": "query", "required": false, "schema": {"$ref": "#/components/schemas/ProjectCompression", "default": "zstd"}}, {"name": "compression_level", "in": "query", "required": false, "schema": {"type": "integer", "title": "Compression Level"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/import": {"post": {"tags": ["Projects"], "summary": "Import Project", "description": "Import a project from a portable archive.\n\nRequired privilege: Project.Allocate", "operationId": "import_project_v3_projects__project_id__import_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "name", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}}], "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Project"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/duplicate": {"post": {"tags": ["Projects"], "summary": "Duplicate Project", "description": "Duplicate a project.\n\nRequired privilege: Project.Audit", "operationId": "duplicate_project_v3_projects__project_id__duplicate_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ProjectDuplicate"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Project"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "409": {"description": "Could not duplicate project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/locked": {"get": {"tags": ["Projects"], "summary": "Locked Project", "description": "Returns whether a project is locked or not.\n\nRequired privilege: Project.Audit", "operationId": "locked_project_v3_projects__project_id__locked_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "boolean", "title": "Response Locked Project V3 Projects Project Id Locked Get"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/lock": {"post": {"tags": ["Projects"], "summary": "Lock Project", "description": "Lock all drawings and nodes in a given project.\n\nRequired privilege: Project.Audit", "operationId": "lock_project_v3_projects__project_id__lock_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/unlock": {"post": {"tags": ["Projects"], "summary": "Unlock Project", "description": "Unlock all drawings and nodes in a given project.\n\nRequired privilege: Project.Modify", "operationId": "unlock_project_v3_projects__project_id__unlock_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/files/{file_path}": {"get": {"tags": ["Projects"], "summary": "Get File", "description": "Return a file from a project.\n\nRequired privilege: Project.Audit", "operationId": "get_file_v3_projects__project_id__files__file_path__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "file_path", "in": "path", "required": true, "schema": {"type": "string", "title": "File Path"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["Projects"], "summary": "Write File", "description": "Write a file to a project.\n\nRequired privilege: Project.Modify", "operationId": "write_file_v3_projects__project_id__files__file_path__post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "file_path", "in": "path", "required": true, "schema": {"type": "string", "title": "File Path"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/gns3file": {"get": {"tags": ["Projects"], "summary": "Get Project Gns3 File", "description": "Return the .gns3 topology file of a project.\n\nRequired privilege: Project.Audit", "operationId": "get_project_gns3_file_v3_projects__project_id__gns3file_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/templates/{template_id}": {"post": {"tags": ["Projects"], "summary": "Create Node From Template", "description": "Create a new node from a template.\n\nRequired privilege: Node.Allocate", "operationId": "create_node_from_template_v3_projects__project_id__templates__template_id__post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "template_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Template Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/TemplateUsage"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Node"}}}}, "404": {"description": "Could not find project or template", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes": {"post": {"tags": ["Nodes"], "summary": "Create Node", "description": "Create a new node.\n\nRequired privilege: Node.Allocate", "operationId": "create_node_v3_projects__project_id__nodes_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/NodeCreate"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Node"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "409": {"description": "Could not create node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "get": {"tags": ["Nodes"], "summary": "Get Nodes", "description": "Return all nodes belonging to a given project.\n\nRequired privilege: Node.Audit\n\nQuery Parameters:\n- tags: Filter by tags. Multiple tags are ANDed together.\n Example: ?tags=vendor:cisco&tags=model:7200", "operationId": "get_nodes_v3_projects__project_id__nodes_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "tags", "in": "query", "required": false, "schema": {"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}], "description": "Filter by tags (e.g. tags=vendor:cisco&tags=model:7200)", "title": "Tags"}, "description": "Filter by tags (e.g. tags=vendor:cisco&tags=model:7200)"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Node"}, "title": "Response Get Nodes V3 Projects Project Id Nodes Get"}}}}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/start": {"post": {"tags": ["Nodes"], "summary": "Start All Nodes", "description": "Start all nodes belonging to a given project.\n\nRequired privilege: Node.PowerMgmt", "operationId": "start_all_nodes_v3_projects__project_id__nodes_start_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/stop": {"post": {"tags": ["Nodes"], "summary": "Stop All Nodes", "description": "Stop all nodes belonging to a given project.\n\nRequired privilege: Node.PowerMgmt", "operationId": "stop_all_nodes_v3_projects__project_id__nodes_stop_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/suspend": {"post": {"tags": ["Nodes"], "summary": "Suspend All Nodes", "description": "Suspend all nodes belonging to a given project.\n\nRequired privilege: Node.PowerMgmt", "operationId": "suspend_all_nodes_v3_projects__project_id__nodes_suspend_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/reload": {"post": {"tags": ["Nodes"], "summary": "Reload All Nodes", "description": "Reload all nodes belonging to a given project.\n\nRequired privilege: Node.PowerMgmt", "operationId": "reload_all_nodes_v3_projects__project_id__nodes_reload_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}": {"get": {"tags": ["Nodes"], "summary": "Get Node", "description": "Return a node from a given project.\n\nRequired privilege: Node.Audit", "operationId": "get_node_v3_projects__project_id__nodes__node_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Node"}}}}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Nodes"], "summary": "Update Node", "description": "Update a node.\n\nRequired privilege: Node.Modify", "operationId": "update_node_v3_projects__project_id__nodes__node_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/NodeUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Node"}}}}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Nodes"], "summary": "Delete Node", "description": "Delete a node from a project.\n\nRequired privilege: Node.Allocate", "operationId": "delete_node_v3_projects__project_id__nodes__node_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "409": {"description": "Cannot delete node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/duplicate": {"post": {"tags": ["Nodes"], "summary": "Duplicate Node", "description": "Duplicate a node.\n\nRequired privilege: Node.Allocate", "operationId": "duplicate_node_v3_projects__project_id__nodes__node_id__duplicate_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/NodeDuplicate"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Node"}}}}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/start": {"post": {"tags": ["Nodes"], "summary": "Start Node", "description": "Start a node.\n\nRequired privilege: Node.PowerMgmt", "operationId": "start_node_v3_projects__project_id__nodes__node_id__start_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"content": {"application/json": {"schema": {"anyOf": [{"type": "object", "additionalProperties": true}, {"type": "null"}], "title": "Start Data"}}}}, "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/stop": {"post": {"tags": ["Nodes"], "summary": "Stop Node", "description": "Stop a node.\n\nRequired privilege: Node.PowerMgmt", "operationId": "stop_node_v3_projects__project_id__nodes__node_id__stop_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/suspend": {"post": {"tags": ["Nodes"], "summary": "Suspend Node", "description": "Suspend a node.\n\nRequired privilege: Node.PowerMgmt", "operationId": "suspend_node_v3_projects__project_id__nodes__node_id__suspend_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/reload": {"post": {"tags": ["Nodes"], "summary": "Reload Node", "description": "Reload a node.\n\nRequired privilege: Node.PowerMgmt", "operationId": "reload_node_v3_projects__project_id__nodes__node_id__reload_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/isolate": {"post": {"tags": ["Nodes"], "summary": "Isolate Node", "description": "Isolate a node (suspend all attached links).\n\nRequired privilege: Link.Modify", "operationId": "isolate_node_v3_projects__project_id__nodes__node_id__isolate_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/unisolate": {"post": {"tags": ["Nodes"], "summary": "Unisolate Node", "description": "Un-isolate a node (resume all attached suspended links).\n\nRequired privilege: Link.Modify", "operationId": "unisolate_node_v3_projects__project_id__nodes__node_id__unisolate_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/links": {"get": {"tags": ["Nodes"], "summary": "Get Node Links", "description": "Return all the links connected to a node.\n\nRequired privilege: Link.Audit", "operationId": "get_node_links_v3_projects__project_id__nodes__node_id__links_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Link"}, "title": "Response Get Node Links V3 Projects Project Id Nodes Node Id Links Get"}}}}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/dynamips/auto_idlepc": {"get": {"tags": ["Nodes"], "summary": "Auto Idlepc", "description": "Compute an Idle-PC value for a Dynamips node\n\nRequired privilege: Node.Audit", "operationId": "auto_idlepc_v3_projects__project_id__nodes__node_id__dynamips_auto_idlepc_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Response Auto Idlepc V3 Projects Project Id Nodes Node Id Dynamips Auto Idlepc Get"}}}}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/dynamips/idlepc_proposals": {"get": {"tags": ["Nodes"], "summary": "Idlepc Proposals", "description": "Compute a list of potential idle-pc values for a Dynamips node\n\nRequired privilege: Node.Audit", "operationId": "idlepc_proposals_v3_projects__project_id__nodes__node_id__dynamips_idlepc_proposals_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"type": "string"}, "title": "Response Idlepc Proposals V3 Projects Project Id Nodes Node Id Dynamips Idlepc Proposals Get"}}}}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/qemu/disk_image/{disk_name}": {"post": {"tags": ["Nodes"], "summary": "Create Disk Image", "description": "Create a Qemu disk image.\n\nRequired privilege: Node.Allocate", "operationId": "create_disk_image_v3_projects__project_id__nodes__node_id__qemu_disk_image__disk_name__post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "disk_name", "in": "path", "required": true, "schema": {"type": "string", "title": "Disk Name"}}, {"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/QemuDiskImageCreate"}}}}, "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Nodes"], "summary": "Update Disk Image", "description": "Update a Qemu disk image.\n\nRequired privilege: Node.Allocate", "operationId": "update_disk_image_v3_projects__project_id__nodes__node_id__qemu_disk_image__disk_name__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "disk_name", "in": "path", "required": true, "schema": {"type": "string", "title": "Disk Name"}}, {"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/QemuDiskImageUpdate"}}}}, "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Nodes"], "summary": "Delete Disk Image", "description": "Delete a Qemu disk image.\n\nRequired privilege: Node.Allocate", "operationId": "delete_disk_image_v3_projects__project_id__nodes__node_id__qemu_disk_image__disk_name__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "disk_name", "in": "path", "required": true, "schema": {"type": "string", "title": "Disk Name"}}, {"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/files": {"get": {"tags": ["Nodes"], "summary": "List Node Files", "description": "List files in a node directory with detailed metadata.\n\nBy default lists only the current directory level (non-recursive).\nUse recursive=true for a full recursive listing.\n\nRequired privilege: Node.Audit", "operationId": "list_node_files_v3_projects__project_id__nodes__node_id__files_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "path", "in": "query", "required": false, "schema": {"type": "string", "description": "Subdirectory path within node directory", "default": "", "title": "Path"}, "description": "Subdirectory path within node directory"}, {"name": "recursive", "in": "query", "required": false, "schema": {"type": "boolean", "description": "Recursively list all files", "default": false, "title": "Recursive"}, "description": "Recursively list all files"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/NodeFile"}, "title": "Response List Node Files V3 Projects Project Id Nodes Node Id Files Get"}}}}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/files/{file_path}": {"get": {"tags": ["Nodes"], "summary": "Get File", "description": "Return a file from the node directory.\n\nRequired privilege: Node.Audit", "operationId": "get_file_v3_projects__project_id__nodes__node_id__files__file_path__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "file_path", "in": "path", "required": true, "schema": {"type": "string", "title": "File Path"}}, {"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["Nodes"], "summary": "Post File", "description": "Write a file in the node directory.\n\nRequired privilege: Node.Modify", "operationId": "post_file_v3_projects__project_id__nodes__node_id__files__file_path__post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "file_path", "in": "path", "required": true, "schema": {"type": "string", "title": "File Path"}}, {"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Nodes"], "summary": "Delete Node File", "description": "Delete a file from the node directory.\n\nRequired privilege: Node.Modify", "operationId": "delete_node_file_v3_projects__project_id__nodes__node_id__files__file_path__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "file_path", "in": "path", "required": true, "schema": {"type": "string", "title": "File Path"}}, {"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/console/reset": {"post": {"tags": ["Nodes"], "summary": "Reset Console All Nodes", "description": "Reset console for all nodes belonging to the project.\n\nRequired privilege: Node.Console", "operationId": "reset_console_all_nodes_v3_projects__project_id__nodes_console_reset_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/nodes/{node_id}/console/reset": {"post": {"tags": ["Nodes"], "summary": "Console Reset", "description": "Reset a console for a given node.\n\nRequired privilege: Node.Console", "operationId": "console_reset_v3_projects__project_id__nodes__node_id__console_reset_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Node Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or node", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/links": {"get": {"tags": ["Links"], "summary": "Get Links", "description": "Return all links for a given project.\n\nRequired privilege: Link.Audit", "operationId": "get_links_v3_projects__project_id__links_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Link"}, "title": "Response Get Links V3 Projects Project Id Links Get"}}}}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["Links"], "summary": "Create Link", "description": "Create a new link.\n\nRequired privilege: Link.Allocate", "operationId": "create_link_v3_projects__project_id__links_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LinkCreate"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Link"}}}}, "404": {"description": "Could not find project", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "409": {"description": "Could not create link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/links/{link_id}/available_filters": {"get": {"tags": ["Links"], "summary": "Get Filters", "description": "Return all filters available for a given link.\n\nRequired privilege: Link.Audit", "operationId": "get_filters_v3_projects__project_id__links__link_id__available_filters_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"type": "object", "additionalProperties": true}, "title": "Response Get Filters V3 Projects Project Id Links Link Id Available Filters Get"}}}}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/links/{link_id}": {"get": {"tags": ["Links"], "summary": "Get Link", "description": "Return a link.\n\nRequired privilege: Link.Audit", "operationId": "get_link_v3_projects__project_id__links__link_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Link"}}}}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Links"], "summary": "Update Link", "description": "Update a link.\n\nRequired privilege: Link.Modify", "operationId": "update_link_v3_projects__project_id__links__link_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LinkUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Link"}}}}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Links"], "summary": "Delete Link", "description": "Delete a link.\n\nRequired privilege: Link.Allocate", "operationId": "delete_link_v3_projects__project_id__links__link_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/links/{link_id}/reset": {"post": {"tags": ["Links"], "summary": "Reset Link", "description": "Reset a link.\n\nRequired privilege: Link.Modify", "operationId": "reset_link_v3_projects__project_id__links__link_id__reset_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Link"}}}}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/links/{link_id}/capture/start": {"post": {"tags": ["Links"], "summary": "Start Capture", "description": "Start packet capture on the link.\n\nRequired privilege: Link.Capture", "operationId": "start_capture_v3_projects__project_id__links__link_id__capture_start_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LinkCapture"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Link"}}}}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/links/{link_id}/capture/stop": {"post": {"tags": ["Links"], "summary": "Stop Capture", "description": "Stop packet capture on the link.\n\nRequired privilege: Link.Capture", "operationId": "stop_capture_v3_projects__project_id__links__link_id__capture_stop_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/links/{link_id}/capture/wireshark/restart": {"post": {"tags": ["Links"], "summary": "Restart Wireshark", "description": "Restart Wireshark window without stopping the capture.\n\nThis allows recovery after accidentally closing the Wireshark window.\n\nRequired privilege: Link.Capture", "operationId": "restart_wireshark_v3_projects__project_id__links__link_id__capture_wireshark_restart_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Response Restart Wireshark V3 Projects Project Id Links Link Id Capture Wireshark Restart Post"}}}}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/links/{link_id}/capture/stream": {"get": {"tags": ["Links"], "summary": "Stream Pcap", "description": "Stream the PCAP capture file from compute.\n\nRequired privilege: Link.Capture", "operationId": "stream_pcap_v3_projects__project_id__links__link_id__capture_stream_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/links/{link_id}/capture/file": {"get": {"tags": ["Links"], "summary": "Download Capture File", "description": "Download the PCAP capture file.\n\nThis endpoint allows downloading the capture file even while capture is active.\nThe file is streamed directly, so partial data may be received if capture is still running.\n\nRequired privilege: Link.Capture", "operationId": "download_capture_file_v3_projects__project_id__links__link_id__capture_file_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "responses": {"200": {"description": "Successful Response"}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/links/{link_id}/markers": {"get": {"tags": ["Links"], "summary": "Get Markers", "description": "Return all traffic-insight markers configured on this link.\n\nRequired privilege: Link.Audit", "operationId": "get_markers_v3_projects__project_id__links__link_id__markers_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Response Get Markers V3 Projects Project Id Links Link Id Markers Get"}}}}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["Links"], "summary": "Create Marker", "description": "Attach a traffic-insight marker to the link.\nOn BPF match uBridge emits MARK signals and appends packets to a pcap.\n\nRequired privilege: Link.Modify", "operationId": "create_marker_v3_projects__project_id__links__link_id__markers_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MarkerCreate"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Response Create Marker V3 Projects Project Id Links Link Id Markers Post"}}}}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/links/{link_id}/markers/{marker_name}": {"delete": {"tags": ["Links"], "summary": "Delete Marker", "description": "Remove a traffic-insight marker from the link.\n\nRequired privilege: Link.Modify", "operationId": "delete_marker_v3_projects__project_id__links__link_id__markers__marker_name__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "marker_name", "in": "path", "required": true, "schema": {"type": "string", "title": "Marker Name"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Links"], "summary": "Update Marker", "description": "Update a traffic-insight marker (change BPF, tag, or enabled).\n\nRequired privilege: Link.Modify", "operationId": "update_marker_v3_projects__project_id__links__link_id__markers__marker_name__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "marker_name", "in": "path", "required": true, "schema": {"type": "string", "title": "Marker Name"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MarkerUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Response Update Marker V3 Projects Project Id Links Link Id Markers Marker Name Put"}}}}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/links/{link_id}/iface": {"get": {"tags": ["Links"], "summary": "Get Iface", "description": "Return iface info for links to Cloud or NAT devices.\n\nRequired privilege: Link.Audit", "operationId": "get_iface_v3_projects__project_id__links__link_id__iface_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "link_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Link Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"anyOf": [{"$ref": "#/components/schemas/UDPPortInfo"}, {"$ref": "#/components/schemas/EthernetPortInfo"}], "title": "Response Get Iface V3 Projects Project Id Links Link Id Iface Get"}}}}, "404": {"description": "Could not find project or link", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/drawings": {"get": {"tags": ["Drawings"], "summary": "Get Drawings", "description": "Return the list of all drawings for a given project.\n\nRequired privilege: Drawing.Audit", "operationId": "get_drawings_v3_projects__project_id__drawings_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Drawing"}, "title": "Response Get Drawings V3 Projects Project Id Drawings Get"}}}}, "404": {"description": "Project or drawing not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["Drawings"], "summary": "Create Drawing", "description": "Create a new drawing.\n\nRequired privilege: Drawing.Allocate", "operationId": "create_drawing_v3_projects__project_id__drawings_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Drawing"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Drawing"}}}}, "404": {"description": "Project or drawing not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/drawings/{drawing_id}": {"get": {"tags": ["Drawings"], "summary": "Get Drawing", "description": "Return a drawing.\n\nRequired privilege: Drawing.Audit", "operationId": "get_drawing_v3_projects__project_id__drawings__drawing_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "drawing_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Drawing Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Drawing"}}}}, "404": {"description": "Project or drawing not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Drawings"], "summary": "Update Drawing", "description": "Update a drawing.\n\nRequired privilege: Drawing.Modify", "operationId": "update_drawing_v3_projects__project_id__drawings__drawing_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "drawing_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Drawing Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Drawing"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Drawing"}}}}, "404": {"description": "Project or drawing not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Drawings"], "summary": "Delete Drawing", "description": "Delete a drawing.\n\nRequired privilege: Drawing.Allocate", "operationId": "delete_drawing_v3_projects__project_id__drawings__drawing_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "drawing_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Drawing Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Project or drawing not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/symbols": {"get": {"tags": ["Symbols"], "summary": "Get Symbols", "description": "Return all symbols.\n\nRequired privilege: Symbol.Audit", "operationId": "get_symbols_v3_symbols_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"additionalProperties": true, "type": "object"}, "type": "array", "title": "Response Get Symbols V3 Symbols Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/symbols/{symbol_id}/raw": {"get": {"tags": ["Symbols"], "summary": "Get Symbol", "description": "Download a symbol file.\n\nRequired privilege: Symbol.Audit", "operationId": "get_symbol_v3_symbols__symbol_id__raw_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "symbol_id", "in": "path", "required": true, "schema": {"type": "string", "title": "Symbol Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Could not find symbol", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["Symbols"], "summary": "Upload Symbol", "description": "Upload a symbol file.\n\nRequired privilege: Symbol.Allocate", "operationId": "upload_symbol_v3_symbols__symbol_id__raw_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "symbol_id", "in": "path", "required": true, "schema": {"type": "string", "title": "Symbol Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/symbols/{symbol_id}/dimensions": {"get": {"tags": ["Symbols"], "summary": "Get Symbol Dimensions", "description": "Get a symbol dimensions.\n\nRequired privilege: Symbol.Audit", "operationId": "get_symbol_dimensions_v3_symbols__symbol_id__dimensions_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "symbol_id", "in": "path", "required": true, "schema": {"type": "string", "title": "Symbol Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Response Get Symbol Dimensions V3 Symbols Symbol Id Dimensions Get"}}}}, "404": {"description": "Could not find symbol", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/symbols/default_symbols": {"get": {"tags": ["Symbols"], "summary": "Get Default Symbols", "description": "Return all default symbols.\n\nRequired privilege: Symbol.Audit", "operationId": "get_default_symbols_v3_symbols_default_symbols_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"additionalProperties": true, "type": "object", "title": "Response Get Default Symbols V3 Symbols Default Symbols Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/symbols/{symbol_id}": {"delete": {"tags": ["Symbols"], "summary": "Delete Symbol", "description": "Delete a custom symbol file.\n\nRequired privilege: Symbol.Allocate", "operationId": "delete_symbol_v3_symbols__symbol_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "symbol_id", "in": "path", "required": true, "schema": {"type": "string", "title": "Symbol Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/snapshots": {"post": {"tags": ["Snapshots"], "summary": "Create Snapshot", "description": "Create a new snapshot of a project.\n\nRequired privilege: Snapshot.Allocate", "operationId": "create_snapshot_v3_projects__project_id__snapshots_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SnapshotCreate"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Snapshot"}}}}, "404": {"description": "Could not find project or snapshot", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "get": {"tags": ["Snapshots"], "summary": "Get Snapshots", "description": "Return all snapshots belonging to a given project.\n\nRequired privilege: Snapshot.Audit", "operationId": "get_snapshots_v3_projects__project_id__snapshots_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Snapshot"}, "title": "Response Get Snapshots V3 Projects Project Id Snapshots Get"}}}}, "404": {"description": "Could not find project or snapshot", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/snapshots/{snapshot_id}": {"delete": {"tags": ["Snapshots"], "summary": "Delete Snapshot", "description": "Delete a snapshot.\n\nRequired privilege: Snapshot.Allocate", "operationId": "delete_snapshot_v3_projects__project_id__snapshots__snapshot_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "snapshot_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Snapshot Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Could not find project or snapshot", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/projects/{project_id}/snapshots/{snapshot_id}/restore": {"post": {"tags": ["Snapshots"], "summary": "Restore Snapshot", "description": "Restore a snapshot.\n\nRequired privilege: Snapshot.Restore", "operationId": "restore_snapshot_v3_projects__project_id__snapshots__snapshot_id__restore_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "snapshot_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Snapshot Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Project"}}}}, "404": {"description": "Could not find project or snapshot", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/computes": {"post": {"tags": ["Computes"], "summary": "Create Compute", "description": "Create a new compute on the controller.\n\nRequired privilege: Compute.Allocate", "operationId": "create_compute_v3_computes_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "connect", "in": "query", "required": false, "schema": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": false, "title": "Connect"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ComputeCreate"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Compute"}}}}, "404": {"description": "Could not connect to compute", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "409": {"description": "Could not create compute", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "401": {"description": "Invalid authentication for compute", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "get": {"tags": ["Computes"], "summary": "Get Computes", "description": "Return all computes known by the controller.\n\nRequired privilege: Compute.Audit", "operationId": "get_computes_v3_computes_get", "security": [{"OAuth2PasswordBearer": []}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Compute"}, "title": "Response Get Computes V3 Computes Get"}}}}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/computes/{compute_id}/connect": {"post": {"tags": ["Computes"], "summary": "Connect Compute", "description": "Connect to compute on the controller.\n\nRequired privilege: Compute.Audit", "operationId": "connect_compute_v3_computes__compute_id__connect_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/computes/{compute_id}": {"get": {"tags": ["Computes"], "summary": "Get Compute", "description": "Return a compute from the controller.\n\nRequired privilege: Compute.Audit", "operationId": "get_compute_v3_computes__compute_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Compute"}}}}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Computes"], "summary": "Update Compute", "description": "Update a compute on the controller.\n\nRequired privilege: Compute.Modify", "operationId": "update_compute_v3_computes__compute_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ComputeUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Compute"}}}}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Computes"], "summary": "Delete Compute", "description": "Delete a compute from the controller.\n\nRequired privilege: Compute.Allocate", "operationId": "delete_compute_v3_computes__compute_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/computes/{compute_id}/docker/images": {"get": {"tags": ["Computes"], "summary": "Docker Get Images", "description": "Get Docker images from a compute.", "operationId": "docker_get_images_v3_computes__compute_id__docker_images_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/ComputeDockerImage"}, "title": "Response Docker Get Images V3 Computes Compute Id Docker Images Get"}}}}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/computes/{compute_id}/docker/images/pull": {"post": {"tags": ["Computes"], "summary": "Docker Pull Image", "description": "Pull or update a Docker image on a compute.\n\nRequired privilege: Compute.Modify", "operationId": "docker_pull_image_v3_computes__compute_id__docker_images_pull_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Body_docker_pull_image_v3_computes__compute_id__docker_images_pull_post"}}}}, "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/computes/{compute_id}/virtualbox/vms": {"get": {"tags": ["Computes"], "summary": "Virtualbox Vms", "description": "Get VirtualBox VMs from a compute.", "operationId": "virtualbox_vms_v3_computes__compute_id__virtualbox_vms_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/ComputeVirtualBoxVM"}, "title": "Response Virtualbox Vms V3 Computes Compute Id Virtualbox Vms Get"}}}}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/computes/{compute_id}/vmware/vms": {"get": {"tags": ["Computes"], "summary": "Vmware Vms", "description": "Get VMware VMs from a compute.", "operationId": "vmware_vms_v3_computes__compute_id__vmware_vms_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/ComputeVMwareVM"}, "title": "Response Vmware Vms V3 Computes Compute Id Vmware Vms Get"}}}}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/computes/{compute_id}/dynamips/auto_idlepc": {"post": {"tags": ["Computes"], "summary": "Dynamips Autoidlepc", "description": "Find a suitable Idle-PC value for a given IOS image. This may take a few minutes.", "operationId": "dynamips_autoidlepc_v3_computes__compute_id__dynamips_auto_idlepc_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AutoIdlePC"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/computes/{compute_id}/{emulator}/{endpoint_path}": {"get": {"tags": ["Computes"], "summary": "Forward Get", "description": "Forward a GET request to a compute.\nRead the full compute API documentation for available routes.", "operationId": "forward_get_v3_computes__compute_id___emulator___endpoint_path__get", "deprecated": true, "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}, {"name": "emulator", "in": "path", "required": true, "schema": {"type": "string", "title": "Emulator"}}, {"name": "endpoint_path", "in": "path", "required": true, "schema": {"type": "string", "title": "Endpoint Path"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Forward Get V3 Computes Compute Id Emulator Endpoint Path Get"}}}}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["Computes"], "summary": "Forward Post", "description": "Forward a POST request to a compute.\nRead the full compute API documentation for available routes.", "operationId": "forward_post_v3_computes__compute_id___emulator___endpoint_path__post", "deprecated": true, "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}, {"name": "emulator", "in": "path", "required": true, "schema": {"type": "string", "title": "Emulator"}}, {"name": "endpoint_path", "in": "path", "required": true, "schema": {"type": "string", "title": "Endpoint Path"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Compute Data"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Forward Post V3 Computes Compute Id Emulator Endpoint Path Post"}}}}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Computes"], "summary": "Forward Put", "description": "Forward a PUT request to a compute.\nRead the full compute API documentation for available routes.", "operationId": "forward_put_v3_computes__compute_id___emulator___endpoint_path__put", "deprecated": true, "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "compute_id", "in": "path", "required": true, "schema": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}, {"name": "emulator", "in": "path", "required": true, "schema": {"type": "string", "title": "Emulator"}}, {"name": "endpoint_path", "in": "path", "required": true, "schema": {"type": "string", "title": "Endpoint Path"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Compute Data"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Forward Put V3 Computes Compute Id Emulator Endpoint Path Put"}}}}, "404": {"description": "Compute not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/appliances": {"get": {"tags": ["Appliances"], "summary": "Get Appliances", "description": "Return all appliances known by the controller.\n\nRequired privilege: Appliance.Audit", "operationId": "get_appliances_v3_appliances_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "update", "in": "query", "required": false, "schema": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": false, "title": "Update"}}, {"name": "symbol_theme", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Symbol Theme"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"oneOf": [{"$ref": "#/components/schemas/ApplianceV1_6"}, {"$ref": "#/components/schemas/ApplianceV8"}], "discriminator": {"propertyName": "registry_version", "mapping": {"1": "#/components/schemas/ApplianceV1_6", "2": "#/components/schemas/ApplianceV1_6", "3": "#/components/schemas/ApplianceV1_6", "4": "#/components/schemas/ApplianceV1_6", "5": "#/components/schemas/ApplianceV1_6", "6": "#/components/schemas/ApplianceV1_6", "8": "#/components/schemas/ApplianceV8"}}}, "title": "Response Get Appliances V3 Appliances Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/appliances/{appliance_id}": {"get": {"tags": ["Appliances"], "summary": "Get Appliance", "description": "Get an appliance file.\n\nRequired privilege: Appliance.Audit", "operationId": "get_appliance_v3_appliances__appliance_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "appliance_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Appliance Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"oneOf": [{"$ref": "#/components/schemas/ApplianceV1_6"}, {"$ref": "#/components/schemas/ApplianceV8"}], "discriminator": {"propertyName": "registry_version", "mapping": {"1": "#/components/schemas/ApplianceV1_6", "2": "#/components/schemas/ApplianceV1_6", "3": "#/components/schemas/ApplianceV1_6", "4": "#/components/schemas/ApplianceV1_6", "5": "#/components/schemas/ApplianceV1_6", "6": "#/components/schemas/ApplianceV1_6", "8": "#/components/schemas/ApplianceV8"}}, "title": "Response Get Appliance V3 Appliances Appliance Id Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/appliances/{appliance_id}/version": {"post": {"tags": ["Appliances"], "summary": "Add Appliance Version", "description": "Add a version to an appliance.\n\nRequired privilege: Appliance.Allocate", "operationId": "add_appliance_version_v3_appliances__appliance_id__version_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "appliance_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Appliance Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"anyOf": [{"$ref": "#/components/schemas/ApplianceVersion"}, {"$ref": "#/components/schemas/ApplianceVersionV8"}], "title": "Appliance Version"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Response Add Appliance Version V3 Appliances Appliance Id Version Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/appliances/{appliance_id}/install": {"post": {"tags": ["Appliances"], "summary": "Install Appliance", "description": "Install an appliance.\n\nRequired privilege: Appliance.Allocate", "operationId": "install_appliance_v3_appliances__appliance_id__install_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "appliance_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Appliance Id"}}, {"name": "version", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Version"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/netmiko/device_types": {"get": {"tags": ["Netmiko"], "summary": "Get Netmiko Device Types", "description": "Return the device types supported by the Netmiko library installed on this server.\n\nRequired privilege: None (authenticated users only)", "operationId": "get_netmiko_device_types_v3_netmiko_device_types_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/NetmikoDeviceTypeList"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/pools": {"get": {"tags": ["Resource pools"], "summary": "Get Resource Pools", "description": "Get all resource pools.\n\nRequired privilege: Pool.Audit", "operationId": "get_resource_pools_v3_pools_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/ResourcePool"}, "type": "array", "title": "Response Get Resource Pools V3 Pools Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["Resource pools"], "summary": "Create Resource Pool", "description": "Create a new resource pool\n\nRequired privilege: Pool.Allocate", "operationId": "create_resource_pool_v3_pools_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResourcePoolCreate"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResourcePool"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/pools/{resource_pool_id}": {"get": {"tags": ["Resource pools"], "summary": "Get Resource Pool", "description": "Get a resource pool.\n\nRequired privilege: Pool.Audit", "operationId": "get_resource_pool_v3_pools__resource_pool_id__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "resource_pool_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Resource Pool Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResourcePool"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Resource pools"], "summary": "Update Resource Pool", "description": "Update a resource pool.\n\nRequired privilege: Pool.Modify", "operationId": "update_resource_pool_v3_pools__resource_pool_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "resource_pool_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Resource Pool Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResourcePoolUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResourcePool"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Resource pools"], "summary": "Delete Resource Pool", "description": "Delete a resource pool.\n\nRequired privilege: Pool.Allocate", "operationId": "delete_resource_pool_v3_pools__resource_pool_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "resource_pool_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Resource Pool Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/pools/{resource_pool_id}/resources": {"get": {"tags": ["Resource pools"], "summary": "Get Pool Resources", "description": "Get all resource in a pool.\n\nRequired privilege: Pool.Audit", "operationId": "get_pool_resources_v3_pools__resource_pool_id__resources_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "resource_pool_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Resource Pool Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Resource"}, "title": "Response Get Pool Resources V3 Pools Resource Pool Id Resources Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/pools/{resource_pool_id}/resources/{resource_id}": {"put": {"tags": ["Resource pools"], "summary": "Add Resource To Pool", "description": "Add resource to a resource pool.\n\nRequired privilege: Pool.Modify", "operationId": "add_resource_to_pool_v3_pools__resource_pool_id__resources__resource_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "resource_pool_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Resource Pool Id"}}, {"name": "resource_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Resource Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Resource pools"], "summary": "Remove Resource From Pool", "description": "Remove resource from a resource pool.\n\nRequired privilege: Pool.Modify", "operationId": "remove_resource_from_pool_v3_pools__resource_pool_id__resources__resource_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "resource_pool_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Resource Pool Id"}}, {"name": "resource_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Resource Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/gns3vm/engines": {"get": {"tags": ["GNS3 VM"], "summary": "Get Engines", "description": "Return the list of supported engines for the GNS3VM.", "operationId": "get_engines_v3_gns3vm_engines_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"additionalProperties": true, "type": "object"}, "type": "array", "title": "Response Get Engines V3 Gns3Vm Engines Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "deprecated": true, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/gns3vm/engines/{engine}/vms": {"get": {"tags": ["GNS3 VM"], "summary": "Get Vms", "description": "Return all the available VMs for a specific virtualization engine.", "operationId": "get_vms_v3_gns3vm_engines__engine__vms_get", "deprecated": true, "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "engine", "in": "path", "required": true, "schema": {"type": "string", "title": "Engine"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"type": "object", "additionalProperties": true}, "title": "Response Get Vms V3 Gns3Vm Engines Engine Vms Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/gns3vm": {"get": {"tags": ["GNS3 VM"], "summary": "Get Gns3Vm Settings", "description": "Return the GNS3 VM settings.", "operationId": "get_gns3vm_settings_v3_gns3vm_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/GNS3VM"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "deprecated": true, "security": [{"OAuth2PasswordBearer": []}]}, "put": {"tags": ["GNS3 VM"], "summary": "Update Gns3Vm Settings", "description": "Update the GNS3 VM settings.", "operationId": "update_gns3vm_settings_v3_gns3vm_put", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/GNS3VM"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/GNS3VM"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "deprecated": true, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/access/users/{user_id}/llm-model-configs": {"get": {"tags": ["LLM Model Configurations"], "summary": "Get User Llm Model Configs", "description": "Get user's effective LLM model configurations (own + inherited from groups).\n\nRequired privilege: LLMConfig.Audit", "operationId": "get_user_llm_model_configs_v3_access_users__user_id__llm_model_configs_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LLMModelConfigInheritedResponse"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["LLM Model Configurations"], "summary": "Create User Llm Model Config", "description": "Create a new LLM model configuration for a user.\n\nRequired privilege: LLMConfig.Modify\n\nIMPORTANT: context_limit is REQUIRED. Unit is K tokens (e.g., 128 = 128K = 128,000 tokens).\nPlease check your model provider's documentation for the current context window size.", "operationId": "create_user_llm_model_config_v3_access_users__user_id__llm_model_configs_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LLMModelConfigCreate"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LLMModelConfigResponse"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/users/{user_id}/llm-model-configs/own": {"get": {"tags": ["LLM Model Configurations"], "summary": "Get User Own Llm Model Configs", "description": "Get user's own LLM model configurations (excluding inherited ones).\n\nRequired privilege: LLMConfig.Audit", "operationId": "get_user_own_llm_model_configs_v3_access_users__user_id__llm_model_configs_own_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/LLMModelConfigResponse"}, "title": "Response Get User Own Llm Model Configs V3 Access Users User Id Llm Model Configs Own Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/users/{user_id}/llm-model-configs/default": {"get": {"tags": ["LLM Model Configurations"], "summary": "Get User Default Llm Model Config", "description": "Get user's default LLM model configuration.\n\nRequired privilege: LLMConfig.Audit", "operationId": "get_user_default_llm_model_config_v3_access_users__user_id__llm_model_configs_default_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LLMModelConfigResponse"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/users/{user_id}/llm-model-configs/{config_id}": {"put": {"tags": ["LLM Model Configurations"], "summary": "Update User Llm Model Config", "description": "Update a user's LLM model configuration.\nSupports optimistic locking via expected_version field.\n\nRequired privilege: LLMConfig.Modify", "operationId": "update_user_llm_model_config_v3_access_users__user_id__llm_model_configs__config_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Id"}}, {"name": "config_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Config Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LLMModelConfigUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LLMModelConfigResponse"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["LLM Model Configurations"], "summary": "Delete User Llm Model Config", "description": "Delete a user's LLM model configuration.\n\nRequired privilege: LLMConfig.Modify", "operationId": "delete_user_llm_model_config_v3_access_users__user_id__llm_model_configs__config_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Id"}}, {"name": "config_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Config Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/users/{user_id}/llm-model-configs/default/{config_id}": {"put": {"tags": ["LLM Model Configurations"], "summary": "Set User Default Llm Model Config", "description": "Set a user's default LLM model configuration.\n\nRequired privilege: LLMConfig.Modify", "operationId": "set_user_default_llm_model_config_v3_access_users__user_id__llm_model_configs_default__config_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "user_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "User Id"}}, {"name": "config_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Config Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LLMModelConfigResponse"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/groups/{group_id}/llm-model-configs": {"get": {"tags": ["LLM Model Configurations"], "summary": "Get Group Llm Model Configs", "description": "Get all LLM model configurations for a user group.\n\nRequired privilege: Group.Audit", "operationId": "get_group_llm_model_configs_v3_access_groups__group_id__llm_model_configs_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "group_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Group Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LLMModelConfigListResponse"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["LLM Model Configurations"], "summary": "Create Group Llm Model Config", "description": "Create a new LLM model configuration for a user group.\n\nRequired privilege: Group.Modify\n\nIMPORTANT: context_limit is REQUIRED. Unit is K tokens (e.g., 128 = 128K = 128,000 tokens).\nPlease check your model provider's documentation for the current context window size.", "operationId": "create_group_llm_model_config_v3_access_groups__group_id__llm_model_configs_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "group_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Group Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LLMModelConfigCreate"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LLMModelConfigResponse"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/groups/{group_id}/llm-model-configs/default": {"get": {"tags": ["LLM Model Configurations"], "summary": "Get Group Default Llm Model Config", "description": "Get group's default LLM model configuration.\n\nRequired privilege: Group.Audit", "operationId": "get_group_default_llm_model_config_v3_access_groups__group_id__llm_model_configs_default_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "group_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Group Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LLMModelConfigResponse"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/groups/{group_id}/llm-model-configs/{config_id}": {"put": {"tags": ["LLM Model Configurations"], "summary": "Update Group Llm Model Config", "description": "Update a group's LLM model configuration.\nSupports optimistic locking via expected_version field.\n\nRequired privilege: Group.Modify", "operationId": "update_group_llm_model_config_v3_access_groups__group_id__llm_model_configs__config_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "group_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Group Id"}}, {"name": "config_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Config Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LLMModelConfigUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LLMModelConfigResponse"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["LLM Model Configurations"], "summary": "Delete Group Llm Model Config", "description": "Delete a group's LLM model configuration.\n\nRequired privilege: Group.Modify", "operationId": "delete_group_llm_model_config_v3_access_groups__group_id__llm_model_configs__config_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "group_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Group Id"}}, {"name": "config_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Config Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/groups/{group_id}/llm-model-configs/default/{config_id}": {"put": {"tags": ["LLM Model Configurations"], "summary": "Set Group Default Llm Model Config", "description": "Set a group's default LLM model configuration.\n\nRequired privilege: Group.Modify", "operationId": "set_group_default_llm_model_config_v3_access_groups__group_id__llm_model_configs_default__config_id__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "group_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Group Id"}}, {"name": "config_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Config Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LLMModelConfigResponse"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/copilot/reload/skills": {"post": {"tags": ["GNS3 Copilot"], "summary": "Reload Skills", "description": "Hot reload skills and prompts from the external GNS3-Skills repository.\n\nReloads injection skills, system prompts, and forbidden commands\nfrom the skills repository without restarting the server.\n\nRequires superadmin privileges.", "operationId": "reload_skills_v3_copilot_reload_skills_post", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"additionalProperties": true, "type": "object", "title": "Response Reload Skills V3 Copilot Reload Skills Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/copilot/projects/{project_id}/chat/stream": {"post": {"tags": ["GNS3 Copilot"], "summary": "Stream chat responses from GNS3 Copilot", "description": "Send a message to GNS3 Copilot and stream the response via Server-Sent Events (SSE).", "operationId": "stream_chat_v3_copilot_projects__project_id__chat_stream_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChatRequest"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Resource not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/copilot/projects/{project_id}/chat/sessions": {"get": {"tags": ["GNS3 Copilot"], "summary": "List chat sessions", "description": "List all chat sessions for a project, optionally filtered by copilot_mode.", "operationId": "list_sessions_v3_copilot_projects__project_id__chat_sessions_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "copilot_mode", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Copilot Mode"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/ChatSession"}, "title": "Response List Sessions V3 Copilot Projects Project Id Chat Sessions Get"}}}}, "404": {"description": "Resource not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/copilot/projects/{project_id}/chat/sessions/{session_id}/history": {"get": {"tags": ["GNS3 Copilot"], "summary": "Get conversation history", "description": "Retrieve the conversation history for a specific session/thread.", "operationId": "get_history_v3_copilot_projects__project_id__chat_sessions__session_id__history_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "session_id", "in": "path", "required": true, "schema": {"type": "string", "title": "Session Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}, {"name": "limit", "in": "query", "required": false, "schema": {"type": "integer", "default": 100, "title": "Limit"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ConversationHistory"}}}}, "404": {"description": "Resource not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/copilot/projects/{project_id}/chat/sessions/{session_id}": {"delete": {"tags": ["GNS3 Copilot"], "summary": "Delete a chat session", "description": "Delete a specific chat session and its checkpoints.", "operationId": "delete_session_v3_copilot_projects__project_id__chat_sessions__session_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "session_id", "in": "path", "required": true, "schema": {"type": "string", "title": "Session Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"204": {"description": "Successful Response"}, "404": {"description": "Resource not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "patch": {"tags": ["GNS3 Copilot"], "summary": "Rename a chat session", "description": "Rename a specific chat session.", "operationId": "rename_session_v3_copilot_projects__project_id__chat_sessions__session_id__patch", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "session_id", "in": "path", "required": true, "schema": {"type": "string", "title": "Session Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RenameSession"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChatSession"}}}}, "404": {"description": "Resource not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/copilot/projects/{project_id}/chat/sessions/{session_id}/abort": {"post": {"tags": ["GNS3 Copilot"], "summary": "Abort a streaming session", "description": "Abort an ongoing streaming session for a specific session.", "operationId": "abort_session_v3_copilot_projects__project_id__chat_sessions__session_id__abort_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "session_id", "in": "path", "required": true, "schema": {"type": "string", "title": "Session Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Resource not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/copilot/projects/{project_id}/chat/sessions/{session_id}/pin": {"put": {"tags": ["GNS3 Copilot"], "summary": "Pin a chat session", "description": "Pin a chat session to the top of the list.", "operationId": "pin_session_v3_copilot_projects__project_id__chat_sessions__session_id__pin_put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "session_id", "in": "path", "required": true, "schema": {"type": "string", "title": "Session Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChatSession"}}}}, "404": {"description": "Resource not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["GNS3 Copilot"], "summary": "Unpin a chat session", "description": "Unpin a chat session from the top of the list.", "operationId": "unpin_session_v3_copilot_projects__project_id__chat_sessions__session_id__pin_delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "session_id", "in": "path", "required": true, "schema": {"type": "string", "title": "Session Id"}}, {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChatSession"}}}}, "404": {"description": "Resource not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/copilot/projects/{project_id}/chat/inject": {"post": {"tags": ["GNS3 Copilot"], "summary": "Inject a network fault for troubleshooting practice", "description": "Inject a realistic network fault into the GNS3 lab for troubleshooting training.", "operationId": "inject_issue_v3_copilot_projects__project_id__chat_inject_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Project Id"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChatRequest"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "404": {"description": "Resource not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorMessage"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/api-keys": {"get": {"tags": ["API Keys", "API Keys"], "summary": "List Api Keys", "description": "List all API keys for the current user.", "operationId": "list_api_keys_v3_access_api_keys_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"additionalProperties": true, "type": "object"}, "type": "array", "title": "Response List Api Keys V3 Access Api Keys Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["API Keys", "API Keys"], "summary": "Create Api Key", "description": "Create a new API key. The full key is returned only once.", "operationId": "create_api_key_v3_access_api_keys_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ApiKeyCreate"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"additionalProperties": true, "type": "object", "title": "Response Create Api Key V3 Access Api Keys Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/v3/access/api-keys/{api_key_id}/revoke": {"post": {"tags": ["API Keys", "API Keys"], "summary": "Revoke Api Key", "description": "Revoke an API key. It will immediately stop working, but can be restored.", "operationId": "revoke_api_key_v3_access_api_keys__api_key_id__revoke_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "api_key_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Api Key Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Response Revoke Api Key V3 Access Api Keys Api Key Id Revoke Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/api-keys/{api_key_id}/restore": {"post": {"tags": ["API Keys", "API Keys"], "summary": "Restore Api Key", "description": "Restore a previously revoked API key.", "operationId": "restore_api_key_v3_access_api_keys__api_key_id__restore_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "api_key_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Api Key Id"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true, "title": "Response Restore Api Key V3 Access Api Keys Api Key Id Restore Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/access/api-keys/{api_key_id}": {"delete": {"tags": ["API Keys", "API Keys"], "summary": "Delete Api Key", "description": "Permanently delete an API key. Cannot be undone.", "operationId": "delete_api_key_v3_access_api_keys__api_key_id__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "api_key_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Api Key Id"}}], "responses": {"204": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/v3/mcp/": {"get": {"tags": ["MCP", "MCP"], "summary": "Mcp Root", "description": "MCP service metadata.", "operationId": "mcp_root_v3_mcp__get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}}}, "components": {"schemas": {"ACE": {"properties": {"ace_type": {"$ref": "#/components/schemas/ACEType", "description": "Type of the ACE"}, "path": {"type": "string", "title": "Path"}, "propagate": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Propagate", "default": true}, "allowed": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Allowed", "default": true}, "user_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "User Id"}, "group_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Group Id"}, "role_id": {"type": "string", "format": "uuid", "title": "Role Id"}, "created_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Created At"}, "updated_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Updated At"}, "ace_id": {"type": "string", "format": "uuid", "title": "Ace Id"}}, "type": "object", "required": ["ace_type", "path", "role_id", "ace_id"], "title": "ACE"}, "ACECreate": {"properties": {"ace_type": {"$ref": "#/components/schemas/ACEType", "description": "Type of the ACE"}, "path": {"type": "string", "title": "Path"}, "propagate": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Propagate", "default": true}, "allowed": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Allowed", "default": true}, "user_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "User Id"}, "group_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Group Id"}, "role_id": {"type": "string", "format": "uuid", "title": "Role Id"}}, "type": "object", "required": ["ace_type", "path", "role_id"], "title": "ACECreate", "description": "Properties to create an ACE."}, "ACEType": {"type": "string", "enum": ["user", "group"], "title": "ACEType"}, "ACEUpdate": {"properties": {"ace_type": {"$ref": "#/components/schemas/ACEType", "description": "Type of the ACE"}, "path": {"type": "string", "title": "Path"}, "propagate": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Propagate", "default": true}, "allowed": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Allowed", "default": true}, "user_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "User Id"}, "group_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Group Id"}, "role_id": {"type": "string", "format": "uuid", "title": "Role Id"}}, "type": "object", "required": ["ace_type", "path", "role_id"], "title": "ACEUpdate", "description": "Properties to update an ACE."}, "ApiKeyCreate": {"properties": {"name": {"type": "string", "title": "Name"}}, "type": "object", "required": ["name"], "title": "ApiKeyCreate", "description": "Schema for creating a new API key."}, "ApplianceImage": {"properties": {"filename": {"type": "string", "title": "Filename"}, "version": {"type": "string", "title": "Version of the file"}, "md5sum": {"anyOf": [{"type": "string", "pattern": "^[a-f0-9]{32}$"}, {"type": "null"}], "title": "md5sum of the file"}, "filesize": {"type": "integer", "title": "File size in bytes"}, "download_url": {"anyOf": [{"type": "string", "minLength": 1, "format": "uri"}, {"type": "string", "maxLength": 0}, {"type": "null"}], "title": "Download url where you can download the appliance from a browser"}, "direct_download_url": {"anyOf": [{"type": "string", "minLength": 1, "format": "uri"}, {"type": "string", "maxLength": 0}, {"type": "null"}], "title": "Optional. Non authenticated url to the image file where you can download the image."}, "compression": {"anyOf": [{"$ref": "#/components/schemas/Compression"}, {"type": "null"}], "title": "Optional, compression type of direct download url image."}, "checksum": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "checksum of the image file"}, "checksum_type": {"anyOf": [{"$ref": "#/components/schemas/ChecksumType"}, {"type": "null"}], "title": "checksum type of the image file"}, "compression_target": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Optional, file name of the image file inside the compressed file."}}, "type": "object", "required": ["filename", "version", "filesize"], "title": "ApplianceImage", "description": "Appliance image definition - compatible with both versions"}, "ApplianceMetadata": {"properties": {"appliance_id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Appliance Id", "description": "ID of the appliance the template was installed from"}, "description": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Description"}, "vendor_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Vendor Name"}, "vendor_url": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Vendor Url"}, "vendor_logo_url": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Vendor Logo Url"}, "documentation_url": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Documentation Url"}, "product_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Product Name"}, "product_url": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Product Url"}, "status": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Status"}, "availability": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Availability"}, "maintainer": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Maintainer"}, "maintainer_email": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Maintainer Email"}, "installation_instructions": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Installation Instructions"}, "default_username": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default Username"}, "default_password": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default Password"}}, "additionalProperties": true, "type": "object", "title": "ApplianceMetadata", "description": "Metadata kept on a template installed from an appliance: vendor\ninformation, default credentials and other fields that describe\nthe appliance but are not node properties."}, "ApplianceV1_6": {"properties": {"registry_version": {"type": "integer", "enum": [1, 2, 3, 4, 5, 6], "title": "Version of the registry compatible with this appliance"}, "appliance_id": {"type": "string", "format": "uuid", "title": "Appliance ID"}, "name": {"type": "string", "title": "Appliance name"}, "builtin": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Whether the appliance is builtin or not"}, "category": {"$ref": "#/components/schemas/gns3server__schemas__controller__appliances__Category", "title": "Category of the appliance"}, "description": {"type": "string", "title": "Description of the appliance. Could be a marketing description"}, "vendor_name": {"type": "string", "title": "Name of the vendor"}, "vendor_url": {"anyOf": [{"type": "string", "minLength": 1, "format": "uri"}, {"type": "string", "maxLength": 0}, {"type": "null"}], "title": "Website of the vendor"}, "documentation_url": {"anyOf": [{"type": "string", "minLength": 1, "format": "uri"}, {"type": "string", "maxLength": 0}, {"type": "null"}], "title": "An optional documentation for using the appliance on vendor website"}, "product_name": {"type": "string", "title": "Product name"}, "product_url": {"anyOf": [{"type": "string", "minLength": 1, "format": "uri"}, {"type": "string", "maxLength": 0}, {"type": "null"}], "title": "An optional product url on vendor website"}, "status": {"$ref": "#/components/schemas/Status", "title": "Document if the appliance is working or not"}, "availability": {"anyOf": [{"$ref": "#/components/schemas/Availability"}, {"type": "null"}], "title": "About image availability: can be downloaded directly; download requires a free registration; paid but a trial version (time or feature limited) is available; not available publicly"}, "maintainer": {"type": "string", "title": "Maintainer name"}, "maintainer_email": {"anyOf": [{"type": "string", "format": "email"}, {"type": "string", "maxLength": 0}, {"type": "null"}], "title": "Maintainer email"}, "usage": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "How to use the appliance"}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "An optional symbol for the appliance"}, "netmiko_device_type": {"anyOf": [{"type": "string", "pattern": "^[a-z0-9_]+$|^$"}, {"type": "null"}], "title": "Device type for Netmiko-based automation tools"}, "first_port_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Optional name of the first networking port example: eth0"}, "port_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Optional formating of the networking port example: eth{0}"}, "port_segment_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Optional port segment size. A port segment is a block of port. For example Ethernet0/0 Ethernet0/1 is the module 0 with a port segment size of 2"}, "custom_adapters": {"anyOf": [{"items": {"$ref": "#/components/schemas/CustomAdapterItem"}, "type": "array"}, {"type": "null"}], "title": "Optional per-adapter overrides (port name, adapter type, MAC address)"}, "linked_clone": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "False if you don't want to use a single image for all nodes"}, "docker": {"anyOf": [{"$ref": "#/components/schemas/Docker"}, {"type": "null"}], "title": "Docker specific options"}, "iou": {"anyOf": [{"$ref": "#/components/schemas/Iou"}, {"type": "null"}], "title": "IOU specific options"}, "dynamips": {"anyOf": [{"$ref": "#/components/schemas/Dynamips"}, {"type": "null"}], "title": "Dynamips specific options"}, "qemu": {"anyOf": [{"$ref": "#/components/schemas/Qemu"}, {"type": "null"}], "title": "Qemu specific options"}, "tags": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "User-defined metadata tags for the appliance"}, "images": {"anyOf": [{"items": {"$ref": "#/components/schemas/ApplianceImage"}, "type": "array"}, {"type": "null"}], "title": "Images for this appliance"}, "versions": {"anyOf": [{"items": {"$ref": "#/components/schemas/ApplianceVersion"}, "type": "array"}, {"type": "null"}], "title": "Versions of the appliance"}}, "type": "object", "required": ["registry_version", "appliance_id", "name", "category", "description", "vendor_name", "product_name", "status", "maintainer"], "title": "ApplianceV1_6", "description": "GNS3 Appliance model for registry versions 1-6"}, "ApplianceV8": {"properties": {"registry_version": {"type": "integer", "const": 8, "title": "Version of the registry compatible with this appliance (version >=8 introduced breaking changes)"}, "appliance_id": {"type": "string", "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$", "title": "Appliance ID"}, "name": {"type": "string", "title": "Appliance name"}, "builtin": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Whether the appliance is builtin or not"}, "category": {"$ref": "#/components/schemas/gns3server__schemas__controller__appliances__Category", "title": "Category of the appliance"}, "description": {"type": "string", "title": "Description of the appliance. Could be a marketing description"}, "vendor_name": {"type": "string", "title": "Name of the vendor"}, "vendor_url": {"type": "string", "minLength": 1, "format": "uri", "title": "Website of the vendor"}, "vendor_logo_url": {"anyOf": [{"type": "string", "minLength": 1, "format": "uri"}, {"type": "null"}], "title": "Link to the vendor logo (used by the GNS3 marketplace)"}, "documentation_url": {"anyOf": [{"type": "string", "minLength": 1, "format": "uri"}, {"type": "null"}], "title": "An optional documentation for using the appliance on vendor website"}, "product_name": {"type": "string", "title": "Product name"}, "product_url": {"anyOf": [{"type": "string", "minLength": 1, "format": "uri"}, {"type": "null"}], "title": "An optional product url on vendor website"}, "status": {"$ref": "#/components/schemas/Status", "title": "Document if the appliance is working or not"}, "availability": {"anyOf": [{"$ref": "#/components/schemas/Availability"}, {"type": "null"}], "title": "About image availability: can be downloaded directly; download requires a free registration; paid but a trial version (time or feature limited) is available; not available publicly"}, "maintainer": {"type": "string", "title": "Maintainer name"}, "maintainer_email": {"type": "string", "format": "email", "title": "Maintainer email"}, "installation_instructions": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Optional installation instructions"}, "usage": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "How to use the appliance"}, "default_username": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default username for the appliance"}, "default_password": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default password for the appliance"}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "An optional symbol for the appliance"}, "netmiko_device_type": {"anyOf": [{"type": "string", "pattern": "^[a-z0-9_]+$|^$"}, {"type": "null"}], "title": "Device type for Netmiko-based automation tools"}, "tags": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "User-defined metadata tags for the appliance"}, "settings": {"items": {"$ref": "#/components/schemas/TemplateSetting"}, "type": "array", "title": "Settings for running the appliance"}, "images": {"anyOf": [{"items": {"$ref": "#/components/schemas/ApplianceImage"}, "type": "array"}, {"type": "null"}], "title": "Images for this appliance"}, "versions": {"anyOf": [{"items": {"$ref": "#/components/schemas/ApplianceVersionV8"}, "type": "array"}, {"type": "null"}], "title": "Versions of the appliance"}}, "type": "object", "required": ["registry_version", "appliance_id", "name", "category", "description", "vendor_name", "vendor_url", "product_name", "status", "maintainer", "maintainer_email", "settings"], "title": "ApplianceV8", "description": "GNS3 Appliance model for registry version 8"}, "ApplianceVersion": {"properties": {"name": {"type": "string", "title": "Name of the version"}, "idlepc": {"anyOf": [{"type": "string", "pattern": "^0x[0-9a-f]{8}"}, {"type": "null"}], "title": "Idlepc"}, "images": {"anyOf": [{"$ref": "#/components/schemas/ApplianceVersionImages"}, {"type": "null"}], "title": "Images used for this version"}}, "type": "object", "required": ["name"], "title": "ApplianceVersion", "description": "Appliance version definition for v1-6"}, "ApplianceVersionImages": {"properties": {"kernel_image": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Kernel image"}, "initrd": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Initrd disk image"}, "image": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "OS image"}, "bios_image": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Bios image"}, "hda_disk_image": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Hda disk image"}, "hdb_disk_image": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Hdc disk image"}, "hdc_disk_image": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Hdd disk image"}, "hdd_disk_image": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Hdd diskimage"}, "cdrom_image": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "cdrom image"}}, "type": "object", "title": "ApplianceVersionImages", "description": "Appliance version images configuration for v1-6"}, "ApplianceVersionV8": {"properties": {"name": {"type": "string", "title": "Name of the version"}, "settings": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Template settings to use to run the version"}, "idlepc": {"anyOf": [{"type": "string", "pattern": "^0x[0-9a-f]{8}"}, {"type": "null"}], "title": "Idlepc"}, "category": {"anyOf": [{"$ref": "#/components/schemas/gns3server__schemas__controller__appliances__Category"}, {"type": "null"}], "title": "Category of the version"}, "installation_instructions": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Optional installation instructions for the version"}, "usage": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Optional instructions about using the version"}, "default_username": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default username for the version"}, "default_password": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default password for the version"}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "An optional symbol for the version"}, "images": {"anyOf": [{"$ref": "#/components/schemas/ApplianceVersionImages"}, {"type": "null"}], "title": "Images used for this version"}}, "type": "object", "required": ["name"], "title": "ApplianceVersionV8", "description": "Appliance version definition (v8)"}, "AutoIdlePC": {"properties": {"platform": {"type": "string", "title": "Platform", "description": "Cisco platform"}, "image": {"type": "string", "title": "Image", "description": "Image path"}, "ram": {"type": "integer", "title": "Ram", "description": "Amount of RAM in MB"}}, "type": "object", "required": ["platform", "image", "ram"], "title": "AutoIdlePC", "description": "Data for auto Idle-PC request.", "example": {"image": "/path/to/c7200_image.bin", "platform": "c7200", "ram": 256}}, "Availability": {"type": "string", "enum": ["free", "with-registration", "free-to-try", "service-contract"], "title": "Availability", "description": "Image availability enum"}, "Body_docker_pull_image_v3_computes__compute_id__docker_images_pull_post": {"properties": {"image": {"type": "string", "minLength": 1, "pattern": "^\\S+$", "title": "Image"}}, "type": "object", "required": ["image"], "title": "Body_docker_pull_image_v3_computes__compute_id__docker_images_pull_post"}, "Body_load_project_v3_projects_load_post": {"properties": {"path": {"type": "string", "title": "Path"}}, "type": "object", "required": ["path"], "title": "Body_load_project_v3_projects_load_post"}, "Body_login_v3_access_users_login_post": {"properties": {"grant_type": {"anyOf": [{"type": "string", "pattern": "^password$"}, {"type": "null"}], "title": "Grant Type"}, "username": {"type": "string", "title": "Username"}, "password": {"type": "string", "format": "password", "title": "Password"}, "scope": {"type": "string", "title": "Scope", "default": ""}, "client_id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Client Id"}, "client_secret": {"anyOf": [{"type": "string"}, {"type": "null"}], "format": "password", "title": "Client Secret"}}, "type": "object", "required": ["username", "password"], "title": "Body_login_v3_access_users_login_post"}, "BuiltinSymbolTheme": {"type": "string", "enum": ["Classic", "Affinity-square-blue", "Affinity-square-red", "Affinity-square-gray", "Affinity-circle-blue", "Affinity-circle-red", "Affinity-circle-gray"], "title": "BuiltinSymbolTheme"}, "Capabilities": {"properties": {"version": {"type": "string", "title": "Version", "description": "Compute version number"}, "node_types": {"items": {"$ref": "#/components/schemas/NodeType"}, "type": "array", "title": "Node Types", "description": "Node types supported by the compute"}, "platform": {"type": "string", "title": "Platform", "description": "Platform where the compute is running (Linux, Windows or macOS)"}, "cpus": {"type": "integer", "title": "Cpus", "description": "Number of CPUs on this compute"}, "memory": {"type": "integer", "title": "Memory", "description": "Amount of memory on this compute"}, "disk_size": {"type": "integer", "title": "Disk Size", "description": "Disk size on this compute"}}, "type": "object", "required": ["version", "node_types", "platform", "cpus", "memory", "disk_size"], "title": "Capabilities", "description": "Capabilities supported by a compute."}, "ChatRequest": {"properties": {"message": {"type": "string", "title": "Message", "description": "User message content"}, "session_id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Session Id", "description": "Session ID (auto-generated if not provided)"}, "stream": {"type": "boolean", "title": "Stream", "description": "Enable streaming response", "default": true}, "temperature": {"anyOf": [{"type": "number"}, {"type": "null"}], "title": "Temperature", "description": "LLM temperature parameter (NOTE: currently not used. Temperature is loaded from user's LLM config in database. Reserved for future runtime override support.)"}, "mode": {"type": "string", "const": "text", "title": "Mode", "description": "Interaction mode", "default": "text"}}, "type": "object", "required": ["message"], "title": "ChatRequest", "description": "Chat request model."}, "ChatSession": {"properties": {"id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Id", "description": "Database ID"}, "thread_id": {"type": "string", "title": "Thread Id", "description": "Thread/session ID"}, "user_id": {"type": "string", "title": "User Id", "description": "User ID"}, "project_id": {"type": "string", "title": "Project Id", "description": "Associated GNS3 project ID"}, "title": {"type": "string", "title": "Title", "description": "Session title"}, "message_count": {"type": "integer", "title": "Message Count", "description": "Number of messages", "default": 0}, "llm_calls_count": {"type": "integer", "title": "Llm Calls Count", "description": "Number of LLM calls", "default": 0}, "input_tokens": {"type": "integer", "title": "Input Tokens", "description": "Input tokens used", "default": 0}, "output_tokens": {"type": "integer", "title": "Output Tokens", "description": "Output tokens generated", "default": 0}, "total_tokens": {"type": "integer", "title": "Total Tokens", "description": "Total tokens used", "default": 0}, "last_message_at": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Last Message At", "description": "Last message timestamp (ISO 8601)"}, "created_at": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Created At", "description": "Creation timestamp (ISO 8601)"}, "updated_at": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Updated At", "description": "Last update timestamp (ISO 8601)"}, "metadata": {"additionalProperties": true, "type": "object", "title": "Metadata", "description": "Session metadata"}, "stats": {"additionalProperties": true, "type": "object", "title": "Stats", "description": "Session statistics"}, "pinned": {"type": "boolean", "title": "Pinned", "description": "Whether the session is pinned to the top", "default": false}}, "type": "object", "required": ["thread_id", "user_id", "project_id", "title"], "title": "ChatSession", "description": "Chat session model."}, "ChecksumType": {"type": "string", "enum": ["md5"], "title": "ChecksumType", "description": "Checksum type enum"}, "Compression": {"type": "string", "enum": ["bzip2", "gzip", "lzma", "xz", "rar", "zip", "7z"], "title": "Compression", "description": "Compression type enum"}, "Compute": {"properties": {"protocol": {"$ref": "#/components/schemas/Protocol"}, "host": {"type": "string", "title": "Host"}, "port": {"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0, "title": "Port"}, "user": {"type": "string", "title": "User"}, "password": {"anyOf": [{"type": "string", "format": "password", "writeOnly": true}, {"type": "null"}], "title": "Password"}, "name": {"type": "string", "title": "Name"}, "created_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Created At"}, "updated_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Updated At"}, "compute_id": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}, "connected": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Connected", "description": "Whether the controller is connected to the compute or not"}, "cpu_usage_percent": {"anyOf": [{"type": "number", "maximum": 100.0, "minimum": 0.0}, {"type": "null"}], "title": "Cpu Usage Percent", "description": "CPU usage of the compute"}, "memory_usage_percent": {"anyOf": [{"type": "number", "maximum": 100.0, "minimum": 0.0}, {"type": "null"}], "title": "Memory Usage Percent", "description": "Memory usage of the compute"}, "disk_usage_percent": {"anyOf": [{"type": "number", "maximum": 100.0, "minimum": 0.0}, {"type": "null"}], "title": "Disk Usage Percent", "description": "Disk usage of the compute"}, "last_error": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Last Error", "description": "Last error found on the compute"}, "capabilities": {"anyOf": [{"$ref": "#/components/schemas/Capabilities"}, {"type": "null"}]}}, "type": "object", "required": ["protocol", "host", "port", "name", "compute_id"], "title": "Compute", "description": "Data returned for a compute."}, "ComputeCreate": {"properties": {"protocol": {"$ref": "#/components/schemas/Protocol"}, "host": {"type": "string", "title": "Host"}, "port": {"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0, "title": "Port"}, "user": {"type": "string", "title": "User"}, "password": {"anyOf": [{"type": "string", "format": "password", "writeOnly": true}, {"type": "null"}], "title": "Password"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "compute_id": {"anyOf": [{"type": "string"}, {"type": "string", "format": "uuid"}], "title": "Compute Id"}}, "type": "object", "required": ["protocol", "host", "port"], "title": "ComputeCreate", "description": "Data to create a compute.", "example": {"host": "127.0.0.1", "name": "My compute", "password": "password", "port": 3080, "user": "user"}}, "ComputeDockerImage": {"properties": {"image": {"type": "string", "title": "Image", "description": "Docker image name"}}, "type": "object", "required": ["image"], "title": "ComputeDockerImage", "description": "Docker image from compute."}, "ComputeUpdate": {"properties": {"protocol": {"anyOf": [{"$ref": "#/components/schemas/Protocol"}, {"type": "null"}]}, "host": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Host"}, "port": {"anyOf": [{"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Port"}, "user": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "User"}, "password": {"anyOf": [{"type": "string", "format": "password", "writeOnly": true}, {"type": "null"}], "title": "Password"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}}, "type": "object", "title": "ComputeUpdate", "description": "Data to update a compute.", "example": {"host": "10.0.0.1", "port": 8080}}, "ComputeVMwareVM": {"properties": {"vmname": {"type": "string", "title": "Vmname", "description": "VMware VM name"}, "vmx_path": {"type": "string", "title": "Vmx Path", "description": "Path to the vmx file"}}, "type": "object", "required": ["vmname", "vmx_path"], "title": "ComputeVMwareVM", "description": "VMware VM from compute."}, "ComputeVirtualBoxVM": {"properties": {"vmname": {"type": "string", "title": "Vmname", "description": "VirtualBox VM name"}, "ram": {"type": "integer", "title": "Ram", "description": "VirtualBox VM memory"}}, "type": "object", "required": ["vmname", "ram"], "title": "ComputeVirtualBoxVM", "description": "VirtualBox VM from compute."}, "ConsoleType": {"type": "string", "enum": ["vnc", "telnet", "ssh", "http", "https", "spice", "spice+agent", "none", "docker_exec"], "title": "ConsoleType", "description": "Supported console types."}, "ControllerSettingsResponse": {"properties": {"jwt_algorithm": {"type": "string", "title": "Jwt Algorithm", "description": "Algorithm used to sign the JWT tokens", "default": "HS256"}, "jwt_access_token_expire_minutes": {"type": "integer", "title": "Jwt Access Token Expire Minutes", "description": "Lifetime of the JWT access tokens in minutes (24 hours by default)", "default": 1440}, "jwt_refresh_token_expire_minutes": {"type": "integer", "title": "Jwt Refresh Token Expire Minutes", "description": "Lifetime of the JWT refresh tokens in minutes (30 days by default)", "default": 43200}, "default_admin_username": {"type": "string", "title": "Default Admin Username", "description": "Username of the super admin account seeded when the controller database is created; changing it has no effect until the database is re-created (which resets the account)", "default": "admin"}, "default_admin_password": {"type": "string", "format": "password", "title": "Default Admin Password", "description": "Password of the super admin account seeded when the controller database is created; changing it has no effect until the database is re-created (which resets the account)", "default": "**********", "writeOnly": true}}, "type": "object", "title": "ControllerSettingsResponse"}, "ControllerSettingsUpdate": {"properties": {"jwt_algorithm": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Jwt Algorithm"}, "jwt_access_token_expire_minutes": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Jwt Access Token Expire Minutes"}, "jwt_refresh_token_expire_minutes": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Jwt Refresh Token Expire Minutes"}, "default_admin_username": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default Admin Username"}, "default_admin_password": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default Admin Password"}}, "additionalProperties": false, "type": "object", "title": "ControllerSettingsUpdate", "description": "No jwt_secret_key field on purpose (see module docstring)."}, "ConversationHistory": {"properties": {"thread_id": {"type": "string", "title": "Thread Id", "description": "Thread/session ID"}, "title": {"type": "string", "title": "Title", "description": "Conversation title"}, "messages": {"items": {"$ref": "#/components/schemas/OpenAIMessage"}, "type": "array", "title": "Messages", "description": "Conversation messages"}, "created_at": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Created At", "description": "Creation timestamp (ISO 8601)"}, "updated_at": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Updated At", "description": "Last update timestamp (ISO 8601)"}, "llm_calls": {"type": "integer", "title": "Llm Calls", "description": "Total LLM calls in this conversation", "default": 0}}, "type": "object", "required": ["thread_id", "title"], "title": "ConversationHistory", "description": "Conversation history model."}, "Credentials": {"properties": {"username": {"type": "string", "title": "Username"}, "password": {"type": "string", "title": "Password"}}, "type": "object", "required": ["username", "password"], "title": "Credentials"}, "CustomAdapter": {"properties": {"adapter_number": {"type": "integer", "title": "Adapter Number"}, "port_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Port Name"}, "adapter_type": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Adapter Type"}, "mac_address": {"anyOf": [{"type": "string", "pattern": "^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$"}, {"type": "null"}], "title": "Mac Address"}}, "type": "object", "required": ["adapter_number"], "title": "CustomAdapter", "description": "Custom adapter data."}, "CustomAdapterItem": {"properties": {"adapter_number": {"type": "integer", "title": "Adapter number"}, "port_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Custom port name"}, "adapter_type": {"anyOf": [{"$ref": "#/components/schemas/QemuAdapterType"}, {"type": "null"}], "title": "Custom adapter type"}, "mac_address": {"anyOf": [{"type": "string", "pattern": "^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$"}, {"type": "null"}], "title": "Custom MAC address"}}, "type": "object", "required": ["adapter_number"], "title": "CustomAdapterItem", "description": "Custom adapter configuration (v8)"}, "Docker": {"properties": {"adapters": {"type": "integer", "title": "Number of Ethernet adapters"}, "image": {"type": "string", "title": "Docker image in the Docker Hub"}, "start_command": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Command executed when the container start. Empty will use the default"}, "environment": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "One KEY=VAR environment by line"}, "console_type": {"anyOf": [{"$ref": "#/components/schemas/DockerConsoleType"}, {"type": "null"}], "title": "Type of console connection for the administration of the appliance"}, "console_http_port": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Console Http Port", "description": "Internal port in the container of the HTTP server"}, "console_http_path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Console Http Path", "description": "Path of the web interface"}, "extra_hosts": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Extra Hosts", "description": "Hosts which will be written to /etc/hosts into container"}, "extra_volumes": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "Extra Volumes", "description": "Additional directories to make persistent that are not included in the images VOLUME directive"}, "extra_configs": {"anyOf": [{"items": {"$ref": "#/components/schemas/ExtraConfig"}, "type": "array"}, {"type": "null"}], "title": "Extra Configs", "description": "Configuration files injected into the container (bind-mounted read-only)"}}, "type": "object", "required": ["adapters", "image"], "title": "Docker", "description": "Docker configuration for v1-6"}, "DockerConsoleType": {"type": "string", "enum": ["telnet", "ssh", "vnc", "http", "https", "none", "docker_exec"], "title": "DockerConsoleType", "description": "Docker console type enum"}, "DockerPropertiesV8": {"properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name of the template"}, "category": {"anyOf": [{"$ref": "#/components/schemas/gns3server__schemas__controller__appliances__Category"}, {"type": "null"}], "title": "Category of the template"}, "default_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default name format"}, "usage": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "How to use the template"}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Symbol of the template"}, "image": {"type": "string", "title": "Docker image"}, "adapters": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Number of ethernet adapters"}, "start_command": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Command executed when the container start. Empty will use the default"}, "environment": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "One KEY=VAR environment by line"}, "console_type": {"anyOf": [{"$ref": "#/components/schemas/DockerConsoleType"}, {"type": "null"}], "title": "Type of console"}, "console_http_port": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Internal port in the container of the HTTP server"}, "console_http_path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Path of the web interface"}, "console_resolution": {"anyOf": [{"type": "string", "pattern": "^[0-9]+x[0-9]+$"}, {"type": "null"}], "title": "Console resolution for VNC, for example 1024x768"}, "extra_hosts": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Docker extra hosts (added to /etc/hosts)"}, "extra_volumes": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "Additional directories to make persistent"}, "custom_adapters": {"anyOf": [{"items": {"$ref": "#/components/schemas/CustomAdapterItem"}, "type": "array"}, {"type": "null"}], "title": "Custom adapters"}, "extra_configs": {"anyOf": [{"items": {"$ref": "#/components/schemas/ExtraConfig"}, "type": "array"}, {"type": "null"}], "title": "Configuration files injected into the container (bind-mounted read-only)"}}, "type": "object", "required": ["image"], "title": "DockerPropertiesV8", "description": "Docker template properties (v8)"}, "Drawing": {"properties": {"drawing_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Drawing Id"}, "project_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Project Id"}, "x": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "X"}, "y": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Y"}, "z": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Z"}, "locked": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Locked"}, "rotation": {"anyOf": [{"type": "integer", "maximum": 360.0, "minimum": -359.0}, {"type": "null"}], "title": "Rotation"}, "svg": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Svg"}}, "type": "object", "title": "Drawing", "description": "Drawing data."}, "Dynamips": {"properties": {"chassis": {"anyOf": [{"$ref": "#/components/schemas/DynamipsChassis"}, {"type": "null"}], "title": "Chassis type"}, "platform": {"$ref": "#/components/schemas/DynamipsPlatform", "title": "Platform type"}, "ram": {"type": "integer", "minimum": 1.0, "title": "Amount of ram"}, "nvram": {"type": "integer", "minimum": 1.0, "title": "Amount of nvram"}, "startup_config": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Config loaded at startup"}, "wic0": {"anyOf": [{"$ref": "#/components/schemas/DynamipsWic"}, {"type": "null"}]}, "wic1": {"anyOf": [{"$ref": "#/components/schemas/DynamipsWic"}, {"type": "null"}]}, "wic2": {"anyOf": [{"$ref": "#/components/schemas/DynamipsWic"}, {"type": "null"}]}, "slot0": {"anyOf": [{"$ref": "#/components/schemas/DynamipsSlot"}, {"type": "null"}]}, "slot1": {"anyOf": [{"$ref": "#/components/schemas/DynamipsSlot"}, {"type": "null"}]}, "slot2": {"anyOf": [{"$ref": "#/components/schemas/DynamipsSlot"}, {"type": "null"}]}, "slot3": {"anyOf": [{"$ref": "#/components/schemas/DynamipsSlot"}, {"type": "null"}]}, "slot4": {"anyOf": [{"$ref": "#/components/schemas/DynamipsSlot"}, {"type": "null"}]}, "slot5": {"anyOf": [{"$ref": "#/components/schemas/DynamipsSlot"}, {"type": "null"}]}, "slot6": {"anyOf": [{"$ref": "#/components/schemas/DynamipsSlot"}, {"type": "null"}]}, "midplane": {"anyOf": [{"$ref": "#/components/schemas/DynamipsMidplane"}, {"type": "null"}]}, "npe": {"anyOf": [{"$ref": "#/components/schemas/DynamipsNpe"}, {"type": "null"}]}}, "type": "object", "required": ["platform", "ram", "nvram"], "title": "Dynamips", "description": "Dynamips configuration for v1-6"}, "DynamipsChassis": {"type": "string", "enum": ["1720", "1721", "1750", "1751", "1760", "2610", "2620", "2610XM", "2620XM", "2650XM", "2621", "2611XM", "2621XM", "2651XM", "3620", "3640", "3660", ""], "title": "DynamipsChassis", "description": "Dynamips chassis enum"}, "DynamipsMidplane": {"type": "string", "enum": ["std", "vxr"], "title": "DynamipsMidplane", "description": "Dynamips midplane enum"}, "DynamipsNpe": {"type": "string", "enum": ["npe-100", "npe-150", "npe-175", "npe-200", "npe-225", "npe-300", "npe-400", "npe-g2"], "title": "DynamipsNpe", "description": "Dynamips NPE enum"}, "DynamipsPlatform": {"type": "string", "enum": ["c1700", "c2600", "c2691", "c3725", "c3745", "c3600", "c7200"], "title": "DynamipsPlatform", "description": "Dynamips platform enum"}, "DynamipsPropertiesV8": {"properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name of the template"}, "category": {"anyOf": [{"$ref": "#/components/schemas/gns3server__schemas__controller__appliances__Category"}, {"type": "null"}], "title": "Category of the template"}, "default_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default name format"}, "usage": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "How to use the template"}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Symbol of the template"}, "chassis": {"anyOf": [{"$ref": "#/components/schemas/DynamipsChassis"}, {"type": "null"}], "title": "Chassis type"}, "platform": {"anyOf": [{"$ref": "#/components/schemas/DynamipsPlatform"}, {"type": "null"}], "title": "Platform type"}, "ram": {"anyOf": [{"type": "integer", "minimum": 1.0}, {"type": "null"}], "title": "Amount of ram"}, "nvram": {"anyOf": [{"type": "integer", "minimum": 1.0}, {"type": "null"}], "title": "Amount of nvram"}, "idlepc": {"anyOf": [{"type": "string", "pattern": "^0x[0-9a-f]{8}"}, {"type": "null"}], "title": "Idlepc"}, "startup_config": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Config loaded at startup"}, "wic0": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Wic0"}, "wic1": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Wic1"}, "wic2": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Wic2"}, "slot0": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Slot0"}, "slot1": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Slot1"}, "slot2": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Slot2"}, "slot3": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Slot3"}, "slot4": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Slot4"}, "slot5": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Slot5"}, "slot6": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Slot6"}, "midplane": {"anyOf": [{"$ref": "#/components/schemas/DynamipsMidplane"}, {"type": "null"}]}, "npe": {"anyOf": [{"$ref": "#/components/schemas/DynamipsNpe"}, {"type": "null"}]}}, "type": "object", "title": "DynamipsPropertiesV8", "description": "Dynamips template properties (v8)"}, "DynamipsSettings": {"properties": {"allocate_aux_console_ports": {"type": "boolean", "title": "Allocate Aux Console Ports", "description": "Allocate auxiliary console ports on IOS routers", "default": false}, "mmap_support": {"type": "boolean", "title": "Mmap Support", "description": "Use memory-mapped flash files (mmap) to lower the memory usage of routers", "default": true}, "dynamips_path": {"type": "string", "title": "Dynamips Path", "description": "Dynamips executable location, default: search in PATH", "default": "dynamips"}, "sparse_memory_support": {"type": "boolean", "title": "Sparse Memory Support", "description": "Use sparse memory allocation to lower the memory usage of routers", "default": true}, "ghost_ios_support": {"type": "boolean", "title": "Ghost Ios Support", "description": "Enable Ghost IOS support to share memory between identical IOS images", "default": true}}, "type": "object", "title": "DynamipsSettings"}, "DynamipsSettingsUpdate": {"properties": {"allocate_aux_console_ports": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Allocate Aux Console Ports"}, "mmap_support": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Mmap Support"}, "dynamips_path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Dynamips Path"}, "sparse_memory_support": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Sparse Memory Support"}, "ghost_ios_support": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Ghost Ios Support"}}, "additionalProperties": false, "type": "object", "title": "DynamipsSettingsUpdate"}, "DynamipsSlot": {"type": "string", "enum": ["C7200-IO-2FE", "C7200-IO-FE", "C7200-IO-GE-E", "NM-16ESW", "NM-1E", "NM-1FE-TX", "NM-4E", "NM-4T", "PA-2FE-TX", "PA-4E", "PA-4T+", "PA-8E", "PA-8T", "PA-A1", "PA-FE-TX", "PA-GE", "PA-POS-OC3", "C2600-MB-2FE", "C2600-MB-1E", "C1700-MB-1FE", "C2600-MB-2E", "C2600-MB-1FE", "C1700-MB-WIC1", "GT96100-FE", "Leopard-2FE", ""], "title": "DynamipsSlot", "description": "Dynamips slot enum"}, "DynamipsWic": {"type": "string", "enum": ["WIC-1ENET", "WIC-1T", "WIC-2T", ""], "title": "DynamipsWic", "description": "Dynamips WIC enum"}, "Engine": {"type": "string", "enum": ["vmware", "virtualbox", "hyper-v", "none"], "title": "Engine", "description": "\"The engine to use for the GNS3 VM."}, "ErrorMessage": {"properties": {"message": {"type": "string", "title": "Message"}}, "type": "object", "required": ["message"], "title": "ErrorMessage", "description": "Error message."}, "EthernetPortInfo": {"properties": {"node_id": {"type": "string", "format": "uuid", "title": "Node Id"}, "interface": {"type": "string", "title": "Interface"}, "type": {"type": "string", "title": "Type"}}, "type": "object", "required": ["node_id", "interface", "type"], "title": "EthernetPortInfo", "description": "Ethernet port information."}, "ExtraConfig": {"properties": {"target": {"type": "string", "title": "Target", "description": "Absolute path inside the container where the file is mounted"}, "content": {"type": "string", "title": "Content", "description": "File content written by GNS3 and bind-mounted read-only into the container", "default": ""}}, "type": "object", "required": ["target"], "title": "ExtraConfig", "description": "A configuration file injected into a Docker container.\n\nGNS3 writes ``content`` to a host file and bind-mounts it read-only at\n``target`` inside the container. Used to seed NOS startup configs (e.g.\nXRd first-boot config, FRR frr.conf) without rebuilding the image."}, "GNS3VM": {"properties": {"enable": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Enable", "description": "Enable/disable the GNS3 VM"}, "vmname": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Vmname", "description": "GNS3 VM name"}, "when_exit": {"anyOf": [{"$ref": "#/components/schemas/WhenExit"}, {"type": "null"}], "description": "Action when the GNS3 VM exits"}, "headless": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Headless", "description": "Start the GNS3 VM GUI or not"}, "engine": {"anyOf": [{"$ref": "#/components/schemas/Engine"}, {"type": "null"}], "description": "The engine to use for the GNS3 VM"}, "allocate_vcpus_ram": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Allocate Vcpus Ram", "description": "Allocate vCPUS and RAM settings"}, "vcpus": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Vcpus", "description": "Number of CPUs to allocate for the GNS3 VM"}, "ram": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Ram", "description": "Amount of memory to allocate for the GNS3 VM"}, "port": {"anyOf": [{"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Port"}}, "type": "object", "title": "GNS3VM", "description": "GNS3 VM data."}, "HTTPValidationError": {"properties": {"detail": {"items": {"$ref": "#/components/schemas/ValidationError"}, "type": "array", "title": "Detail"}}, "type": "object", "title": "HTTPValidationError"}, "IOULicense": {"properties": {"iourc_content": {"type": "string", "title": "Iourc Content", "description": "Content of iourc file"}, "license_check": {"type": "boolean", "title": "License Check", "description": "Whether the license must be checked or not"}}, "type": "object", "required": ["iourc_content", "license_check"], "title": "IOULicense"}, "IOUSettingsResponse": {"properties": {"iourc_path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Iourc Path", "description": "Path of your .iourc file, the file is searched in $HOME/.iourc if not provided"}, "license_check": {"type": "boolean", "title": "License Check", "description": "Validate the iourc license file (if disabled, IOU will not start and no errors will be shown when the license is invalid)", "default": true}}, "type": "object", "title": "IOUSettingsResponse"}, "IOUSettingsUpdate": {"properties": {"iourc_path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Iourc Path"}, "license_check": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "License Check"}}, "additionalProperties": false, "type": "object", "title": "IOUSettingsUpdate"}, "Image": {"properties": {"filename": {"type": "string", "title": "Filename", "description": "Image filename"}, "path": {"type": "string", "title": "Path", "description": "Image path"}, "image_type": {"$ref": "#/components/schemas/ImageType", "description": "Image type"}, "image_size": {"type": "integer", "title": "Image Size", "description": "Image size in bytes"}, "checksum": {"type": "string", "title": "Checksum", "description": "Checksum value"}, "checksum_algorithm": {"type": "string", "title": "Checksum Algorithm", "description": "Checksum algorithm"}, "created_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Created At"}, "updated_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Updated At"}}, "type": "object", "required": ["filename", "path", "image_type", "image_size", "checksum", "checksum_algorithm"], "title": "Image"}, "ImageType": {"type": "string", "enum": ["qemu", "ios", "iou"], "title": "ImageType"}, "Iou": {"properties": {"ethernet_adapters": {"type": "integer", "title": "Number of Ethernet adapters"}, "serial_adapters": {"type": "integer", "title": "Number of serial adapters"}, "nvram": {"type": "integer", "title": "Host NVRAM"}, "ram": {"type": "integer", "title": "Host RAM"}, "startup_config": {"type": "string", "title": "Config loaded at startup"}}, "type": "object", "required": ["ethernet_adapters", "serial_adapters", "nvram", "ram", "startup_config"], "title": "Iou", "description": "IOU configuration for v1-6"}, "IouPropertiesV8": {"properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name of the template"}, "category": {"anyOf": [{"$ref": "#/components/schemas/gns3server__schemas__controller__appliances__Category"}, {"type": "null"}], "title": "Category of the template"}, "default_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default name format"}, "usage": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "How to use the template"}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Symbol of the template"}, "ethernet_adapters": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Number of ethernet adapters"}, "serial_adapters": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Number of serial adapters"}, "ram": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Host RAM"}, "nvram": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Host NVRAM"}, "startup_config": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Config loaded at startup"}}, "type": "object", "title": "IouPropertiesV8", "description": "IOU template properties (v8)"}, "Kvm": {"type": "string", "enum": ["require", "allow", "disable"], "title": "Kvm", "description": "KVM requirements enum"}, "LLMModelConfigCreate": {"properties": {"name": {"type": "string", "maxLength": 100, "minLength": 1, "title": "Name", "description": "Configuration name"}, "model_type": {"type": "string", "enum": ["text", "vision", "stt", "tts", "multimodal", "embedding", "reranking", "other"], "title": "Model Type", "description": "Model type"}, "is_default": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Is Default", "description": "Set as default configuration", "default": false}, "provider": {"type": "string", "title": "Provider", "description": "LLM provider"}, "base_url": {"type": "string", "title": "Base Url", "description": "API base URL"}, "model": {"type": "string", "title": "Model", "description": "Model name"}, "temperature": {"type": "number", "maximum": 2.0, "minimum": 0.0, "title": "Temperature", "default": 0.7}, "api_key": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Api Key"}, "max_tokens": {"anyOf": [{"type": "integer", "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Max Tokens"}, "context_limit": {"type": "integer", "exclusiveMinimum": 0.0, "title": "Context Limit", "description": "Model context window limit in K tokens (e.g., 128 = 128K tokens)"}, "context_strategy": {"type": "string", "enum": ["conservative", "balanced", "aggressive"], "title": "Context Strategy", "description": "Context trimming strategy", "default": "balanced"}, "copilot_mode": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Copilot Mode", "description": "GNS3-Copilot mode: 'teaching_assistant' or 'lab_automation_assistant'"}}, "additionalProperties": true, "type": "object", "required": ["name", "model_type", "provider", "base_url", "model", "context_limit"], "title": "LLMModelConfigCreate", "description": "Request to create a new LLM model configuration."}, "LLMModelConfigDataWithoutSecret": {"properties": {"provider": {"type": "string", "title": "Provider", "description": "LLM provider (e.g., 'openai', 'anthropic', 'ollama')"}, "base_url": {"type": "string", "title": "Base Url", "description": "API base URL"}, "model": {"type": "string", "title": "Model", "description": "Model name"}, "temperature": {"type": "number", "maximum": 2.0, "minimum": 0.0, "title": "Temperature", "description": "Temperature parameter", "default": 0.7}, "api_key": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Api Key", "description": "API key (always hidden in API responses)"}, "max_tokens": {"anyOf": [{"type": "integer", "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Max Tokens", "description": "Max tokens for generation"}, "context_limit": {"type": "integer", "exclusiveMinimum": 0.0, "title": "Context Limit", "description": "Model context window limit in K tokens (e.g., 128 = 128K tokens)"}, "context_strategy": {"type": "string", "enum": ["conservative", "balanced", "aggressive"], "title": "Context Strategy", "description": "Context trimming strategy: conservative (60%), balanced (75%), aggressive (85%)", "default": "balanced"}, "copilot_mode": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Copilot Mode", "description": "GNS3-Copilot mode: 'teaching_assistant' or 'lab_automation_assistant'"}}, "additionalProperties": true, "type": "object", "required": ["provider", "base_url", "model", "context_limit"], "title": "LLMModelConfigDataWithoutSecret", "description": "LLM model configuration data WITHOUT sensitive information."}, "LLMModelConfigInheritedResponse": {"properties": {"configs": {"items": {"$ref": "#/components/schemas/LLMModelConfigWithSource"}, "type": "array", "title": "Configs"}, "default_config": {"anyOf": [{"$ref": "#/components/schemas/LLMModelConfigWithSource"}, {"type": "null"}]}, "total": {"type": "integer", "title": "Total"}}, "type": "object", "required": ["configs", "total"], "title": "LLMModelConfigInheritedResponse", "description": "Response containing user's effective configs (own + inherited from groups)."}, "LLMModelConfigListResponse": {"properties": {"configs": {"items": {"$ref": "#/components/schemas/LLMModelConfigResponse"}, "type": "array", "title": "Configs"}, "default_config": {"anyOf": [{"$ref": "#/components/schemas/LLMModelConfigResponse"}, {"type": "null"}]}, "total": {"type": "integer", "title": "Total"}}, "type": "object", "required": ["configs", "total"], "title": "LLMModelConfigListResponse", "description": "Response containing a list of model configurations with default."}, "LLMModelConfigResponse": {"properties": {"created_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Created At"}, "updated_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Updated At"}, "config_id": {"type": "string", "format": "uuid", "title": "Config Id"}, "name": {"type": "string", "title": "Name"}, "model_type": {"type": "string", "enum": ["text", "vision", "stt", "tts", "multimodal", "embedding", "reranking", "other"], "title": "Model Type"}, "config": {"$ref": "#/components/schemas/LLMModelConfigDataWithoutSecret"}, "user_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "User Id"}, "group_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Group Id"}, "is_default": {"type": "boolean", "title": "Is Default"}, "version": {"type": "integer", "title": "Version", "description": "Optimistic locking version"}}, "type": "object", "required": ["config_id", "name", "model_type", "config", "is_default", "version"], "title": "LLMModelConfigResponse", "description": "LLM model configuration response (without API key for security)."}, "LLMModelConfigUpdate": {"properties": {"name": {"anyOf": [{"type": "string", "maxLength": 100, "minLength": 1}, {"type": "null"}], "title": "Name"}, "model_type": {"anyOf": [{"type": "string", "enum": ["text", "vision", "stt", "tts", "multimodal", "embedding", "reranking", "other"]}, {"type": "null"}], "title": "Model Type"}, "is_default": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Is Default"}, "expected_version": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Expected Version", "description": "Expected version for optimistic locking"}, "provider": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Provider"}, "base_url": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Base Url"}, "model": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Model"}, "temperature": {"anyOf": [{"type": "number", "maximum": 2.0, "minimum": 0.0}, {"type": "null"}], "title": "Temperature"}, "api_key": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Api Key"}, "max_tokens": {"anyOf": [{"type": "integer"}, {"type": "string"}, {"type": "null"}], "title": "Max Tokens", "description": "Max tokens for generation (can be null)"}, "context_limit": {"anyOf": [{"type": "integer", "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Context Limit", "description": "Model context window limit in K tokens (e.g., 128 = 128K tokens)"}, "context_strategy": {"anyOf": [{"type": "string", "enum": ["conservative", "balanced", "aggressive"]}, {"type": "null"}], "title": "Context Strategy", "description": "Context trimming strategy"}, "copilot_mode": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Copilot Mode", "description": "GNS3-Copilot mode: 'teaching_assistant' or 'lab_automation_assistant'"}}, "additionalProperties": true, "type": "object", "title": "LLMModelConfigUpdate", "description": "Request to update an existing LLM model configuration."}, "LLMModelConfigWithSource": {"properties": {"created_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Created At"}, "updated_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Updated At"}, "config_id": {"type": "string", "format": "uuid", "title": "Config Id"}, "name": {"type": "string", "title": "Name"}, "model_type": {"type": "string", "enum": ["text", "vision", "stt", "tts", "multimodal", "embedding", "reranking", "other"], "title": "Model Type"}, "config": {"$ref": "#/components/schemas/LLMModelConfigDataWithoutSecret"}, "user_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "User Id"}, "group_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Group Id"}, "is_default": {"type": "boolean", "title": "Is Default"}, "version": {"type": "integer", "title": "Version"}, "source": {"type": "string", "title": "Source", "description": "Source: 'user' or 'group'"}, "group_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Group Name", "description": "Group name if source is 'group'"}}, "additionalProperties": true, "type": "object", "required": ["config_id", "name", "model_type", "config", "is_default", "version", "source"], "title": "LLMModelConfigWithSource", "description": "Model configuration with source information (for inheritance, without API key for security)."}, "Label": {"properties": {"text": {"type": "string", "title": "Text"}, "style": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Style", "description": "SVG style attribute. Apply default style if null"}, "x": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "X", "description": "Relative X position of the label. Center it if null"}, "y": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Y", "description": "Relative Y position of the label"}, "rotation": {"anyOf": [{"type": "integer", "maximum": 360.0, "minimum": -359.0}, {"type": "null"}], "title": "Rotation", "description": "Rotation of the label"}}, "type": "object", "required": ["text"], "title": "Label", "description": "Label data."}, "Link": {"properties": {"nodes": {"anyOf": [{"items": {"$ref": "#/components/schemas/LinkNode"}, "type": "array", "maxItems": 2, "minItems": 0}, {"type": "null"}], "title": "Nodes"}, "suspend": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Suspend"}, "link_style": {"anyOf": [{"$ref": "#/components/schemas/LinkStyle"}, {"type": "null"}]}, "filters": {"anyOf": [{"additionalProperties": true, "type": "object"}, {"type": "null"}], "title": "Filters"}, "markers": {"anyOf": [{"additionalProperties": true, "type": "object"}, {"type": "null"}], "title": "Markers", "description": "Traffic-insight markers on this link: name \u2192 {bpf, tag, enabled}"}, "show_filters_icon": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Filters Icon", "description": "Show filters icon in Web UI", "default": true}, "link_id": {"type": "string", "format": "uuid", "title": "Link Id"}, "project_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Project Id"}, "link_type": {"anyOf": [{"$ref": "#/components/schemas/gns3server__schemas__controller__links__LinkType"}, {"type": "null"}]}, "capturing": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Capturing", "description": "Read only property. True if a capture running on the link"}, "capture_file_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Capture File Name", "description": "Read only property. The name of the capture file if a capture is running"}, "capture_file_path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Capture File Path", "description": "Read only property. The full path of the capture file if a capture is running"}, "capture_compute_id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Capture Compute Id", "description": "Read only property. The compute identifier where a capture is running"}, "wireshark": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Wireshark", "description": "Read only property. True if a Web Wireshark session is active on the link", "default": false}}, "type": "object", "required": ["link_id"], "title": "Link"}, "LinkCapture": {"properties": {"data_link_type": {"type": "string", "title": "Data Link Type", "default": "DLT_EN10MB"}, "capture_file_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Capture File Name"}, "wireshark": {"type": "boolean", "title": "Wireshark", "default": false}}, "type": "object", "title": "LinkCapture", "description": "Link capture data."}, "LinkCreate": {"properties": {"nodes": {"items": {"$ref": "#/components/schemas/LinkNode"}, "type": "array", "maxItems": 2, "minItems": 2, "title": "Nodes"}, "suspend": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Suspend"}, "link_style": {"anyOf": [{"$ref": "#/components/schemas/LinkStyle"}, {"type": "null"}]}, "filters": {"anyOf": [{"additionalProperties": true, "type": "object"}, {"type": "null"}], "title": "Filters"}, "markers": {"anyOf": [{"additionalProperties": true, "type": "object"}, {"type": "null"}], "title": "Markers", "description": "Traffic-insight markers on this link: name \u2192 {bpf, tag, enabled}"}, "show_filters_icon": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Filters Icon", "description": "Show filters icon in Web UI", "default": true}, "link_id": {"type": "string", "format": "uuid", "title": "Link Id"}}, "type": "object", "required": ["nodes"], "title": "LinkCreate"}, "LinkNode": {"properties": {"node_id": {"type": "string", "format": "uuid", "title": "Node Id"}, "adapter_number": {"type": "integer", "title": "Adapter Number"}, "port_number": {"type": "integer", "title": "Port Number"}, "label": {"anyOf": [{"$ref": "#/components/schemas/Label"}, {"type": "null"}]}}, "type": "object", "required": ["node_id", "adapter_number", "port_number"], "title": "LinkNode", "description": "Link node data."}, "LinkStyle": {"properties": {"color": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Color"}, "width": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Width"}, "type": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Type"}, "link_type": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Link Type"}, "bezier_curviness": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Bezier Curviness"}, "flowchart_roundness": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Flowchart Roundness"}, "control_offset": {"anyOf": [{"prefixItems": [{"type": "number"}, {"type": "number"}], "type": "array", "maxItems": 2, "minItems": 2}, {"type": "null"}], "title": "Control Offset"}}, "type": "object", "title": "LinkStyle"}, "LinkUpdate": {"properties": {"nodes": {"anyOf": [{"items": {"$ref": "#/components/schemas/LinkNode"}, "type": "array", "maxItems": 2, "minItems": 0}, {"type": "null"}], "title": "Nodes"}, "suspend": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Suspend"}, "link_style": {"anyOf": [{"$ref": "#/components/schemas/LinkStyle"}, {"type": "null"}]}, "filters": {"anyOf": [{"additionalProperties": true, "type": "object"}, {"type": "null"}], "title": "Filters"}, "markers": {"anyOf": [{"additionalProperties": true, "type": "object"}, {"type": "null"}], "title": "Markers", "description": "Traffic-insight markers on this link: name \u2192 {bpf, tag, enabled}"}, "show_filters_icon": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Filters Icon", "description": "Show filters icon in Web UI", "default": true}}, "type": "object", "title": "LinkUpdate"}, "LoggedInUserUpdate": {"properties": {"password": {"anyOf": [{"type": "string", "maxLength": 100, "minLength": 8, "format": "password", "writeOnly": true}, {"type": "null"}], "title": "Password"}, "email": {"anyOf": [{"type": "string", "format": "email"}, {"type": "null"}], "title": "Email"}, "full_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Full Name"}}, "type": "object", "title": "LoggedInUserUpdate", "description": "Properties to update a logged-in user."}, "MarkerCreate": {"properties": {"name": {"anyOf": [{"type": "string", "maxLength": 32, "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]*$"}, {"type": "null"}], "title": "Name", "description": "Unique marker name on the link. Auto-generated when absent."}, "bpf": {"type": "string", "title": "Bpf"}, "tag": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Tag"}, "link_id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Link Id"}, "color": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Color", "description": "User-chosen hex color for this marker in the Web UI, e.g. '#ff5722'"}, "highlight_duration": {"anyOf": [{"type": "integer", "minimum": 1.0}, {"type": "null"}], "title": "Highlight Duration", "description": "How long (milliseconds) the Web UI keeps this marker highlighted after a match. Omitted = use the UI default. Pure render hint \u2014 stored on the link, never sent to uBridge."}, "enabled": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Enabled", "description": "Whether the marker is active. Defaults to true on creation."}, "direction": {"anyOf": [{"type": "string", "pattern": "^(tx|rx|both)$"}, {"type": "null"}], "title": "Direction", "description": "Direction filter: 'tx' = capture node sending only, 'rx' = capture node receiving only, 'both' or null = both directions."}, "capture_node_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Capture Node Id", "description": "Which endpoint's uBridge hosts this marker (the 'observer'). tx/rx in `direction` are interpreted from this node's perspective. Must be one of the link's two endpoints and a marker-capable type. Omitted = server auto-picks (first started marker-capable endpoint)."}, "data_link_type": {"type": "string", "title": "Data Link Type", "description": "pcap link-layer type the marker's BPF compiles against and its capture file is written with (a uBridge `linktype` token). Defaults to DLT_EN10MB (Ethernet), which is omitted from the uBridge command. Only meaningful for serial links: set it to the matching serial DLT from the port's data_link_types \u2014 DLT_C_HDLC / DLT_PPP_SERIAL / DLT_FRELAY / DLT_ATM_RFC1483 \u2014 so the BPF offsets and pcap decode match the encapsulation configured in IOS. Create-only (changing it would invalidate the pcap).", "default": "DLT_EN10MB"}}, "type": "object", "required": ["bpf"], "title": "MarkerCreate", "description": "Body for attaching a traffic-insight marker to a link.\n\n``name`` is optional at the controller REST layer (auto-generated when\nabsent) but always set when the controller forwards to the compute."}, "MarkerDefinitionCreate": {"properties": {"name": {"anyOf": [{"type": "string", "maxLength": 32, "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]*$"}, {"type": "null"}], "title": "Name", "description": "Unique definition name. Auto-generated when absent."}, "bpf": {"type": "string", "title": "Bpf"}, "tag": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Tag"}, "color": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Color", "description": "User-chosen hex color for the marker in the Web UI, e.g. '#ff5722'"}, "highlight_duration": {"anyOf": [{"type": "integer", "minimum": 1.0}, {"type": "null"}], "title": "Highlight Duration", "description": "How long (milliseconds) the Web UI keeps this marker highlighted after a match. Omitted = use the UI default. Pure render hint \u2014 stored with the definition, never sent to uBridge."}, "direction": {"anyOf": [{"type": "string", "pattern": "^(tx|rx|both)$"}, {"type": "null"}], "title": "Direction", "description": "Direction filter: 'tx' = capture node sending only, 'rx' = capture node receiving only, 'both' or null = both directions."}, "data_link_type": {"type": "string", "title": "Data Link Type", "description": "pcap link-layer type for inherited markers on serial links (uBridge `linktype`). Defaults to DLT_EN10MB (Ethernet): the definition then applies only to Ethernet links and serial links are skipped. Set a serial DLT \u2014 DLT_C_HDLC / DLT_PPP_SERIAL / DLT_FRELAY / DLT_ATM_RFC1483 \u2014 to also cover serial links with that encapsulation; Ethernet links stay EN10MB regardless. Changing it re-fans-out.", "default": "DLT_EN10MB"}}, "type": "object", "required": ["bpf"], "title": "MarkerDefinitionCreate", "description": "Body for creating / updating a project-level marker definition.\n\nThe definition is a template \u2014 when applied to a link the marker name is\nprefixed with ``global-`` (e.g. ``arp`` \u2192 ``global-arp``) so it can never\ncollide with a per-link private marker."}, "MarkerUpdate": {"properties": {"bpf": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Bpf"}, "tag": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Tag"}, "direction": {"anyOf": [{"type": "string", "pattern": "^(tx|rx|both)$"}, {"type": "null"}], "title": "Direction", "description": "Direction filter; 'both' or an explicit null clears it to both. Omit to keep."}, "color": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Color", "description": "Hex color render hint, e.g. '#ff5722'"}, "highlight_duration": {"anyOf": [{"type": "integer", "minimum": 1.0}, {"type": "null"}], "title": "Highlight Duration", "description": "UI highlight duration in ms; null = UI default"}, "enabled": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Enabled", "description": "Toggle the marker on/off (instant)."}}, "type": "object", "title": "MarkerUpdate", "description": "Body for updating a marker \u2014 partial update, every field optional.\n\n``bpf`` is optional here (it is required on create). ``capture_node_id`` and\n``name`` are create-only / path-driven and intentionally absent; an explicit\n``direction: null`` clears the direction back to both (omitting keeps it)."}, "NetmikoDeviceType": {"properties": {"name": {"type": "string", "title": "Name", "description": "Device type name to store in the netmiko_device_type field"}, "telnet": {"type": "boolean", "title": "Telnet", "description": "Whether the device type connects over Telnet", "default": false}, "custom": {"type": "boolean", "title": "Custom", "description": "Whether the device type is a GNS3-copilot custom driver (gns3_ prefix)", "default": false}}, "type": "object", "required": ["name"], "title": "NetmikoDeviceType", "description": "A Netmiko device type supported by the installed Netmiko library."}, "NetmikoDeviceTypeList": {"properties": {"netmiko_version": {"type": "string", "title": "Netmiko Version", "description": "Version of the installed Netmiko library"}, "device_types": {"items": {"$ref": "#/components/schemas/NetmikoDeviceType"}, "type": "array", "title": "Device Types", "description": "Supported device types, sorted by name"}}, "type": "object", "required": ["netmiko_version", "device_types"], "title": "NetmikoDeviceTypeList", "description": "List of Netmiko device types supported by the installed Netmiko library."}, "Node": {"properties": {"compute_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "string"}], "title": "Compute Id"}, "name": {"type": "string", "title": "Name"}, "node_type": {"$ref": "#/components/schemas/NodeType"}, "node_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Node Id"}, "console": {"anyOf": [{"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Console", "description": "Console TCP port"}, "console_type": {"anyOf": [{"$ref": "#/components/schemas/ConsoleType"}, {"type": "null"}]}, "console_auto_start": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Console Auto Start", "description": "Automatically start the console when the node has started", "default": false}, "netmiko_device_type": {"anyOf": [{"type": "string", "pattern": "^[a-z0-9_]+$|^$"}, {"type": "null"}], "title": "Netmiko Device Type", "description": "Device type for Netmiko-based automation tools, overrides the template value"}, "default_username": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default Username", "description": "Default username to log into the node, seeded from the template appliance metadata"}, "default_password": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default Password", "description": "Default password to log into the node, seeded from the template appliance metadata"}, "aux": {"anyOf": [{"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Aux", "description": "Auxiliary console TCP port"}, "aux_type": {"anyOf": [{"$ref": "#/components/schemas/ConsoleType"}, {"type": "null"}]}, "properties": {"anyOf": [{"additionalProperties": true, "type": "object"}, {"type": "null"}], "title": "Properties", "description": "Properties specific to an emulator"}, "label": {"anyOf": [{"$ref": "#/components/schemas/Label"}, {"type": "null"}]}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Symbol"}, "x": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "X", "default": 0}, "y": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Y", "default": 0}, "z": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Z", "default": 1}, "locked": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Locked", "description": "Whether the element locked or not", "default": false}, "port_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Port Name Format", "description": "Formatting for port name {0} will be replace by port number"}, "port_segment_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Port Segment Size", "description": "Size of the port segment"}, "first_port_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "First Port Name", "description": "Name of the first port"}, "custom_adapters": {"anyOf": [{"items": {"$ref": "#/components/schemas/CustomAdapter"}, "type": "array"}, {"type": "null"}], "title": "Custom Adapters"}, "tags": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "Tags", "description": "User-defined metadata tags (e.g. 'vendor:cisco' or 'model:7200')"}, "template_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Template Id", "description": "Template UUID from which the node has been created. Read only"}, "project_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Project Id"}, "node_directory": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Node Directory", "description": "Working directory of the node. Read only"}, "status": {"anyOf": [{"$ref": "#/components/schemas/NodeStatus"}, {"type": "null"}], "description": "Node status. Read only"}, "command_line": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Command Line", "description": "Command line use to start the node. Read only"}, "width": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Width", "description": "Width of the node. Read only"}, "height": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Height", "description": "Height of the node. Read only"}, "ports": {"anyOf": [{"items": {"$ref": "#/components/schemas/NodePort"}, "type": "array"}, {"type": "null"}], "title": "Ports", "description": "List of node ports. Read only"}, "console_host": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Console Host", "description": "Console host. Warning if the host is 0.0.0.0 or :: (listen on all interfaces) you need to use the same address you use to connect to the controller"}}, "type": "object", "required": ["compute_id", "name", "node_type"], "title": "Node"}, "NodeCreate": {"properties": {"compute_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "string"}], "title": "Compute Id"}, "name": {"type": "string", "title": "Name"}, "node_type": {"$ref": "#/components/schemas/NodeType"}, "node_id": {"type": "string", "format": "uuid", "title": "Node Id"}, "console": {"anyOf": [{"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Console", "description": "Console TCP port"}, "console_type": {"anyOf": [{"$ref": "#/components/schemas/ConsoleType"}, {"type": "null"}]}, "console_auto_start": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Console Auto Start", "description": "Automatically start the console when the node has started", "default": false}, "netmiko_device_type": {"anyOf": [{"type": "string", "pattern": "^[a-z0-9_]+$|^$"}, {"type": "null"}], "title": "Netmiko Device Type", "description": "Device type for Netmiko-based automation tools, overrides the template value"}, "default_username": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default Username", "description": "Default username to log into the node, seeded from the template appliance metadata"}, "default_password": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default Password", "description": "Default password to log into the node, seeded from the template appliance metadata"}, "aux": {"anyOf": [{"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Aux", "description": "Auxiliary console TCP port"}, "aux_type": {"anyOf": [{"$ref": "#/components/schemas/ConsoleType"}, {"type": "null"}]}, "properties": {"anyOf": [{"additionalProperties": true, "type": "object"}, {"type": "null"}], "title": "Properties", "description": "Properties specific to an emulator"}, "label": {"anyOf": [{"$ref": "#/components/schemas/Label"}, {"type": "null"}]}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Symbol"}, "x": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "X", "default": 0}, "y": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Y", "default": 0}, "z": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Z", "default": 1}, "locked": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Locked", "description": "Whether the element locked or not", "default": false}, "port_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Port Name Format", "description": "Formatting for port name {0} will be replace by port number"}, "port_segment_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Port Segment Size", "description": "Size of the port segment"}, "first_port_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "First Port Name", "description": "Name of the first port"}, "custom_adapters": {"anyOf": [{"items": {"$ref": "#/components/schemas/CustomAdapter"}, "type": "array"}, {"type": "null"}], "title": "Custom Adapters"}, "tags": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "Tags", "description": "User-defined metadata tags (e.g. 'vendor:cisco' or 'model:7200')"}}, "type": "object", "required": ["compute_id", "name", "node_type"], "title": "NodeCreate"}, "NodeDuplicate": {"properties": {"x": {"type": "integer", "title": "X"}, "y": {"type": "integer", "title": "Y"}, "z": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Z", "default": 0}}, "type": "object", "required": ["x", "y"], "title": "NodeDuplicate", "description": "Data to duplicate a node."}, "NodeFile": {"properties": {"path": {"type": "string", "title": "Path", "description": "File name"}, "size": {"type": "integer", "title": "Size", "description": "File size in bytes"}, "created_at": {"type": "string", "title": "Created At", "description": "File creation time (ISO 8601)"}, "modified_at": {"type": "string", "title": "Modified At", "description": "File modification time (ISO 8601)"}, "file_type": {"type": "string", "title": "File Type", "description": "File type determined by the file command"}}, "type": "object", "required": ["path", "size", "created_at", "modified_at", "file_type"], "title": "NodeFile", "description": "Detailed file information for node files."}, "NodePort": {"properties": {"name": {"type": "string", "title": "Name", "description": "Port name"}, "short_name": {"type": "string", "title": "Short Name", "description": "Port name"}, "adapter_number": {"type": "integer", "title": "Adapter Number", "description": "Adapter slot"}, "adapter_type": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Adapter Type", "description": "Adapter type"}, "port_number": {"type": "integer", "title": "Port Number", "description": "Port slot"}, "link_type": {"$ref": "#/components/schemas/gns3server__schemas__controller__nodes__LinkType", "description": "Type of link"}, "data_link_types": {"additionalProperties": true, "type": "object", "title": "Data Link Types", "description": "Available PCAP types for capture"}, "mac_address": {"anyOf": [{"type": "string", "pattern": "^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$"}, {"type": "null"}], "title": "Mac Address"}}, "type": "object", "required": ["name", "short_name", "adapter_number", "port_number", "link_type", "data_link_types"], "title": "NodePort", "description": "Node port data."}, "NodeStatus": {"type": "string", "enum": ["stopped", "started", "suspended"], "title": "NodeStatus", "description": "Supported node statuses."}, "NodeType": {"type": "string", "enum": ["cloud", "nat", "ethernet_hub", "ethernet_switch", "frame_relay_switch", "atm_switch", "docker", "dynamips", "vpcs", "virtualbox", "vmware", "iou", "qemu"], "title": "NodeType", "description": "Supported node types."}, "NodeUpdate": {"properties": {"compute_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "string"}, {"type": "null"}], "title": "Compute Id"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "node_type": {"anyOf": [{"$ref": "#/components/schemas/NodeType"}, {"type": "null"}]}, "node_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Node Id"}, "console": {"anyOf": [{"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Console", "description": "Console TCP port"}, "console_type": {"anyOf": [{"$ref": "#/components/schemas/ConsoleType"}, {"type": "null"}]}, "console_auto_start": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Console Auto Start", "description": "Automatically start the console when the node has started", "default": false}, "netmiko_device_type": {"anyOf": [{"type": "string", "pattern": "^[a-z0-9_]+$|^$"}, {"type": "null"}], "title": "Netmiko Device Type", "description": "Device type for Netmiko-based automation tools, overrides the template value"}, "default_username": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default Username", "description": "Default username to log into the node, seeded from the template appliance metadata"}, "default_password": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default Password", "description": "Default password to log into the node, seeded from the template appliance metadata"}, "aux": {"anyOf": [{"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Aux", "description": "Auxiliary console TCP port"}, "aux_type": {"anyOf": [{"$ref": "#/components/schemas/ConsoleType"}, {"type": "null"}]}, "properties": {"anyOf": [{"additionalProperties": true, "type": "object"}, {"type": "null"}], "title": "Properties", "description": "Properties specific to an emulator"}, "label": {"anyOf": [{"$ref": "#/components/schemas/Label"}, {"type": "null"}]}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Symbol"}, "x": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "X", "default": 0}, "y": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Y", "default": 0}, "z": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Z", "default": 1}, "locked": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Locked", "description": "Whether the element locked or not", "default": false}, "port_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Port Name Format", "description": "Formatting for port name {0} will be replace by port number"}, "port_segment_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Port Segment Size", "description": "Size of the port segment"}, "first_port_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "First Port Name", "description": "Name of the first port"}, "custom_adapters": {"anyOf": [{"items": {"$ref": "#/components/schemas/CustomAdapter"}, "type": "array"}, {"type": "null"}], "title": "Custom Adapters"}, "tags": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "Tags", "description": "User-defined metadata tags (e.g. 'vendor:cisco' or 'model:7200')"}}, "type": "object", "title": "NodeUpdate", "description": "Data to update a node."}, "OpenAIMessage": {"properties": {"id": {"type": "string", "title": "Id", "description": "Message ID"}, "role": {"type": "string", "enum": ["user", "assistant", "system", "tool"], "title": "Role", "description": "Message role"}, "content": {"type": "string", "title": "Content", "description": "Message content"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name", "description": "Tool message name"}, "tool_call_id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Tool Call Id", "description": "Associated tool call ID (for tool messages)"}, "tool_calls": {"anyOf": [{"items": {"$ref": "#/components/schemas/OpenAIToolCall"}, "type": "array"}, {"type": "null"}], "title": "Tool Calls", "description": "Tool calls (for assistant messages)"}, "metadata": {"additionalProperties": true, "type": "object", "title": "Metadata", "description": "Message metadata (includes created_at)"}}, "type": "object", "required": ["id", "role", "content"], "title": "OpenAIMessage", "description": "Message model for conversation history."}, "OpenAIToolCall": {"properties": {"id": {"type": "string", "title": "Id", "description": "Tool call ID"}, "type": {"type": "string", "const": "function", "title": "Type", "description": "Tool call type", "default": "function"}, "function": {"additionalProperties": true, "type": "object", "title": "Function", "description": "Function name and arguments"}}, "type": "object", "required": ["id", "function"], "title": "OpenAIToolCall", "description": "Tool call information (OpenAI compatible format)."}, "Privilege": {"properties": {"name": {"type": "string", "title": "Name"}, "description": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Description"}, "created_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Created At"}, "updated_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Updated At"}, "privilege_id": {"type": "string", "format": "uuid", "title": "Privilege Id"}}, "type": "object", "required": ["name", "privilege_id"], "title": "Privilege"}, "Project": {"properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "project_id": {"type": "string", "format": "uuid", "title": "Project Id"}, "path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Path", "description": "Project directory"}, "auto_close": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Close", "description": "Close project when last client leaves"}, "auto_open": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Open", "description": "Project opens when GNS3 starts"}, "auto_start": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Start", "description": "Project starts when opened"}, "scene_height": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Scene Height", "description": "Height of the drawing area"}, "scene_width": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Scene Width", "description": "Width of the drawing area"}, "zoom": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Zoom", "description": "Zoom of the drawing area"}, "show_layers": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Layers", "description": "Show layers on the drawing area"}, "snap_to_grid": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Snap To Grid", "description": "Snap to grid on the drawing area"}, "show_grid": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Grid", "description": "Show the grid on the drawing area"}, "grid_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Grid Size", "description": "Grid size for the drawing area for nodes"}, "drawing_grid_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Drawing Grid Size", "description": "Grid size for the drawing area for drawings"}, "show_interface_labels": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Interface Labels", "description": "Show interface labels on the drawing area"}, "supplier": {"anyOf": [{"$ref": "#/components/schemas/Supplier"}, {"type": "null"}], "description": "Supplier of the project"}, "variables": {"anyOf": [{"items": {"$ref": "#/components/schemas/Variable"}, "type": "array"}, {"type": "null"}], "title": "Variables", "description": "Variables required to run the project"}, "status": {"anyOf": [{"$ref": "#/components/schemas/ProjectStatus"}, {"type": "null"}]}, "filename": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Filename"}, "created_by": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Created By", "description": "Username of the user who created the project"}}, "type": "object", "required": ["project_id"], "title": "Project"}, "ProjectCompression": {"type": "string", "enum": ["none", "zip", "bzip2", "lzma", "zstd"], "title": "ProjectCompression", "description": "Supported project compression."}, "ProjectCreate": {"properties": {"name": {"type": "string", "title": "Name"}, "project_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Project Id"}, "path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Path", "description": "Project directory"}, "auto_close": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Close", "description": "Close project when last client leaves"}, "auto_open": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Open", "description": "Project opens when GNS3 starts"}, "auto_start": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Start", "description": "Project starts when opened"}, "scene_height": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Scene Height", "description": "Height of the drawing area"}, "scene_width": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Scene Width", "description": "Width of the drawing area"}, "zoom": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Zoom", "description": "Zoom of the drawing area"}, "show_layers": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Layers", "description": "Show layers on the drawing area"}, "snap_to_grid": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Snap To Grid", "description": "Snap to grid on the drawing area"}, "show_grid": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Grid", "description": "Show the grid on the drawing area"}, "grid_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Grid Size", "description": "Grid size for the drawing area for nodes"}, "drawing_grid_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Drawing Grid Size", "description": "Grid size for the drawing area for drawings"}, "show_interface_labels": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Interface Labels", "description": "Show interface labels on the drawing area"}, "supplier": {"anyOf": [{"$ref": "#/components/schemas/Supplier"}, {"type": "null"}], "description": "Supplier of the project"}, "variables": {"anyOf": [{"items": {"$ref": "#/components/schemas/Variable"}, "type": "array"}, {"type": "null"}], "title": "Variables", "description": "Variables required to run the project"}}, "type": "object", "required": ["name"], "title": "ProjectCreate", "description": "Properties for project creation."}, "ProjectDuplicate": {"properties": {"name": {"type": "string", "title": "Name"}, "project_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Project Id"}, "path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Path", "description": "Project directory"}, "auto_close": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Close", "description": "Close project when last client leaves"}, "auto_open": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Open", "description": "Project opens when GNS3 starts"}, "auto_start": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Start", "description": "Project starts when opened"}, "scene_height": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Scene Height", "description": "Height of the drawing area"}, "scene_width": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Scene Width", "description": "Width of the drawing area"}, "zoom": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Zoom", "description": "Zoom of the drawing area"}, "show_layers": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Layers", "description": "Show layers on the drawing area"}, "snap_to_grid": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Snap To Grid", "description": "Snap to grid on the drawing area"}, "show_grid": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Grid", "description": "Show the grid on the drawing area"}, "grid_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Grid Size", "description": "Grid size for the drawing area for nodes"}, "drawing_grid_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Drawing Grid Size", "description": "Grid size for the drawing area for drawings"}, "show_interface_labels": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Interface Labels", "description": "Show interface labels on the drawing area"}, "supplier": {"anyOf": [{"$ref": "#/components/schemas/Supplier"}, {"type": "null"}], "description": "Supplier of the project"}, "variables": {"anyOf": [{"items": {"$ref": "#/components/schemas/Variable"}, "type": "array"}, {"type": "null"}], "title": "Variables", "description": "Variables required to run the project"}, "reset_mac_addresses": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Reset Mac Addresses", "description": "Reset MAC addresses for this project", "default": false}}, "type": "object", "required": ["name"], "title": "ProjectDuplicate", "description": "Properties for project duplication."}, "ProjectStatus": {"type": "string", "enum": ["opened", "closed"], "title": "ProjectStatus", "description": "Supported project statuses."}, "ProjectUpdate": {"properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "project_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Project Id"}, "path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Path", "description": "Project directory"}, "auto_close": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Close", "description": "Close project when last client leaves"}, "auto_open": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Open", "description": "Project opens when GNS3 starts"}, "auto_start": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Start", "description": "Project starts when opened"}, "scene_height": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Scene Height", "description": "Height of the drawing area"}, "scene_width": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Scene Width", "description": "Width of the drawing area"}, "zoom": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Zoom", "description": "Zoom of the drawing area"}, "show_layers": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Layers", "description": "Show layers on the drawing area"}, "snap_to_grid": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Snap To Grid", "description": "Snap to grid on the drawing area"}, "show_grid": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Grid", "description": "Show the grid on the drawing area"}, "grid_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Grid Size", "description": "Grid size for the drawing area for nodes"}, "drawing_grid_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Drawing Grid Size", "description": "Grid size for the drawing area for drawings"}, "show_interface_labels": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Show Interface Labels", "description": "Show interface labels on the drawing area"}, "supplier": {"anyOf": [{"$ref": "#/components/schemas/Supplier"}, {"type": "null"}], "description": "Supplier of the project"}, "variables": {"anyOf": [{"items": {"$ref": "#/components/schemas/Variable"}, "type": "array"}, {"type": "null"}], "title": "Variables", "description": "Variables required to run the project"}}, "type": "object", "title": "ProjectUpdate", "description": "Properties for project update."}, "Protocol": {"type": "string", "enum": ["http", "https"], "title": "Protocol", "description": "Protocol supported to communicate with a compute."}, "Qemu": {"properties": {"adapter_type": {"$ref": "#/components/schemas/QemuAdapterType", "title": "Type of network adapter"}, "adapters": {"type": "integer", "title": "Number of adapters"}, "ram": {"type": "integer", "title": "RAM allocated to the appliance (MB)"}, "cpus": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Number of Virtual CPU"}, "hda_disk_interface": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskInterface"}, {"type": "null"}], "title": "Disk interface for the installed hda_disk_image"}, "hdb_disk_interface": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskInterface"}, {"type": "null"}], "title": "Disk interface for the installed hdb_disk_image"}, "hdc_disk_interface": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskInterface"}, {"type": "null"}], "title": "Disk interface for the installed hdc_disk_image"}, "hdd_disk_interface": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskInterface"}, {"type": "null"}], "title": "Disk interface for the installed hdd_disk_image"}, "arch": {"$ref": "#/components/schemas/QemuPlatform", "title": "Architecture emulated"}, "console_type": {"$ref": "#/components/schemas/QemuConsoleType", "title": "Type of console connection for the administration of the appliance"}, "boot_priority": {"anyOf": [{"$ref": "#/components/schemas/QemuBootPriority"}, {"type": "null"}], "title": "Disk boot priority"}, "kernel_command_line": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Command line parameters sent to the kernel"}, "kvm": {"$ref": "#/components/schemas/Kvm", "title": "KVM requirements"}, "options": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Optional additional qemu command line options"}, "cpu_throttling": {"anyOf": [{"type": "number", "maximum": 100.0, "minimum": 0.0}, {"type": "null"}], "title": "Throttle the CPU"}, "on_close": {"anyOf": [{"$ref": "#/components/schemas/QemuOnClose"}, {"type": "null"}], "title": "Action to execute on the VM is closed"}, "process_priority": {"anyOf": [{"$ref": "#/components/schemas/QemuProcessPriority"}, {"type": "null"}], "title": "Process priority for QEMU"}}, "type": "object", "required": ["adapter_type", "adapters", "ram", "arch", "console_type", "kvm"], "title": "Qemu", "description": "QEMU configuration for v1-6"}, "QemuAdapterType": {"type": "string", "enum": ["e1000", "i82550", "i82551", "i82557a", "i82557b", "i82557c", "i82558a", "i82558b", "i82559a", "i82559b", "i82559c", "i82559er", "i82562", "i82801", "igb", "ne2k_pci", "pcnet", "rtl8139", "virtio", "virtio-net-pci", "vmxnet3"], "title": "QemuAdapterType", "description": "Qemu adapter type enum"}, "QemuBootPriority": {"type": "string", "enum": ["c", "d", "n", "cn", "cd", "dn", "dc", "nc", "nd"], "title": "QemuBootPriority", "description": "Boot priority enum"}, "QemuConsoleType": {"type": "string", "enum": ["telnet", "ssh", "vnc", "spice", "spice+agent", "none"], "title": "QemuConsoleType", "description": "Qemu console type enum"}, "QemuDiskImageAdapterType": {"type": "string", "enum": ["ide", "lsilogic", "buslogic", "legacyESX"], "title": "QemuDiskImageAdapterType", "description": "Supported Qemu disk image on/off options."}, "QemuDiskImageCreate": {"properties": {"format": {"$ref": "#/components/schemas/QemuDiskImageFormat", "description": "Image format type"}, "size": {"type": "integer", "title": "Size", "description": "Image size in Megabytes"}, "preallocation": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImagePreallocation"}, {"type": "null"}]}, "cluster_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Cluster Size"}, "refcount_bits": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Refcount Bits"}, "lazy_refcounts": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImageOnOff"}, {"type": "null"}]}, "subformat": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImageSubformat"}, {"type": "null"}]}, "static": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImageOnOff"}, {"type": "null"}]}, "zeroed_grain": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImageOnOff"}, {"type": "null"}]}, "adapter_type": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImageAdapterType"}, {"type": "null"}]}}, "type": "object", "required": ["format", "size"], "title": "QemuDiskImageCreate"}, "QemuDiskImageFormat": {"type": "string", "enum": ["qcow2", "qcow", "vpc", "vdi", "vdmk", "raw"], "title": "QemuDiskImageFormat", "description": "Supported Qemu disk image formats."}, "QemuDiskImageOnOff": {"type": "string", "enum": ["on", "off"], "title": "QemuDiskImageOnOff", "description": "Supported Qemu image on/off options."}, "QemuDiskImagePreallocation": {"type": "string", "enum": ["off", "metadata", "falloc", "full"], "title": "QemuDiskImagePreallocation", "description": "Supported Qemu disk image pre-allocation options."}, "QemuDiskImageSubformat": {"type": "string", "enum": ["dynamic", "fixed", "streamOptimized", "twoGbMaxExtentSparse", "twoGbMaxExtentFlat", "monolithicSparse", "monolithicFlat"], "title": "QemuDiskImageSubformat", "description": "Supported Qemu disk image sub-format options."}, "QemuDiskImageUpdate": {"properties": {"format": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImageFormat"}, {"type": "null"}], "description": "Image format type"}, "size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Size", "description": "Image size in Megabytes"}, "preallocation": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImagePreallocation"}, {"type": "null"}]}, "cluster_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Cluster Size"}, "refcount_bits": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Refcount Bits"}, "lazy_refcounts": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImageOnOff"}, {"type": "null"}]}, "subformat": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImageSubformat"}, {"type": "null"}]}, "static": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImageOnOff"}, {"type": "null"}]}, "zeroed_grain": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImageOnOff"}, {"type": "null"}]}, "adapter_type": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskImageAdapterType"}, {"type": "null"}]}, "extend": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Extend", "description": "Number of Megabytes to extend the image"}}, "type": "object", "title": "QemuDiskImageUpdate"}, "QemuDiskInterface": {"type": "string", "enum": ["ide", "sata", "nvme", "scsi", "sd", "mtd", "floppy", "pflash", "virtio", "none"], "title": "QemuDiskInterface", "description": "Disk interface enum"}, "QemuOnClose": {"type": "string", "enum": ["power_off", "shutdown_signal", "save_vm_state"], "title": "QemuOnClose", "description": "Qemu on_close action enum"}, "QemuPlatform": {"type": "string", "enum": ["aarch64", "alpha", "arm", "cris", "i386", "lm32", "m68k", "microblaze", "microblazeel", "mips", "mips64", "mips64el", "mipsel", "moxie", "or32", "ppc", "ppc64", "ppcemb", "s390x", "sh4", "sh4eb", "sparc", "sparc64", "tricore", "unicore32", "x86_64", "xtensa", "xtensaeb"], "title": "QemuPlatform", "description": "Qemu platform enum"}, "QemuProcessPriority": {"type": "string", "enum": ["realtime", "very high", "high", "normal", "low", "very low"], "title": "QemuProcessPriority", "description": "Qemu process priority enum"}, "QemuPropertiesV8": {"properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name of the template"}, "category": {"anyOf": [{"$ref": "#/components/schemas/gns3server__schemas__controller__appliances__Category"}, {"type": "null"}], "title": "Category of the template"}, "default_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default name format"}, "usage": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "How to use the template"}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Symbol of the template"}, "adapter_type": {"anyOf": [{"$ref": "#/components/schemas/QemuAdapterType"}, {"type": "null"}], "title": "Type of network adapter"}, "adapters": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Number of adapters"}, "custom_adapters": {"anyOf": [{"items": {"$ref": "#/components/schemas/CustomAdapterItem"}, "type": "array"}, {"type": "null"}], "title": "Custom adapters"}, "first_port_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Optional name of the first networking port example: eth0"}, "port_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Optional formating of the networking port example: eth{0}"}, "port_segment_size": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Optional port segment size. A port segment is a block of port. For example Ethernet0/0 Ethernet0/1 is the module 0 with a port segment size of 2"}, "linked_clone": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "False if you don't want to use a single image for all nodes"}, "ram": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Ram allocated to the appliance (MB)"}, "cpus": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Number of Virtual CPU"}, "hda_disk_interface": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskInterface"}, {"type": "null"}], "title": "Disk interface for the installed hda_disk_image"}, "hdb_disk_interface": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskInterface"}, {"type": "null"}], "title": "Disk interface for the installed hdb_disk_image"}, "hdc_disk_interface": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskInterface"}, {"type": "null"}], "title": "Disk interface for the installed hdc_disk_image"}, "hdd_disk_interface": {"anyOf": [{"$ref": "#/components/schemas/QemuDiskInterface"}, {"type": "null"}], "title": "Disk interface for the installed hdd_disk_image"}, "platform": {"anyOf": [{"$ref": "#/components/schemas/QemuPlatform"}, {"type": "null"}], "title": "Platform to emulate"}, "console_type": {"anyOf": [{"$ref": "#/components/schemas/QemuConsoleType"}, {"type": "null"}], "title": "Type of console connection for the administration of the appliance"}, "boot_priority": {"anyOf": [{"$ref": "#/components/schemas/QemuBootPriority"}, {"type": "null"}], "title": "Optional define the disk boot priory. Refer to -boot option in qemu manual for more details."}, "kernel_command_line": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Command line parameters send to the kernel"}, "kvm": {"anyOf": [{"$ref": "#/components/schemas/Kvm"}, {"type": "null"}], "title": "KVM requirements"}, "options": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Optional additional qemu command line options"}, "cpu_throttling": {"anyOf": [{"type": "integer", "maximum": 800.0, "minimum": 0.0}, {"type": "null"}], "title": "Throttle the CPU"}, "tpm": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Enable the Trusted Platform Module (TPM)"}, "uefi": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Enable the UEFI boot mode"}, "on_close": {"anyOf": [{"$ref": "#/components/schemas/QemuOnClose"}, {"type": "null"}], "title": "Action to execute on the VM is closed"}, "process_priority": {"anyOf": [{"$ref": "#/components/schemas/QemuProcessPriority"}, {"type": "null"}], "title": "Process priority for QEMU"}}, "type": "object", "title": "QemuPropertiesV8", "description": "Qemu template properties (v8)"}, "QemuSettings": {"properties": {"enable_monitor": {"type": "boolean", "title": "Enable Monitor", "description": "Use the Qemu monitor feature to communicate with Qemu VMs", "default": true}, "monitor_host": {"type": "string", "title": "Monitor Host", "description": "IP used to listen for the monitor", "default": "127.0.0.1"}, "enable_hardware_acceleration": {"type": "boolean", "title": "Enable Hardware Acceleration", "description": "Enable hardware acceleration (KVM)", "default": true}, "require_hardware_acceleration": {"type": "boolean", "title": "Require Hardware Acceleration", "description": "Require hardware acceleration in order to start VMs", "default": false}, "allow_unsafe_options": {"type": "boolean", "title": "Allow Unsafe Options", "description": "Allow unsafe additional command line options", "default": false}, "ovmf_firmware_dir": {"type": "string", "title": "Ovmf Firmware Dir", "description": "Path to the OVMF firmware directory", "default": "/usr/share/OVMF"}}, "type": "object", "title": "QemuSettings"}, "QemuSettingsUpdate": {"properties": {"enable_monitor": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Enable Monitor"}, "monitor_host": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Monitor Host"}, "enable_hardware_acceleration": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Enable Hardware Acceleration"}, "require_hardware_acceleration": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Require Hardware Acceleration"}, "allow_unsafe_options": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Allow Unsafe Options"}, "ovmf_firmware_dir": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Ovmf Firmware Dir"}}, "additionalProperties": false, "type": "object", "title": "QemuSettingsUpdate"}, "RefreshTokenRequest": {"properties": {"refresh_token": {"type": "string", "title": "Refresh Token"}}, "type": "object", "required": ["refresh_token"], "title": "RefreshTokenRequest", "description": "Schema for requesting a token refresh."}, "RenameSession": {"properties": {"title": {"type": "string", "maxLength": 255, "minLength": 1, "title": "Title", "description": "New session title"}}, "type": "object", "required": ["title"], "title": "RenameSession", "description": "Rename session request model."}, "Resource": {"properties": {"resource_id": {"type": "string", "format": "uuid", "title": "Resource Id"}, "resource_type": {"$ref": "#/components/schemas/ResourceType", "description": "Type of the resource"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "created_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Created At"}, "updated_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Updated At"}}, "type": "object", "required": ["resource_id", "resource_type"], "title": "Resource"}, "ResourcePool": {"properties": {"name": {"type": "string", "title": "Name"}, "created_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Created At"}, "updated_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Updated At"}, "resource_pool_id": {"type": "string", "format": "uuid", "title": "Resource Pool Id"}}, "type": "object", "required": ["name", "resource_pool_id"], "title": "ResourcePool"}, "ResourcePoolCreate": {"properties": {"name": {"type": "string", "title": "Name"}}, "type": "object", "required": ["name"], "title": "ResourcePoolCreate", "description": "Properties to create a resource pool."}, "ResourcePoolUpdate": {"properties": {"name": {"type": "string", "title": "Name"}}, "type": "object", "required": ["name"], "title": "ResourcePoolUpdate", "description": "Properties to update a resource pool."}, "ResourceType": {"type": "string", "enum": ["project"], "title": "ResourceType"}, "Role": {"properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "description": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Description"}, "created_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Created At"}, "updated_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Updated At"}, "role_id": {"type": "string", "format": "uuid", "title": "Role Id"}, "is_builtin": {"type": "boolean", "title": "Is Builtin"}, "privileges": {"items": {"$ref": "#/components/schemas/Privilege"}, "type": "array", "title": "Privileges"}}, "type": "object", "required": ["role_id", "is_builtin", "privileges"], "title": "Role"}, "RoleCreate": {"properties": {"name": {"type": "string", "title": "Name"}, "description": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Description"}}, "type": "object", "required": ["name"], "title": "RoleCreate", "description": "Properties to create a role."}, "RoleUpdate": {"properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "description": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Description"}}, "type": "object", "title": "RoleUpdate", "description": "Properties to update a role."}, "ServerProtocol": {"type": "string", "enum": ["http", "https"], "title": "ServerProtocol"}, "ServerSettingsResponse": {"properties": {"local": {"type": "boolean", "title": "Local", "description": "Local server mode, set by the --local command line argument (not meant to be set by hand)", "default": false}, "enable_http_auth": {"type": "boolean", "title": "Enable Http Auth", "description": "Enable compute HTTP authentication", "default": true}, "name": {"type": "string", "title": "Name", "description": "Server name, default is what is returned by socket.gethostname()", "default": "guobin.localhost (controller)"}, "protocol": {"$ref": "#/components/schemas/ServerProtocol", "description": "Protocol used by the server: http or https", "default": "http"}, "host": {"type": "string", "title": "Host", "description": "IP address where the server listens for connections", "default": "0.0.0.0"}, "port": {"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0, "title": "Port", "description": "HTTP port used to control the server", "default": 3080}, "secrets_dir": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Secrets Dir", "description": "Directory where secrets are stored (e.g. the JWT secret key)"}, "certfile": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Certfile", "description": "SSL certificate file, requires enable_ssl"}, "certkey": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Certkey", "description": "SSL key file, requires enable_ssl"}, "enable_ssl": {"type": "boolean", "title": "Enable Ssl", "description": "Enable SSL encryption", "default": false}, "images_path": {"type": "string", "title": "Images Path", "description": "Path where binary images are stored", "default": "~/GNS3/images"}, "projects_path": {"type": "string", "title": "Projects Path", "description": "Path where user projects are stored", "default": "~/GNS3/projects"}, "appliances_path": {"type": "string", "title": "Appliances Path", "description": "Path where custom user appliances are stored", "default": "~/GNS3/appliances"}, "symbols_path": {"type": "string", "title": "Symbols Path", "description": "Path where custom user symbols are stored", "default": "~/GNS3/symbols"}, "configs_path": {"type": "string", "title": "Configs Path", "description": "Path where custom user configs are stored", "default": "~/GNS3/configs"}, "resources_path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Resources Path", "description": "Path where files like built-in appliances and Docker resources are stored (defaults to the local user data directory)"}, "default_symbol_theme": {"$ref": "#/components/schemas/BuiltinSymbolTheme", "description": "Default symbol theme, e.g. \"Classic\" or \"Affinity-square-blue\"", "default": "Affinity-square-blue"}, "allow_raw_images": {"type": "boolean", "title": "Allow Raw Images", "description": "Allow raw images to be uploaded to the server", "default": true}, "auto_discover_images": {"type": "boolean", "title": "Auto Discover Images", "description": "Automatically discover images in the images directory", "default": true}, "report_errors": {"type": "boolean", "title": "Report Errors", "description": "Automatically send crash reports to the GNS3 team", "default": true}, "additional_images_paths": {"items": {"type": "string"}, "type": "array", "title": "Additional Images Paths", "description": "Additional paths to look for images (semicolon-separated in the configuration file)"}, "console_start_port_range": {"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0, "title": "Console Start Port Range", "description": "First console port of the range allocated to devices", "default": 5000}, "console_end_port_range": {"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0, "title": "Console End Port Range", "description": "Last console port of the range allocated to devices", "default": 10000}, "vnc_console_start_port_range": {"type": "integer", "maximum": 65535.0, "minimum": 5900.0, "title": "Vnc Console Start Port Range", "description": "First VNC console port of the range allocated to devices", "default": 5900}, "vnc_console_end_port_range": {"type": "integer", "maximum": 65535.0, "minimum": 5900.0, "title": "Vnc Console End Port Range", "description": "Last VNC console port of the range allocated to devices", "default": 10000}, "udp_start_port_range": {"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0, "title": "Udp Start Port Range", "description": "First UDP port of the range allocated for inter-device communication (two ports per link)", "default": 10000}, "udp_end_port_range": {"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0, "title": "Udp End Port Range", "description": "Last UDP port of the range allocated for inter-device communication (two ports per link)", "default": 30000}, "ubridge_path": {"type": "string", "title": "Ubridge Path", "description": "uBridge executable location, default: search in PATH", "default": "ubridge"}, "ubridge_control_transport": {"$ref": "#/components/schemas/UbridgeControlTransport", "description": "uBridge control channel transport: \"unix\" (AF_UNIX + SO_PEERCRED, recommended on Linux) or \"tcp\" (loopback, kept for backward compatibility)", "default": "unix"}, "marker_listen_host": {"type": "string", "title": "Marker Listen Host", "description": "Marker (traffic-insight) UDP sink listen host: one listener per compute process receives uBridge MARK signals from every uBridge on this host", "default": "127.0.0.1"}, "marker_listen_port": {"type": "integer", "maximum": 65535.0, "minimum": 0.0, "title": "Marker Listen Port", "description": "Marker UDP sink listen port (0 lets the operating system choose a free port)", "default": 3070}, "compute_username": {"type": "string", "title": "Compute Username", "description": "Username for compute HTTP authentication, \"gns3\" is the default", "default": "gns3"}, "compute_password": {"type": "string", "format": "password", "title": "Compute Password", "description": "Password for compute HTTP authentication, a randomly generated password is used if not set", "default": "", "writeOnly": true}, "allowed_interfaces": {"items": {"type": "string"}, "type": "array", "title": "Allowed Interfaces", "description": "Only allow these interfaces to be used by GNS3, for the Cloud node for example (comma-separated; do not forget virbr0 for the NAT node to work)"}, "default_nat_interface": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default Nat Interface", "description": "Interface used by the NAT node, default is virbr0 on Linux (requires libvirt)"}, "allow_remote_console": {"type": "boolean", "title": "Allow Remote Console", "description": "Allow console connections from remote machines (console ports only accept local connections by default)", "default": false}, "enable_builtin_templates": {"type": "boolean", "title": "Enable Builtin Templates", "description": "Enable the built-in templates", "default": true}, "install_builtin_appliances": {"type": "boolean", "title": "Install Builtin Appliances", "description": "Install the built-in appliances", "default": true}, "skills_repo_url": {"type": "string", "title": "Skills Repo Url", "description": "Git repository URL for the external GNS3 Copilot skills (injection skills, prompts and device skills)", "default": "https://github.com/gns3/gns3-skills.git"}, "skills_repo_branch": {"type": "string", "title": "Skills Repo Branch", "description": "Git branch of the skills repository", "default": "main"}, "skills_auto_update": {"type": "boolean", "title": "Skills Auto Update", "description": "Automatically pull updates from the skills repository when reloading", "default": true}, "mcp_enable_dns_rebinding_protection": {"type": "boolean", "title": "Mcp Enable Dns Rebinding Protection", "description": "Enable MCP transport DNS rebinding protection (allowed hosts and origins must be configured)", "default": false}, "mcp_allowed_hosts": {"items": {"type": "string"}, "type": "array", "title": "Mcp Allowed Hosts", "description": "Allowed hosts for MCP connections, only \"host:*\" port wildcards are supported (e.g. \"127.0.0.1:*\")"}, "mcp_allowed_origins": {"items": {"type": "string"}, "type": "array", "title": "Mcp Allowed Origins", "description": "Allowed origins for MCP connections (e.g. \"http://localhost:*\")"}}, "type": "object", "title": "ServerSettingsResponse"}, "ServerSettingsUpdate": {"properties": {"local": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Local"}, "enable_http_auth": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Enable Http Auth"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "protocol": {"anyOf": [{"$ref": "#/components/schemas/ServerProtocol"}, {"type": "null"}]}, "host": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Host"}, "port": {"anyOf": [{"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Port"}, "secrets_dir": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Secrets Dir"}, "certfile": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Certfile"}, "certkey": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Certkey"}, "enable_ssl": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Enable Ssl"}, "images_path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Images Path"}, "projects_path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Projects Path"}, "appliances_path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Appliances Path"}, "symbols_path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Symbols Path"}, "configs_path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Configs Path"}, "resources_path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Resources Path"}, "default_symbol_theme": {"anyOf": [{"$ref": "#/components/schemas/BuiltinSymbolTheme"}, {"type": "null"}]}, "allow_raw_images": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Allow Raw Images"}, "auto_discover_images": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Auto Discover Images"}, "report_errors": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Report Errors"}, "additional_images_paths": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "Additional Images Paths"}, "console_start_port_range": {"anyOf": [{"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Console Start Port Range"}, "console_end_port_range": {"anyOf": [{"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Console End Port Range"}, "vnc_console_start_port_range": {"anyOf": [{"type": "integer", "maximum": 65535.0, "minimum": 5900.0}, {"type": "null"}], "title": "Vnc Console Start Port Range"}, "vnc_console_end_port_range": {"anyOf": [{"type": "integer", "maximum": 65535.0, "minimum": 5900.0}, {"type": "null"}], "title": "Vnc Console End Port Range"}, "udp_start_port_range": {"anyOf": [{"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Udp Start Port Range"}, "udp_end_port_range": {"anyOf": [{"type": "integer", "maximum": 65535.0, "exclusiveMinimum": 0.0}, {"type": "null"}], "title": "Udp End Port Range"}, "ubridge_path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Ubridge Path"}, "ubridge_control_transport": {"anyOf": [{"$ref": "#/components/schemas/UbridgeControlTransport"}, {"type": "null"}]}, "marker_listen_host": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Marker Listen Host"}, "marker_listen_port": {"anyOf": [{"type": "integer", "maximum": 65535.0, "minimum": 0.0}, {"type": "null"}], "title": "Marker Listen Port"}, "compute_username": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Compute Username"}, "compute_password": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Compute Password"}, "allowed_interfaces": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "Allowed Interfaces"}, "default_nat_interface": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default Nat Interface"}, "allow_remote_console": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Allow Remote Console"}, "enable_builtin_templates": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Enable Builtin Templates"}, "install_builtin_appliances": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Install Builtin Appliances"}, "skills_repo_url": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Skills Repo Url"}, "skills_repo_branch": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Skills Repo Branch"}, "skills_auto_update": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Skills Auto Update"}, "mcp_enable_dns_rebinding_protection": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Mcp Enable Dns Rebinding Protection"}, "mcp_allowed_hosts": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "Mcp Allowed Hosts"}, "mcp_allowed_origins": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "Mcp Allowed Origins"}}, "additionalProperties": false, "type": "object", "title": "ServerSettingsUpdate", "description": "Every field optional: JSON null removes the option from the configuration\nfile (restoring its default), missing fields are left untouched."}, "SettingsResponse": {"properties": {"Server": {"$ref": "#/components/schemas/ServerSettingsResponse"}, "Controller": {"$ref": "#/components/schemas/ControllerSettingsResponse"}, "VPCS": {"$ref": "#/components/schemas/VPCSSettings"}, "Dynamips": {"$ref": "#/components/schemas/DynamipsSettings"}, "IOU": {"$ref": "#/components/schemas/IOUSettingsResponse"}, "Qemu": {"$ref": "#/components/schemas/QemuSettings"}, "WebWireshark": {"$ref": "#/components/schemas/WebWiresharkSettings"}}, "type": "object", "required": ["Server", "Controller", "VPCS", "Dynamips", "IOU", "Qemu", "WebWireshark"], "title": "SettingsResponse"}, "SettingsUpdate": {"properties": {"Server": {"anyOf": [{"$ref": "#/components/schemas/ServerSettingsUpdate"}, {"type": "null"}]}, "Controller": {"anyOf": [{"$ref": "#/components/schemas/ControllerSettingsUpdate"}, {"type": "null"}]}, "VPCS": {"anyOf": [{"$ref": "#/components/schemas/VPCSSettingsUpdate"}, {"type": "null"}]}, "Dynamips": {"anyOf": [{"$ref": "#/components/schemas/DynamipsSettingsUpdate"}, {"type": "null"}]}, "IOU": {"anyOf": [{"$ref": "#/components/schemas/IOUSettingsUpdate"}, {"type": "null"}]}, "Qemu": {"anyOf": [{"$ref": "#/components/schemas/QemuSettingsUpdate"}, {"type": "null"}]}, "WebWireshark": {"anyOf": [{"$ref": "#/components/schemas/WebWiresharkSettingsUpdate"}, {"type": "null"}]}}, "additionalProperties": false, "type": "object", "title": "SettingsUpdate"}, "SettingsUpdateResponse": {"properties": {"Server": {"$ref": "#/components/schemas/ServerSettingsResponse"}, "Controller": {"$ref": "#/components/schemas/ControllerSettingsResponse"}, "VPCS": {"$ref": "#/components/schemas/VPCSSettings"}, "Dynamips": {"$ref": "#/components/schemas/DynamipsSettings"}, "IOU": {"$ref": "#/components/schemas/IOUSettingsResponse"}, "Qemu": {"$ref": "#/components/schemas/QemuSettings"}, "WebWireshark": {"$ref": "#/components/schemas/WebWiresharkSettings"}, "restart_required": {"items": {"type": "string"}, "type": "array", "title": "Restart Required", "description": "Changed 'Section.option' settings that require a server restart to take effect"}}, "type": "object", "required": ["Server", "Controller", "VPCS", "Dynamips", "IOU", "Qemu", "WebWireshark"], "title": "SettingsUpdateResponse"}, "Snapshot": {"properties": {"name": {"type": "string", "title": "Name", "description": "Name of the snapshot"}, "description": {"type": "string", "title": "Description", "description": "Description of the snapshot"}, "snapshot_id": {"type": "string", "format": "uuid", "title": "Snapshot Id"}, "project_id": {"type": "string", "format": "uuid", "title": "Project Id"}, "filename": {"type": "string", "title": "Filename", "description": "Filename of the snapshot"}, "created_at": {"type": "integer", "title": "Created At", "description": "Date of the snapshot (UTC timestamp)"}}, "type": "object", "required": ["name", "description", "snapshot_id", "project_id", "filename", "created_at"], "title": "Snapshot"}, "SnapshotCreate": {"properties": {"name": {"type": "string", "title": "Name", "description": "Name of the snapshot"}, "description": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Description", "description": "Description of the snapshot"}}, "type": "object", "required": ["name"], "title": "SnapshotCreate", "description": "Properties for snapshot creation."}, "Status": {"type": "string", "enum": ["stable", "experimental", "broken"], "title": "Status", "description": "Appliance status enum"}, "Supplier": {"properties": {"logo": {"type": "string", "title": "Logo", "description": "Path to the project supplier logo"}, "url": {"anyOf": [{"type": "string", "maxLength": 2083, "minLength": 1, "format": "uri"}, {"type": "null"}], "title": "Url", "description": "URL to the project supplier site"}}, "type": "object", "required": ["logo"], "title": "Supplier"}, "Template": {"properties": {"template_id": {"type": "string", "format": "uuid", "title": "Template Id"}, "name": {"type": "string", "title": "Name"}, "version": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Version"}, "category": {"$ref": "#/components/schemas/gns3server__schemas__controller__templates__Category"}, "default_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default Name Format"}, "symbol": {"type": "string", "title": "Symbol"}, "template_type": {"$ref": "#/components/schemas/NodeType"}, "compute_id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Compute Id"}, "usage": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Usage", "default": ""}, "netmiko_device_type": {"anyOf": [{"type": "string", "pattern": "^[a-z0-9_]+$|^$"}, {"type": "null"}], "title": "Netmiko Device Type", "description": "Device type for Netmiko-based automation tools (e.g. 'cisco_xr' or 'nokia_srl')"}, "tags": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "Tags", "description": "User-defined metadata tags (e.g. 'vendor:cisco' or 'model:7200')"}, "appliance_metadata": {"anyOf": [{"$ref": "#/components/schemas/ApplianceMetadata"}, {"type": "null"}], "description": "Metadata inherited from the appliance the template was installed from"}, "created_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Created At"}, "updated_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Updated At"}, "builtin": {"type": "boolean", "title": "Builtin"}}, "additionalProperties": true, "type": "object", "required": ["template_id", "name", "category", "symbol", "template_type", "builtin"], "title": "Template"}, "TemplateCreate": {"properties": {"template_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Template Id"}, "name": {"type": "string", "title": "Name"}, "version": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Version"}, "category": {"anyOf": [{"$ref": "#/components/schemas/gns3server__schemas__controller__templates__Category"}, {"type": "null"}]}, "default_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default Name Format"}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Symbol"}, "template_type": {"$ref": "#/components/schemas/NodeType"}, "compute_id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Compute Id"}, "usage": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Usage", "default": ""}, "netmiko_device_type": {"anyOf": [{"type": "string", "pattern": "^[a-z0-9_]+$|^$"}, {"type": "null"}], "title": "Netmiko Device Type", "description": "Device type for Netmiko-based automation tools (e.g. 'cisco_xr' or 'nokia_srl')"}, "tags": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "Tags", "description": "User-defined metadata tags (e.g. 'vendor:cisco' or 'model:7200')"}, "appliance_metadata": {"anyOf": [{"$ref": "#/components/schemas/ApplianceMetadata"}, {"type": "null"}], "description": "Metadata inherited from the appliance the template was installed from"}}, "additionalProperties": true, "type": "object", "required": ["name", "template_type"], "title": "TemplateCreate", "description": "Properties to create a template."}, "TemplateSetting": {"properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name of the settings set"}, "default": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Whether these are the default settings"}, "inherit_default_properties": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Whether the default properties should be used", "default": true}, "template_type": {"$ref": "#/components/schemas/TemplateType", "title": "Type of emulator properties"}, "template_properties": {"anyOf": [{"$ref": "#/components/schemas/QemuPropertiesV8"}, {"$ref": "#/components/schemas/DynamipsPropertiesV8"}, {"$ref": "#/components/schemas/IouPropertiesV8"}, {"$ref": "#/components/schemas/DockerPropertiesV8"}], "title": "Properties for the template"}}, "type": "object", "required": ["template_type", "template_properties"], "title": "TemplateSetting", "description": "Emulator settings configuration (v8)"}, "TemplateType": {"type": "string", "enum": ["docker", "iou", "dynamips", "qemu"], "title": "TemplateType", "description": "Template type enum"}, "TemplateUpdate": {"properties": {"template_id": {"anyOf": [{"type": "string", "format": "uuid"}, {"type": "null"}], "title": "Template Id"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "version": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Version"}, "category": {"anyOf": [{"$ref": "#/components/schemas/gns3server__schemas__controller__templates__Category"}, {"type": "null"}]}, "default_name_format": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Default Name Format"}, "symbol": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Symbol"}, "template_type": {"anyOf": [{"$ref": "#/components/schemas/NodeType"}, {"type": "null"}]}, "compute_id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Compute Id"}, "usage": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Usage", "default": ""}, "netmiko_device_type": {"anyOf": [{"type": "string", "pattern": "^[a-z0-9_]+$|^$"}, {"type": "null"}], "title": "Netmiko Device Type", "description": "Device type for Netmiko-based automation tools (e.g. 'cisco_xr' or 'nokia_srl')"}, "tags": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "title": "Tags", "description": "User-defined metadata tags (e.g. 'vendor:cisco' or 'model:7200')"}, "appliance_metadata": {"anyOf": [{"$ref": "#/components/schemas/ApplianceMetadata"}, {"type": "null"}], "description": "Metadata inherited from the appliance the template was installed from"}}, "additionalProperties": true, "type": "object", "title": "TemplateUpdate"}, "TemplateUsage": {"properties": {"x": {"type": "integer", "title": "X"}, "y": {"type": "integer", "title": "Y"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name", "description": "Use this name to create a new node"}, "compute_id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Compute Id", "description": "Used if the template doesn't have a default compute"}}, "type": "object", "required": ["x", "y"], "title": "TemplateUsage"}, "Token": {"properties": {"access_token": {"type": "string", "title": "Access Token"}, "token_type": {"type": "string", "title": "Token Type"}, "refresh_token": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Refresh Token"}}, "type": "object", "required": ["access_token", "token_type"], "title": "Token"}, "UDPPortInfo": {"properties": {"node_id": {"type": "string", "format": "uuid", "title": "Node Id"}, "lport": {"type": "integer", "title": "Lport"}, "rhost": {"type": "string", "title": "Rhost"}, "rport": {"type": "integer", "title": "Rport"}, "type": {"type": "string", "title": "Type"}}, "type": "object", "required": ["node_id", "lport", "rhost", "rport", "type"], "title": "UDPPortInfo", "description": "UDP port information."}, "UbridgeControlTransport": {"type": "string", "enum": ["tcp", "unix"], "title": "UbridgeControlTransport"}, "User": {"properties": {"username": {"anyOf": [{"type": "string", "minLength": 3, "pattern": "[a-zA-Z0-9_-]+$"}, {"type": "null"}], "title": "Username"}, "is_active": {"type": "boolean", "title": "Is Active", "default": true}, "email": {"anyOf": [{"type": "string", "format": "email"}, {"type": "null"}], "title": "Email"}, "full_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Full Name"}, "created_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Created At"}, "updated_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Updated At"}, "user_id": {"type": "string", "format": "uuid", "title": "User Id"}, "last_login": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Last Login"}, "is_superadmin": {"type": "boolean", "title": "Is Superadmin", "default": false}}, "type": "object", "required": ["user_id"], "title": "User"}, "UserCreate": {"properties": {"username": {"type": "string", "minLength": 3, "pattern": "[a-zA-Z0-9_-]+$", "title": "Username"}, "is_active": {"type": "boolean", "title": "Is Active", "default": true}, "email": {"anyOf": [{"type": "string", "format": "email"}, {"type": "null"}], "title": "Email"}, "full_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Full Name"}, "password": {"type": "string", "maxLength": 100, "minLength": 8, "format": "password", "title": "Password", "writeOnly": true}}, "type": "object", "required": ["username", "password"], "title": "UserCreate", "description": "Properties to create a user."}, "UserGroup": {"properties": {"name": {"anyOf": [{"type": "string", "minLength": 3, "pattern": "[a-zA-Z0-9_-]+$"}, {"type": "null"}], "title": "Name"}, "created_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Created At"}, "updated_at": {"anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "title": "Updated At"}, "user_group_id": {"type": "string", "format": "uuid", "title": "User Group Id"}, "is_builtin": {"type": "boolean", "title": "Is Builtin"}}, "type": "object", "required": ["user_group_id", "is_builtin"], "title": "UserGroup"}, "UserGroupCreate": {"properties": {"name": {"anyOf": [{"type": "string", "minLength": 3, "pattern": "[a-zA-Z0-9_-]+$"}, {"type": "null"}], "title": "Name"}}, "type": "object", "required": ["name"], "title": "UserGroupCreate", "description": "Properties to create a user group."}, "UserGroupUpdate": {"properties": {"name": {"anyOf": [{"type": "string", "minLength": 3, "pattern": "[a-zA-Z0-9_-]+$"}, {"type": "null"}], "title": "Name"}}, "type": "object", "title": "UserGroupUpdate", "description": "Properties to update a user group."}, "UserUpdate": {"properties": {"username": {"anyOf": [{"type": "string", "minLength": 3, "pattern": "[a-zA-Z0-9_-]+$"}, {"type": "null"}], "title": "Username"}, "is_active": {"type": "boolean", "title": "Is Active", "default": true}, "email": {"anyOf": [{"type": "string", "format": "email"}, {"type": "null"}], "title": "Email"}, "full_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Full Name"}, "password": {"anyOf": [{"type": "string", "maxLength": 100, "minLength": 8, "format": "password", "writeOnly": true}, {"type": "null"}], "title": "Password"}}, "type": "object", "title": "UserUpdate", "description": "Properties to update a user."}, "VPCSSettings": {"properties": {"vpcs_path": {"type": "string", "title": "Vpcs Path", "description": "VPCS executable location, default: search in PATH", "default": "vpcs"}}, "type": "object", "title": "VPCSSettings"}, "VPCSSettingsUpdate": {"properties": {"vpcs_path": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Vpcs Path"}}, "additionalProperties": false, "type": "object", "title": "VPCSSettingsUpdate"}, "ValidationError": {"properties": {"loc": {"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]}, "type": "array", "title": "Location"}, "msg": {"type": "string", "title": "Message"}, "type": {"type": "string", "title": "Error Type"}, "input": {"title": "Input"}, "ctx": {"type": "object", "title": "Context"}}, "type": "object", "required": ["loc", "msg", "type"], "title": "ValidationError"}, "Variable": {"properties": {"name": {"type": "string", "title": "Name", "description": "Variable name"}, "value": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Value", "description": "Variable value"}}, "type": "object", "required": ["name"], "title": "Variable"}, "Version": {"properties": {"controller_host": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Controller Host", "description": "Controller hostname or IP address"}, "version": {"type": "string", "title": "Version", "description": "Version number"}, "local": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Local", "description": "Whether this is a local server or not"}}, "type": "object", "required": ["version"], "title": "Version"}, "WebWiresharkSettings": {"properties": {"enabled": {"type": "boolean", "title": "Enabled", "description": "Enable the Web Wireshark feature (container-based Wireshark in the browser)", "default": true}, "image": {"type": "string", "title": "Image", "description": "Docker image for the Web Wireshark containers", "default": "gns3/web-wireshark:latest"}, "network_subnet": {"type": "string", "title": "Network Subnet", "description": "Docker network subnet for the Web Wireshark containers (change it if it conflicts with your existing network)", "default": "172.31.0.0/22"}, "memory": {"type": "string", "title": "Memory", "description": "Memory limit per container (e.g. \"512m\", \"2g\")", "default": "2g"}, "cpus": {"type": "number", "title": "Cpus", "description": "CPU cores per container (e.g. 1.0, 2.0)", "default": 1.0}, "pids_limit": {"type": "integer", "title": "Pids Limit", "description": "Process limit per container", "default": 1000}}, "type": "object", "title": "WebWiresharkSettings"}, "WebWiresharkSettingsUpdate": {"properties": {"enabled": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Enabled"}, "image": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Image"}, "network_subnet": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Network Subnet"}, "memory": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Memory"}, "cpus": {"anyOf": [{"type": "number"}, {"type": "null"}], "title": "Cpus"}, "pids_limit": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Pids Limit"}}, "additionalProperties": false, "type": "object", "title": "WebWiresharkSettingsUpdate"}, "WhenExit": {"type": "string", "enum": ["stop", "suspend", "keep"], "title": "WhenExit", "description": "What to do with the VM when GNS3 VM exits."}, "gns3server__schemas__controller__appliances__Category": {"type": "string", "enum": ["router", "multilayer_switch", "switch", "firewall", "guest"], "title": "Category", "description": "Appliance category enum"}, "gns3server__schemas__controller__links__LinkType": {"type": "string", "enum": ["ethernet", "serial"], "title": "LinkType", "description": "Link type."}, "gns3server__schemas__controller__nodes__LinkType": {"type": "string", "enum": ["ethernet", "serial"], "title": "LinkType", "description": "Supported link types."}, "gns3server__schemas__controller__templates__Category": {"type": "string", "enum": ["router", "switch", "guest", "firewall"], "title": "Category", "description": "Supported categories"}}, "securitySchemes": {"OAuth2PasswordBearer": {"type": "oauth2", "flows": {"password": {"scopes": {}, "tokenUrl": "/v3/access/users/login"}}}}}} \ No newline at end of file diff --git a/gns3server/agent/__init__.py b/gns3server/agent/__init__.py index 1861ed37e..d914a912c 100644 --- a/gns3server/agent/__init__.py +++ b/gns3server/agent/__init__.py @@ -15,14 +15,15 @@ # along with this program. If not, see . """ -Agent module with optional AI Copilot support. +Agent module with optional AI Copilot and MCP support. -This module provides the AI Copilot functionality as an optional feature. -If the AI dependencies are not installed, the module will be disabled but -will not prevent the server from starting. +This module provides the AI Copilot and MCP (Model Context Protocol) +functionality as optional features. If the respective dependencies are +not installed, the affected features will be disabled but will not +prevent the server from starting. Installation: - pip install gns3-server[ai-copilot] + pip install gns3-server[ai-features] # Install all AI features """ import logging @@ -49,7 +50,8 @@ except ImportError as e: # AI dependencies not installed, disable AI Copilot feature logging.warning( f"AI Copilot dependencies not installed: {e}. " - "AI features will be disabled. Install with: pip install gns3-server[ai-copilot]" + "AI features will be disabled. " + "Install with: pip install gns3-server[ai-features]" ) AI_COPILOT_AVAILABLE = False @@ -63,7 +65,7 @@ except ImportError as e: """ raise RuntimeError( "AI Copilot is not available. " - "Install AI dependencies with: pip install gns3-server[ai-copilot]" + "Install AI dependencies with: pip install gns3-server[ai-features]" ) class ProjectAgentManager: @@ -74,12 +76,33 @@ except ImportError as e: def __init__(self): raise RuntimeError( "AI Copilot is not available. " - "Install AI dependencies with: pip install gns3-server[ai-copilot]" + "Install AI dependencies with: pip install gns3-server[ai-features]" ) +# Feature flag: MCP (Model Context Protocol) is available +MCP_AVAILABLE = False + +# Try to import MCP dependencies +try: + # Use importlib so the top-level SDK name "mcp" is not bound in this + # namespace — it would shadow the gns3server.agent.mcp subpackage. + import importlib + importlib.import_module("mcp.server.fastmcp") + MCP_AVAILABLE = True +except ImportError: + # MCP dependencies not installed, disable MCP feature + logging.warning( + "MCP dependencies not installed. " + "MCP features will be disabled. " + "Install with: pip install gns3-server[ai-features]" + ) + MCP_AVAILABLE = False + + __all__ = [ "AI_COPILOT_AVAILABLE", + "MCP_AVAILABLE", "get_project_agent_manager", "ProjectAgentManager", ] diff --git a/gns3server/agent/gns3_copilot/agent/gns3_copilot.py b/gns3server/agent/gns3_copilot/agent/gns3_copilot.py index a675d2074..b5173668c 100644 --- a/gns3server/agent/gns3_copilot/agent/gns3_copilot.py +++ b/gns3server/agent/gns3_copilot/agent/gns3_copilot.py @@ -88,6 +88,7 @@ from gns3server.agent.gns3_copilot.tools_v2 import ( ) from gns3server.agent.gns3_copilot.tools_v2 import GNS3CreateNodeTool from gns3server.agent.gns3_copilot.tools_v2 import GNS3LinkTool +from gns3server.agent.gns3_copilot.tools_v2 import GNS3PacketFilterTool from gns3server.agent.gns3_copilot.tools_v2 import GNS3StartNodeTool from gns3server.agent.gns3_copilot.tools_v2 import GNS3StopNodeTool from gns3server.agent.gns3_copilot.tools_v2 import GNS3SuspendNodeTool @@ -144,6 +145,7 @@ LAB_AUTOMATION_ASSISTANT_MODE_TOOLS = [ TROUBLESHOOTING_INJECTION_MODE_TOOLS = [ ExecuteMultipleDeviceCommands(), # Get device configurations (READ-ONLY) ExecuteMultipleDeviceConfigCommands(), # Inject configuration changes + GNS3PacketFilterTool(), # Manage packet filters on links (delay, loss, corrupt, etc.) InjectionSkillsTool(), # Query injection skills and fault types GNS3TopologyTool(), # Get topology information ] diff --git a/gns3server/agent/gns3_copilot/configs/skills_config.py b/gns3server/agent/gns3_copilot/configs/skills_config.py index 077b1e870..3a10332d9 100644 --- a/gns3server/agent/gns3_copilot/configs/skills_config.py +++ b/gns3server/agent/gns3_copilot/configs/skills_config.py @@ -34,7 +34,7 @@ from gns3server.config import Config # Default skills repository configuration SKILLS_CONFIG = { # Git repository URL for skills - "repo_url": "https://github.com/yueguobin/GNS3-Skills.git", + "repo_url": "https://github.com/gns3/gns3-skills.git", # Git branch to use "branch": "main", diff --git a/gns3server/agent/gns3_copilot/gns3_client/__init__.py b/gns3server/agent/gns3_copilot/gns3_client/__init__.py index 57b618f61..100ed500c 100644 --- a/gns3server/agent/gns3_copilot/gns3_client/__init__.py +++ b/gns3server/agent/gns3_copilot/gns3_client/__init__.py @@ -26,27 +26,27 @@ """ GNS3 Client Package -This package provides a Python interface for interacting with GNS3 servers. -Adapted from the upstream gns3fy project with modifications for compatibility -with langchain and reduced dependency conflicts. +This package provides the shared GNS3 REST client layer: -Main classes: -- Gns3Connector: Connector for GNS3 server API interaction -- Project: GNS3 Project management -- Node: GNS3 Node management -- Link: GNS3 Link management -- GNS3TopologyTool: GNS3 topology reading tool -- GNS3ProjectInfoTool: GNS3 project info tool +- Gns3Connector (connector.py): authenticated HTTP session for the + controller API — v2 basic / v3 JWT auth, token refresh, error extraction +- api_handlers.py: endpoint handlers taking ``(params, gns3_ctx)`` dicts, + shared by the copilot tools and the MCP service +- project_inventory.py: nodes/links aggregation for the topology context +- GNS3TopologyTool / GNS3ProjectInfoTool: LangChain reader tools Main functions: - get_gns3_connector: Factory function to create Gns3Connector - get_gns3_connector_with_llm_config: Create connector AND retrieve LLM config - get_gns3_server_host: Get GNS3 server hostname from Controller or Config -- get_llm_config: Get LLM model configuration for a user +- get_llm_config: Get user's default LLM config with API key -Upstream gns3fy: https://github.com/davidban77/gns3fy +The connector is adapted from the upstream gns3fy project +(https://github.com/davidban77/gns3fy). """ +from .api_handlers import build_gns3_ctx +from .connector import Gns3Connector from .connector_factory import get_gns3_connector from .connector_factory import get_gns3_connector_with_llm_config from .connector_factory import get_gns3_server_host @@ -55,13 +55,6 @@ from .context_helpers import get_current_jwt_token from .context_helpers import get_current_llm_config from .context_helpers import set_current_jwt_token from .context_helpers import set_current_llm_config -from .custom_gns3fy import CONSOLE_TYPES -from .custom_gns3fy import LINK_TYPES -from .custom_gns3fy import NODE_TYPES -from .custom_gns3fy import Gns3Connector -from .custom_gns3fy import Link -from .custom_gns3fy import Node -from .custom_gns3fy import Project from .gns3_project_info import GNS3ProjectInfoTool from .gns3_topology_reader import GNS3TopologyTool @@ -79,12 +72,7 @@ __url__ = "https://github.com/yueguobin/gns3-copilot" __all__ = [ "Gns3Connector", - "Project", - "Node", - "Link", - "NODE_TYPES", - "CONSOLE_TYPES", - "LINK_TYPES", + "build_gns3_ctx", "GNS3TopologyTool", "GNS3ProjectInfoTool", "get_gns3_connector", diff --git a/gns3server/agent/gns3_copilot/gns3_client/api_handlers.py b/gns3server/agent/gns3_copilot/gns3_client/api_handlers.py new file mode 100644 index 000000000..831dace7b --- /dev/null +++ b/gns3server/agent/gns3_copilot/gns3_client/api_handlers.py @@ -0,0 +1,1010 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# +# GNS3-Copilot - AI-powered Network Lab Assistant for GNS3 +# +# This file is part of GNS3-Copilot project. +# +# GNS3-Copilot is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# GNS3-Copilot is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNS3-Copilot. If not, see . +# +# Copyright (C) 2025 Yue Guobin (岳国宾) +# Author: Yue Guobin (岳国宾) +# +# Project Home: https://github.com/yueguobin/gns3-copilot +# + +""" +Shared GNS3 REST API handler layer. + +Handlers receive ``(params: dict, gns3_ctx: dict)`` and call the GNS3 REST +API directly via ``Gns3Connector.http_call`` — no ORM-style wrapper objects. + +This module is the single implementation shared by two consumers: + +- the MCP service (``gns3server.agent.mcp``) re-exports these handlers as + MCP tools, and +- gns3-copilot tools (``tools_v2``) call them directly. + +``gns3_ctx`` carries the per-request connection info: + +- ``server_url`` (str): GNS3 server base URL +- ``jwt_token`` (str): a JWT — API keys must be exchanged for a JWT by the + entry point before calling handlers (see ``mcp._resolve_token``) +- ``jwt_username`` / ``jwt_token_version`` (optional): only needed by + handlers that mint short-lived tokens for console/download URLs + +Copilot-side callers build the context with :func:`build_gns3_ctx`, which +pulls the request-scoped user JWT from the context variables. +""" + +from typing import Any +from concurrent.futures import ThreadPoolExecutor + +import hashlib +import logging + +from gns3server.services import access_ticket_service +from gns3server.services.access_tickets import DEFAULT_TICKET_TTL + +from gns3server.agent.gns3_copilot.gns3_client.connector import Gns3Connector + +log = logging.getLogger(__name__) + +BATCH_MAX_WORKERS = 100 + +# ── Constants ────────────────────────────────────────────────────────────── + +# Maximum bytes to return from get_node_file (safety net). +# Larger files are truncated with a truncated=True flag. +MAX_NODE_FILE_BYTES = 50 * 1024 # 50 KiB + +VALID_NODE_FIELDS = { + # NodeBase + "compute_id", "name", "node_type", "node_id", + "console", "console_type", "console_auto_start", + "aux", "aux_type", "properties", "label", "symbol", + "x", "y", "z", "locked", + "port_name_format", "port_segment_size", "first_port_name", + "custom_adapters", "tags", + # Node + "template_id", "project_id", "node_directory", "status", + "command_line", "width", "height", "ports", "console_host", +} + +VALID_LINK_FIELDS = { + "link_id", "project_id", "link_type", "nodes", "suspend", + "link_style", "filters", "show_filters_icon", + "capturing", "capture_file_name", "capture_file_path", + "capture_compute_id", "wireshark", +} + +LINK_DEFAULT_FIELDS = ["link_id", "link_type", "nodes"] + + +# ── Helpers ──────────────────────────────────────────────────────────────── + +def _get_connector(gns3_ctx: dict[str, Any]): + return Gns3Connector( + url=gns3_ctx["server_url"], + jwt_token=gns3_ctx["jwt_token"], + api_version=3, + verify=False, + ) + + +def build_gns3_ctx( + jwt_token: str | None = None, url: str | None = None +) -> dict[str, Any] | None: + """ + Build a handler ``gns3_ctx`` for in-process copilot callers. + + The JWT is taken from the request-scoped context variable when not + passed explicitly (mirroring ``get_gns3_connector``); the URL uses the + same Controller → Config → fallback detection order. + + Returns None when no JWT token is available. + """ + from gns3server.agent.gns3_copilot.gns3_client.connector_factory import ( + _detect_url_for_api, + ) + from gns3server.agent.gns3_copilot.gns3_client.context_helpers import ( + get_current_jwt_token, + ) + + token = jwt_token or get_current_jwt_token() + if not token: + return None + return { + "server_url": url or _detect_url_for_api(), + "jwt_token": token, + "jwt_username": None, + "jwt_token_version": 0, + } + + +def _filter_node_response(node: dict, fields: list[str] = None) -> dict: + """Filter node response to only include requested fields.""" + if not fields: + fields = ["node_id", "name", "node_type", "status", "console"] + return {k: node[k] for k in fields if k in node} + + +def _filter_link_response(link: dict, fields: list[str] = None) -> dict: + """Filter link response to only include requested fields.""" + if not fields: + fields = LINK_DEFAULT_FIELDS + return {k: link[k] for k in fields if k in link} + + +def _normalize_link_nodes(nodes) -> list[dict[str, Any]]: + """ + Normalize link node entries, accepting both standard object format and + compact array format to reduce token usage. + + Standard: [{"node_id": "uuid", "adapter_number": 0, "port_number": 0}] + Compact: ["uuid", 0, 0, "uuid", 0, 0] + + Returns the normalized list, or raises ValueError with a clear message + on format errors so the AI can self-correct. + """ + if not nodes: + return nodes + if not isinstance(nodes, list): + raise ValueError(f"nodes must be a list, got {type(nodes).__name__}: {nodes}") + # Standard object format: [{"node_id": "...", ...}] + if isinstance(nodes[0], dict): + return nodes + # Compact array format: ["uuid", ad, pt, "uuid", ad, pt"] + if all(not isinstance(n, dict) for n in nodes): + if len(nodes) != 6: + raise ValueError( + f"Compact link format requires exactly 6 elements " + f"[node_id, adapter, port, node_id, adapter, port], " + f"but got {len(nodes)} elements: {nodes}" + ) + if not isinstance(nodes[0], str) or not isinstance(nodes[3], str): + raise ValueError( + f"Compact link format expects node_id (string) at positions 0 and 3, " + f"got types {type(nodes[0]).__name__} and {type(nodes[3]).__name__}: {nodes}" + ) + return [ + {"node_id": nodes[0], "adapter_number": nodes[1], "port_number": nodes[2]}, + {"node_id": nodes[3], "adapter_number": nodes[4], "port_number": nodes[5]}, + ] + raise ValueError( + f"Unrecognized link nodes format. " + f"Use standard [{{\"node_id\":\"..\",\"adapter_number\":0,\"port_number\":0}},...] " + f"or compact [\"id\",0,0,\"id\",0,0], got: {nodes}" + ) + + +# ── Node handlers ────────────────────────────────────────────────────────── + +def get_nodes_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + conn = _get_connector(gns3_ctx) + nodes = conn.http_call("get", f"{conn.base_url}/projects/{project_id}/nodes").json() + fields = params.get("fields") + if fields: + if not isinstance(fields, list): + return {"error": "fields must be a list of field names, e.g. [\"name\", \"status\"]"} + invalid = [f for f in fields if f not in VALID_NODE_FIELDS] + if invalid: + return { + "error": f"Unknown fields: {invalid}", + "available_fields": sorted(VALID_NODE_FIELDS), + } + nodes = [{k: n[k] for k in fields if k in n} for n in nodes] + return {"nodes": nodes, "count": len(nodes)} + + +def get_node_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + node_id = params.get("node_id") + if not project_id or not node_id: + return {"error": "project_id and node_id are required"} + conn = _get_connector(gns3_ctx) + node = conn.http_call("get", f"{conn.base_url}/projects/{project_id}/nodes/{node_id}").json() + + fields = params.get("fields") + if fields: + if not isinstance(fields, list): + return {"error": "fields must be a list of field names, e.g. [\"name\", \"status\"]"} + invalid = [f for f in fields if f not in VALID_NODE_FIELDS] + if invalid: + return { + "error": f"Unknown fields: {invalid}", + "available_fields": sorted(VALID_NODE_FIELDS), + } + return {k: node[k] for k in fields if k in node} + + return node + + +def _batch_lifecycle(project_id, node_ids, action, conn, action_label): + """Helper to run a lifecycle action on multiple nodes in parallel.""" + def _act(nid): + try: + conn.http_call("post", f"{conn.base_url}/projects/{project_id}/nodes/{nid}/{action}") + return {"node_id": nid, "status": "success", "message": f"Node {nid} {action_label}"} + except Exception as e: + return {"node_id": nid, "status": "error", "error": str(e)} + with ThreadPoolExecutor(max_workers=min(len(node_ids), BATCH_MAX_WORKERS)) as pool: + return list(pool.map(_act, node_ids)) + + +def start_node_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + node_ids = params.get("node_ids") + if node_ids: + if not isinstance(node_ids, list): + return {"error": "node_ids must be a list"} + conn = _get_connector(gns3_ctx) + return _batch_lifecycle(project_id, node_ids, "start", conn, "started") + node_id = params.get("node_id") + if not node_id: + return {"error": "node_id or node_ids is required"} + conn = _get_connector(gns3_ctx) + conn.http_call("post", f"{conn.base_url}/projects/{project_id}/nodes/{node_id}/start", json_data={}) + return {"message": f"Node {node_id} started", "node_id": node_id} + + +def stop_node_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + node_ids = params.get("node_ids") + if node_ids: + if not isinstance(node_ids, list): + return {"error": "node_ids must be a list"} + conn = _get_connector(gns3_ctx) + return _batch_lifecycle(project_id, node_ids, "stop", conn, "stopped") + node_id = params.get("node_id") + if not node_id: + return {"error": "node_id or node_ids is required"} + conn = _get_connector(gns3_ctx) + conn.http_call("post", f"{conn.base_url}/projects/{project_id}/nodes/{node_id}/stop", json_data={}) + return {"message": f"Node {node_id} stopped", "node_id": node_id} + + +def suspend_node_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + node_ids = params.get("node_ids") + if node_ids: + if not isinstance(node_ids, list): + return {"error": "node_ids must be a list"} + conn = _get_connector(gns3_ctx) + return _batch_lifecycle(project_id, node_ids, "suspend", conn, "suspended") + node_id = params.get("node_id") + if not node_id: + return {"error": "node_id or node_ids is required"} + conn = _get_connector(gns3_ctx) + conn.http_call("post", f"{conn.base_url}/projects/{project_id}/nodes/{node_id}/suspend") + return {"message": f"Node {node_id} suspended", "node_id": node_id} + + +def create_node_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + + fields = params.get("fields") + if fields is not None and not isinstance(fields, list): + return {"error": "fields must be a list, e.g. [\"node_id\", \"name\"]"} + + nodes = params.get("nodes") + # Batch mode: nodes=[{template_id?, x, y, name?, compute_id?}] + # When top-level template_id is set, it applies to all nodes as a default + if nodes is not None: + if not isinstance(nodes, list) or not nodes: + return {"error": "nodes must be a non-empty array"} + default_tid = params.get("template_id") + conn = _get_connector(gns3_ctx) + def _create_one(node_data): + tid = node_data.get("template_id", default_tid) + if not tid: + return {"template_id": tid, "status": "error", "error": "template_id is required"} + try: + url = f"{conn.base_url}/projects/{project_id}/templates/{tid}" + body = { + "x": node_data.get("x", 0), + "y": node_data.get("y", 0), + "compute_id": node_data.get("compute_id", "local"), + } + node_name = node_data.get("name") + if node_name: + body["name"] = node_name + resp = conn.http_call("post", url, json_data=body).json() + return {"template_id": tid, "status": "success", "node": _filter_node_response(resp, fields)} + except Exception as e: + return {"template_id": tid, "status": "error", "error": str(e)} + if any(not node.get("name") for node in nodes): + # The controller assigns default names (R-1, R-2, ...) and console + # ports in request arrival order, and a parallel fan-out makes the + # arrival order depend on thread scheduling. Batches that rely on + # default naming are therefore created sequentially so those + # server-side assignments follow the submission order; batches + # where every node has an explicit name stay parallel. + return [_create_one(node) for node in nodes] + with ThreadPoolExecutor(max_workers=min(len(nodes), BATCH_MAX_WORKERS)) as pool: + # pool.map keeps the submission order, so callers can correlate + # results with the nodes they sent regardless of completion order + return list(pool.map(_create_one, nodes)) + + # Single mode + template_id = params.get("template_id") + if not template_id: + return {"error": "template_id is required"} + conn = _get_connector(gns3_ctx) + data = { + "x": params.get("x", 0), + "y": params.get("y", 0), + "compute_id": params.get("compute_id", "local"), + } + node_name = params.get("name") + if node_name: + data["name"] = node_name + url = f"{conn.base_url}/projects/{project_id}/templates/{template_id}" + resp = conn.http_call("post", url, json_data=data).json() + return _filter_node_response(resp, fields) + + +def delete_node_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + node_ids = params.get("node_ids") + if node_ids: + if not isinstance(node_ids, list): + return {"error": "node_ids must be a list"} + conn = _get_connector(gns3_ctx) + def _del(nid): + try: + conn.http_call("delete", f"{conn.base_url}/projects/{project_id}/nodes/{nid}") + return {"node_id": nid, "status": "success", "message": f"Node {nid} deleted"} + except Exception as e: + return {"node_id": nid, "status": "error", "error": str(e)} + with ThreadPoolExecutor(max_workers=min(len(node_ids), BATCH_MAX_WORKERS)) as pool: + return list(pool.map(_del, node_ids)) + node_id = params.get("node_id") + if not node_id: + return {"error": "node_id or node_ids is required"} + conn = _get_connector(gns3_ctx) + conn.http_call("delete", f"{conn.base_url}/projects/{project_id}/nodes/{node_id}") + return {"message": f"Node {node_id} deleted", "node_id": node_id} + + +def update_node_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + node_id = params.get("node_id") + if not project_id or not node_id: + return {"error": "project_id and node_id are required"} + conn = _get_connector(gns3_ctx) + + # Extract update parameters - handle nested kwargs structure from MCP clients + if "kwargs" in params and isinstance(params["kwargs"], dict): + update_data = params["kwargs"] + else: + update_data = {k: v for k, v in params.items() if k not in ("project_id", "node_id", "kwargs")} + + url = f"{conn.base_url}/projects/{project_id}/nodes/{node_id}" + return conn.http_call("put", url, json_data=update_data).json() + + +def get_node_console_info_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + node_id = params.get("node_id") + if not project_id or not node_id: + return {"error": "project_id and node_id are required"} + conn = _get_connector(gns3_ctx) + node = conn.http_call("get", f"{conn.base_url}/projects/{project_id}/nodes/{node_id}").json() + + console_type = node.get("console_type", "unknown") + # Short-lived console ticket (10 min, multi-use, bound to this node's + # console endpoints). Deliberately a short random string instead of a JWT: + # LLM clients retype this URL into shell commands and reliably corrupted + # the ~200-char JWT previously embedded here (dropped header segment → + # "Missing 'alg' value in header" on the server). + username = gns3_ctx.get("jwt_username") + ticket = access_ticket_service.mint( + username, + token_version=gns3_ctx.get("jwt_token_version", 0), + project_id=project_id, + node_id=node_id, + ) if username else None + raw_url = f"{gns3_ctx['server_url']}/v3/projects/{project_id}/nodes/{node_id}/console/ws" + if ticket: + raw_url += f"?token={ticket}" + # Convert http scheme to ws for direct websocat usage + ws_url = raw_url.replace("https://", "wss://").replace("http://", "ws://") + + result = { + "node_id": node_id, + "node_name": node.get("name"), + "console_type": console_type, + "ws_url": ws_url, + "command": f"websocat -t --no-close {ws_url}", + } + if ticket: + # Fingerprint of the minted token: compare it against what actually reached the + # server (logged on WebSocket auth rejection) to detect copy corruption, and + # re-request the URL once token_ttl_seconds has elapsed. + result["token_sha256_prefix"] = hashlib.sha256(ticket.encode()).hexdigest()[:8] + result["token_ttl_seconds"] = DEFAULT_TICKET_TTL + if console_type in ("vnc",) and ticket: + # Same node binding covers the vnc endpoint (identical path params) + result["vnc_url"] = f"/v3/projects/{project_id}/nodes/{node_id}/console/vnc?token={ticket}" + return result + + +# ── Node file handlers ──────────────────────────────────────────────────── + + +def list_node_files_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + node_id = params.get("node_id") + if not project_id or not node_id: + return {"error": "project_id and node_id are required"} + conn = _get_connector(gns3_ctx) + url = f"{conn.base_url}/projects/{project_id}/nodes/{node_id}/files" + query = {} + if params.get("path"): + query["path"] = params["path"] + if params.get("recursive"): + query["recursive"] = "true" + files = conn.http_call("get", url, params=query if query else None).json() + return {"files": files, "count": len(files)} + + +def get_node_file_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + node_id = params.get("node_id") + file_path = params.get("file_path") + if not project_id or not node_id or not file_path: + return {"error": "project_id, node_id and file_path are required"} + + offset = params.get("offset", 0) + limit = params.get("limit", 200) + + conn = _get_connector(gns3_ctx) + url = f"{conn.base_url}/projects/{project_id}/nodes/{node_id}/files/{file_path}" + raw = conn.http_call("get", url).text + + total_bytes = len(raw.encode("utf-8")) + truncated = False + if total_bytes > MAX_NODE_FILE_BYTES: + raw = raw[:MAX_NODE_FILE_BYTES] + truncated = True + + # keepends keeps the content byte-faithful: the trailing newline of the + # last line and any \r\n endings survive the round trip + lines = raw.splitlines(keepends=True) + total_lines = len(lines) + + # Apply offset/limit + selected = lines[offset: offset + limit] if offset < total_lines else [] + has_more = (offset + limit) < total_lines or truncated + content = "".join(selected) + + return { + "file_path": file_path, + "content": content, + "metadata": { + "total_lines": total_lines, + "total_bytes": total_bytes, + "offset": offset, + "limit": limit, + "returned_lines": len(selected), + "returned_bytes": len(content.encode("utf-8")), + "truncated": truncated or has_more, + "has_more": has_more, + }, + } + + +def write_node_file_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + node_id = params.get("node_id") + file_path = params.get("file_path") + content = params.get("content") + if not project_id or not node_id or not file_path or content is None: + return {"error": "project_id, node_id, file_path and content are required"} + conn = _get_connector(gns3_ctx) + url = f"{conn.base_url}/projects/{project_id}/nodes/{node_id}/files/{file_path}" + conn.http_call("post", url, data=content, headers={"Content-Type": "text/plain"}) + return {"message": f"File {file_path} written to node {node_id}", "file_path": file_path, "node_id": node_id} + + +def delete_node_file_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + node_id = params.get("node_id") + file_path = params.get("file_path") + if not project_id or not node_id or not file_path: + return {"error": "project_id, node_id and file_path are required"} + conn = _get_connector(gns3_ctx) + url = f"{conn.base_url}/projects/{project_id}/nodes/{node_id}/files/{file_path}" + conn.http_call("delete", url) + return {"message": f"File {file_path} deleted from node {node_id}", "file_path": file_path, "node_id": node_id} + + +# ── Node bulk / advanced handlers ──────────────────────────────────────── + + +def start_all_nodes_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + conn = _get_connector(gns3_ctx) + conn.http_call("post", f"{conn.base_url}/projects/{project_id}/nodes/start") + return {"message": "All nodes started", "project_id": project_id} + + +def stop_all_nodes_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + conn = _get_connector(gns3_ctx) + conn.http_call("post", f"{conn.base_url}/projects/{project_id}/nodes/stop") + return {"message": "All nodes stopped", "project_id": project_id} + + +def suspend_all_nodes_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + conn = _get_connector(gns3_ctx) + conn.http_call("post", f"{conn.base_url}/projects/{project_id}/nodes/suspend") + return {"message": "All nodes suspended", "project_id": project_id} + + +def duplicate_node_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + node_id = params.get("node_id") + if not project_id or not node_id: + return {"error": "project_id and node_id are required"} + conn = _get_connector(gns3_ctx) + data = {k: v for k, v in params.items() if k not in ("project_id", "node_id") and v is not None} + result = conn.http_call("post", f"{conn.base_url}/projects/{project_id}/nodes/{node_id}/duplicate", json_data=data).json() + return {"message": f"Node {node_id} duplicated", "node": result} + + +def isolate_node_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + node_id = params.get("node_id") + if not project_id or not node_id: + return {"error": "project_id and node_id are required"} + conn = _get_connector(gns3_ctx) + conn.http_call("post", f"{conn.base_url}/projects/{project_id}/nodes/{node_id}/isolate") + return {"message": f"Node {node_id} isolated (all links suspended)", "node_id": node_id} + + +def unisolate_node_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + node_id = params.get("node_id") + if not project_id or not node_id: + return {"error": "project_id and node_id are required"} + conn = _get_connector(gns3_ctx) + conn.http_call("post", f"{conn.base_url}/projects/{project_id}/nodes/{node_id}/unisolate") + return {"message": f"Node {node_id} unisolated (links resumed)", "node_id": node_id} + + +def get_node_links_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + node_id = params.get("node_id") + if not project_id or not node_id: + return {"error": "project_id and node_id are required"} + conn = _get_connector(gns3_ctx) + links = conn.http_call("get", f"{conn.base_url}/projects/{project_id}/nodes/{node_id}/links").json() + return {"links": links, "count": len(links)} + + +# ── Link handlers ────────────────────────────────────────────────────────── + +def get_links_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + conn = _get_connector(gns3_ctx) + links = conn.http_call("get", f"{conn.base_url}/projects/{project_id}/links").json() + fields = params.get("fields") + if fields: + if not isinstance(fields, list): + return {"error": "fields must be a list, e.g. [\"link_id\", \"nodes\"]"} + invalid = [f for f in fields if f not in VALID_LINK_FIELDS] + if invalid: + return { + "error": f"Unknown fields: {invalid}", + "available_fields": sorted(VALID_LINK_FIELDS), + } + links = [{k: link[k] for k in fields if k in link} for link in links] + return {"links": links, "count": len(links)} + + +def get_link_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + link_id = params.get("link_id") + if not project_id or not link_id: + return {"error": "project_id and link_id are required"} + conn = _get_connector(gns3_ctx) + return conn.http_call("get", f"{conn.base_url}/projects/{project_id}/links/{link_id}").json() + + +def available_filters_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + """ + List the packet filter types available for a link (GNS3 API v3 only). + + Returns a list of filter descriptors (frequency_drop, packet_loss, + delay, corrupt, bpf) with their parameters. + """ + project_id = params.get("project_id") + link_id = params.get("link_id") + if not project_id or not link_id: + return {"error": "project_id and link_id are required"} + conn = _get_connector(gns3_ctx) + url = f"{conn.base_url}/projects/{project_id}/links/{link_id}/available_filters" + return conn.http_call("get", url).json() + + +def create_link_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + + fields = params.get("fields") + if fields is not None and not isinstance(fields, list): + return {"error": "fields must be a list, e.g. [\"link_id\", \"nodes\"]"} + + links = params.get("links") + # Batch mode: links=[{nodes, link_type?, filters?, suspend?}] + if links is not None: + if not isinstance(links, list) or not links: + return {"error": "links must be a non-empty array"} + conn = _get_connector(gns3_ctx) + def _create_one(link_data): + raw_nodes = link_data.get("nodes") + if not raw_nodes: + return {"status": "error", "error": "nodes is required for each link"} + try: + body = {"nodes": _normalize_link_nodes(raw_nodes)} + if link_data.get("link_type"): + body["link_type"] = link_data["link_type"] + if link_data.get("filters"): + body["filters"] = link_data["filters"] + if link_data.get("suspend"): + body["suspend"] = link_data["suspend"] + url = f"{conn.base_url}/projects/{project_id}/links" + resp = conn.http_call("post", url, json_data=body).json() + return {"status": "success", "link": _filter_link_response(resp, fields)} + except Exception as e: + return {"status": "error", "error": str(e)} + with ThreadPoolExecutor(max_workers=min(len(links), BATCH_MAX_WORKERS)) as pool: + # pool.map keeps the submission order, so callers can correlate + # results with the links they sent regardless of completion order + return list(pool.map(_create_one, links)) + + # Single mode + nodes = params.get("nodes") + if not nodes: + return {"error": "nodes is required"} + conn = _get_connector(gns3_ctx) + data = {"nodes": _normalize_link_nodes(nodes)} + if "link_type" in params: + data["link_type"] = params["link_type"] + if "filters" in params: + data["filters"] = params["filters"] + if "suspend" in params: + data["suspend"] = params["suspend"] + url = f"{conn.base_url}/projects/{project_id}/links" + resp = conn.http_call("post", url, json_data=data).json() + return _filter_link_response(resp, fields) + + +def delete_link_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + link_ids = params.get("link_ids") + if link_ids: + if not isinstance(link_ids, list): + return {"error": "link_ids must be a list"} + conn = _get_connector(gns3_ctx) + def _del(lid): + try: + conn.http_call("delete", f"{conn.base_url}/projects/{project_id}/links/{lid}") + return {"link_id": lid, "status": "success", "message": f"Link {lid} deleted"} + except Exception as e: + return {"link_id": lid, "status": "error", "error": str(e)} + with ThreadPoolExecutor(max_workers=min(len(link_ids), BATCH_MAX_WORKERS)) as pool: + return list(pool.map(_del, link_ids)) + link_id = params.get("link_id") + if not link_id: + return {"error": "link_id or link_ids is required"} + conn = _get_connector(gns3_ctx) + conn.http_call("delete", f"{conn.base_url}/projects/{project_id}/links/{link_id}") + return {"message": f"Link {link_id} deleted", "link_id": link_id} + + +def update_link_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + link_id = params.get("link_id") + if not project_id or not link_id: + return {"error": "project_id and link_id are required"} + conn = _get_connector(gns3_ctx) + + # Extract update parameters - handle nested kwargs structure from MCP clients + if "kwargs" in params and isinstance(params["kwargs"], dict): + update_data = params["kwargs"] + else: + update_data = {k: v for k, v in params.items() if k not in ("project_id", "link_id", "kwargs")} + + url = f"{conn.base_url}/projects/{project_id}/links/{link_id}" + return conn.http_call("put", url, json_data=update_data).json() + + +# ── Link capture / reset handlers ────────────────────────────────────── + + +def reset_link_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + link_ids = params.get("link_ids") + if link_ids: + if not isinstance(link_ids, list): + return {"error": "link_ids must be a list"} + conn = _get_connector(gns3_ctx) + def _rst(lid): + try: + url = f"{conn.base_url}/projects/{project_id}/links/{lid}/reset" + r = conn.http_call("post", url).json() + return {"link_id": lid, "status": "reset", "link": r} + except Exception as e: + return {"link_id": lid, "status": "error", "error": str(e)} + with ThreadPoolExecutor(max_workers=min(len(link_ids), BATCH_MAX_WORKERS)) as pool: + return list(pool.map(_rst, link_ids)) + link_id = params.get("link_id") + if not link_id: + return {"error": "link_id or link_ids is required"} + conn = _get_connector(gns3_ctx) + url = f"{conn.base_url}/projects/{project_id}/links/{link_id}/reset" + result = conn.http_call("post", url).json() + return {"message": f"Link {link_id} reset", "link": result} + + +def _batch_capture(project_id, link_ids, action, data_builder, conn): + """Helper for batch capture start/stop.""" + def _act(lid): + try: + url = f"{conn.base_url}/projects/{project_id}/links/{lid}/capture/{action}" + kwargs = data_builder(lid) if data_builder else {} + conn.http_call("post", url, **kwargs) + return {"link_id": lid, "status": "success"} + except Exception as e: + return {"link_id": lid, "status": "error", "error": str(e)} + with ThreadPoolExecutor(max_workers=min(len(link_ids), BATCH_MAX_WORKERS)) as pool: + return list(pool.map(_act, link_ids)) + + +def start_capture_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + link_ids = params.get("link_ids") + if link_ids: + if not isinstance(link_ids, list): + return {"error": "link_ids must be a list"} + conn = _get_connector(gns3_ctx) + dlt = params.get("data_link_type", "DLT_EN10MB") + ws = params.get("wireshark", False) + fname = params.get("capture_file_name") + def _build(lid): + data = {"data_link_type": dlt, "wireshark": ws} + if fname: + data["capture_file_name"] = fname + return {"json_data": data} + return _batch_capture(project_id, link_ids, "start", _build, conn) + link_id = params.get("link_id") + if not link_id: + return {"error": "link_id or link_ids is required"} + conn = _get_connector(gns3_ctx) + data = { + "data_link_type": params.get("data_link_type", "DLT_EN10MB"), + "wireshark": params.get("wireshark", False), + } + if params.get("capture_file_name"): + data["capture_file_name"] = params["capture_file_name"] + url = f"{conn.base_url}/projects/{project_id}/links/{link_id}/capture/start" + result = conn.http_call("post", url, json_data=data).json() + return {"message": f"Capture started on link {link_id}", "link": result} + + +def stop_capture_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + link_ids = params.get("link_ids") + if link_ids: + if not isinstance(link_ids, list): + return {"error": "link_ids must be a list"} + conn = _get_connector(gns3_ctx) + return _batch_capture(project_id, link_ids, "stop", None, conn) + link_id = params.get("link_id") + if not link_id: + return {"error": "link_id or link_ids is required"} + conn = _get_connector(gns3_ctx) + url = f"{conn.base_url}/projects/{project_id}/links/{link_id}/capture/stop" + conn.http_call("post", url) + return {"message": f"Capture stopped on link {link_id}", "link_id": link_id} + + +def download_capture_file_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + username = gns3_ctx.get("jwt_username") + token_version = gns3_ctx.get("jwt_token_version", 0) + + def _download(link_id: str) -> dict[str, Any]: + # short-lived ticket bound to this exact download path — LLM clients + # retyping curl commands corrupted the long Bearer JWT this used to embed + path = f"/v3/projects/{project_id}/links/{link_id}/capture/file" + url = f"{gns3_ctx['server_url']}{path}" + ticket = access_ticket_service.mint(username, token_version=token_version, path=path) if username else None + if ticket: + url += f"?token={ticket}" + entry = {"link_id": link_id, "download_url": url} + if ticket: + entry["curl_command"] = f"curl -L -o capture_{link_id}.pcap '{url}'" + return entry + + link_ids = params.get("link_ids") + if link_ids: + if not isinstance(link_ids, list): + return {"error": "link_ids must be a list"} + results = [_download(lid) for lid in link_ids] + return {"downloads": results, "count": len(results), "note": "Files are in pcap format. URLs include a 10-minute ticket."} + + link_id = params.get("link_id") + if not link_id: + return {"error": "link_id or link_ids is required"} + result = _download(link_id) + result["note"] = "The file is in pcap format and can be analyzed with Wireshark or tcpdump." + if "curl_command" in result: + result["curl_command"] = f"curl -L -o capture.pcap '{result['download_url']}'" + result["note"] += " The download URL includes a 10-minute ticket." + return result + + +# ── Marker (traffic-insight) handlers ────────────────────────────────── + + +def link_marker_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + """ + Manage traffic-insight markers on a specific link. + + Actions: + - create: POST /projects/{pid}/links/{lid}/markers + - update: PUT /projects/{pid}/links/{lid}/markers/{name} + - delete: DELETE /projects/{pid}/links/{lid}/markers/{name} + """ + project_id = params.get("project_id") + link_id = params.get("link_id") + action = params.get("action") + if not all([project_id, link_id, action]): + return {"error": "project_id, link_id and action are required"} + if action not in ("create", "update", "delete"): + return {"error": f"Unknown action: {action}. Supported: create, update, delete"} + + conn = _get_connector(gns3_ctx) + base = f"{conn.base_url}/projects/{project_id}/links/{link_id}/markers" + + if action == "create": + bpf = params.get("bpf") + if not bpf: + return {"error": "bpf is required for create action"} + body: dict[str, Any] = {"bpf": bpf} + for opt in ("name", "tag", "capture_node_id", "color", "highlight_duration", "data_link_type"): + if params.get(opt) is not None: + body[opt] = params[opt] + # direction: "tx"/"rx" set a one-way filter; "both"/omitted = no filter. + if params.get("direction") in ("tx", "rx"): + body["direction"] = params["direction"] + return conn.http_call("post", base, json_data=body).json() + + marker_name = params.get("marker_name") + if not marker_name: + return {"error": "marker_name is required for update/delete actions"} + + url = f"{base}/{marker_name}" + + if action == "update": + body = {} + for opt in ("bpf", "tag", "enabled", "color", "highlight_duration"): + if params.get(opt) is not None: + body[opt] = params[opt] + # direction tri-state: omitted=preserve, "tx"/"rx"=set, "both"=clear (→ null). + direction = params.get("direction") + if direction == "both": + body["direction"] = None + elif direction in ("tx", "rx"): + body["direction"] = direction + if not body: + return {"error": "At least one update field is required (bpf, tag, enabled, direction, color, highlight_duration)"} + return conn.http_call("put", url, json_data=body).json() + + # action == "delete" + conn.http_call("delete", url) + return {"message": f"Marker '{marker_name}' deleted from link {link_id}", "link_id": link_id, "marker_name": marker_name} + + +def marker_definition_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + """ + Manage project-level marker definitions (auto-fanout to all links). + + Actions: + - create: POST /projects/{pid}/marker-definitions → fans out global-{name} to every link + - update: PUT /projects/{pid}/marker-definitions/{name} + - delete: DELETE /projects/{pid}/marker-definitions/{name} + - list: GET /projects/{pid}/marker-definitions + """ + project_id = params.get("project_id") + action = params.get("action") + if not all([project_id, action]): + return {"error": "project_id and action are required"} + if action not in ("create", "update", "delete", "list"): + return {"error": f"Unknown action: {action}. Supported: create, update, delete, list"} + + conn = _get_connector(gns3_ctx) + base = f"{conn.base_url}/projects/{project_id}/marker-definitions" + + if action == "list": + return conn.http_call("get", base).json() + + if action == "create": + bpf = params.get("bpf") + if not bpf: + return {"error": "bpf is required for create action"} + body: dict[str, Any] = {"bpf": bpf} + for opt in ("name", "tag", "color", "highlight_duration", "data_link_type"): + if params.get(opt) is not None: + body[opt] = params[opt] + # No direction: a definition fans out to every link and auto-selects its + # capture node on each, so tx/rx (which is relative to that node) has no + # consistent meaning. Encode direction in the BPF instead. + return conn.http_call("post", base, json_data=body).json() + + def_name = params.get("def_name") + if not def_name: + return {"error": "def_name is required for update/delete actions"} + + url = f"{base}/{def_name}" + + if action == "update": + body = {} + for opt in ("bpf", "tag", "color", "highlight_duration", "data_link_type"): + if params.get(opt) is not None: + body[opt] = params[opt] + if not body: + return {"error": "At least one update field is required (bpf, tag, color, highlight_duration, data_link_type)"} + return conn.http_call("put", url, json_data=body).json() + + # action == "delete" + conn.http_call("delete", url) + return {"message": f"Marker definition '{def_name}' deleted", "project_id": project_id, "def_name": def_name} diff --git a/gns3server/agent/gns3_copilot/gns3_client/connector.py b/gns3server/agent/gns3_copilot/gns3_client/connector.py new file mode 100644 index 000000000..bd5fbcf9c --- /dev/null +++ b/gns3server/agent/gns3_copilot/gns3_client/connector.py @@ -0,0 +1,309 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# +# GNS3-Copilot - AI-powered Network Lab Assistant for GNS3 +# +# This file is part of GNS3-Copilot project. +# +# GNS3-Copilot is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# GNS3-Copilot is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNS3-Copilot. If not, see . +# +# Copyright (C) 2025 Yue Guobin (岳国宾) +# Author: Yue Guobin (岳国宾) +# +# Project Home: https://github.com/yueguobin/gns3-copilot +# + +""" +GNS3 REST API connector. + +A minimal authenticated HTTP session for the GNS3 controller API: URL/base +URL handling, v2 basic / v3 JWT authentication and token refresh, plus GNS3 +error extraction. Callers make requests through ``http_call`` — the +endpoint-specific logic lives in ``api_handlers``. + +The class is adapted from the upstream gns3fy project +(https://github.com/davidban77/gns3fy) Gns3Connector. + +⚠️ WARNING: This module is shared with the MCP (Model Context Protocol) +service. Modifications must be tested with BOTH gns3-copilot AND MCP. +""" + +import time +from typing import Any + +import jwt +import requests +import urllib3 +from requests import HTTPError + + +class Gns3Connector: + """ + Connector to be used for interaction against the GNS3 server controller API. + + **Attributes:** + + - `url` (str): URL of the GNS3 server (**required**) + - `user` (str): User used for authentication + - `cred` (str): Password used for authentication + - `jwt_token` (str): JWT token for direct authentication (API v3) + - `verify` (bool): Whether or not to verify SSL + - `api_version` (int): GNS3 server REST API version + - `api_calls`: Counter of amount of `http_calls` has been performed + - `base_url`: url passed + api_version + - `session`: Requests Session object + + **Returns:** + + `Gns3Connector` instance + + **Example:** + + ```python + >>> # API v2 with basic auth + >>> server = Gns3Connector( + ... url="http://
:3080", user="admin", cred="password", + ... api_version=2 + ... ) + >>> # API v3 with username/password (auto-fetches JWT token) + >>> server = Gns3Connector( + ... url="http://
:3080", user="admin", cred="password", + ... api_version=3 + ... ) + >>> # API v3 with direct JWT token + >>> server = Gns3Connector( + ... url="http://
:3080", + ... jwt_token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + ... api_version=3 + ... ) + >>> print(server.http_call("get", f"{server.base_url}/version").json()) + {'local': False, 'version': '2.2.0b4'} + ``` + """ + + access_token: str | None + token_expiry: float | None + + def __init__( + self, + url: str | None = None, + user: str | None = None, + cred: str | None = None, + jwt_token: str | None = None, + verify: bool = False, + api_version: int = 2, + ) -> None: + # Disable SSL warnings + urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + + if url is None: + raise ValueError("URL is required for Gns3Connector") + self.url = url.strip("/") # Store original URL for reference + self.base_url = f"{self.url}/v{api_version}" + self.user = user + self.cred = cred + self.headers = {"Content-Type": "application/json"} + self.verify = verify + self.api_calls = 0 + + # v3 authentication attributes + # If jwt_token is provided directly, use it; otherwise will be + # fetched via username/password + self.access_token = jwt_token + self.token_expiry = None + self.auth_type = "basic" if api_version == 2 else "jwt" + self.api_version = api_version + + # Create session object + self._create_session() + + def _create_session(self) -> None: + """ + Creates the requests.Session object and applies the necessary parameters + """ + self.session = requests.Session() # pragma: no cover + # Increase connection pool size to support concurrent MCP batch operations + adapter = requests.adapters.HTTPAdapter(pool_connections=500, pool_maxsize=1000) + self.session.mount("http://", adapter) + self.session.mount("https://", adapter) + self.session.headers["Accept"] = "application/json" # pragma: no cover + + # Set authentication based on API version + if ( + self.auth_type == "basic" + and self.user is not None + and self.cred is not None + ): + self.session.auth = (self.user, self.cred) # pragma: no cover + + elif self.auth_type == "jwt" and self.access_token: + self.session.headers["Authorization"] = ( + f"Bearer {self.access_token}" + ) + + def _authenticate_v3(self) -> None: + """ + Performs v3 API authentication using username and password to get JWT token. + Skips authentication if a JWT token is already provided. + """ + # If token is already provided, skip authentication + if self.access_token: + return + + if not self.user or not self.cred: + raise ValueError( + "Username and password are required for v3 authentication " + "when no JWT token is provided" + ) + + # Construct authentication URL (v3 API uses different base URL) + auth_url = ( + f"{self.base_url.replace('/v3', '')}/v3/access/users/authenticate" + ) + auth_data = {"username": self.user, "password": self.cred} + + # Use temporary session for authentication + temp_session = requests.Session() + temp_session.headers["Content-Type"] = "application/json" + + try: + response = temp_session.post( + auth_url, json=auth_data, verify=self.verify, timeout=10.0 + ) + if response.status_code == 200: + auth_result = response.json() + self.access_token = auth_result["access_token"] + # Update session with new token + self.session.headers["Authorization"] = ( + f"Bearer {self.access_token}" + ) + else: + raise HTTPError( + f"v3 API authentication failed: {response.status_code} - " + f"{response.text}" + ) + except Exception as e: + raise HTTPError(f"v3 API authentication error: {str(e)}") from e + + def _is_token_expired(self) -> bool: + """ + Check if the JWT token is expired (basic implementation) + """ + token = self.access_token + if not token: + return True + + try: + # Decode token without verification to check expiry + decoded: dict[str, Any] = jwt.decode( + token, options={"verify_signature": False} + ) + exp = decoded.get("exp") + if exp is not None: + return time.time() > float(exp) + return False + except (jwt.PyJWTError, ValueError, TypeError): + return True + + def _refresh_token(self) -> None: + """ + Refresh the JWT token (for now, just re-authenticate) + """ + print("Refreshing v3 API token...") + self._authenticate_v3() + + def http_call( + self, + method: str, + url: str, + data: Any | None = None, + json_data: dict[str, Any] | list[Any] | None = None, + headers: dict[str, str] | None = None, + verify: bool = False, + params: dict[str, Any] | None = None, + ) -> requests.Response: + """ + Executes HTTP operations and handles GNS3-specific error logic. + """ + + # Handle JWT authentication + if ( + self.auth_type == "jwt" + and not self.access_token + and self.user + and self.cred + ): + self._authenticate_v3() + + # Get request function (e.g., session.get, session.post) + caller = getattr(self.session, method.lower()) + + # Prepare request parameters, avoiding multiple repeated calls to caller + kwargs: dict[str, Any] = { + "headers": headers, + "params": params, + "verify": verify, + "timeout": 30.0, # Main request timeout (auth call uses 10s) + } + if data is not None: + kwargs["data"] = data + elif json_data is not None: + kwargs["json"] = json_data + + # Execute request + _response: requests.Response = caller(url, **kwargs) + + self.api_calls += 1 + + try: + _response.raise_for_status() + except HTTPError as e: + # Throw enhanced error + raise self._extract_gns3_error(e) from e + + return _response + + def _extract_gns3_error(self, e: HTTPError) -> HTTPError: + """ + Extract GNS3-specific JSON error information from HTTPError. + If parsing fails, return the original error. + """ + # e.response might be None, need explicit check + response = e.response + if response is None: + return e + + try: + # Only attempt parsing when Content-Type is JSON + if ( + "application/json" + in response.headers.get("Content-Type", "").lower() + ): + error_json = response.json() + status = error_json.get("status", "Unknown Status") + message = error_json.get( + "message", "No message provided in JSON." + ) + # Construct a more descriptive new error + new_err = HTTPError( + f"{status}: {message} (Original {response.status_code} Error)", + response=response, + ) + return new_err + except Exception: + # If JSON parsing fails, return error with original text + return HTTPError( + f"Original Error: {str(e)}. GNS3 response text: {response.text}", + response=response, + ) + return e diff --git a/gns3server/agent/gns3_copilot/gns3_client/connector_factory.py b/gns3server/agent/gns3_copilot/gns3_client/connector_factory.py index a2144b2b9..724291782 100644 --- a/gns3server/agent/gns3_copilot/gns3_client/connector_factory.py +++ b/gns3server/agent/gns3_copilot/gns3_client/connector_factory.py @@ -29,6 +29,10 @@ GNS3 Connector Factory Module This module provides factory functions for creating Gns3Connector instances with JWT token authentication and context-aware configuration management. +⚠️ WARNING: This module is shared with the MCP (Model Context Protocol) service. +The get_gns3_connector() function is used by both gns3-copilot and MCP. +Modifications must be tested with BOTH. + Features: - Context variable based request-scoped data management (JWT tokens, LLM config) @@ -50,7 +54,7 @@ from gns3server.agent.gns3_copilot.gns3_client.context_helpers import ( ) # Local imports -from gns3server.agent.gns3_copilot.gns3_client.custom_gns3fy import ( +from gns3server.agent.gns3_copilot.gns3_client.connector import ( Gns3Connector, ) diff --git a/gns3server/agent/gns3_copilot/gns3_client/custom_gns3fy.py b/gns3server/agent/gns3_copilot/gns3_client/custom_gns3fy.py deleted file mode 100644 index cb3770d9f..000000000 --- a/gns3server/agent/gns3_copilot/gns3_client/custom_gns3fy.py +++ /dev/null @@ -1,3032 +0,0 @@ -# SPDX-License-Identifier: GPL-3.0-or-later -# -# GNS3-Copilot - AI-powered Network Lab Assistant for GNS3 -# -# This file is part of GNS3-Copilot project. -# -# GNS3-Copilot is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# GNS3-Copilot is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -# You should have received a copy of the GNU General Public License -# along with GNS3-Copilot. If not, see . -# -# Copyright (C) 2025 Yue Guobin (岳国宾) -# Author: Yue Guobin (岳国宾) -# -# Project Home: https://github.com/yueguobin/gns3-copilot -# - -""" -Adapted gns3fy module for GNS3-Copilot - -This module is based on the upstream gns3fy project -(https://github.com/davidban77/gns3fy). - -Modifications made for GNS3-Copilot: -- Adjusted pydantic usages and dataclass configuration to reduce dependency - conflicts with langchain (pydantic version/api differences) -- Kept the original API surface where possible but simplified - validators/config -- Added JWT token authentication support -- Integrated with context-aware connector factory - -Note: This file is adapted from upstream gns3fy for compatibility with -GNS3-Copilot's architecture. - -Upstream: https://github.com/davidban77/gns3fy -""" - -import os -import time -from collections.abc import Callable -from dataclasses import field -from functools import wraps -from math import cos -from math import pi -from math import sin -from typing import Any -from typing import ParamSpec -from typing import TypeVar -from typing import cast -from urllib.parse import urlparse - -import jwt -import requests -import urllib3 -from pydantic import ConfigDict -from pydantic import field_validator -from pydantic.dataclasses import dataclass -from requests import HTTPError - -P = ParamSpec("P") -R = TypeVar("R") -F = TypeVar("F", bound=Callable[..., Any]) - -config = ConfigDict(validate_assignment=True, extra="ignore") - -NODE_TYPES = [ - "cloud", - "nat", - "ethernet_hub", - "ethernet_switch", - "frame_relay_switch", - "atm_switch", - "docker", - "dynamips", - "vpcs", - "traceng", - "virtualbox", - "vmware", - "iou", - "qemu", -] - -CONSOLE_TYPES = [ - "vnc", - "telnet", - "http", - "https", - "spice", - "spice+agent", - "none", - "null", -] - -LINK_TYPES = ["ethernet", "serial"] - - -class Gns3Connector: - """ - Connector to be use for interaction against GNS3 server controller API. - - **Attributes:** - - - `url` (str): URL of the GNS3 server (**required**) - - `user` (str): User used for authentication - - `cred` (str): Password used for authentication - - `jwt_token` (str): JWT token for direct authentication (API v3) - - `verify` (bool): Whether or not to verify SSL - - `api_version` (int): GNS3 server REST API version - - `api_calls`: Counter of amount of `http_calls` has been performed - - `base_url`: url passed + api_version - - `session`: Requests Session object - - **Returns:** - - `Gns3Connector` instance - - **Example:** - - ```python - >>> # API v2 with basic auth - >>> server = Gns3Connector( - ... url="http://
:3080", user="admin", cred="password", - ... api_version=2 - ... ) - >>> # API v3 with username/password (auto-fetches JWT token) - >>> server = Gns3Connector( - ... url="http://
:3080", user="admin", cred="password", - ... api_version=3 - ... ) - >>> # API v3 with direct JWT token - >>> server = Gns3Connector( - ... url="http://
:3080", - ... jwt_token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", - ... api_version=3 - ... ) - >>> print(server.get_version()) - {'local': False, 'version': '2.2.0b4'} - ``` - """ - - access_token: str | None - token_expiry: float | None - - def __init__( - self, - url: str | None = None, - user: str | None = None, - cred: str | None = None, - jwt_token: str | None = None, - verify: bool = False, - api_version: int = 2, - ) -> None: - # Disable SSL warnings - urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) - - if url is None: - raise ValueError("URL is required for Gns3Connector") - self.url = url.strip("/") # Store original URL for reference - self.base_url = f"{self.url}/v{api_version}" - self.user = user - self.cred = cred - self.headers = {"Content-Type": "application/json"} - self.verify = verify - self.api_calls = 0 - - # v3 authentication attributes - # If jwt_token is provided directly, use it; otherwise will be - # fetched via username/password - self.access_token = jwt_token - self.token_expiry = None - self.auth_type = "basic" if api_version == 2 else "jwt" - self.api_version = api_version - - # Create session object - self._create_session() - - def _create_session(self) -> None: - """ - Creates the requests.Session object and applies the necessary parameters - """ - self.session = requests.Session() # pragma: no cover - self.session.headers["Accept"] = "application/json" # pragma: no cover - - # Set authentication based on API version - if ( - self.auth_type == "basic" - and self.user is not None - and self.cred is not None - ): - self.session.auth = (self.user, self.cred) # pragma: no cover - - elif self.auth_type == "jwt" and self.access_token: - self.session.headers["Authorization"] = ( - f"Bearer {self.access_token}" - ) - - def _authenticate_v3(self) -> None: - """ - Performs v3 API authentication using username and password to get JWT token. - Skips authentication if a JWT token is already provided. - """ - # If token is already provided, skip authentication - if self.access_token: - return - - if not self.user or not self.cred: - raise ValueError( - "Username and password are required for v3 authentication " - "when no JWT token is provided" - ) - - # Construct authentication URL (v3 API uses different base URL) - auth_url = ( - f"{self.base_url.replace('/v3', '')}/v3/access/users/authenticate" - ) - auth_data = {"username": self.user, "password": self.cred} - - # Use temporary session for authentication - temp_session = requests.Session() - temp_session.headers["Content-Type"] = "application/json" - - try: - response = temp_session.post( - auth_url, json=auth_data, verify=self.verify, timeout=10.0 - ) - if response.status_code == 200: - auth_result = response.json() - self.access_token = auth_result["access_token"] - # Update session with new token - self.session.headers["Authorization"] = ( - f"Bearer {self.access_token}" - ) - # print(f"Successfully authenticated to v3 API, token obtained") - else: - raise HTTPError( - f"v3 API authentication failed: {response.status_code} - " - f"{response.text}" - ) - except Exception as e: - raise HTTPError(f"v3 API authentication error: {str(e)}") from e - - def _is_token_expired(self) -> bool: - """ - Check if the JWT token is expired (basic implementation) - """ - token = self.access_token - if not token: - return True - - try: - # Decode token without verification to check expiry - decoded: dict[str, Any] = jwt.decode( - token, options={"verify_signature": False} - ) - exp = decoded.get("exp") - if exp is not None: - return time.time() > float(exp) - return False - except (jwt.PyJWTError, ValueError, TypeError): - return True - - def _refresh_token(self) -> None: - """ - Refresh the JWT token (for now, just re-authenticate) - """ - print("Refreshing v3 API token...") - self._authenticate_v3() - - def http_call( - self, - method: str, - url: str, - data: Any | None = None, - json_data: dict[str, Any] | list[Any] | None = None, - headers: dict[str, str] | None = None, - verify: bool = False, - params: dict[str, Any] | None = None, - ) -> requests.Response: - """ - Executes HTTP operations and handles GNS3-specific error logic. - """ - # Handle JWT authentication - if ( - self.auth_type == "jwt" - and not self.access_token - and self.user - and self.cred - ): - self._authenticate_v3() - - # Get request function (e.g., session.get, session.post) - caller = getattr(self.session, method.lower()) - - # Prepare request parameters, avoiding multiple repeated calls to caller - kwargs: dict[str, Any] = { - "headers": headers, - "params": params, - "verify": verify, - "timeout": 10.0, # Fixed 10-second timeout for all GNS3 API requests - } - if data is not None: - kwargs["data"] = data - elif json_data is not None: - kwargs["json"] = json_data - - # Execute request - _response: requests.Response = caller(url, **kwargs) - - self.api_calls += 1 - - try: - _response.raise_for_status() - except HTTPError as e: - # Throw enhanced error - raise self._extract_gns3_error(e) from e - - return _response - - def _extract_gns3_error(self, e: HTTPError) -> HTTPError: - """ - Extract GNS3-specific JSON error information from HTTPError. - If parsing fails, return the original error. - """ - # e.response might be None, need explicit check - response = e.response - if response is None: - return e - - try: - # Only attempt parsing when Content-Type is JSON - if ( - "application/json" - in response.headers.get("Content-Type", "").lower() - ): - error_json = response.json() - status = error_json.get("status", "Unknown Status") - message = error_json.get( - "message", "No message provided in JSON." - ) - # Construct a more descriptive new error - new_err = HTTPError( - f"{status}: {message} (Original {response.status_code} Error)", - response=response, - ) - return new_err - except Exception: - # If JSON parsing fails, return error with original text - return HTTPError( - f"Original Error: {str(e)}. GNS3 response text: {response.text}", - response=response, - ) - return e - - def get_version(self) -> dict[str, Any]: - """ - Returns the version information of GNS3 server - """ - response = self.http_call("get", url=f"{self.base_url}/version") - return cast(dict[str, Any], response.json()) - - def projects_summary( - self, is_print: bool = True - ) -> list[tuple[str, str, int, int, str]] | None: - """ - Returns a summary of the projects in the server. If `is_print` is `False`, it - will return a list of tuples like: - - `[(name, project_id, total_nodes, total_links, status) ...]` - """ - _projects_summary = [] - for _p in self.get_projects(): - # Retrieve the project stats - _stats = self.http_call( - "get", f"{self.base_url}/projects/{_p['project_id']}/stats" - ).json() - if is_print: - print( - f"{_p['name']}: {_p['project_id']} -- Nodes: {_stats['nodes']} -- " - f"Links: {_stats['links']} -- Status: {_p['status']}" - ) - _projects_summary.append( - ( - _p["name"], - _p["project_id"], - _stats["nodes"], - _stats["links"], - _p["status"], - ) - ) - - return _projects_summary if not is_print else None - - def get_projects(self) -> list[dict[str, Any]]: - """ - Returns the list of the projects on the server - """ - response = self.http_call( - "get", url=f"{self.base_url}/projects" - ).json() - return cast(list[dict[str, Any]], response) - - def get_project( - self, name: str | None = None, project_id: str | None = None - ) -> dict[str, Any] | None: - """ - Retrieves a project from either a name or ID - - **Required Attributes:** - - - `name` or `project_id` - """ - if project_id: - _response = self.http_call( - "get", url=f"{self.base_url}/projects/{project_id}" - ) - return cast(dict[str, Any], _response.json()) - elif name: - try: - return next( - p for p in self.get_projects() if p["name"] == name - ) - except StopIteration: - # Project not found - return None - else: - raise ValueError("Must provide either a name or project_id") - - def templates_summary( - self, is_print: bool = True - ) -> list[tuple[str, str, str, bool, str, str]] | None: - """ - Returns a summary of the templates in the server. If `is_print` is `False`, it - will return a list of tuples like: - - `[(name, template_id, template_type, builtin, console_type, category) ...]` - """ - _templates_summary = [] - for _t in self.get_templates(): - if "console_type" not in _t: - _t["console_type"] = "N/A" - if is_print: - print( - f"{_t['name']}: {_t['template_id']} -- Type: {_t['template_type']}" - f" -- Builtin: {_t['builtin']} -- Console: {_t['console_type']} -- " - f"Category: {_t['category']}" - ) - _templates_summary.append( - ( - _t["name"], - _t["template_id"], - _t["template_type"], - _t["builtin"], - _t["console_type"], - _t["category"], - ) - ) - - return _templates_summary if not is_print else None - - def get_templates(self) -> list[dict[str, Any]]: - """ - Returns the templates defined on the server. - """ - _response_data = self.http_call( - "get", url=f"{self.base_url}/templates" - ).json() - return cast(list[dict[str, Any]], _response_data) - - def get_template( - self, name: str | None = None, template_id: str | None = None - ) -> dict[str, Any] | None: - """ - Retrieves a template from either a name or ID - - **Required Attributes:** - - - `name` or `template_id` - """ - if template_id: - _response_json = self.http_call( - "get", url=f"{self.base_url}/templates/{template_id}" - ).json() - return cast(dict[str, Any], _response_json) - elif name: - try: - return next( - t for t in self.get_templates() if t["name"] == name - ) - except StopIteration: - # Template name not found - return None - else: - raise ValueError("Must provide either a name or template_id") - - def update_template( - self, - name: str | None = None, - template_id: str | None = None, - **kwargs: Any, - ) -> dict[str, Any]: - """ - Updates a template by giving its name or UUID. For more information [API INFO] - (http://api.gns3.net/en/2.2/api/v2/controller/template/ - templatestemplateid.html#put-v2-templates-template-id) - - **Required Attributes:** - - - `name` or `template_id` - - **Optional Attributes (can be passed via kwargs):** - - - `tags` (list): List of tags for the template (e.g., - ["device_type:cisco_ios_telnet", "platform:cisco_ios"]) - - Any other template attributes supported by GNS3 API - """ - # Get existing template - _template = self.get_template(name=name, template_id=template_id) - # Type check: handle case where get_template might return None - if _template is None: - raise ValueError( - f"Template not found (name={name}, id={template_id})" - ) - # Update local dictionary and send request - _template.update(**kwargs) - - response = self.http_call( - "put", - url=f"{self.base_url}/templates/{_template['template_id']}", - json_data=_template, - ) - # Return JSON and handle Any type errors - return cast(dict[str, Any], response.json()) - - def create_template(self, **kwargs: Any) -> dict[str, Any]: - """ - Creates a template by giving its attributes. For more information [API INFO] - (http://api.gns3.net/en/2.2/api/v2/controller/template/ - templates.html#post-v2-templates) - - **Required Attributes:** - - - `name` - - `compute_id` by default is 'local' - - `template_type` - - **Optional Attributes (can be passed via kwargs):** - - - `tags` (list): List of tags for the template (e.g., - ["device_type:cisco_ios_telnet", "platform:cisco_ios"]) - - Any other template attributes supported by GNS3 API - - **Example:** - - ```python - >>> connector.create_template( - ... name="cisco_router", - ... template_type="dynamips", - ... tags=["device_type:cisco_ios_telnet", "platform:cisco_ios"] - ... ) - ``` - """ - # kwargs["name"] might raise KeyError at runtime, for more robust - # code we can use get first - template_name = kwargs.get("name") - if not template_name: - raise ValueError( - "Attribute 'name' is required to create a template" - ) - - # Check if template already exists - _template = self.get_template(name=kwargs["name"]) - if _template: - raise ValueError(f"Template already used: {kwargs['name']}") - - # Set default values - if "compute_id" not in kwargs: - kwargs["compute_id"] = "local" - - # Send request - response = self.http_call( - "post", url=f"{self.base_url}/templates", json_data=kwargs - ) - # Return and convert type - return cast(dict[str, Any], response.json()) - - def delete_template( - self, name: str | None = None, template_id: str | None = None - ) -> None: - """ - Deletes a template by giving its attributes. For more information [API INFO] - (http://api.gns3.net/en/2.2/api/v2/controller/template/ - templatestemplateid.html#id16) - - **Required Attributes:** - - - `name` or `template_id` - """ - # Logic handling: if only name is given, need to first get template_id - if name and not template_id: - _template = self.get_template(name=name) - # Type narrowing: check if _template is None - if _template is None: - raise ValueError(f"Template with name '{name}' not found.") - - template_id = _template["template_id"] - - # Final check: ensure template_id has a value at this point - if not template_id: - raise ValueError( - "Must provide either a 'name' or 'template_id' to delete a template." - ) - - self.http_call( - "delete", url=f"{self.base_url}/templates/{template_id}" - ) - - def get_nodes(self, project_id: str) -> list[dict[str, Any]]: - """ - Retieves the nodes defined on the project - - **Required Attributes:** - - - `project_id` - """ - _response_data = self.http_call( - "get", url=f"{self.base_url}/projects/{project_id}/nodes" - ).json() - - return cast(list[dict[str, Any]], _response_data) - - def get_node(self, project_id: str, node_id: str) -> dict[str, Any]: - """ - Returns the node by locating its ID. - - **Required Attributes:** - - - `project_id` - - `node_id` - """ - _url = f"{self.base_url}/projects/{project_id}/nodes/{node_id}" - _response_data = self.http_call("get", _url).json() - return cast(dict[str, Any], _response_data) - - def get_links(self, project_id: str) -> list[dict[str, Any]]: - """ - Retrieves the links defined in the project. - - **Required Attributes:** - - - `project_id` - """ - _response_data = self.http_call( - "get", url=f"{self.base_url}/projects/{project_id}/links" - ).json() - - return cast(list[dict[str, Any]], _response_data) - - def get_link(self, project_id: str, link_id: str) -> dict[str, Any]: - """ - Returns the link by locating its ID. - - **Required Attributes:** - - - `project_id` - - `link_id` - """ - _url = f"{self.base_url}/projects/{project_id}/links/{link_id}" - _response_data = self.http_call("get", _url).json() - - return cast(dict[str, Any], _response_data) - - def create_project(self, **kwargs: Any) -> dict[str, Any]: - """ - Pass a dictionary type object with the project parameters to be created. - - **Required Attributes:** - - - `name` - - **Returns** - - JSON project information - """ - _url = f"{self.base_url}/projects" - if "name" not in kwargs: - raise ValueError("Parameter 'name' is mandatory") - _response = self.http_call("post", _url, json_data=kwargs) - - return cast(dict[str, Any], _response.json()) - - def delete_project(self, project_id: str) -> None: - """ - Deletes a project from server. - - **Required Attributes:** - - - `project_id` - """ - _url = f"{self.base_url}/projects/{project_id}" - self.http_call("delete", _url) - return None - - def get_computes(self) -> list[dict[str, Any]]: - """ - Returns a list of computes. - - **Returns:** - - List of dictionaries of the computes attributes like cpu/memory usage - """ - _url = f"{self.base_url}/computes" - _response_data = self.http_call("get", _url).json() - - return cast(list[dict[str, Any]], _response_data) - - def get_compute(self, compute_id: str = "local") -> dict[str, Any]: - """ - Returns a compute. - - **Returns:** - - Dictionary of the compute attributes like cpu/memory usage - """ - _url = f"{self.base_url}/computes/{compute_id}" - _response_data = self.http_call("get", _url).json() - - return cast(dict[str, Any], _response_data) - - def get_compute_images( - self, emulator: str, compute_id: str = "local" - ) -> list[dict[str, Any]]: - """ - Returns a list of images available for a compute. - - **Required Attributes:** - - - `emulator`: the likes of 'qemu', 'iou', 'docker' ... - - `compute_id` By default is 'local' - - **Returns:** - - List of dictionaries with images available for the compute for the specified - emulator - """ - _url = f"{self.base_url}/computes/{compute_id}/{emulator}/images" - _response_data = self.http_call("get", _url).json() - - return cast(list[dict[str, Any]], _response_data) - - def upload_compute_image( - self, emulator: str, file_path: str, compute_id: str = "local" - ) -> None: - """ - uploads an image for use by a compute. - - **Required Attributes:** - - - `emulator`: the likes of 'qemu', 'iou', 'docker' ... - - `file_path`: path of file to be uploaded - - `compute_id` By default is 'local' - """ - if not os.path.exists(file_path): - raise FileNotFoundError(f"Could not find file: {file_path}") - - _filename = os.path.basename(file_path) - _url = f"{self.base_url}/computes/{compute_id}/{emulator}/images/{_filename}" - with open(file_path, "rb") as f: - self.http_call("post", _url, data=f) - - return None - - def get_compute_ports(self, compute_id: str = "local") -> dict[str, Any]: - """ - Returns ports used and configured by a compute. - - **Required Attributes:** - - - `compute_id` By default is 'local' - - **Returns:** - - Dictionary of `console_ports` used and range, as well as the `udp_ports` - """ - _url = f"{self.base_url}/computes/{compute_id}/ports" - _response_data = self.http_call("get", _url).json() - - return cast(dict[str, Any], _response_data) - - -def verify_connector_and_id(f: F) -> F: - """ - Main checker for connector object and respective object's ID for their retrieval - or actions methods. - """ - - @wraps(f) - def wrapper(self: Any, *args: Any, **kwargs: Any) -> Any: - _conn = self.connector - _project_id = self.project_id - - if _conn is None: - raise ValueError("Gns3Connector not assigned under 'connector'") - if _project_id is None: - raise ValueError("Need to submit project_id") - - # Checks for Node - if self.__class__.__name__ == "Node": - if not self.node_id: - if not self.name: - raise ValueError("Need to either submit node_id or name") - - # Try to retrieve the node_id - _url = f"{_conn.base_url}/projects/{_project_id}/nodes" - _response = _conn.http_call("get", _url) - - extracted = [ - node - for node in _response.json() - if node["name"] == self.name - ] - if len(extracted) > 1: # pragma: no cover - raise ValueError( - "Multiple nodes found with same name. Need to submit node_id" - ) - self.node_id = extracted[0]["node_id"] - # Checks for Link - if self.__class__.__name__ == "Link": - if not self.link_id: - raise ValueError("Need to submit link_id") - return f(self, *args, **kwargs) - - return cast(F, wrapper) - - -@dataclass(config=config) -class Link: - """ - GNS3 Link API object. For more information visit: [Links Endpoint API information]( - http://api.gns3.net/en/2.2/api/v2/controller/link/projectsprojectidlinks.html) - - **Attributes:** - - - `link_id` (str): Link UUID (**required** to be set when using `get` method) - - `link_type` (enum): Possible values: ethernet, serial - - `link_style` (dict): Describes the visual style of the link - - `project_id` (str): Project UUID (**required**) - - `connector` (object): `Gns3Connector` instance used for interaction (**required**) - - `suspend` (bool): Suspend the link - - `nodes` (list): List of the Nodes and ports (**required** when using `create` - method, see Features/Link creation on the docs) - - `filters` (dict): Packet filter. This allow to simulate latency and errors - - `capturing` (bool): Read only property. True if a capture running on the link - - `capture_file_path` (str): Read only property. The full path of the capture file - if capture is running - - `capture_file_name` (str): Read only property. The name of the capture file if - capture is running - - **Returns:** - - `Link` instance - - **Example:** - - ```python - >>> link = Link(project_id=, link_id= connector=) - >>> link.get() - >>> print(link.link_type) - 'ethernet' - ``` - """ - - link_id: str | None = None - link_type: str | None = None - link_style: Any | None = None - project_id: str | None = None - suspend: bool | None = None - nodes: list[Any] | None = None - filters: dict | None = None - capturing: bool | None = None - capture_file_path: str | None = None - capture_file_name: str | None = None - capture_compute_id: str | None = None - - connector: Any | None = field(default=None, repr=False) - - @field_validator("link_type") - @classmethod - def _valid_link_type(cls, value: str | None) -> str | None: - if value not in LINK_TYPES and value is not None: - raise ValueError(f"Not a valid link_type - {value}") - return value - - @field_validator("suspend") - @classmethod - def _valid_suspend(cls, value: bool | None) -> bool | None: - if type(value) is not bool and value is not None: - raise ValueError(f"Not a valid suspend - {value}") - return value - - @field_validator("filters") - @classmethod - def _valid_filters( - cls, value: dict[str, Any] | None - ) -> dict[str, Any] | None: - if type(value) is not dict and value is not None: - raise ValueError(f"Not a valid filters - {value}") - return value - - def _update(self, data_dict: dict[str, Any]) -> None: - for k, v in data_dict.items(): - if k in self.__dict__.keys(): - self.__setattr__(k, v) - - @verify_connector_and_id - def get(self) -> None: - """ - Retrieves the information from the link endpoint. - - **Required Attributes:** - - - `project_id` - - `connector` - - `link_id` - """ - _conn = self.connector - _project_id = self.project_id - - if _conn is None: - raise ValueError("Gns3Connector not assigned under 'connector'") - if _project_id is None: - raise ValueError("Need to submit project_id") - - _url = f"{_conn.base_url}/projects/{_project_id}/links/{self.link_id}" - _response = _conn.http_call("get", _url) - - # Update object - self._update(_response.json()) - - @verify_connector_and_id - def delete(self) -> None: - """ - Deletes a link endpoint from the project. It sets to `None` the attributes - `link_id` when executed sucessfully - - **Required Attributes:** - - - `project_id` - - `connector` - - `link_id` - """ - _conn = self.connector - _project_id = self.project_id - _link_id = self.link_id - - if _conn is None: - raise ValueError("Gns3Connector not assigned under 'connector'") - if _project_id is None: - raise ValueError("Need to submit project_id") - if _link_id is None: - raise ValueError( - "Link ID is missing. The link might have already been deleted." - ) - - _url = f"{_conn.base_url}/projects/{_project_id}/links/{self.link_id}" - - _conn.http_call("delete", _url) - - self.project_id = None - self.link_id = None - - def create(self) -> None: - """ - Creates a link endpoint - - **Required Attributes:** - - - `project_id` - - `connector` - - `nodes` - """ - if not self.connector: - raise ValueError("Gns3Connector not assigned under 'connector'") - if not self.project_id: - raise ValueError("Need to submit project_id") - - _url = f"{self.connector.base_url}/projects/{self.project_id}/links" - - data = { - k: v - for k, v in self.__dict__.items() - if k not in ("connector", "__initialised__") - if v is not None - } - - _response = self.connector.http_call("post", _url, json_data=data) - - # Now update it - self._update(_response.json()) - - @verify_connector_and_id - def update(self, **kwargs: Any) -> None: - """ - Updates the link instance by passing the keyword arguments of the attributes - you want updated - - Example: - - ```python - link1.update(suspend=True) - ``` - - This will update the link `suspend` attribute to `True` - - **Required Attributes:** - - - `project_id` - - `connector` - - `link_id` - """ - if not self.connector: - raise ValueError("Gns3Connector not assigned under 'connector'") - if not self.project_id: - raise ValueError("Need to submit project_id") - - _url = ( - f"{self.connector.base_url}/projects/{self.project_id}/links/" - f"{self.link_id}" - ) - - # TODO: Verify that the passed kwargs are supported ones - _response = self.connector.http_call("put", _url, json_data=kwargs) - - # Update object - self._update(_response.json()) - - -@dataclass(config=config) -class Node: - """ - GNS3 Node API object. For more information visit: [Node Endpoint API information]( - http://api.gns3.net/en/2.2/api/v2/controller/node/projectsprojectidnodes.html) - - **Attributes:** - - - `name` (str): Node name (**required** when using `create` method) - - `project_id` (str): Project UUID (**required**) - - `node_id` (str): Node UUID (**required** when using `get` method) - - `compute_id` (str): Compute identifier (**required**, default=local) - - `node_type` (enum): frame_relay_switch, atm_switch, docker, dynamips, vpcs, - traceng, virtualbox, vmware, iou, qemu (**required** when using `create` method) - - `connector` (object): `Gns3Connector` instance used for interaction (**required**) - - `template_id`: Template UUID from the which the node is from. - - `template`: Template name from the which the node is from. - - `node_directory` (str): Working directory of the node. Read only - - `status` (enum): Possible values: stopped, started, suspended - - `ports` (list): List of node ports, READ only - - `port_name_format` (str): Formating for port name {0} will be replace by port - number - - `port_segment_size` (int): Size of the port segment - - `first_port_name` (str): Name of the first port - - `properties` (dict): Properties specific to an emulator - - `locked` (bool): Whether the element locked or not - - `label` (dict): TBC - - `console` (int): Console TCP port - - `console_host` (str): Console host - - `console_auto_start` (bool): Automatically start the console when the node has - started - - `command_line` (str): Command line use to start the node - - `custom_adapters` (list): TBC - - `height` (int): Height of the node, READ only - - `width` (int): Width of the node, READ only - - `symbol` (str): Symbol of the node - - `x` (int): X position of the node - - `y` (int): Y position of the node - - `z (int): Z position of the node - - **Returns:** - - `Node` instance - - **Example:** - - ```python - >>> alpine = Node(name="alpine1", node_type="docker", template="alpine", - project_id=, connector=) - >>> alpine.create() - >>> print(alpine.node_id) - 'SOME-UUID-GENERATED' - ``` - """ - - name: str | None = None - project_id: str | None = None - node_id: str | None = None - compute_id: str = "local" - node_type: str | None = None - node_directory: str | None = None - status: str | None = None - ports: list | None = None - port_name_format: str | None = None - port_segment_size: int | None = None - first_port_name: str | None = None - locked: bool | None = None - label: Any | None = None - console: int | None = None - console_host: str | None = None - console_type: str | None = None - console_auto_start: bool | None = None - command_line: str | None = None - custom_adapters: list[Any] | None = None - height: int | None = None - width: int | None = None - symbol: str | None = None - x: int | None = None - y: int | None = None - z: int | None = None - template_id: str | None = None - properties: Any | None = None - tags: list[str] | None = None - - template: str | None = None - links: list[Link] = field(default_factory=list, repr=False) - connector: Any | None = field(default=None, repr=False) - - @field_validator("node_type") - @classmethod - def _valid_node_type(cls, value: Any) -> Any: - if value not in NODE_TYPES and value is not None: - raise ValueError(f"Not a valid node_type - {value}") - return value - - @field_validator("console_type") - @classmethod - def _valid_console_type(cls, value: Any) -> Any: - if value not in CONSOLE_TYPES and value is not None: - raise ValueError(f"Not a valid console_type - {value}") - return value - - @field_validator("status") - @classmethod - def _valid_status(cls, value: Any) -> Any: - if ( - value not in ("stopped", "started", "suspended") - and value is not None - ): - raise ValueError(f"Not a valid status - {value}") - return value - - def _update(self, data_dict: dict[str, Any]) -> None: - for k, v in data_dict.items(): - if k in self.__dict__: - setattr(self, k, v) - - @verify_connector_and_id - def get(self, get_links: bool = True) -> None: - """ - Retrieves the node information. When `get_links` is `True` it also retrieves the - links respective to the node. - - **Required Attributes:** - - - `project_id` - - `connector` - - `node_id` - """ - if not self.connector: - raise ValueError("Gns3Connector not assigned under 'connector'") - if not self.project_id: - raise ValueError("Need to submit project_id") - - _url = ( - f"{self.connector.base_url}/projects/{self.project_id}/nodes/" - f"{self.node_id}" - ) - _response = self.connector.http_call("get", _url) - - # Update object - self._update(_response.json()) - - if get_links: - self.get_links() - - @verify_connector_and_id - def get_links(self) -> None: - """ - Retrieves the links of the respective node. They will be saved at the `links` - attribute - - **Required Attributes:** - - - `project_id` - - `connector` - - `node_id` - """ - if not self.connector: - raise ValueError("Gns3Connector not assigned under 'connector'") - if not self.project_id: - raise ValueError("Need to submit project_id") - - _url = ( - f"{self.connector.base_url}/projects/{self.project_id}/nodes" - f"/{self.node_id}/links" - ) - _response = self.connector.http_call("get", _url) - - # Create the Link array but cleanup cache if there is one - if self.links: - self.links = [] - for _link in _response.json(): - self.links.append(Link(connector=self.connector, **_link)) - - @verify_connector_and_id - def start(self) -> bool | None: - """ - Starts the node. - - **Required Attributes:** - - - `project_id` - - `connector` - - `node_id` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}/nodes/{self.node_id}/start" - if "v2" in _url.lower(): # api_version 2 - _response = _conn.http_call( - "post", - _url, - ) - - # Update object or perform get if change was not reflected - if _response.json().get("status") == "started": - self._update(_response.json()) - else: - self.get() # pragma: no cover - - return True - - else: - # api_version 3 - _response = _conn.http_call( - "post", _url, json_data={"additionalProp1": {}} - ) - # successful response code 204 - if _response.status_code in (204,): - self.get() - return True - else: - try: - error_detail = _response.json() - except Exception: - error_detail = getattr( - _response, "text", "No response body" - ) - - _msg = ( - "Failed to start node: " - f"{getattr(_response, 'status_code', 'Unknown Status')}, " - f"Detail: {error_detail}" - ) - raise RuntimeError(_msg) from None - - @verify_connector_and_id - def stop(self) -> bool | None: - """ - Stops the node. - - **Required Attributes:** - - - `project_id` - - `connector` - - `node_id` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}/nodes/{self.node_id}/stop" - if "v2" in _url.lower(): # api_version 2 - _response = _conn.http_call( - "post", - _url, - ) - - # Update object or perform get if change was not reflected - if _response.json().get("status") == "stopped": - self._update(_response.json()) - else: - self.get() # pragma: no cover - - return True - else: - # api_version 3 - _response = _conn.http_call( - "post", _url, json_data={"additionalProp1": {}} - ) - # successful response code 204 - if _response.status_code in (204,): - self.get() - return True - else: - try: - error_detail = _response.json() - except Exception: - error_detail = _response.text - _msg = ( - f"Failed to stop node: {_response.status_code}, " - f"Detail: {error_detail}" - ) - raise RuntimeError(_msg) from None - - @verify_connector_and_id - def reload(self) -> bool | None: - """ - Reloads the node. - - **Required Attributes:** - - - `project_id` - - `connector` - - `node_id` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - _node_id = self.node_id - assert _node_id is not None - - _url = ( - f"{_conn.base_url}/projects/{_project_id}/nodes/{_node_id}/reload" - ) - _response = _conn.http_call("post", _url) - - if "v2" in _url.lower(): # api_version 2 - _response = _conn.http_call( - "post", - _url, - ) - - # Update object or perform get if change was not reflected - if _response.json().get("status") == "started": - self._update(_response.json()) - else: - self.get() # pragma: no cover - return True - - else: - # api_version 3 - _response = _conn.http_call( - "post", _url, json_data={"additionalProp1": {}} - ) - # successful response code 204 - if _response.status_code in (204,): - self.get() - return True - else: - try: - error_detail = _response.json() - except Exception: - error_detail = _response.text - _msg = ( - f"Failed to reload node: {_response.status_code}, " - f"Detail: {error_detail}" - ) - raise RuntimeError(_msg) from None - - @verify_connector_and_id - def suspend(self) -> None: - """ - Suspends the node. - - **Required Attributes:** - - - `project_id` - - `connector` - - `node_id` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - _node_id = self.node_id - assert _node_id is not None - - _url = ( - f"{_conn.base_url}/projects/{_project_id}/nodes/{_node_id}/suspend" - ) - _response = _conn.http_call("post", _url) - - # Update object or perform get if change was not reflected - if _response.json().get("status") == "suspended": - self._update(_response.json()) - else: - self.get() # pragma: no cover - - @verify_connector_and_id - def update(self, **kwargs: Any) -> None: - """ - Updates the node instance by passing the keyword arguments of the attributes - you want updated - - Example: - - ```python - router01.update(name="router01-CSX") - ``` - - This will update the project `auto_close` attribute to `True` - - **Required Attributes:** - - - `project_id` - - `connector` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - _node_id = self.node_id - assert _node_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}/nodes/{_node_id}" - - # TODO: Verify that the passed kwargs are supported ones - _response = _conn.http_call("put", _url, json_data=kwargs) - - # Update object - self._update(_response.json()) - - def create(self) -> None: - """ - Creates a node. - - By default it will fetch the nodes properties for creation based on the - `template` or `template_id` attribute supplied. This can be overriden/updated - by sending a dictionary of the properties under `extra_properties`. - - **Required Node instance attributes:** - - - `project_id` - - `connector` - - `compute_id`: Defaults to "local" - - `template` or `template_id` - if not passed as arguments - """ - if self.node_id: - raise ValueError("Node already created") - if not self.connector: - raise ValueError("Gns3Connector not assigned under 'connector'") - if not self.project_id: - raise ValueError("Node object needs to have project_id attribute") - if not self.template_id: - if self.template: - _template = self.connector.get_template(name=self.template) - if _template is None: - raise ValueError(f"Template {self.template} not found") - self.template_id = self.connector.get_template( - name=self.template - ).get("template_id") - else: - raise ValueError("Need either 'template' of 'template_id'") - - cached_data = { - k: v - for k, v in self.__dict__.items() - if k - not in ( - "project_id", - "template", - "template_id", - "links", - "connector", - "__initialised__", - ) - if v is not None - } - - _url = ( - f"{self.connector.base_url}/projects/{self.project_id}/" - f"templates/{self.template_id}" - ) - - _response = self.connector.http_call( - "post", - _url, - json_data={"x": 0, "y": 0, "compute_id": self.compute_id}, - ) - - self._update(_response.json()) - - # Update the node attributes based on cached data - self.update(**cached_data) - - @verify_connector_and_id - def delete(self) -> None: - """ - Deletes the node from the project. It sets to `None` the attributes `node_id` - and `name` when executed successfully - - **Required Attributes:** - - - `project_id` - - `connector` - - `node_id` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - _node_id = self.node_id - assert _node_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}/nodes/{_node_id}" - - _conn.http_call("delete", _url) - - self.project_id = None - self.node_id = None - self.name = None - - @verify_connector_and_id - def get_file(self, path: str) -> str: - """ - Retrieve a file in the node directory. - - **Required Attributes:** - - - `project_id` - - `connector` - - `path`: Node's relative path of the file - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - _node_id = self.node_id - assert _node_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}/nodes/{_node_id}/files/{path}" - - return cast(str, _conn.http_call("get", _url).text) - - @verify_connector_and_id - def write_file(self, path: str, data: Any) -> None: - """ - Places a file content on a specified node file path. Used mainly for docker - images. - - Example to update an alpine docker network interfaces: - - ```python - >>> data = ''' - auto eth0 - iface eth0 inet dhcp - ''' - - >>> alpine_node.write_file(path='/etc/network/interfaces', data=data) - ``` - - **Required Attributes:** - - - `project_id` - - `connector` - - `path`: Node's relative path of the file - - `data`: Data to be included in the file - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - _node_id = self.node_id - assert _node_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}/nodes/{_node_id}/files/{path}" - - _conn.http_call("post", _url, data=data) - - -@dataclass(config=config) -class Project: - """ - GNS3 Project API object. For more information visit: [Project Endpoint API - information](http://api.gns3.net/en/2.2/api/v2/controller/project/projects.html) - - **Attributes:** - - - `name`: Project name (**required** when using `create` method) - - `project_id` (str): Project UUID (**required**) - - `connector` (object): `Gns3Connector` instance used for interaction (**required**) - - `status` (enum): Possible values: opened, closed - - `path` (str): Path of the project on the server - - `filename` (str): Project filename - - `auto_start` (bool): Project start when opened - - `auto_close` (bool): Project auto close when client cut off the notifications feed - - `auto_open` (bool): Project open when GNS3 start - - `drawing_grid_size` (int): Grid size for the drawing area for drawings - - `grid_size` (int): Grid size for the drawing area for nodes - - `scene_height` (int): Height of the drawing area - - `scene_width` (int): Width of the drawing area - - `show_grid` (bool): Show the grid on the drawing area - - `show_interface_labels` (bool): Show interface labels on the drawing area - - `show_layers` (bool): Show layers on the drawing area - - `snap_to_grid` (bool): Snap to grid on the drawing area - - `supplier` (dict): Supplier of the project - - `variables` (list): Variables required to run the project - - `zoom` (int): Zoom of the drawing area - - `stats` (dict): Project stats - -.`drawings` (list): List of drawings present on the project - - `nodes` (list): List of `Node` instances present on the project - - `links` (list): List of `Link` instances present on the project - - **Returns:** - - `Project` instance - - **Example:** - - ```python - >>> lab = Project(name="lab", connector=) - >>> lab.create() - >>> print(lab.status) - 'opened' - ``` - """ - - name: str | None = None - project_id: str | None = None - status: str | None = None - locked: bool | None = None - path: str | None = None - filename: str | None = None - auto_start: bool | None = None - auto_close: bool | None = None - auto_open: bool | None = None - drawing_grid_size: int | None = None - grid_size: int | None = None - scene_height: int | None = None - scene_width: int | None = None - show_grid: bool | None = None - show_interface_labels: bool | None = None - show_layers: bool | None = None - snap_to_grid: bool | None = None - supplier: Any | None = None - variables: list | None = None - zoom: int | None = None - - stats: dict[str, Any] | None = None - snapshots: list[dict] | None = None - drawings: list[dict] | None = None - nodes: list[Node] = field(default_factory=list, repr=False) - links: list[Link] = field(default_factory=list, repr=False) - connector: Any | None = field(default=None, repr=False) - - @field_validator("status") - @classmethod - def _valid_status(cls, value: Any) -> Any: - if value != "opened" and value != "closed" and value is not None: - raise ValueError("status must be opened or closed") - return value - - def _update(self, data_dict: dict[str, Any]) -> None: - for k, v in data_dict.items(): - if k in self.__dict__: - setattr(self, k, v) - - def get( - self, - get_links: bool = True, - get_nodes: bool = True, - get_stats: bool = True, - ) -> None: - """ - Retrieves the projects information. - - - `get_links`: When true it also queries for the links inside the project - - `get_nodes`: When true it also queries for the nodes inside the project - - `get_stats`: When true it also queries for the stats inside the project - - It `get_stats` is set to `True`, it also verifies if snapshots and drawings are - inside the project and stores them in their respective attributes - (`snapshots` and `drawings`) - - **Required Attributes:** - - - `connector` - - `project_id` or `name` - """ - if not self.connector: - raise ValueError("Gns3Connector not assigned under 'connector'") - - # Get projects if no ID was provided by the name - if not self.project_id: - if not self.name: - raise ValueError("Need to submit either project_id or name") - _url = f"{self.connector.base_url}/projects" - # Get all projects and filter the respective project - _response = self.connector.http_call("get", _url) - - # Filter the respective project - for _project in _response.json(): - if _project.get("name") == self.name: - self.project_id = _project.get("project_id") - - # Get project - _url = f"{self.connector.base_url}/projects/{self.project_id}" - _response = self.connector.http_call("get", _url) - - # Update object - self._update(_response.json()) - - if get_stats: - self.get_stats() - if self.stats is not None: - if self.stats.get("snapshots", 0) > 0: - self.get_snapshots() - if self.stats.get("drawings", 0) > 0: - self.get_drawings() - if get_nodes: - self.get_nodes() - if get_links: - self.get_links() - - def create(self) -> None: - """ - Creates the project. - - **Required Attributes:** - - - `name` - - `connector` - """ - if not self.name: - raise ValueError("Need to submit project name") - if not self.connector: - raise ValueError("Gns3Connector not assigned under 'connector'") - - _url = f"{self.connector.base_url}/projects" - - data = { - k: v - for k, v in self.__dict__.items() - if k - not in ( - "stats", - "nodes", - "links", - "connector", - "__initialised__", - ) - if v is not None - } - - _response = self.connector.http_call("post", _url, json_data=data) - - # Now update it - self._update(_response.json()) - - @verify_connector_and_id - def update(self, **kwargs: Any) -> None: - """ - Updates the project instance by passing the keyword arguments of the attributes - you want updated - - Example: - - ```python - lab.update(auto_close=True) - ``` - - This will update the project `auto_close` attribute to `True` - - **Required Attributes:** - - - `project_id` - - `connector` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}" - - # TODO: Verify that the passed kwargs are supported ones - _response = _conn.http_call("put", _url, json_data=kwargs) - - # Update object - self._update(_response.json()) - - @verify_connector_and_id - def delete(self) -> None: - """ - Deletes the project from the server. It sets to `None` the attributes - `project_id` and `name` when executed successfully - - **Required Attributes:** - - - `project_id` - - `connector` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}" - - _conn.http_call("delete", _url) - - self.project_id = None - self.name = None - - @verify_connector_and_id - def close(self) -> None: - """ - Closes the project on the server. - - **Required Attributes:** - - - `project_id` - - `connector` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}/close" - - _response = _conn.http_call("post", _url) - - # Update object - if _response.status_code == 204: - self.status = "closed" - - @verify_connector_and_id - def open(self) -> None: - """ - Opens the project on the server. - - **Required Attributes:** - - - `project_id` - - `connector` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}/open" - - _response = _conn.http_call("post", _url) - - # Update object - self._update(_response.json()) - - @verify_connector_and_id - def get_stats(self) -> None: - """ - Retrieve the stats of the project. - - **Required Attributes:** - - - `project_id` - - `connector` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}/stats" - - _response = _conn.http_call("get", _url) - - # Update object - self.stats = _response.json() - - @verify_connector_and_id - def get_file(self, path: str) -> str: - """ - Retrieve a file in the project directory. Beware you have warranty to be able to - access only to file global to the project. - - **Required Attributes:** - - - `project_id` - - `connector` - - `path`: Project's relative path of the file - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}/files/{path}" - - return cast(str, _conn.http_call("get", _url).text) - - @verify_connector_and_id - def write_file(self, path: str, data: Any) -> None: - """ - Places a file content on a specified project file path. Beware you have warranty - to be able to access only to file global to the project. - - Example to create a README.txt for the project: - - ```python - >>> data = ''' - This is a README description! - ''' - - >>> project.write_file(path='README.txt', data=data) - ``` - - **Required Attributes:** - - - `project_id` - - `connector` - - `path`: Project's relative path of the file - - `data`: Data to be included in the file - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}/files/{path}" - - _conn.http_call("post", _url, data=data) - - @verify_connector_and_id - def get_nodes(self) -> None: - """ - Retrieve the nodes of the project. - - **Required Attributes:** - - - `project_id` - - `connector` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}/nodes" - - _response = _conn.http_call("get", _url) - - # Create the Nodes array but cleanup cache if there is one - if self.nodes: - self.nodes = [] - for _node in _response.json(): - _n = Node(connector=self.connector, **_node) - _n.project_id = self.project_id - self.nodes.append(_n) - - @verify_connector_and_id - def get_links(self) -> None: - """ - Retrieve the links of the project. - - **Required Attributes:** - - - `project_id` - - `connector` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}/links" - - _response = _conn.http_call("get", _url) - - # Create the Nodes array but cleanup cache if there is one - if self.links: - self.links = [] - for _link in _response.json(): - _l = Link(connector=self.connector, **_link) - _l.project_id = self.project_id - self.links.append(_l) - - @verify_connector_and_id - def start_nodes(self, poll_wait_time: int = 5) -> None: - """ - Starts all the nodes inside the project. - - - `poll_wait_time` is used as a delay when performing the next query of the - nodes status. - - **Required Attributes:** - - - `project_id` - - `connector` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}/nodes/start" - - _conn.http_call("post", _url) - - # Update object - time.sleep(poll_wait_time) - self.get_nodes() - - @verify_connector_and_id - def stop_nodes(self, poll_wait_time: int = 5) -> None: - """ - Stops all the nodes inside the project. - - - `poll_wait_time` is used as a delay when performing the next query of the - nodes status. - - **Required Attributes:** - - - `project_id` - - `connector` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}/nodes/stop" - - _conn.http_call("post", _url) - - # Update object - time.sleep(poll_wait_time) - self.get_nodes() - - @verify_connector_and_id - def reload_nodes(self, poll_wait_time: int = 5) -> None: - """ - Reloads all the nodes inside the project. - - - `poll_wait_time` is used as a delay when performing the next query of the - nodes status. - - **Required Attributes:** - - - `project_id` - - `connector` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}/nodes/reload" - - _conn.http_call("post", _url) - - # Update object - time.sleep(poll_wait_time) - self.get_nodes() - - @verify_connector_and_id - def suspend_nodes(self, poll_wait_time: int = 5) -> None: - """ - Suspends all the nodes inside the project. - - - `poll_wait_time` is used as a delay when performing the next query of the - nodes status. - - **Required Attributes:** - - - `project_id` - - `connector` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}/nodes/suspend" - - _conn.http_call("post", _url) - - # Update object - time.sleep(poll_wait_time) - self.get_nodes() - - def nodes_summary( - self, is_print: bool = True - ) -> list[tuple[Any, ...]] | None: - """ - Returns a summary of the nodes insode the project. If `is_print` is `False`, it - will return a list of tuples like: - - `[(node_name, node_status, node_console, node_id) ...]` - - **Required Attributes:** - - - `project_id` - - `connector` - """ - - if not self.nodes: - self.get_nodes() - - _nodes_summary = [] - for _n in self.nodes: - if is_print: - print( - f"{_n.name}: {_n.status} -- Console: {_n.console} -- " - f"ID: {_n.node_id}" - ) - _nodes_summary.append((_n.name, _n.status, _n.console, _n.node_id)) - - return _nodes_summary if not is_print else None - - def nodes_inventory(self) -> dict[str | None, Any]: - """ - Returns an inventory-style dictionary of the nodes - - Example: - - `{ - "router01": { - "server": "127.0.0.1", - "name": "router01", - "node_id": uuid, - "console_port": 5077, - "type": "vEOS", - "ports": "[port detila]", - "x": 100, - "y": 200 - } - }` - - **Required Attributes:** - - - `project_id` - - `connector` - """ - - if not self.nodes: - self.get_nodes() - - _nodes_inventory = {} - conn = self.connector - if not conn: - raise ValueError( - "Gns3Connector not assigned. Please set the connector first." - ) - - _server = urlparse(conn.base_url).hostname - - for _n in self.nodes: - _nodes_inventory.update( - { - _n.name: { - "server": _server, - "name": _n.name, - "node_id": _n.node_id, - "console_port": _n.console, - "console_type": _n.console_type, - "type": _n.node_type, - "ports": _n.ports, - "status": _n.status, - # "template": _n.template, - "x": _n.x, - "y": _n.y, - "tags": _n.tags if _n.tags else [], - } - } - ) - - return _nodes_inventory - - def links_summary( - self, is_print: bool = True - ) -> list[dict[str, str]] | None: - """ - Returns a summary of the links inside the project. If `is_print` is False, - it will return a list of dicts like: - - `[{"link_id": "xxx", "node_a": "R1", "port_a": "Eth0/0", "node_b": "R2", "port_b": "Eth0/0"}, ...]` - - **Required Attributes:** - - - `project_id` - - `connector` - """ - # Ensure data is loaded - if not self.nodes: - self.get_nodes() - if not self.links: - self.get_links() - # If None, program errors here instead of continuing - assert self.links is not None, "Links must be loaded" - assert self.nodes is not None, "Nodes must be loaded" - - _links_summary: list[dict[str, str]] = [] - - for _l in self.links: - if not _l.nodes: - continue - _side_a = _l.nodes[0] - _side_b = _l.nodes[1] - - try: - # Add type-safe lookup logic - _node_a = next( - x for x in self.nodes if x.node_id == _side_a["node_id"] - ) - # Ensure getting str to resolve [return-value] error - _port_a = str( - next( - x["name"] - for x in (_node_a.ports or []) - if x["port_number"] == _side_a["port_number"] - and x["adapter_number"] == _side_a["adapter_number"] - ) - ) - - _node_b = next( - x for x in self.nodes if x.node_id == _side_b["node_id"] - ) - _port_b = str( - next( - x["name"] - for x in (_node_b.ports or []) - if x["port_number"] == _side_b["port_number"] - and x["adapter_number"] == _side_b["adapter_number"] - ) - ) - - # Ensure name is not None - name_a = str(_node_a.name) if _node_a.name else "Unknown" - name_b = str(_node_b.name) if _node_b.name else "Unknown" - - endpoint_a = f"{name_a}: {_port_a}" - endpoint_b = f"{name_b}: {_port_b}" - - if is_print: - print(f"{endpoint_a} ---- {endpoint_b}") - - _links_summary.append({ - "link_id": _l.link_id, - "node_a": name_a, - "port_a": _port_a, - "node_b": name_b, - "port_b": _port_b - }) - - except (StopIteration, KeyError, AttributeError): - # Prevent errors when list comprehension can't match data - continue - return _links_summary if not is_print else None - - def _search_node(self, key: str, value: Any) -> Any | None: - "Performs a search based on a key and value" - # Retrive nodes if neccesary - if not self.nodes: - self.get_nodes() - - try: - return [_p for _p in self.nodes if getattr(_p, key) == value][0] - except IndexError: - return None - - def get_node( - self, name: str | None = None, node_id: str | None = None - ) -> Any | None: - """ - Returns the Node object by searching for the `name` or the `node_id`. - - **Required Attributes:** - - - `project_id` - - `connector` - - **Required keyword arguments:** - - `name` or `node_id` - - **NOTE:** Run method `get_nodes()` manually to refresh list of nodes if - necessary - """ - if node_id: - return self._search_node(key="node_id", value=node_id) - elif name: - return self._search_node(key="name", value=name) - else: - raise ValueError("name or node_ide must be provided") - - def _search_link(self, key: str, value: Any) -> Any | None: - "Performs a search based on a key and value" - # Retrive links if neccesary - if not self.links: - self.get_links() - - try: - return next(_p for _p in self.links if getattr(_p, key) == value) - except StopIteration: - return None - - def get_link(self, link_id: str) -> Any | None: - """ - Returns the Link object by locating its ID - - **Required Attributes:** - - - `project_id` - - `connector` - - `link_id` - - **NOTE:** Run method `get_links()` manually to refresh list of links if - necessary - """ - return self._search_link(key="link_id", value=link_id) - - def create_node(self, **kwargs: Any) -> None: - """ - Creates a node. To know available parameters see `Node` object, specifically - the `create` method. The most basic example would be: - - ```python - project.create_node(name='test-switch01', template='Ethernet switch') - ``` - - **Required Project instance attributes:** - - - `project_id` - - `connector` - - **Required keyword aguments:** - - - `template` or `template_id` - """ - if not self.nodes: - self.get_nodes() - - _node = Node( - project_id=self.project_id, connector=self.connector, **kwargs - ) - - _node.create() - self.nodes.append(_node) - print( - f"Created: {_node.name} -- Type: {_node.node_type} -- " - f"Console: {_node.console}" - ) - - def create_link( - self, node_a: str, port_a: str, node_b: str, port_b: str - ) -> None: - """ - Creates a link. - - **Required Attributes:** - - - `project_id` - - `connector` - - `node_a`: Node name of the A side - - `port_a`: Port name of the A side (must match the `name` attribute of the - port) - - `node_b`: Node name of the B side - - `port_b`: Port name of the B side (must match the `name` attribute of the - port) - """ - if not self.nodes: - self.get_nodes() - if not self.links: - self.get_links() - - _node_a = self.get_node(name=node_a) - if not _node_a: - raise ValueError(f"node_a: {node_a} not found") - try: - _port_a = [_p for _p in _node_a.ports if _p["name"] == port_a][0] - except IndexError: - raise ValueError(f"port_a: {port_a} not found") from None - - _node_b = self.get_node(name=node_b) - if not _node_b: - raise ValueError(f"node_b: {node_b} not found") - try: - _port_b = [_p for _p in _node_b.ports if _p["name"] == port_b][0] - except IndexError: - raise ValueError(f"port_b: {port_b} not found") from None - - _matches = [] - for _l in self.links: - if not _l.nodes: - continue - if ( - _l.nodes[0]["node_id"] == _node_a.node_id - and _l.nodes[0]["adapter_number"] == _port_a["adapter_number"] - and _l.nodes[0]["port_number"] == _port_a["port_number"] - ): - _matches.append(_l) - elif ( - _l.nodes[1]["node_id"] == _node_b.node_id - and _l.nodes[1]["adapter_number"] == _port_b["adapter_number"] - and _l.nodes[1]["port_number"] == _port_b["port_number"] - ): - _matches.append(_l) # pragma: no cover - if _matches: - raise ValueError( - f"At least one port is used, ID: {_matches[0].link_id}" - ) - - # Now create the link! - _link = Link( - project_id=self.project_id, - connector=self.connector, - nodes=[ - { - "node_id": _node_a.node_id, - "adapter_number": _port_a["adapter_number"], - "port_number": _port_a["port_number"], - "label": {"text": _port_a.get("short_name") or _port_a["name"]}, - }, - { - "node_id": _node_b.node_id, - "adapter_number": _port_b["adapter_number"], - "port_number": _port_b["port_number"], - "label": {"text": _port_b.get("short_name") or _port_b["name"]}, - }, - ], - ) - - _link.create() - self.links.append(_link) - print(f"Created Link-ID: {_link.link_id} -- Type: {_link.link_type}") - - def delete_link( - self, node_a: str, port_a: str, node_b: str, port_b: str - ) -> None: - """ - Deletes a link. - - **Required Attributes:** - - - `project_id` - - `connector` - - `node_a`: Node name of the A side - - `port_a`: Port name of the A side (must match the `name` attribute of the - port) - - `node_b`: Node name of the B side - - `port_b`: Port name of the B side (must match the `name` attribute of the - port) - """ - if not self.nodes: - self.get_nodes() # pragma: no cover - if not self.links: - self.get_links() # pragma: no cover - - # checking link info - _node_a = self.get_node(name=node_a) - if not _node_a: - raise ValueError(f"node_a: {node_a} not found") - try: - _port_a = [_p for _p in _node_a.ports if _p["name"] == port_a][0] - except IndexError: - raise ValueError(f"port_a: {port_a} not found") from None - - _node_b = self.get_node(name=node_b) - if not _node_b: - raise ValueError(f"node_b: {node_b} not found") - try: - _port_b = [_p for _p in _node_b.ports if _p["name"] == port_b][0] - except IndexError: - raise ValueError(f"port_b: {port_b} not found") from None - - _matches = [] - for _l in self.links: - if not _l.nodes: - continue - if ( - _l.nodes[0]["node_id"] == _node_a.node_id - and _l.nodes[0]["adapter_number"] == _port_a["adapter_number"] - and _l.nodes[0]["port_number"] == _port_a["port_number"] - ): - _matches.append(_l) - elif ( - _l.nodes[1]["node_id"] == _node_b.node_id - and _l.nodes[1]["adapter_number"] == _port_b["adapter_number"] - and _l.nodes[1]["port_number"] == _port_b["port_number"] - ): - _matches.append(_l) - if not _matches: - raise ValueError( - f"Link not found: {node_a, port_a, node_b, port_b}" - ) # pragma: no cover - - # now to delete the link via GNS3_api - _link = _matches[0] - self.links.remove(_link) - _link_id = _link.link_id - _link.delete() - print( - f"Deleted Link-ID: {_link_id} From node {node_a}, port: {port_a} <--> " - f"to node {node_b}, port: {port_b}" - ) - - @verify_connector_and_id - def get_snapshots(self) -> None: - """ - Retrieves list of snapshots of the project - - **Required Project instance attributes:** - - - `project_id` - - `connector` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}/snapshots" - - _response = _conn.http_call("get", _url) - self.snapshots = _response.json() - - def _search_snapshot(self, key: str, value: Any) -> dict[str, Any] | None: - "Performs a search based on a key and value" - if not self.snapshots: - self.get_snapshots() - - try: - return next( - _p for _p in (self.snapshots or []) if _p[key] == value - ) - except StopIteration: - return None - - def get_snapshot( - self, name: str | None = None, snapshot_id: str | None = None - ) -> dict[str, Any] | None: - """ - Returns the Snapshot by searching for the `name` or the `snapshot_id`. - - **Required Attributes:** - - - `project_id` - - `connector` - - **Required keyword arguments:** - - `name` or `snapshot_id` - """ - if snapshot_id: - return self._search_snapshot(key="snapshot_id", value=snapshot_id) - elif name: - return self._search_snapshot(key="name", value=name) - else: - raise ValueError("name or snapshot_id must be provided") - - @verify_connector_and_id - def create_snapshot(self, name: str) -> None: - """ - Creates a snapshot of the project - - **Required Project instance attributes:** - - - `project_id` - - `connector` - - **Required keyword aguments:** - - - `name` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - self.get_snapshots() - - _snapshot = self.get_snapshot(name=name) - if _snapshot: - raise ValueError("Snapshot already created") - - _url = f"{_conn.nector.base_url}/projects/{_project_id}/snapshots" - - _response = _conn.http_call("post", _url, json_data={"name": name}) - - _snapshot = _response.json() - - if self.snapshots is None: - self.snapshots = [] - - self.snapshots.append(_snapshot) - print(f"Created snapshot: {_snapshot['name']}") - - @verify_connector_and_id - def delete_snapshot( - self, name: str | None = None, snapshot_id: str | None = None - ) -> None: - """ - Deletes a snapshot of the project - - **Required Project instance attributes:** - - - `project_id` - - `connector` - - **Required keyword aguments:** - - - `name` or `snapshot_id` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - self.get_snapshots() - - _snapshot = self.get_snapshot(name=name, snapshot_id=snapshot_id) - if not _snapshot: - raise ValueError("Snapshot not found") - - _url = ( - f"{_conn.base_url}/projects/{_project_id}/snapshots/" - f"{_snapshot['snapshot_id']}" - ) - - _conn.http_call("delete", _url) - - self.get_snapshots() - - @verify_connector_and_id - def restore_snapshot( - self, name: str | None = None, snapshot_id: str | None = None - ) -> None: - """ - Restore a snapshot from disk - - **Required Project instance attributes:** - - - `project_id` - - `connector` - - **Required keyword aguments:** - - - `name` or `snapshot_id` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - self.get_snapshots() - - _snapshot = self.get_snapshot(name=name, snapshot_id=snapshot_id) - if not _snapshot: - raise ValueError("Snapshot not found") - - _url = ( - f"{_conn.base_url}/projects/{_project_id}/snapshots/" - f"{_snapshot['snapshot_id']}/restore" - ) - - _conn.http_call("post", _url) - - # Update the whole project - self.get() - - def arrange_nodes_circular(self, radius: int = 120) -> None: - """ - Re-arrgange the existing nodes - in a circular fashion - - **Attributes:** - - - project instance created - - **Example** - - ```python - >>> proj = Project(name='project_name', connector=Gns3connector) - >>> proj.arrange_nodes() - ``` - """ - - self.get() - if self.status != "opened": - self.open() # pragma: no cover - - _angle = (2 * pi) / len(self.nodes) - # The Y Axis is inverted in GNS3, so the -Y is UP - for index, n in enumerate(self.nodes): - _x = int(radius * (sin(_angle * index))) - _y = int(radius * (-cos(_angle * index))) - n.update(x=_x, y=_y) - - def get_drawing( - self, drawing_id: str | None = None - ) -> dict[str, Any] | None: - """ - Returns the drawing by searching for the `svg` or the `drawing_id`. - - **Required Attributes:** - - - `project_id` - - `connector` - - **Required keyword arguments:** - - `svg` or `drawing_id` - """ - if not self.drawings: - self.get_drawings() - - try: - return next( - _drawing - for _drawing in (self.drawings or []) - if _drawing["drawing_id"] == drawing_id - ) - except (StopIteration, KeyError, TypeError): - return None - - @verify_connector_and_id - def get_drawings(self) -> None: - """ - Retrieves list of drawings of the project - - **Required Project instance attributes:** - - - `project_id` - - `connector` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}/drawings" - - _response = _conn.http_call("get", _url) - self.drawings = _response.json() - - @verify_connector_and_id - def create_drawing( - self, - svg: str, - x: int = 0, - y: int = 0, - z: int = 0, - locked: bool = False, - rotation: int = 0, - ) -> dict[str, Any]: - """ - Creates a new drawing in the project - - API: POST /v2/projects/{project_id}/drawings - - Required Project instance attributes: - - - `project_id` - - `connector` - - Required parameters: - - - `svg`: SVG content string - - Optional parameters: - - - `x`: X coordinate (default: 0) - - `y`: Y coordinate (default: 0) - - `z`: Z layer (default: 0) - - `locked`: Whether to lock the drawing (default: False) - - `rotation`: Rotation angle in degrees, range -359 to 359 (default: 0) - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}/drawings" - - # Prepare request body - request_body = { - "svg": svg, - "x": x, - "y": y, - "z": z, - "locked": locked, - "rotation": rotation, - } - - # Send POST request to create drawing - _response = _conn.http_call("post", _url, json_data=request_body) - - # Refresh drawings list - self.get_drawings() - - return cast(dict[str, Any], _response.json()) - - @verify_connector_and_id - def update_drawing( - self, - drawing_id: str, - svg: str | None = None, - locked: bool | None = None, - x: int | None = None, - y: int | None = None, - z: int | None = None, - ) -> dict[str, Any]: - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - _url = f"{_conn.base_url}/projects/{_project_id}/drawings/{drawing_id}" - - # Ensure data exists - if not self.drawings: - self.get_drawings() - - # Type guard: inform Mypy that self.drawings is now an iterable list - # Use or [] with next to find target object - current_drawing = next( - ( - d - for d in (self.drawings or []) - if d.get("drawing_id") == drawing_id - ), - None, - ) - - if current_drawing is None: - raise ValueError( - f"Drawing with ID {drawing_id} not found in project." - ) - - # If parameter is None, get original value from current object - # This way, Mypy won't report errors for list comprehensions of each field - final_svg = svg if svg is not None else current_drawing.get("svg") - final_locked = ( - locked if locked is not None else current_drawing.get("locked") - ) - final_x = x if x is not None else current_drawing.get("x") - final_y = y if y is not None else current_drawing.get("y") - final_z = z if z is not None else current_drawing.get("z") - - # Execute update - response = _conn.http_call( - "put", - _url, - json_data={ - "svg": final_svg, - "locked": final_locked, - "x": final_x, - "y": final_y, - "z": final_z, - }, - ) - - # Update local cache - self.get_drawings() - - return cast(dict[str, Any], response.json()) - - @verify_connector_and_id - def delete_drawing(self, drawing_id: str | None = None) -> None: - """ - Deletes a drawing of the project - - **Required Project instance attributes:** - - - `project_id` - - `connector` - - **Required keyword aguments:** - - - `drawing_id` - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - self.get_drawings() - - _drawing = self.get_drawing(drawing_id=drawing_id) - if not _drawing: - raise ValueError("drawing not found") - - _url = ( - f"{_conn.base_url}/projects/{_project_id}/drawings/" - f"{_drawing['drawing_id']}" - ) - - _conn.http_call("delete", _url) - - self.get_drawings() - - @verify_connector_and_id - def get_locked(self) -> bool: - """ - Retrieve locked status of the project. - - Returns whether the project is locked or not. - - API: GET /v3/projects/{project_id}/locked - - Required Attributes: - - - `project_id` - - `connector` - - Returns: - bool: True if project is locked, False otherwise - - Raises: - ValueError: If called with GNS3 API v2 (not supported) - - Note: - This method is only available in GNS3 v3 API - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - # Check API version - only v3 supports lock operations - if _conn.api_version != 3: - raise ValueError( - "Project lock/unlock operations are only supported in GNS3 API v3. " - f"Current API version: v{_conn.api_version}" - ) - - _url = f"{_conn.base_url}/projects/{_project_id}/locked" - - _response = _conn.http_call("get", _url) - locked_status = cast(bool, _response.json()) - - # Update the locked attribute - self.locked = locked_status - - return locked_status - - @verify_connector_and_id - def lock_project(self) -> None: - """ - Lock all drawings and nodes in the project. - - API: POST /v3/projects/{project_id}/lock - - Required Attributes: - - - `project_id` - - `connector` - - Raises: - ValueError: If called with GNS3 API v2 (not supported) - - Note: - This method is only available in GNS3 v3 API - Returns 204 on success (no content) - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - # Check API version - only v3 supports lock operations - if _conn.api_version != 3: - raise ValueError( - "Project lock/unlock operations are only supported in GNS3 API v3. " - f"Current API version: v{_conn.api_version}" - ) - - _url = f"{_conn.base_url}/projects/{_project_id}/lock" - - _conn.http_call("post", _url) - - # Update the locked attribute - self.locked = True - - @verify_connector_and_id - def unlock_project(self) -> None: - """ - Unlock all drawings and nodes in the project. - - API: POST /v3/projects/{project_id}/unlock - - Required Attributes: - - - `project_id` - - `connector` - - Raises: - ValueError: If called with GNS3 API v2 (not supported) - - Note: - This method is only available in GNS3 v3 API - Returns 204 on success (no content) - """ - _conn = self.connector - assert _conn is not None - _project_id = self.project_id - assert _project_id is not None - - # Check API version - only v3 supports lock operations - if _conn.api_version != 3: - raise ValueError( - "Project lock/unlock operations are only supported in GNS3 API v3. " - f"Current API version: v{_conn.api_version}" - ) - - _url = f"{_conn.base_url}/projects/{_project_id}/unlock" - - _conn.http_call("post", _url) - - # Update the locked attribute - self.locked = False diff --git a/gns3server/agent/gns3_copilot/gns3_client/gns3_project_info.py b/gns3server/agent/gns3_copilot/gns3_client/gns3_project_info.py index f9029489a..eedb72a00 100644 --- a/gns3server/agent/gns3_copilot/gns3_client/gns3_project_info.py +++ b/gns3server/agent/gns3_copilot/gns3_client/gns3_project_info.py @@ -38,8 +38,12 @@ from typing import Any from langchain.tools import BaseTool -from gns3server.agent.gns3_copilot.gns3_client import Project -from gns3server.agent.gns3_copilot.gns3_client import get_gns3_connector +from gns3server.agent.gns3_copilot.gns3_client.api_handlers import ( + build_gns3_ctx, +) +from gns3server.agent.gns3_copilot.gns3_client.project_inventory import ( + fetch_project_inventory, +) # Configure logging logger = logging.getLogger(__name__) @@ -101,11 +105,11 @@ class GNS3ProjectInfoTool(BaseTool): ) } - # Initialize Gns3Connector using factory function + # Build handler context (JWT + server URL from request context) logger.debug("Connecting to GNS3 server...") - server = get_gns3_connector() + gns3_ctx = build_gns3_ctx() - if server is None: + if gns3_ctx is None: logger.error("Failed to create GNS3 connector") return { "error": ( @@ -118,37 +122,33 @@ class GNS3ProjectInfoTool(BaseTool): logger.info( f"Retrieving project info for project_id: {project_id}" ) - project = Project(project_id=project_id, connector=server) - project.get() # Load project details + inventory = fetch_project_inventory(gns3_ctx, project_id) # Get node and link counts - nodes_inventory = project.nodes_inventory() - links_summary = project.links_summary(is_print=False) - - node_count = len(nodes_inventory) if nodes_inventory else 0 - link_count = len(links_summary) if links_summary else 0 + node_count = len(inventory["nodes_inventory"]) + link_count = len(inventory["links_summary"]) # Build result in tuple format consistent with GNS3ProjectList result = { - "project_id": project.project_id, - "name": project.name, - "status": project.status, + "project_id": inventory["project_id"], + "name": inventory["name"], + "status": inventory["status"], "node_count": node_count, "link_count": link_count, "tuple": ( - project.name, - project.project_id, + inventory["name"], + inventory["project_id"], node_count, link_count, - project.status, + inventory["status"], ), } # Log result logger.info( "Project info retrieved: name=%s, status=%s, nodes=%d, links=%d", - project.name, - project.status, + inventory["name"], + inventory["status"], node_count, link_count, ) diff --git a/gns3server/agent/gns3_copilot/gns3_client/gns3_topology_reader.py b/gns3server/agent/gns3_copilot/gns3_client/gns3_topology_reader.py index 6365e6be4..59bd33507 100644 --- a/gns3server/agent/gns3_copilot/gns3_client/gns3_topology_reader.py +++ b/gns3server/agent/gns3_copilot/gns3_client/gns3_topology_reader.py @@ -30,6 +30,10 @@ This module provides a LangChain BaseTool to retrieve the topology of a specific GNS3 project by project ID. Returns nodes, links, and project metadata. +⚠️ WARNING: This module is shared with the MCP (Model Context Protocol) service. +GNS3TopologyTool._run() is called by MCP device config handlers. +The jwt_token/url parameters were added for MCP compatibility. +Modifications must be tested with BOTH gns3-copilot AND MCP. """ import copy @@ -39,8 +43,12 @@ from typing import Any from langchain.tools import BaseTool -from gns3server.agent.gns3_copilot.gns3_client import Project -from gns3server.agent.gns3_copilot.gns3_client import get_gns3_connector +from gns3server.agent.gns3_copilot.gns3_client.api_handlers import ( + build_gns3_ctx, +) +from gns3server.agent.gns3_copilot.gns3_client.project_inventory import ( + fetch_project_inventory, +) # Configure logging logger = logging.getLogger(__name__) @@ -70,6 +78,8 @@ class GNS3TopologyTool(BaseTool): tool_input: Any = None, run_manager: Any = None, project_id: str | None = None, + jwt_token: str | None = None, + url: str | None = None, ) -> dict: """ Synchronous method to retrieve the topology of a specific GNS3 project. @@ -80,6 +90,8 @@ class GNS3TopologyTool(BaseTool): run_manager: Callback manager for tool run. project_id: The UUID of the specific GNS3 project to retrieve topology from. + jwt_token: JWT token for authentication (used by MCP handlers). + url: GNS3 server URL (used by MCP handlers). Returns: dict: A dictionary containing the project ID, name, status, nodes, @@ -101,11 +113,13 @@ class GNS3TopologyTool(BaseTool): "Please provide a valid project UUID." } - # Initialize Gns3Connector using factory function + # Build handler context (JWT + server URL) + # jwt_token/url can be passed explicitly (e.g. from MCP handlers) + # or auto-detected (e.g. from gns3-copilot agent) logger.debug("Connecting to GNS3 server...") - server = get_gns3_connector() + gns3_ctx = build_gns3_ctx(jwt_token=jwt_token, url=url) - if server is None: + if gns3_ctx is None: logger.error("Failed to create GNS3 connector") return { "error": "Failed to connect to GNS3 server. Please check " @@ -114,18 +128,17 @@ class GNS3TopologyTool(BaseTool): # Use the provided project_id directly logger.info(f"Retrieving topology for project_id: {project_id}") - project = Project(project_id=project_id, connector=server) - project.get() # Load project details + inventory = fetch_project_inventory(gns3_ctx, project_id) # Get topology JSON: includes nodes (devices), links, etc. topology = { - "project_id": project.project_id, - "name": project.name, - "status": project.status, + "project_id": inventory["project_id"], + "name": inventory["name"], + "status": inventory["status"], "nodes": self._clean_nodes_ports( - copy.deepcopy(project.nodes_inventory()) + copy.deepcopy(inventory["nodes_inventory"]) ), - "links": project.links_summary(is_print=False), + "links": inventory["links_summary"], } # Log topology result diff --git a/gns3server/agent/gns3_copilot/gns3_client/project_inventory.py b/gns3server/agent/gns3_copilot/gns3_client/project_inventory.py new file mode 100644 index 000000000..a4b6782da --- /dev/null +++ b/gns3server/agent/gns3_copilot/gns3_client/project_inventory.py @@ -0,0 +1,148 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# +# GNS3-Copilot - AI-powered Network Lab Assistant for GNS3 +# +# This file is part of GNS3-Copilot project. +# +# GNS3-Copilot is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# GNS3-Copilot is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNS3-Copilot. If not, see . +# +# Copyright (C) 2025 Yue Guobin (岳国宾) +# Author: Yue Guobin (岳国宾) +# +# Project Home: https://github.com/yueguobin/gns3-copilot +# + +""" +Project inventory aggregation over the raw GNS3 REST listings. + +Replaces the aggregation previously living on the ``Project`` dataclass +(``nodes_inventory`` / ``links_summary``). The output shapes are kept +field-for-field: they feed the LLM topology context and the Nornir +inventory, so any change here is consumer-visible. +""" + +from typing import Any +from urllib.parse import urlparse + +from gns3server.agent.gns3_copilot.gns3_client.api_handlers import _get_connector + + +def build_nodes_inventory( + nodes: list[dict[str, Any]], server_host: str | None +) -> dict[str, Any]: + """ + Build an inventory-style dict keyed by node name. + + Shape (per node name): + {server, name, node_id, console_port, console_type, type, ports, + status, x, y, tags, netmiko_device_type, default_username, + default_password} + """ + inventory: dict[str, Any] = {} + for n in nodes: + inventory[n.get("name")] = { + "server": server_host, + "name": n.get("name"), + "node_id": n.get("node_id"), + "console_port": n.get("console"), + "console_type": n.get("console_type"), + "type": n.get("node_type"), + "ports": n.get("ports"), + "status": n.get("status"), + "x": n.get("x"), + "y": n.get("y"), + "tags": n.get("tags") if n.get("tags") else [], + "netmiko_device_type": n.get("netmiko_device_type"), + "default_username": n.get("default_username"), + "default_password": n.get("default_password"), + } + return inventory + + +def build_links_summary( + nodes: list[dict[str, Any]], links: list[dict[str, Any]] +) -> list[dict[str, str]]: + """ + Build a human/LLM-friendly link list resolving node and port names. + + Shape (per link): {link_id, node_a, port_a, node_b, port_b}. + Links whose endpoints cannot be resolved are skipped, mirroring the + original Project.links_summary behavior. + """ + summary: list[dict[str, str]] = [] + for link in links: + if not link.get("nodes"): + continue + side_a = link["nodes"][0] + side_b = link["nodes"][1] + try: + node_a = next( + x for x in nodes if x.get("node_id") == side_a["node_id"] + ) + port_a = str( + next( + p["name"] + for p in (node_a.get("ports") or []) + if p["port_number"] == side_a["port_number"] + and p["adapter_number"] == side_a["adapter_number"] + ) + ) + node_b = next( + x for x in nodes if x.get("node_id") == side_b["node_id"] + ) + port_b = str( + next( + p["name"] + for p in (node_b.get("ports") or []) + if p["port_number"] == side_b["port_number"] + and p["adapter_number"] == side_b["adapter_number"] + ) + ) + name_a = str(node_a["name"]) if node_a.get("name") else "Unknown" + name_b = str(node_b["name"]) if node_b.get("name") else "Unknown" + summary.append({ + "link_id": link.get("link_id"), + "node_a": name_a, + "port_a": port_a, + "node_b": name_b, + "port_b": port_b, + }) + except (StopIteration, KeyError, AttributeError): + # Prevent errors when lookups can't match data + continue + return summary + + +def fetch_project_inventory( + gns3_ctx: dict[str, Any], project_id: str +) -> dict[str, Any]: + """ + Fetch a project's metadata, nodes and links and return the aggregated + inventory — the equivalent of the old ``Project.get()`` + + ``nodes_inventory()`` + ``links_summary()`` sequence (minus the + stats/snapshots/drawings calls no consumer ever read). + """ + conn = _get_connector(gns3_ctx) + base = conn.base_url + project = conn.http_call("get", f"{base}/projects/{project_id}").json() + nodes = conn.http_call("get", f"{base}/projects/{project_id}/nodes").json() + links = conn.http_call("get", f"{base}/projects/{project_id}/links").json() + server_host = urlparse(gns3_ctx["server_url"]).hostname + return { + "project_id": project.get("project_id", project_id), + "name": project.get("name"), + "status": project.get("status"), + "nodes_inventory": build_nodes_inventory(nodes, server_host), + "links_summary": build_links_summary(nodes, links), + } diff --git a/gns3server/agent/gns3_copilot/prompts/__init__.py b/gns3server/agent/gns3_copilot/prompts/__init__.py index 26607487a..de0aee451 100644 --- a/gns3server/agent/gns3_copilot/prompts/__init__.py +++ b/gns3server/agent/gns3_copilot/prompts/__init__.py @@ -29,7 +29,7 @@ Prompts Module for GNS3-Copilot This package provides system prompts loading utilities for the GNS3-Copilot AI agent. All system prompts are now loaded from the external GNS3-Skills repository: -https://github.com/yueguobin/GNS3-Skills +https://github.com/gns3/gns3-skills Available prompts (loaded from external repository): - lab_automation_assistant.md: Lab automation mode (diagnostics + config) diff --git a/gns3server/agent/gns3_copilot/skills/loader.py b/gns3server/agent/gns3_copilot/skills/loader.py index 307b81e89..fee183ef6 100644 --- a/gns3server/agent/gns3_copilot/skills/loader.py +++ b/gns3server/agent/gns3_copilot/skills/loader.py @@ -98,7 +98,131 @@ class SkillsLoader: def load_device_skills(self) -> Dict[str, Dict[str, Any]]: """ - Load all device/feature skills from YAML files. + Load all device skills from YAML files. + + Supports two layouts: + - Single file: device/.yaml + - Split directory: device//_base.yaml + device//.yaml + (topic files are merged into the base skill under "topics", + keyed by their "topic" field) + + Returns: + Dictionary mapping skill keys to skill definitions + """ + if yaml is None: + logger.error("PyYAML is not installed. Cannot load skills from YAML.") + return {} + + skills: Dict[str, Dict[str, Any]] = {} + device_dir = self.skills_dir / "device" + + if not device_dir.exists(): + logger.warning(f"Device skills directory not found: {device_dir}") + return {} + + for entry in sorted(device_dir.iterdir()): + if entry.is_file() and entry.suffix == ".yaml": + self._load_single_device_skill(skills, entry) + elif entry.is_dir(): + self._load_split_device_skill(skills, entry) + + logger.debug(f"Loaded {len(skills)} device skills from device directory") + return skills + + def _load_single_device_skill(self, skills: Dict[str, Dict[str, Any]], yaml_file: Path) -> None: + """ + Load a single-file device skill into the skills dictionary. + """ + try: + skill_data = self._load_yaml(yaml_file) + if not skill_data: + logger.warning(f"Skipping empty YAML file: {yaml_file}") + return + # Use device_type from YAML content as the key + # Fallback to filename stem if device_type not present + skill_key = skill_data.get("device_type") + if not skill_key: + skill_key = yaml_file.stem + logger.warning(f"No device_type in {yaml_file}, using filename '{skill_key}' as key") + skills[skill_key] = skill_data + logger.debug(f"Loaded device skill: {skill_key} from {yaml_file}") + except Exception as e: + logger.error(f"Failed to load skill from {yaml_file}: {e}") + + def _load_split_device_skill(self, skills: Dict[str, Dict[str, Any]], device_path: Path) -> None: + """ + Load a split device skill (directory with _base.yaml + topic files). + + The base file provides the device-level skill; every other YAML file + in the directory is a protocol topic merged under "topics". + """ + base_file = device_path / "_base.yaml" + if not base_file.exists(): + logger.error(f"No _base.yaml in device directory: {device_path}, skipping") + return + + try: + base_data = self._load_yaml(base_file) + except Exception as e: + logger.error(f"Failed to load skill from {base_file}: {e}") + return + if not base_data: + logger.warning(f"Skipping empty YAML file: {base_file}") + return + + skill_key = base_data.get("device_type") + if not skill_key: + skill_key = device_path.name + logger.warning(f"No device_type in {base_file}, using directory name '{skill_key}' as key") + + # Seed topics from the base file (if any), then merge topic files + base_topics = base_data.get("topics") + topics: Dict[str, Any] = dict(base_topics) if isinstance(base_topics, dict) else {} + + for yaml_file in sorted(device_path.glob("*.yaml")): + if yaml_file.name == "_base.yaml": + continue + try: + topic_data = self._load_yaml(yaml_file) + if not topic_data: + logger.warning(f"Skipping empty YAML file: {yaml_file}") + continue + + topic_device_type = topic_data.pop("device_type", None) + if topic_device_type is not None and topic_device_type != skill_key: + logger.error( + f"device_type mismatch in {yaml_file}: '{topic_device_type}' " + f"!= base '{skill_key}', skipping topic" + ) + continue + + topic_key = topic_data.pop("topic", None) + if not topic_key: + topic_key = yaml_file.stem + logger.warning(f"No 'topic' field in {yaml_file}, using filename '{topic_key}'") + if topic_key in topics: + logger.warning(f"Duplicate topic '{topic_key}' in {device_path.name} (from {yaml_file}), overwriting") + + # category/topics belong to the base skill only + topic_data.pop("category", None) + topic_data.pop("topics", None) + + topics[topic_key] = topic_data + logger.debug(f"Loaded device topic: {skill_key}/{topic_key} from {yaml_file}") + except Exception as e: + logger.error(f"Failed to load topic from {yaml_file}: {e}") + + if topics: + base_data["topics"] = topics + skills[skill_key] = base_data + logger.debug(f"Loaded device skill: {skill_key} from {device_path} ({len(topics)} topics)") + + def load_feature_skills(self) -> Dict[str, Dict[str, Any]]: + """ + Load all feature skills from YAML files. + + Feature skills are network planning and design functionalities + (e.g., topology planner), not device-specific features. Returns: Dictionary mapping skill keys to skill definitions @@ -108,13 +232,13 @@ class SkillsLoader: return {} skills = {} - device_dir = self.skills_dir / "device" + feature_dir = self.skills_dir / "feature" - if not device_dir.exists(): - logger.warning(f"Device skills directory not found: {device_dir}") + if not feature_dir.exists(): + logger.warning(f"Feature skills directory not found: {feature_dir}") return {} - for yaml_file in device_dir.glob("*.yaml"): + for yaml_file in feature_dir.glob("*.yaml"): try: skill_data = self._load_yaml(yaml_file) if not skill_data: @@ -127,11 +251,11 @@ class SkillsLoader: skill_key = yaml_file.stem logger.warning(f"No device_type in {yaml_file}, using filename '{skill_key}' as key") skills[skill_key] = skill_data - logger.debug(f"Loaded device skill: {skill_key} from {yaml_file}") + logger.debug(f"Loaded feature skill: {skill_key} from {yaml_file}") except Exception as e: - logger.error(f"Failed to load skill from {yaml_file}: {e}") + logger.error(f"Failed to load feature skill from {yaml_file}: {e}") - logger.debug(f"Loaded {len(skills)} device skills from {device_dir}") + logger.debug(f"Loaded {len(skills)} feature skills from feature directory") return skills def load_prompt(self, prompt_name: str) -> str: diff --git a/gns3server/agent/gns3_copilot/skills/manager.py b/gns3server/agent/gns3_copilot/skills/manager.py index 52abdad13..e14b8a8dc 100644 --- a/gns3server/agent/gns3_copilot/skills/manager.py +++ b/gns3server/agent/gns3_copilot/skills/manager.py @@ -76,12 +76,12 @@ class SkillsManager: Initialize the skills manager. Args: - repo_url: Git repository URL (default: https://github.com/yueguobin/GNS3-Skills.git) + repo_url: Git repository URL (default: https://github.com/gns3/gns3-skills.git) branch: Git branch to use (default: "main") auto_update: Whether to automatically pull updates on reload """ if repo_url is None: - repo_url = "https://github.com/yueguobin/GNS3-Skills.git" + repo_url = "https://github.com/gns3/gns3-skills.git" # Get local path from GNS3 config directory config_dir = Config.instance().config_dir @@ -221,15 +221,30 @@ class SkillsManager: logger.warning("No injection skills loaded, keeping existing skills") return False - # Validate injection skills + # Validate injection skills (drop invalid ones before merging) + valid_injection_skills = {} for skill_key, skill_data in new_injection_skills.items(): - if not self.loader.validate_skill_format(skill_data): + if self.loader.validate_skill_format(skill_data): + valid_injection_skills[skill_key] = skill_data + else: logger.error(f"Invalid skill format for {skill_key}, skipping") - continue - # Load new device/feature skills from YAML files + if not valid_injection_skills: + logger.warning("No valid injection skills loaded, keeping existing skills") + return False + new_injection_skills = valid_injection_skills + + # Load new device skills from YAML files new_device_skills = self.loader.load_device_skills() + # Load new feature skills from YAML files + new_feature_skills = self.loader.load_feature_skills() + + # Merge device and feature skills into single registry + all_skills = {} + all_skills.update(new_device_skills) + all_skills.update(new_feature_skills) + # Update registries (safe replace - never leaves dict empty) for k in list(INJECTION_SKILLS_REGISTRY): if k not in new_injection_skills: @@ -237,11 +252,11 @@ class SkillsManager: INJECTION_SKILLS_REGISTRY.update(new_injection_skills) for k in list(SKILLS_REGISTRY): - if k not in new_device_skills: + if k not in all_skills: del SKILLS_REGISTRY[k] - SKILLS_REGISTRY.update(new_device_skills) + SKILLS_REGISTRY.update(all_skills) - logger.info(f"Loaded {len(new_injection_skills)} injection skills and {len(new_device_skills)} device skills") + logger.info(f"Loaded {len(new_injection_skills)} injection skills, {len(new_device_skills)} device skills, and {len(new_feature_skills)} feature skills") return True except Exception as e: logger.error(f"Failed to reload skills: {e}") diff --git a/gns3server/agent/gns3_copilot/skills/registry.py b/gns3server/agent/gns3_copilot/skills/registry.py index 4d0cf1a53..a003413be 100644 --- a/gns3server/agent/gns3_copilot/skills/registry.py +++ b/gns3server/agent/gns3_copilot/skills/registry.py @@ -373,6 +373,7 @@ def get_skill( category: str | None = None, detail: str = "full", issue: str | None = None, + topic: str | None = None, ) -> dict[str, Any]: """ Get skill by device_type, with configurable detail level. @@ -382,6 +383,9 @@ def get_skill( category: Optional category filter detail: Detail level - "index" (names only), "summary" (+desc/sev/diff), "full" (all) issue: Optional specific issue key to retrieve + topic: Optional protocol topic to retrieve (split devices only). + Topic bodies are NEVER included without an explicit topic + request - all other detail levels return a topic index. Returns: Skill dictionary (detail varies by level), or error dict @@ -412,6 +416,28 @@ def get_skill( ], } + topics = skill.get("topics", {}) + + # Single topic lookup (topic bodies stay out of every other response) + if topic: + topic_data = topics.get(topic) + if not topic_data: + for key, data in topics.items(): + if key.lower() == topic.lower(): + topic_data = data + topic = key + break + if not topic_data: + return { + "error": f"Unknown topic '{topic}' in {device_type}", + "available_topics": list(topics.keys()), + } + return { + "device_type": device_type, + "skill_name": skill.get("name"), + "topic": {topic: topic_data}, + } + issues = skill.get("issues", {}) # Single issue lookup (most token-efficient) @@ -429,17 +455,20 @@ def get_skill( } if detail == "index": - # Minimal: only issue keys and names (90%+ token savings) - return { + # Minimal: only issue/topic keys and names (90%+ token savings) + result = { "device_type": device_type, "name": skill.get("name"), "description": skill.get("description"), "issues": {k: v["name"] for k, v in issues.items()}, } + if topics: + result["topics"] = {k: v.get("name", k) for k, v in topics.items()} + return result if detail == "summary": # Moderate: names + description + severity + difficulty - return { + result = { "device_type": device_type, "name": skill.get("name"), "description": skill.get("description"), @@ -453,14 +482,22 @@ def get_skill( for k, v in issues.items() }, } + if topics: + result["topics"] = { + k: {"name": v.get("name", k), "description": v.get("description", "")} + for k, v in topics.items() + } + return result - # Full detail (original behavior) - result = dict(skill) + # Full detail: topic bodies are replaced by the topic index + result = {k: v for k, v in skill.items() if k != "topics"} result["device_type"] = device_type + if topics: + result["topics"] = {k: v.get("name", k) for k, v in topics.items()} return result -def list_available_skills(category: str | None = None) -> list[dict[str, str]]: +def list_available_skills(category: str | None = None) -> list[dict[str, Any]]: """List all available device/feature skills, optionally filtered by category.""" skills = [] for did, skill in SKILLS_REGISTRY.items(): @@ -470,12 +507,14 @@ def list_available_skills(category: str | None = None) -> list[dict[str, str]]: "device_type": did, "name": skill.get("name", did), "category": skill.get("category"), + "topic_count": len(skill.get("topics", {})), }) else: skills.append({ "device_type": did, "name": skill.get("name", did), "category": skill.get("category"), + "topic_count": len(skill.get("topics", {})), }) return skills @@ -642,15 +681,21 @@ class DeviceSkillsTool(BaseTool): Provides access to device command knowledge (VPCS), topology planning, etc. For fault injection skills, use the injection_skills tool. - INPUT FORMAT (JSON string): - { - "action": "get", # "get" (default) or "list" - "device_type": "gns3_vpcs_telnet", # Required for action="get" - "detail": "full" # "full" (default) for complete skill information - } + TOKEN-EFFICIENT USAGE: + 1. List devices: {"action": "list"} + 2. List topics of a device: {"device_type": "frr_vtysh", "detail": "index"} + 3. Get ONE protocol topic (devices with topics): {"device_type": "frr_vtysh", "topic": "bgp"} + 4. Devices without topics: {"device_type": "gns3_vpcs_telnet"} - For action="list": - {"action": "list"} # Lists all available device/feature skills + Topic bodies are NEVER returned without an explicit "topic" - fetching a + device without one only returns its base skill plus the topic index, so + always request the specific protocol topic before configuring it. + + PARAMETERS: + - action: "list" or "get" (default "get") + - device_type: Required for action="get" (e.g., "frr_vtysh") + - topic: Protocol topic key from the topic index (e.g., "ospf", "bgp") + - detail: "index" | "summary" | "full" (default "full") """ def _run( @@ -693,8 +738,9 @@ class DeviceSkillsTool(BaseTool): category = params.get("category") detail = params.get("detail", "full") issue = params.get("issue") + topic = params.get("topic") - skill = get_skill(device_type, category, detail=detail, issue=issue) + skill = get_skill(device_type, category, detail=detail, issue=issue, topic=topic) return json.dumps(skill, ensure_ascii=False, indent=2) diff --git a/gns3server/agent/gns3_copilot/tools_v2/__init__.py b/gns3server/agent/gns3_copilot/tools_v2/__init__.py index f99450be4..49957fc79 100644 --- a/gns3server/agent/gns3_copilot/tools_v2/__init__.py +++ b/gns3server/agent/gns3_copilot/tools_v2/__init__.py @@ -32,6 +32,7 @@ This package provides various tools for interacting with GNS3 network simulator: - Multiple device command execution using Nornir - VPCS device configuration using Netmiko - Node and link management +- Packet filter management Main modules: - config_tools_nornir: Multiple device configuration command execution tool using Nornir @@ -42,6 +43,7 @@ Main modules: - gns3_start_node: GNS3 node startup tool - gns3_get_node_temp: GNS3 template retrieval tool - gns3_update_node_name: GNS3 node name update tool +- gns3_packet_filter: GNS3 packet filter management tool Note: GNS3TopologyTool is now available from gns3_client package @@ -54,6 +56,7 @@ from .display_tools_nornir import ExecuteMultipleDeviceCommands from .gns3_create_link import GNS3LinkTool from .gns3_create_node import GNS3CreateNodeTool from .gns3_get_node_temp import GNS3TemplateTool +from .gns3_packet_filter import GNS3PacketFilterTool from .gns3_start_node import GNS3StartNodeQuickTool from .gns3_start_node import GNS3StartNodeTool from .gns3_stop_node import GNS3StopNodeTool @@ -79,6 +82,7 @@ __all__ = [ "ExecuteMultipleDeviceCommands", "GNS3CreateNodeTool", "GNS3LinkTool", + "GNS3PacketFilterTool", "GNS3StartNodeTool", "GNS3StartNodeQuickTool", "GNS3StopNodeTool", diff --git a/gns3server/agent/gns3_copilot/tools_v2/config_tools_nornir.py b/gns3server/agent/gns3_copilot/tools_v2/config_tools_nornir.py index 44af6a703..285e221dc 100644 --- a/gns3server/agent/gns3_copilot/tools_v2/config_tools_nornir.py +++ b/gns3server/agent/gns3_copilot/tools_v2/config_tools_nornir.py @@ -26,6 +26,11 @@ This module provides a tool to execute configuration commands on multiple devices in a GNS3 topology using Nornir. + +⚠️ WARNING: This module is shared with the MCP (Model Context Protocol) service. +ExecuteMultipleDeviceConfigCommands._run() is called by the MCP device_config_send handler. +The jwt_token/url parameters were added for MCP compatibility. +Modifications must be tested with BOTH gns3-copilot AND MCP. """ import json @@ -169,6 +174,8 @@ class ExecuteMultipleDeviceConfigCommands(BaseTool): self, tool_input: str, # or Union[str, List[Any], Dict[str, Any]] run_manager: CallbackManagerForToolRun | None = None, + jwt_token: str | None = None, + url: str | None = None, **kwargs: Any, ) -> list[dict[str, Any]]: """ @@ -177,6 +184,8 @@ class ExecuteMultipleDeviceConfigCommands(BaseTool): Args: tool_input (str): A JSON string containing project_id and device configuration commands to execute. + jwt_token: JWT token for GNS3 API auth (MCP handlers). + url: GNS3 server URL (MCP handlers). Returns: List[Dict[str, Any]]: A list of dicts containing device names and @@ -214,11 +223,11 @@ class ExecuteMultipleDeviceConfigCommands(BaseTool): # Prepare device hosts data try: hosts_data = self._prepare_device_hosts_data( - device_configs_list, project_id + device_configs_list, project_id, jwt_token=jwt_token, url=url ) except ValueError as e: logger.error("Failed to prepare device hosts data: %s", e) - return [{"error": str(e)}] + return [{"status": "failed", "error": str(e)}] # Check if any devices have errors (e.g., missing device_type tag) error_devices = { @@ -245,7 +254,7 @@ class ExecuteMultipleDeviceConfigCommands(BaseTool): dynamic_nr = self._initialize_nornir(hosts_data) except ValueError as e: logger.error("Failed to initialize Nornir: %s", e) - return [{"error": str(e)}] + return [{"status": "failed", "error": str(e)}] results = [] @@ -269,7 +278,7 @@ class ExecuteMultipleDeviceConfigCommands(BaseTool): logger.error( "Error executing configurations on all devices: %s", e ) - return [{"error": f"Execution error: {str(e)}"}] + return [{"status": "failed", "error": f"Execution error: {str(e)}"}] logger.info( "Multiple device configuration execution completed. Results: %s", @@ -359,7 +368,7 @@ class ExecuteMultipleDeviceConfigCommands(BaseTool): "Invalid JSON string received as tool input: %s", e ) return ( - [{"error": f"Invalid JSON string input from model: {e}"}], + [{"status": "failed", "error": f"Invalid JSON string input from model: {e}"}], None, ) else: @@ -380,7 +389,7 @@ class ExecuteMultipleDeviceConfigCommands(BaseTool): if not project_id: error_msg = "Missing required 'project_id' field in input" logger.error(error_msg) - return ([{"error": error_msg}], None) + return ([{"status": "failed", "error": error_msg}], None) if not self._validate_project_id(project_id): error_msg = ( @@ -388,13 +397,13 @@ class ExecuteMultipleDeviceConfigCommands(BaseTool): "Expected UUID format." ) logger.error(error_msg) - return ([{"error": error_msg}], None) + return ([{"status": "failed", "error": error_msg}], None) # Validate device_configs if not isinstance(device_configs, list): error_msg = "'device_configs' must be an array" logger.error(error_msg) - return ([{"error": error_msg}], None) + return ([{"status": "failed", "error": error_msg}], None) if not device_configs: logger.warning("Device configs list is empty.") @@ -417,7 +426,7 @@ class ExecuteMultipleDeviceConfigCommands(BaseTool): f"{type(parsed_input).__name__}" ) logger.error(error_msg) - return ([{"error": error_msg}], None) + return ([{"status": "failed", "error": error_msg}], None) def _validate_project_id(self, project_id: str) -> bool: """ @@ -547,6 +556,8 @@ class ExecuteMultipleDeviceConfigCommands(BaseTool): self, device_config_list: list[dict[str, Any]], project_id: str | None = None, + jwt_token: str | None = None, + url: str | None = None, ) -> dict[str, dict[str, Any]]: """Prepare device hosts data from topology information.""" # Extract device names list @@ -556,7 +567,9 @@ class ExecuteMultipleDeviceConfigCommands(BaseTool): ] # Get device port information with project_id - hosts_data = get_device_ports_from_topology(device_names, project_id) + hosts_data = get_device_ports_from_topology( + device_names, project_id, jwt_token=jwt_token, url=url + ) if not hosts_data: error_msg = ( diff --git a/gns3server/agent/gns3_copilot/tools_v2/display_tools_nornir.py b/gns3server/agent/gns3_copilot/tools_v2/display_tools_nornir.py index abe7eae47..7c4f45622 100644 --- a/gns3server/agent/gns3_copilot/tools_v2/display_tools_nornir.py +++ b/gns3server/agent/gns3_copilot/tools_v2/display_tools_nornir.py @@ -26,6 +26,11 @@ This module provides a tool to execute display commands on multiple devices in a GNS3 topology using Nornir. + +⚠️ WARNING: This module is shared with the MCP (Model Context Protocol) service. +ExecuteMultipleDeviceCommands._run() is called by the MCP device_command_run handler. +The jwt_token/url parameters were added for MCP compatibility. +Modifications must be tested with BOTH gns3-copilot AND MCP. """ import json @@ -171,6 +176,8 @@ class ExecuteMultipleDeviceCommands(BaseTool): self, tool_input: str | bytes | list[Any] | dict[str, Any], run_manager: CallbackManagerForToolRun | None = None, + jwt_token: str | None = None, + url: str | None = None, **kwargs: Any, ) -> list[dict[str, Any]]: """ @@ -181,6 +188,8 @@ class ExecuteMultipleDeviceCommands(BaseTool): Args: tool_input: JSON string with project_id and diagnostic commands. + jwt_token: JWT token for GNS3 API auth (MCP handlers). + url: GNS3 server URL (MCP handlers). Returns: List[Dict]: A list of dicts with device names and outputs. @@ -210,11 +219,11 @@ class ExecuteMultipleDeviceCommands(BaseTool): # Prepare device hosts data try: hosts_data = self._prepare_device_hosts_data( - device_configs_list, project_id + device_configs_list, project_id, jwt_token=jwt_token, url=url ) except ValueError as e: logger.error("Failed to prepare device hosts data: %s", e) - return [{"error": str(e)}] + return [{"status": "failed", "error": str(e)}] # Check if any devices have errors (e.g., missing device_type tag) error_devices = { @@ -241,7 +250,7 @@ class ExecuteMultipleDeviceCommands(BaseTool): dynamic_nr = self._initialize_nornir(hosts_data) except ValueError as e: logger.error("Failed to initialize Nornir: %s", e) - return [{"error": str(e)}] + return [{"status": "failed", "error": str(e)}] results = [] @@ -263,7 +272,7 @@ class ExecuteMultipleDeviceCommands(BaseTool): except Exception as e: # Overall execution failed logger.error("Error executing display on all devices: %s", e) - return [{"error": f"Execution error: {str(e)}"}] + return [{"status": "failed", "error": f"Execution error: {str(e)}"}] logger.debug( "Multiple device display execution completed. Results: %s", @@ -361,7 +370,7 @@ class ExecuteMultipleDeviceCommands(BaseTool): "Invalid JSON string received as tool input: %s", e ) return ( - [{"error": f"Invalid JSON string input from model: {e}"}], + [{"status": "failed", "error": f"Invalid JSON string input from model: {e}"}], None, ) else: @@ -382,18 +391,18 @@ class ExecuteMultipleDeviceCommands(BaseTool): if not project_id: error_msg = "Missing required 'project_id' field in input" logger.error(error_msg) - return ([{"error": error_msg}], None) + return ([{"status": "failed", "error": error_msg}], None) if not self._validate_project_id(project_id): error_msg = f"Invalid project_id: {project_id}. Expected UUID." logger.error(error_msg) - return ([{"error": error_msg}], None) + return ([{"status": "failed", "error": error_msg}], None) # Validate device_configs if not isinstance(device_configs, list): error_msg = "'device_configs' must be an array" logger.error(error_msg) - return ([{"error": error_msg}], None) + return ([{"status": "failed", "error": error_msg}], None) if not device_configs: logger.warning("Device configs list is empty.") @@ -414,7 +423,7 @@ class ExecuteMultipleDeviceCommands(BaseTool): f"or legacy JSON array, got {type(parsed_input).__name__}" ) logger.error(error_msg) - return ([{"error": error_msg}], None) + return ([{"status": "failed", "error": error_msg}], None) def _validate_project_id(self, project_id: str) -> bool: """ @@ -490,6 +499,8 @@ class ExecuteMultipleDeviceCommands(BaseTool): self, device_config_list: list[dict[str, Any]], project_id: str | None = None, + jwt_token: str | None = None, + url: str | None = None, ) -> dict[str, dict[str, Any]]: """Prepare device hosts data from topology information.""" # Extract device names list @@ -499,7 +510,9 @@ class ExecuteMultipleDeviceCommands(BaseTool): ] # Get device port information with project_id - hosts_data = get_device_ports_from_topology(device_names, project_id) + hosts_data = get_device_ports_from_topology( + device_names, project_id, jwt_token=jwt_token, url=url + ) if not hosts_data: error_msg = ( diff --git a/gns3server/agent/gns3_copilot/tools_v2/gns3_create_link.py b/gns3server/agent/gns3_copilot/tools_v2/gns3_create_link.py index 677f7d642..86d28769f 100644 --- a/gns3server/agent/gns3_copilot/tools_v2/gns3_create_link.py +++ b/gns3server/agent/gns3_copilot/tools_v2/gns3_create_link.py @@ -38,8 +38,11 @@ from typing import Any from langchain.tools import BaseTool from langchain_core.callbacks import CallbackManagerForToolRun -from gns3server.agent.gns3_copilot.gns3_client import Link -from gns3server.agent.gns3_copilot.gns3_client import get_gns3_connector +from gns3server.agent.gns3_copilot.gns3_client.api_handlers import ( + build_gns3_ctx, + create_link_handler, + get_nodes_handler, +) # Configure logging logger = logging.getLogger(__name__) @@ -133,11 +136,11 @@ class GNS3LinkTool(BaseTool): {"error": "Invalid links data: must be a non-empty array"} ] - # Initialize Gns3Connector using factory function + # Build handler context (JWT + server URL from request context) logger.info("Connecting to GNS3 server...") - gns3_server = get_gns3_connector() + gns3_ctx = build_gns3_ctx() - if gns3_server is None: + if gns3_ctx is None: logger.error("Failed to create GNS3 connector") return [ { @@ -148,6 +151,12 @@ class GNS3LinkTool(BaseTool): } ] + # Fetch all nodes once for port resolution + listing = get_nodes_handler({"project_id": project_id}, gns3_ctx) + if "error" in listing: + return [{"error": listing["error"]}] + nodes_by_id = {n["node_id"]: n for n in listing["nodes"]} + created_links = [] # Process each link definition @@ -170,13 +179,9 @@ class GNS3LinkTool(BaseTool): created_links.append({"error": error_msg}) continue - # Get node details - node1 = gns3_server.get_node( - project_id=project_id, node_id=node_id1 - ) - node2 = gns3_server.get_node( - project_id=project_id, node_id=node_id2 - ) + # Get node details from the pre-fetched map + node1 = nodes_by_id.get(node_id1) + node2 = nodes_by_id.get(node_id2) if not node1 or not node2: error_msg = f"Node not found in link {i}" logger.error(error_msg) @@ -206,39 +211,48 @@ class GNS3LinkTool(BaseTool): created_links.append({"error": error_msg}) continue - # Create the link - link = Link( - project_id=project_id, - connector=gns3_server, - nodes=[ - { - "node_id": node_id1, - "adapter_number": port1_info.get( - "adapter_number", 0 - ), - "port_number": port1_info.get( - "port_number", 0 - ), - "label": {"text": port1_info.get("short_name") or port1}, - }, - { - "node_id": node_id2, - "adapter_number": port2_info.get( - "adapter_number", 0 - ), - "port_number": port2_info.get( - "port_number", 0 - ), - "label": {"text": port2_info.get("short_name") or port2}, - }, - ], + # Create the link via the shared REST handler + link_resp = create_link_handler( + { + "project_id": project_id, + "nodes": [ + { + "node_id": node_id1, + "adapter_number": port1_info.get( + "adapter_number", 0 + ), + "port_number": port1_info.get( + "port_number", 0 + ), + "label": { + "text": port1_info.get("short_name") + or port1 + }, + }, + { + "node_id": node_id2, + "adapter_number": port2_info.get( + "adapter_number", 0 + ), + "port_number": port2_info.get( + "port_number", 0 + ), + "label": { + "text": port2_info.get("short_name") + or port2 + }, + }, + ], + "fields": ["link_id"], + }, + gns3_ctx, ) - link.create() - link.get() + if "error" in link_resp: + raise RuntimeError(link_resp["error"]) # Collect link details link_info = { - "link_id": link.link_id, + "link_id": link_resp.get("link_id"), "node_id1": node_id1, "port1": port1, "node_id2": node_id2, diff --git a/gns3server/agent/gns3_copilot/tools_v2/gns3_create_node.py b/gns3server/agent/gns3_copilot/tools_v2/gns3_create_node.py index 49f7188fe..e03e42ec4 100644 --- a/gns3server/agent/gns3_copilot/tools_v2/gns3_create_node.py +++ b/gns3server/agent/gns3_copilot/tools_v2/gns3_create_node.py @@ -38,8 +38,10 @@ from typing import Any from langchain.tools import BaseTool from langchain_core.callbacks import CallbackManagerForToolRun -from gns3server.agent.gns3_copilot.gns3_client import Node -from gns3server.agent.gns3_copilot.gns3_client import get_gns3_connector +from gns3server.agent.gns3_copilot.gns3_client.api_handlers import ( + build_gns3_ctx, + create_node_handler, +) # Configure logging logger = logging.getLogger(__name__) @@ -194,11 +196,11 @@ class GNS3CreateNodeTool(BaseTool): f"template_id, x, or y." } - # Initialize Gns3Connector using factory function + # Build handler context (JWT + server URL from request context) logger.info("Connecting to GNS3 server...") - gns3_server = get_gns3_connector() + gns3_ctx = build_gns3_ctx() - if gns3_server is None: + if gns3_ctx is None: logger.error("Failed to create GNS3 connector") return { "error": "Failed to connect to GNS3 server. " @@ -228,22 +230,22 @@ class GNS3CreateNodeTool(BaseTool): name, ) - # Create node - node = Node( - project_id=project_id, - template_id=template_id, - x=x, - y=y, - name=name, - connector=gns3_server, + # Create node via the shared REST handler + created = create_node_handler( + { + "project_id": project_id, + "template_id": template_id, + "x": x, + "y": y, + "name": name, + }, + gns3_ctx, ) - node.create() - - # Retrieve node details - node.get() + if "error" in created: + raise RuntimeError(created["error"]) node_info = { - "node_id": node.node_id, - "name": node.name, + "node_id": created.get("node_id"), + "name": created.get("name"), "status": "success", } diff --git a/gns3server/agent/gns3_copilot/tools_v2/gns3_get_node_temp.py b/gns3server/agent/gns3_copilot/tools_v2/gns3_get_node_temp.py index fabd78f5a..e10e6390f 100644 --- a/gns3server/agent/gns3_copilot/tools_v2/gns3_get_node_temp.py +++ b/gns3server/agent/gns3_copilot/tools_v2/gns3_get_node_temp.py @@ -139,7 +139,9 @@ class GNS3TemplateTool(BaseTool): } # Retrieve all available templates - templates = gns3_server.get_templates() + templates = gns3_server.http_call( + "get", f"{gns3_server.base_url}/templates" + ).json() # Filter out utility templates and extract relevant info template_info = [] diff --git a/gns3server/agent/gns3_copilot/tools_v2/gns3_packet_filter.py b/gns3server/agent/gns3_copilot/tools_v2/gns3_packet_filter.py new file mode 100644 index 000000000..562a80f1b --- /dev/null +++ b/gns3server/agent/gns3_copilot/tools_v2/gns3_packet_filter.py @@ -0,0 +1,500 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# +# GNS3-Copilot - AI-powered Network Lab Assistant for GNS3 +# +# This file is part of GNS3-Copilot project. +# +# GNS3-Copilot is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# GNS3-Copilot is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNS3-Copilot. If not, see . +# +# Copyright (C) 2025 Yue Guobin (岳国宾) +# Author: Yue Guobin (岳国宾) +# +# Project Home: https://github.com/yueguobin/gns3-copilot +# + +""" + +GNS3 packet filter management tool for network simulation. + +Provides functionality to manage packet filters on GNS3 links, +including latency, packet loss, corruption, and BPF filtering. +""" + +import json +import logging +import subprocess +from pprint import pprint +from typing import Any + +from langchain.tools import BaseTool +from langchain_core.callbacks import CallbackManagerForToolRun + +from gns3server.agent.gns3_copilot.gns3_client.api_handlers import ( + available_filters_handler, + build_gns3_ctx, + get_link_handler, + update_link_handler, +) + +# Configure logging +logger = logging.getLogger(__name__) + + +class GNS3PacketFilterTool(BaseTool): + """ + A LangChain tool to manage packet filters on GNS3 links. + + Supports getting available filters, setting filters, and clearing filters + on network links to simulate various network conditions. + + **Input:** + A JSON object with project_id, link_id, action, and optional filter parameters. + Note: show_filters_icon is automatically set to false by default to hide the filter + icon in the GNS3 Web UI. + + Example input for getting available filters: + { + "project_id": "uuid-of-project", + "link_id": "uuid-of-link", + "action": "get_available" + } + + Example input for setting filters: + { + "project_id": "uuid-of-project", + "link_id": "uuid-of-link", + "action": "set", + "filters": { + "delay": [100, 10], + "packet_loss": [5] + }, + "show_filters_icon": false + } + + Example input for getting current filters: + { + "project_id": "uuid-of-project", + "link_id": "uuid-of-link", + "action": "get" + } + + Example input for clearing filters: + { + "project_id": "uuid-of-project", + "link_id": "uuid-of-link", + "action": "clear" + } + + **Output:** + A dictionary containing the action result. + + For "get_available": returns list of available filter types + For "set": returns updated link information with applied filters + For "get": returns current filters configured on the link + For "clear": returns confirmation that filters were cleared + """ + + name: str = "manage_gns3_packet_filter" + description: str = """ + Manages packet filters on GNS3 links to inject network faults and simulate network conditions. + + This tool is primarily used for fault injection scenarios to create realistic network problems + for troubleshooting practice, such as latency, packet loss, and corruption. + + By default, the filter icon in the GNS3 Web UI is hidden (show_filters_icon=false) to avoid + visual clutter when injecting faults for troubleshooting exercises. + + Supported actions: + - "get_available": Get list of available filter types for the link + - "set": Set packet filters on the link to inject network faults + - "get": Get current filters configured on the link + + Common filter types for fault injection: + - "frequency_drop": Drop every Nth packet (parameter: frequency, -1 to 32767) + - "packet_loss": Packet loss percentage (parameter: chance, 0-100) + - "delay": Delay in ms with optional jitter (parameters: latency 0-32767, jitter 0-32767) + - "corrupt": Packet corruption percentage (parameter: chance, 0-100) + - "bpf": Berkeley Packet Filter (parameter: filter expression text) + + Input is a JSON object with: + - project_id (str): GNS3 project UUID + - link_id (str): GNS3 link UUID + - action (str): One of "get_available", "set", "get", "clear" + - filters (dict, optional): Filter configuration for "set" action + + Example for getting available filters: + { + "project_id": "uuid-of-project", + "link_id": "uuid-of-link", + "action": "get_available" + } + + Example for setting delay and packet loss: + { + "project_id": "uuid-of-project", + "link_id": "uuid-of-link", + "action": "set", + "filters": { + "delay": [100, 10], + "packet_loss": [5] + } + } + + Returns a dictionary with action result, filter information, or error message. + """ + + def _run( + self, + tool_input: str, + run_manager: CallbackManagerForToolRun | None = None, + **kwargs: Any, + ) -> dict[str, Any]: + """ + Manages packet filters on a GNS3 link. + + Args: + tool_input: A JSON string with project_id, link_id, action, and optional filters. + run_manager: LangChain run manager (unused). + + Returns: + dict: A dictionary with action result or an error message. + """ + # Log received input + logger.info("Received input: %s", tool_input) + + try: + # Parse input JSON + input_data = json.loads(tool_input) + project_id = input_data.get("project_id") + link_id = input_data.get("link_id") + action = input_data.get("action") + show_filters_icon = input_data.get("show_filters_icon", False) + + # Validate required fields + if not project_id: + logger.error("Invalid input: Missing project_id.") + return {"error": "Missing project_id."} + + if not link_id: + logger.error("Invalid input: Missing link_id.") + return {"error": "Missing link_id."} + + if not action: + logger.error("Invalid input: Missing action.") + return {"error": "Missing action."} + + # Validate action + valid_actions = ["get_available", "set", "get", "clear"] + if action not in valid_actions: + logger.error("Invalid action: %s. Must be one of %s", action, valid_actions) + return { + "error": f"Invalid action: {action}. Must be one of {valid_actions}" + } + + # Validate filters for "set" action + if action == "set": + filters = input_data.get("filters") + if not filters or not isinstance(filters, dict): + logger.error("Invalid input: 'set' action requires 'filters' dict.") + return { + "error": "'set' action requires 'filters' dict with filter configuration." + } + + # Build handler context (JWT + server URL from request context) + logger.info("Connecting to GNS3 server...") + gns3_ctx = build_gns3_ctx() + + if gns3_ctx is None: + logger.error("Failed to create GNS3 connector") + return { + "error": "Failed to connect to GNS3 server. " + "Please check your configuration." + } + + # Execute action + logger.info( + "Processing packet filter action '%s' for link %s...", action, link_id + ) + if action == "get_available": + result = self._get_available_filters(gns3_ctx, project_id, link_id) + elif action == "set": + filters = input_data.get("filters", {}) + result = self._set_filters( + gns3_ctx, project_id, link_id, filters, show_filters_icon + ) + elif action == "get": + result = self._get_filters(gns3_ctx, project_id, link_id) + elif action == "clear": + result = self._clear_filters( + gns3_ctx, project_id, link_id, show_filters_icon + ) + else: + result = {"error": f"Unknown action: {action}"} + + # Log result + logger.info("Packet filter action '%s' completed successfully.", action) + + return result + + except json.JSONDecodeError as e: + logger.error("Invalid JSON input: %s", e) + return {"error": f"Invalid JSON input: {e}"} + except Exception as e: + logger.error("Failed to process packet filter request: %s", e) + return { + "error": f"Failed to process packet filter request: {str(e)}" + } + + def _get_available_filters( + self, gns3_ctx: dict, project_id: str, link_id: str + ) -> dict[str, Any]: + """Get available filter types for the link.""" + try: + filters = available_filters_handler( + {"project_id": project_id, "link_id": link_id}, gns3_ctx + ) + if "error" in filters: + raise RuntimeError(filters["error"]) + logger.info("Retrieved %d available filter types.", len(filters)) + return { + "action": "get_available", + "link_id": link_id, + "available_filters": filters, + "count": len(filters), + "status": "success", + } + except Exception as e: + logger.error("Failed to get available filters: %s", e) + return { + "action": "get_available", + "link_id": link_id, + "error": f"Failed to get available filters: {str(e)}", + "status": "failed", + } + + def _validate_bpf_syntax(self, bpf_expression: str) -> dict[str, Any]: + """ + Validate BPF filter expression syntax using tshark. + + Args: + bpf_expression: BPF filter expression to validate + + Returns: + dict with 'valid' (bool) and 'error' (str or None) keys + """ + try: + # Use tshark to validate BPF syntax with 1 second timeout + # Use -i lo (loopback) to avoid "(null)" interface in error messages + result = subprocess.run( + ["tshark", "-f", bpf_expression, "-i", "lo"], + timeout=1, + capture_output=True, + text=True, + ) + + # Check if output contains "Invalid" indicating syntax error + if "Invalid" in result.stdout or "Invalid" in result.stderr: + error_lines = [] + if "Invalid" in result.stderr: + error_lines.extend( + line for line in result.stderr.split("\n") if "Invalid" in line + ) + if "Invalid" in result.stdout: + error_lines.extend( + line for line in result.stdout.split("\n") if "Invalid" in line + ) + + # Strip interface suffix (e.g., "for interface 'lo'") for cleaner error + error_msg_parts = [] + for line in error_lines: + clean = line.split(" for interface")[0].strip() + if clean: + error_msg_parts.append(clean) + error_msg = " ".join(error_msg_parts) if error_msg_parts else "Invalid BPF syntax" + logger.warning("BPF syntax validation failed: %s", error_msg) + return {"valid": False, "error": error_msg} + + logger.info("BPF syntax validation passed") + return {"valid": True, "error": None} + + except subprocess.TimeoutExpired: + # Timeout is expected behavior - tshark waits for traffic + # No "Invalid" in output means syntax is correct + logger.info("BPF syntax validation passed (timeout expected)") + return {"valid": True, "error": None} + + except FileNotFoundError: + # tshark not installed - skip validation + logger.warning( + "tshark not found, skipping BPF syntax validation. " + "Install tshark to enable BPF validation." + ) + return {"valid": True, "error": None} + + except Exception as e: + logger.error("Unexpected error during BPF validation: %s", e) + return {"valid": False, "error": f"BPF validation error: {str(e)}"} + + def _set_filters( + self, + gns3_ctx: dict, + project_id: str, + link_id: str, + filters: dict[str, Any], + show_filters_icon: bool = False, + ) -> dict[str, Any]: + """Set packet filters on the link.""" + try: + # Validate BPF syntax if BPF filter is present + if "bpf" in filters: + bpf_filters = filters["bpf"] + if isinstance(bpf_filters, list): + # Validate each BPF expression + for idx, bpf_expr in enumerate(bpf_filters): + if isinstance(bpf_expr, str): + validation = self._validate_bpf_syntax(bpf_expr) + if not validation["valid"]: + return { + "action": "set", + "link_id": link_id, + "error": f"BPF syntax error at index {idx}: {validation['error']}", + "status": "failed", + } + elif isinstance(bpf_filters, str): + # Single BPF expression + validation = self._validate_bpf_syntax(bpf_filters) + if not validation["valid"]: + return { + "action": "set", + "link_id": link_id, + "error": f"BPF syntax error: {validation['error']}", + "status": "failed", + } + + # Update filters — the PUT response is the updated link + updated = update_link_handler( + { + "project_id": project_id, + "link_id": link_id, + "kwargs": { + "filters": filters, + "show_filters_icon": show_filters_icon, + }, + }, + gns3_ctx, + ) + if "error" in updated: + raise RuntimeError(updated["error"]) + + logger.info("Successfully set filters on link %s", link_id) + return { + "action": "set", + "link_id": link_id, + "filters": updated.get("filters"), + "status": "success", + "message": "Filters applied successfully", + } + except Exception as e: + logger.error("Failed to set filters: %s", e) + return { + "action": "set", + "link_id": link_id, + "error": f"Failed to set filters: {str(e)}", + "status": "failed", + } + + def _get_filters( + self, gns3_ctx: dict, project_id: str, link_id: str + ) -> dict[str, Any]: + """Get current filters configured on the link.""" + try: + link = get_link_handler( + {"project_id": project_id, "link_id": link_id}, gns3_ctx + ) + if "error" in link: + raise RuntimeError(link["error"]) + + logger.info("Retrieved current filters for link %s", link_id) + return { + "action": "get", + "link_id": link_id, + "filters": link.get("filters"), + "status": "success", + } + except Exception as e: + logger.error("Failed to get filters: %s", e) + return { + "action": "get", + "link_id": link_id, + "error": f"Failed to get filters: {str(e)}", + "status": "failed", + } + + def _clear_filters( + self, + gns3_ctx: dict, + project_id: str, + link_id: str, + show_filters_icon: bool = False, + ) -> dict[str, Any]: + """Clear all filters from the link.""" + try: + # Clear filters by setting an empty dict — the PUT response + # is the updated link + updated = update_link_handler( + { + "project_id": project_id, + "link_id": link_id, + "kwargs": { + "filters": {}, + "show_filters_icon": show_filters_icon, + }, + }, + gns3_ctx, + ) + if "error" in updated: + raise RuntimeError(updated["error"]) + + logger.info("Successfully cleared filters on link %s", link_id) + return { + "action": "clear", + "link_id": link_id, + "filters": updated.get("filters"), + "status": "success", + "message": "Filters cleared successfully", + } + except Exception as e: + logger.error("Failed to clear filters: %s", e) + return { + "action": "clear", + "link_id": link_id, + "error": f"Failed to clear filters: {str(e)}", + "status": "failed", + } + + +if __name__ == "__main__": + # Test the tool locally + # TODO: Replace with actual project and link UUIDs + test_input = json.dumps( + { + "project_id": "d7fc094c-685e-4db1-ac11-5e33a1b2e066", + "link_id": "link-uuid-here", + "action": "get_available", + } + ) + + tool = GNS3PacketFilterTool() + result = tool._run(test_input) + pprint(result) diff --git a/gns3server/agent/gns3_copilot/tools_v2/gns3_start_node.py b/gns3server/agent/gns3_copilot/tools_v2/gns3_start_node.py index dc9a5fa10..8e749d35a 100644 --- a/gns3server/agent/gns3_copilot/tools_v2/gns3_start_node.py +++ b/gns3server/agent/gns3_copilot/tools_v2/gns3_start_node.py @@ -39,8 +39,11 @@ from typing import Any from langchain.tools import BaseTool from langchain_core.callbacks import CallbackManagerForToolRun -from gns3server.agent.gns3_copilot.gns3_client import Node -from gns3server.agent.gns3_copilot.gns3_client import get_gns3_connector +from gns3server.agent.gns3_copilot.gns3_client.api_handlers import ( + build_gns3_ctx, + get_nodes_handler, + start_node_handler, +) # Configure logging logger = logging.getLogger(__name__) @@ -64,7 +67,7 @@ def calculate_startup_time(nodes: list) -> int: - If any node is a slow device: use conservative startup time Args: - nodes: List of node objects with node_type attribute + nodes: List of node dicts with a "node_type" key Returns: Calculated wait time in seconds @@ -73,7 +76,7 @@ def calculate_startup_time(nodes: list) -> int: return 60 # Default: 60 seconds for empty list # Get all node types - node_types = [getattr(node, "node_type", "default") for node in nodes] + node_types = [node.get("node_type") or "default" for node in nodes] # Check if all nodes are fast startup devices (VPCS or IOU) fast_types = {"vpcs", "iou"} @@ -198,106 +201,88 @@ class GNS3StartNodeTool(BaseTool): logger.error("node_ids must be a list.") return {"error": "node_ids must be a list."} - # Initialize Gns3Connector using factory function + # Build handler context (JWT + server URL from request context) logger.info("Connecting to GNS3 server...") - gns3_server = get_gns3_connector() + gns3_ctx = build_gns3_ctx() - if gns3_server is None: + if gns3_ctx is None: logger.error("Failed to create GNS3 connector") return { "error": "Failed to connect to GNS3 server. " "Please check your configuration." } - # First loop: Get node info and send start commands for all nodes + # Phase 1: fetch node info (including node_type) in one call logger.info( "Retrieving node info for %d nodes in project %s...", len(node_ids), project_id, ) - nodes = [] - for node_id in node_ids: - try: - node = Node( - project_id=project_id, - node_id=node_id, - connector=gns3_server, - ) - # Get node info (including node_type) - node.get() - if node.node_id: - nodes.append(node) - logger.info( - "Node %s (%s) type: %s", - node_id, - node.name, - node.node_type, - ) - else: - logger.error( - "Node %s not found in project %s", - node_id, - project_id, - ) - except Exception as e: + listing = get_nodes_handler({"project_id": project_id}, gns3_ctx) + if "error" in listing: + return {"error": listing["error"]} + nodes_by_id = {n["node_id"]: n for n in listing["nodes"]} + nodes = [nodes_by_id[nid] for nid in node_ids if nid in nodes_by_id] + for node in nodes: + logger.info( + "Node %s (%s) type: %s", + node["node_id"], + node.get("name"), + node.get("node_type"), + ) + for nid in node_ids: + if nid not in nodes_by_id: logger.error( - "Failed to get node info for %s: %s", - node_id, - e, + "Node %s not found in project %s", nid, project_id ) # Calculate startup time based on node types wait_time = calculate_startup_time(nodes) - # Send start commands for all nodes + # Phase 2: send start commands for all nodes (parallel batch) logger.info( "Sending start commands for %d nodes in project %s...", len(nodes), project_id, ) - for node in nodes: - try: - node.start() - logger.info("Start command sent for node %s", node.node_id) - except Exception as e: + start_results = start_node_handler( + {"project_id": project_id, "node_ids": [n["node_id"] for n in nodes]}, + gns3_ctx, + ) + for r in start_results: + if r.get("status") == "error": logger.error( "Failed to send start command for node %s: %s", - node.node_id, - e, + r.get("node_id"), + r.get("error"), ) + else: + logger.info("Start command sent for node %s", r.get("node_id")) # Show progress bar with calculated wait time show_progress_bar( duration=wait_time, interval=1, node_count=len(nodes) ) - # Second loop: Get status for all nodes + # Phase 3: get final status for all nodes (one call) results = [] logger.info("Retrieving status for %d nodes...", len(nodes)) + listing = get_nodes_handler({"project_id": project_id}, gns3_ctx) + if "error" in listing: + return {"error": listing["error"]} + final_by_id = {n["node_id"]: n for n in listing["nodes"]} for node in nodes: - try: - node.get() # Get latest status - node_info = { - "node_id": node.node_id, - "name": node.name or "N/A", - "status": node.status or "unknown", + node_info = final_by_id.get(node["node_id"], node) + results.append( + { + "node_id": node["node_id"], + "name": node_info.get("name") or "N/A", + "status": node_info.get("status") or "unknown", } - results.append(node_info) - except Exception as e: - logger.error( - "Failed to get status for node %s: %s", node.node_id, e - ) - results.append( - { - "node_id": node.node_id, - "name": getattr(node, "name", "N/A"), - "status": "error", - "error": str(e), - } - ) + ) # Handle nodes that failed to be retrieved initially - retrieved_node_ids = {node.node_id for node in nodes} + retrieved_node_ids = {node["node_id"] for node in nodes} for node_id in node_ids: if node_id not in retrieved_node_ids: results.append( @@ -405,77 +390,90 @@ class GNS3StartNodeQuickTool(BaseTool): logger.error("node_ids must be a list.") return {"error": "node_ids must be a list."} - # Initialize Gns3Connector using factory function + # Build handler context (JWT + server URL from request context) logger.info("Connecting to GNS3 server...") - gns3_server = get_gns3_connector() + gns3_ctx = build_gns3_ctx() - if gns3_server is None: + if gns3_ctx is None: logger.error("Failed to create GNS3 connector") return { "error": "Failed to connect to GNS3 server. " "Please check your configuration." } - # Send start commands for all nodes and collect initial status + # Verify nodes exist and capture pre-start info (one call) + listing = get_nodes_handler({"project_id": project_id}, gns3_ctx) + if "error" in listing: + return {"error": listing["error"]} + nodes_by_id = {n["node_id"]: n for n in listing["nodes"]} + + # Send start commands for all nodes (parallel batch) logger.info( "Sending start commands for %d nodes in project %s...", len(node_ids), project_id, ) results = [] + known_ids = [nid for nid in node_ids if nid in nodes_by_id] + start_results = start_node_handler( + {"project_id": project_id, "node_ids": known_ids}, gns3_ctx + ) + start_errors = { + r["node_id"]: r.get("error") + for r in start_results + if r.get("status") == "error" + } + + # Get immediate status (likely 'starting' or 'stopped') — one call + listing = get_nodes_handler({"project_id": project_id}, gns3_ctx) + if "error" in listing: + return {"error": listing["error"]} + after_by_id = {n["node_id"]: n for n in listing["nodes"]} for node_id in node_ids: - try: - node = Node( - project_id=project_id, - node_id=node_id, - connector=gns3_server, + if node_id not in nodes_by_id: + logger.error( + "Node %s not found in project %s", node_id, project_id ) - # Verify node exists and get current info - node.get() - if not node.node_id: - logger.error( - "Node %s not found in project %s", - node_id, - project_id, - ) - results.append( - { - "node_id": node_id, - "name": "N/A", - "status": "error", - "error": "Node not found", - } - ) - continue - - # Send start command - node.start() - logger.info( - "Start command sent for node %s (%s)", - node_id, - node.name, - ) - - # Get immediate status (likely 'starting' or 'stopped') - node.get() - node_info = { - "node_id": node.node_id, - "name": node.name or "N/A", - "status": node.status or "unknown", - } - results.append(node_info) - - except Exception as e: - logger.error("Failed to start node %s: %s", node_id, e) results.append( { "node_id": node_id, "name": "N/A", "status": "error", - "error": str(e), + "error": "Node not found", } ) + continue + + if node_id in start_errors: + logger.error( + "Failed to start node %s: %s", + node_id, + start_errors[node_id], + ) + results.append( + { + "node_id": node_id, + "name": nodes_by_id[node_id].get("name") or "N/A", + "status": "error", + "error": start_errors[node_id], + } + ) + continue + + logger.info( + "Start command sent for node %s (%s)", + node_id, + nodes_by_id[node_id].get("name"), + ) + current = after_by_id.get(node_id, nodes_by_id[node_id]) + results.append( + { + "node_id": node_id, + "name": current.get("name") or "N/A", + "status": current.get("status") or "unknown", + } + ) # Analyze results (count based on successful command sending) successful_nodes = [ diff --git a/gns3server/agent/gns3_copilot/tools_v2/gns3_stop_node.py b/gns3server/agent/gns3_copilot/tools_v2/gns3_stop_node.py index 66d6de515..a6cdfc2a2 100644 --- a/gns3server/agent/gns3_copilot/tools_v2/gns3_stop_node.py +++ b/gns3server/agent/gns3_copilot/tools_v2/gns3_stop_node.py @@ -38,8 +38,11 @@ from typing import Any from langchain.tools import BaseTool from langchain_core.callbacks import CallbackManagerForToolRun -from gns3server.agent.gns3_copilot.gns3_client import Node -from gns3server.agent.gns3_copilot.gns3_client import get_gns3_connector +from gns3server.agent.gns3_copilot.gns3_client.api_handlers import ( + build_gns3_ctx, + get_nodes_handler, + stop_node_handler, +) # Configure logging logger = logging.getLogger(__name__) @@ -103,75 +106,83 @@ class GNS3StopNodeTool(BaseTool): logger.error("node_ids must be a list.") return {"error": "node_ids must be a list."} - # Initialize Gns3Connector using factory function + # Build handler context (JWT + server URL from request context) logger.info("Connecting to GNS3 server...") - gns3_server = get_gns3_connector() + gns3_ctx = build_gns3_ctx() - if gns3_server is None: + if gns3_ctx is None: logger.error("Failed to create GNS3 connector") return { "error": "Failed to connect to GNS3 server. " "Please check your configuration." } - # Stop all nodes and collect results + # Verify nodes exist and capture names (one call) + listing = get_nodes_handler({"project_id": project_id}, gns3_ctx) + if "error" in listing: + return {"error": listing["error"]} + nodes_by_id = {n["node_id"]: n for n in listing["nodes"]} + + # Stop all nodes (parallel batch) and collect results logger.info( "Stopping %d nodes in project %s...", len(node_ids), project_id, ) results = [] + known_ids = [nid for nid in node_ids if nid in nodes_by_id] + stop_results = stop_node_handler( + {"project_id": project_id, "node_ids": known_ids}, gns3_ctx + ) + stop_errors = { + r["node_id"]: r.get("error") + for r in stop_results + if r.get("status") == "error" + } + + # Get updated status — one call + listing = get_nodes_handler({"project_id": project_id}, gns3_ctx) + if "error" in listing: + return {"error": listing["error"]} + after_by_id = {n["node_id"]: n for n in listing["nodes"]} for node_id in node_ids: - try: - node = Node( - project_id=project_id, - node_id=node_id, - connector=gns3_server, + if node_id not in nodes_by_id: + logger.error( + "Node %s not found in project %s", node_id, project_id ) - # Verify node exists and get current info - node.get() - if not node.node_id: - logger.error( - "Node %s not found in project %s", - node_id, - project_id, - ) - results.append( - { - "node_id": node_id, - "name": "N/A", - "status": "error", - "error": "Node not found", - } - ) - continue - - # Send stop command - node.stop() - logger.info( - "Stop command sent for node %s (%s)", - node_id, - node.name, - ) - - # Get updated status - node.get() - node_info = { - "node_id": node.node_id, - "name": node.name or "N/A", - "status": node.status or "unknown", - } - results.append(node_info) - - except Exception as e: - logger.error("Failed to stop node %s: %s", node_id, e) results.append( { "node_id": node_id, "name": "N/A", "status": "error", - "error": str(e), + "error": "Node not found", + } + ) + elif node_id in stop_errors: + logger.error( + "Failed to stop node %s: %s", node_id, stop_errors[node_id] + ) + results.append( + { + "node_id": node_id, + "name": nodes_by_id[node_id].get("name") or "N/A", + "status": "error", + "error": stop_errors[node_id], + } + ) + else: + logger.info( + "Stop command sent for node %s (%s)", + node_id, + nodes_by_id[node_id].get("name"), + ) + current = after_by_id.get(node_id, nodes_by_id[node_id]) + results.append( + { + "node_id": node_id, + "name": current.get("name") or "N/A", + "status": current.get("status") or "unknown", } ) diff --git a/gns3server/agent/gns3_copilot/tools_v2/gns3_suspend_node.py b/gns3server/agent/gns3_copilot/tools_v2/gns3_suspend_node.py index 0bef3190c..c7d28025e 100644 --- a/gns3server/agent/gns3_copilot/tools_v2/gns3_suspend_node.py +++ b/gns3server/agent/gns3_copilot/tools_v2/gns3_suspend_node.py @@ -39,8 +39,11 @@ from typing import Any from langchain.tools import BaseTool from langchain_core.callbacks import CallbackManagerForToolRun -from gns3server.agent.gns3_copilot.gns3_client import Node -from gns3server.agent.gns3_copilot.gns3_client import get_gns3_connector +from gns3server.agent.gns3_copilot.gns3_client.api_handlers import ( + build_gns3_ctx, + get_nodes_handler, + suspend_node_handler, +) # Configure logging logger = logging.getLogger(__name__) @@ -109,75 +112,85 @@ class GNS3SuspendNodeTool(BaseTool): logger.error("node_ids must be a list.") return {"error": "node_ids must be a list."} - # Initialize Gns3Connector using factory function + # Build handler context (JWT + server URL from request context) logger.info("Connecting to GNS3 server...") - gns3_server = get_gns3_connector() + gns3_ctx = build_gns3_ctx() - if gns3_server is None: + if gns3_ctx is None: logger.error("Failed to create GNS3 connector") return { "error": "Failed to connect to GNS3 server. " "Please check your configuration." } - # Suspend all nodes and collect results + # Verify nodes exist and capture names (one call) + listing = get_nodes_handler({"project_id": project_id}, gns3_ctx) + if "error" in listing: + return {"error": listing["error"]} + nodes_by_id = {n["node_id"]: n for n in listing["nodes"]} + + # Suspend all nodes (parallel batch) and collect results logger.info( "Suspending %d nodes in project %s...", len(node_ids), project_id, ) results = [] + known_ids = [nid for nid in node_ids if nid in nodes_by_id] + suspend_results = suspend_node_handler( + {"project_id": project_id, "node_ids": known_ids}, gns3_ctx + ) + suspend_errors = { + r["node_id"]: r.get("error") + for r in suspend_results + if r.get("status") == "error" + } + + # Get updated status — one call + listing = get_nodes_handler({"project_id": project_id}, gns3_ctx) + if "error" in listing: + return {"error": listing["error"]} + after_by_id = {n["node_id"]: n for n in listing["nodes"]} for node_id in node_ids: - try: - node = Node( - project_id=project_id, - node_id=node_id, - connector=gns3_server, + if node_id not in nodes_by_id: + logger.error( + "Node %s not found in project %s", node_id, project_id ) - # Verify node exists and get current info - node.get() - if not node.node_id: - logger.error( - "Node %s not found in project %s", - node_id, - project_id, - ) - results.append( - { - "node_id": node_id, - "name": "N/A", - "status": "error", - "error": "Node not found", - } - ) - continue - - # Send suspend command - node.suspend() - logger.info( - "Suspend command sent for node %s (%s)", - node_id, - node.name, - ) - - # Get updated status - node.get() - node_info = { - "node_id": node.node_id, - "name": node.name or "N/A", - "status": node.status or "unknown", - } - results.append(node_info) - - except Exception as e: - logger.error("Failed to suspend node %s: %s", node_id, e) results.append( { "node_id": node_id, "name": "N/A", "status": "error", - "error": str(e), + "error": "Node not found", + } + ) + elif node_id in suspend_errors: + logger.error( + "Failed to suspend node %s: %s", + node_id, + suspend_errors[node_id], + ) + results.append( + { + "node_id": node_id, + "name": nodes_by_id[node_id].get("name") or "N/A", + "status": "error", + "error": suspend_errors[node_id], + } + ) + else: + logger.info( + "Suspend command sent for node %s (%s)", + node_id, + nodes_by_id[node_id].get("name"), + ) + current = after_by_id.get(node_id, nodes_by_id[node_id]) + results.append( + { + "node_id": node_id, + "name": current.get("name") or "N/A", + "status": current.get("status") or "unknown", } ) diff --git a/gns3server/agent/gns3_copilot/tools_v2/gns3_update_node_name.py b/gns3server/agent/gns3_copilot/tools_v2/gns3_update_node_name.py index eca74795f..f6b00171b 100644 --- a/gns3server/agent/gns3_copilot/tools_v2/gns3_update_node_name.py +++ b/gns3server/agent/gns3_copilot/tools_v2/gns3_update_node_name.py @@ -38,8 +38,11 @@ from typing import Any from langchain.tools import BaseTool from langchain_core.callbacks import CallbackManagerForToolRun -from gns3server.agent.gns3_copilot.gns3_client import Node -from gns3server.agent.gns3_copilot.gns3_client import get_gns3_connector +from gns3server.agent.gns3_copilot.gns3_client.api_handlers import ( + build_gns3_ctx, + get_nodes_handler, + update_node_handler, +) # Configure logging logger = logging.getLogger(__name__) @@ -126,17 +129,23 @@ class GNS3UpdateNodeNameTool(BaseTool): "error": f"Node {i + 1} missing node_id or new_name." } - # Initialize Gns3Connector + # Build handler context (JWT + server URL from request context) logger.info("Connecting to GNS3 server...") - gns3_server = get_gns3_connector() + gns3_ctx = build_gns3_ctx() - if gns3_server is None: + if gns3_ctx is None: logger.error("Failed to create GNS3 connector") return { "error": "Failed to connect to GNS3 server. " "Please check your configuration." } + # Fetch current node names in one call (old names + existence) + listing = get_nodes_handler({"project_id": project_id}, gns3_ctx) + if "error" in listing: + return {"error": listing["error"]} + nodes_by_id = {n["node_id"]: n for n in listing["nodes"]} + # Update node names logger.info( "Updating names for %d nodes in project %s...", @@ -158,21 +167,25 @@ class GNS3UpdateNodeNameTool(BaseTool): new_name, ) - # Get node to retrieve current name - node = Node( - project_id=project_id, - node_id=node_id, - connector=gns3_server, + node = nodes_by_id.get(node_id) + if node is None: + raise ValueError("Node not found") + old_name = node.get("name") + + # Update node name — the PUT response is the updated node + updated = update_node_handler( + { + "project_id": project_id, + "node_id": node_id, + "name": new_name, + }, + gns3_ctx, ) - node.get() - old_name = node.name + if "error" in updated: + raise RuntimeError(updated["error"]) + current_name = updated.get("name") - # Update node name - node.update(name=new_name) - - # Verify update - node.get() - if node.name == new_name: + if current_name == new_name: node_info = { "node_id": node_id, "old_name": old_name, @@ -190,7 +203,7 @@ class GNS3UpdateNodeNameTool(BaseTool): "node_id": node_id, "old_name": old_name, "new_name": new_name, - "current_name": node.name, + "current_name": current_name, "status": "failed", "error": "Name verification failed", } diff --git a/gns3server/agent/gns3_copilot/tools_v2/vpcs_tools_netmiko.py b/gns3server/agent/gns3_copilot/tools_v2/vpcs_tools_netmiko.py index e143b700e..8339baefc 100644 --- a/gns3server/agent/gns3_copilot/tools_v2/vpcs_tools_netmiko.py +++ b/gns3server/agent/gns3_copilot/tools_v2/vpcs_tools_netmiko.py @@ -26,6 +26,11 @@ """ This module provides a tool to execute commands on VPCS devices in a GNS3 topology using Nornir with Netmiko. + +⚠️ WARNING: This module is shared with the MCP (Model Context Protocol) service. +VPCSCommands._run() is called by the MCP vpcs_config_set handler. +The jwt_token/url parameters were added for MCP compatibility. +Modifications must be tested with BOTH gns3-copilot AND MCP. """ import json @@ -154,6 +159,8 @@ class VPCSCommands(BaseTool): self, tool_input: str | bytes | list[Any] | dict[str, Any], run_manager: CallbackManagerForToolRun | None = None, + jwt_token: str | None = None, + url: str | None = None, **kwargs: Any, ) -> list[dict[str, Any]]: """ @@ -161,6 +168,8 @@ class VPCSCommands(BaseTool): Args: tool_input: JSON string with project_id and VPCS commands. + jwt_token: JWT token for GNS3 API auth (MCP handlers). + url: GNS3 server URL (MCP handlers). Returns: List of dicts with device names and command outputs. @@ -183,11 +192,11 @@ class VPCSCommands(BaseTool): # Prepare device hosts data try: hosts_data = self._prepare_device_hosts_data( - device_configs_list, project_id + device_configs_list, project_id, jwt_token=jwt_token, url=url ) except ValueError as e: logger.error("Failed to prepare device hosts data: %s", e) - return [{"error": str(e)}] + return [{"status": "failed", "error": str(e)}] # Check if any devices have errors (e.g., missing device) error_devices = { @@ -214,7 +223,7 @@ class VPCSCommands(BaseTool): dynamic_nr = self._initialize_nornir(hosts_data) except ValueError as e: logger.error("Failed to initialize Nornir: %s", e) - return [{"error": str(e)}] + return [{"status": "failed", "error": str(e)}] results = [] @@ -235,7 +244,7 @@ class VPCSCommands(BaseTool): except Exception as e: # Overall execution failed logger.error("Error executing commands on all VPCS devices: %s", e) - return [{"error": f"Execution error: {str(e)}"}] + return [{"status": "failed", "error": f"Execution error: {str(e)}"}] logger.debug( "VPCS command execution completed. Results: %s", @@ -329,7 +338,7 @@ class VPCSCommands(BaseTool): "Invalid JSON string received as tool input: %s", e ) return ( - [{"error": f"Invalid JSON string input from model: {e}"}], + [{"status": "failed", "error": f"Invalid JSON string input from model: {e}"}], None, ) else: @@ -348,18 +357,18 @@ class VPCSCommands(BaseTool): if not project_id: error_msg = "Missing required 'project_id' field in input" logger.error(error_msg) - return ([{"error": error_msg}], None) + return ([{"status": "failed", "error": error_msg}], None) if not self._validate_project_id(project_id): error_msg = f"Invalid project_id: {project_id}. Expected UUID." logger.error(error_msg) - return ([{"error": error_msg}], None) + return ([{"status": "failed", "error": error_msg}], None) # Validate device_configs if not isinstance(device_configs, list): error_msg = "'device_configs' must be an array" logger.error(error_msg) - return ([{"error": error_msg}], None) + return ([{"status": "failed", "error": error_msg}], None) if not device_configs: logger.warning("Device configs list is empty.") @@ -373,7 +382,7 @@ class VPCSCommands(BaseTool): f"got {type(parsed_input).__name__}" ) logger.error(error_msg) - return ([{"error": error_msg}], None) + return ([{"status": "failed", "error": error_msg}], None) def _validate_project_id(self, project_id: str) -> bool: """ @@ -408,7 +417,11 @@ class VPCSCommands(BaseTool): } def _prepare_device_hosts_data( - self, device_configs_list: list[dict[str, Any]], project_id: str + self, + device_configs_list: list[dict[str, Any]], + project_id: str, + jwt_token: str | None = None, + url: str | None = None, ) -> dict[str, dict[str, Any]]: """ Prepare Nornir inventory hosts data for VPCS devices. @@ -416,6 +429,8 @@ class VPCSCommands(BaseTool): Args: device_configs_list: List of device configurations project_id: GNS3 project ID + jwt_token: JWT token for GNS3 API auth (MCP handlers). + url: GNS3 server URL (MCP handlers). Returns: Dictionary mapping device names to their host data @@ -431,7 +446,7 @@ class VPCSCommands(BaseTool): # Get device port mappings from topology device_ports = get_device_ports_from_topology( - device_names, project_id=project_id + device_names, project_id=project_id, jwt_token=jwt_token, url=url ) # Build Nornir inventory hosts data @@ -446,6 +461,25 @@ class VPCSCommands(BaseTool): port = device_ports[device_name]["port"] + node_type = device_ports[device_name].get("node_type") + if node_type != "vpcs": + # VPCS syntax typed into another node's CLI is silently + # discarded (e.g. IOS answers "% Invalid input"), so reject + # mismatched devices before a console session is opened + logger.error( + "Device '%s' is a %s node, not a VPCS node", + device_name, + node_type or "unknown-type", + ) + hosts_data[device_name] = { + "error": ( + f"Device '{device_name}' is a {node_type or 'unknown-type'} node, " + "not a VPCS node; use device_config_send / device_show_run " + "for network devices" + ) + } + continue + # VPCS devices use gns3_vpcs_telnet device type hosts_data[device_name] = { "port": port, @@ -544,8 +578,8 @@ class VPCSCommands(BaseTool): if device_name in hosts_data and "error" in hosts_data[device_name]: results.append({ "device_name": device_name, - "status": "error", - "output": hosts_data[device_name]["error"], + "status": "failed", + "error": hosts_data[device_name]["error"], "commands": device_config["commands"], }) continue @@ -559,8 +593,8 @@ class VPCSCommands(BaseTool): error_msg = str(host_result.result) if host_result.result else "Unknown error" results.append({ "device_name": device_name, - "status": "error", - "output": error_msg, + "status": "failed", + "error": error_msg, "commands": device_config["commands"], }) else: @@ -575,8 +609,8 @@ class VPCSCommands(BaseTool): # Device not in task result (shouldn't happen) results.append({ "device_name": device_name, - "status": "error", - "output": f"Device '{device_name}' not in task results", + "status": "failed", + "error": f"Device '{device_name}' not in task results", "commands": device_config["commands"], }) diff --git a/gns3server/agent/gns3_copilot/utils/get_gns3_device_port.py b/gns3server/agent/gns3_copilot/utils/get_gns3_device_port.py index 3ac1bc992..b3127574e 100644 --- a/gns3server/agent/gns3_copilot/utils/get_gns3_device_port.py +++ b/gns3server/agent/gns3_copilot/utils/get_gns3_device_port.py @@ -25,6 +25,11 @@ """ Public module for getting device port information from GNS3 topology + +⚠️ WARNING: This module is shared with the MCP (Model Context Protocol) service. +get_device_ports_from_topology() is called by MCP device config handlers. +The jwt_token/url parameters were added for MCP compatibility. +Modifications must be tested with BOTH gns3-copilot AND MCP. """ import logging @@ -36,6 +41,8 @@ logger = logging.getLogger(__name__) def get_device_ports_from_topology( device_names: list[str], project_id: str | None = None, + jwt_token: str | None = None, + url: str | None = None, ) -> dict[str, dict[str, Any]]: """ Get device connection information from GNS3 topology @@ -43,6 +50,8 @@ def get_device_ports_from_topology( Args: device_names: List of device names to look up project_id: UUID of the specific GNS3 project to retrieve topology from + jwt_token: JWT token for authentication (used by MCP handlers). + url: GNS3 server URL (used by MCP handlers). Returns: Dictionary mapping device names to their connection data: @@ -50,10 +59,11 @@ def get_device_ports_from_topology( "device_name": { "port": console_port, "platform": "huawei", # Extracted from tags + "node_type": "vpcs", # GNS3 node type from the topology "groups": ["network_devices"], # For inheriting shared settings "connection_options": { "netmiko": { - "extras": {"device_type": "huawei_telnet"} # Extracted from tags + "extras": {"device_type": "huawei_telnet"} # netmiko_device_type field, tag fallback } } } @@ -71,7 +81,7 @@ def get_device_ports_from_topology( # Get topology information topo = GNS3TopologyTool() - topology = topo._run(project_id=project_id) + topology = topo._run(project_id=project_id, jwt_token=jwt_token, url=url) # Dynamically build hosts_data from topology hosts_data: dict[str, dict[str, Any]] = {} @@ -93,18 +103,21 @@ def get_device_ports_from_topology( logger.warning("Device '%s' missing console_port", device_name) continue - # Extract device_type and platform from tags - device_type = None + # Extract device_type and platform. + # Precedence: the netmiko_device_type field (node/template/appliance + # level, set in GNS3 server >= 3.x) wins over the device_type: + # tag, which remains as a fallback. + device_type = node_info.get("netmiko_device_type") platform = None tags = node_info.get("tags", []) for tag in tags: - if tag.startswith("device_type:"): + if tag.startswith("device_type:") and device_type is None: device_type = tag.split(":", 1)[1].strip() elif tag.startswith("platform:"): platform = tag.split(":", 1)[1].strip() - # Return error if device_type not found in tags + # Return error if device_type not found anywhere # Using a default would cause command execution errors if device_type is None: tested_device_types = ( @@ -112,8 +125,9 @@ def get_device_ports_from_topology( "gns3_ruijie_telnet (custom Ruijie)" ) error_msg = ( - f"Device '{device_name}': device_type tag not found. " - f"Please add 'device_type:' tag to this device in GNS3. " + f"Device '{device_name}': no device type found. " + f"Set the template/node 'netmiko_device_type' field (e.g. 'cisco_ios_telnet'), " + f"or add a 'device_type:' tag to this device in GNS3. " f"To configure via Web UI: right-click the device -> Configure -> Tags -> add 'device_type:'. " f"Tested types: {tested_device_types}. " f"Current tags: {tags}" @@ -125,7 +139,7 @@ def get_device_ports_from_topology( continue logger.debug( - "Device '%s': extracted device_type=%s from tags", + "Device '%s': device_type=%s", device_name, device_type, ) @@ -147,9 +161,14 @@ def get_device_ports_from_topology( # This is the Nornir best practice - each host has its own # connection configuration (device_type), while sharing common # settings (hostname, timeout) via group inheritance. - hosts_data[device_name] = { + # node_type (the GNS3 node type, e.g. vpcs/iou/docker) lets callers + # reject mismatched devices before opening a console connection; + # DictInventory ignores keys it does not know, so carrying it here + # is safe for entries fed straight into Nornir. + host_entry = { "port": node_info["console_port"], "platform": platform, + "node_type": node_info.get("type"), "groups": ["network_devices"], # For inheriting hostname, timeout, etc. "connection_options": { "netmiko": { @@ -158,6 +177,16 @@ def get_device_ports_from_topology( }, } + # Per-node default credentials (seeded from the template appliance + # metadata) override the group's empty fallback. Only inject when + # set, so credential-less devices keep inheriting the group values. + if node_info.get("default_username"): + host_entry["username"] = node_info["default_username"] + if node_info.get("default_password"): + host_entry["password"] = node_info["default_password"] + + hosts_data[device_name] = host_entry + logger.info("Returning %d device port mappings", len(hosts_data)) return hosts_data diff --git a/gns3server/agent/mcp/__init__.py b/gns3server/agent/mcp/__init__.py new file mode 100644 index 000000000..d6179d24b --- /dev/null +++ b/gns3server/agent/mcp/__init__.py @@ -0,0 +1,1640 @@ +# +# Copyright (C) 2026 GNS3 Technologies Inc. +# Author: Yue Guobin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +MCP (Model Context Protocol) service for GNS3 server. + +Implements the standard MCP protocol over SSE transport using FastMCP: + + /v3/mcp/sse — SSE stream + /v3/mcp/messages/ — JSON-RPC messages + +Tools are registered via @mcp.tool() decorators. +""" + +import contextvars +import json +import asyncio +import logging +import socket +from uuid import UUID +import bcrypt +from typing import Any, Annotated +from urllib.parse import parse_qs + +from fastapi import APIRouter +from fastapi.responses import Response + +from pydantic import Field + +from mcp.server.fastmcp import FastMCP +from mcp.server.transport_security import TransportSecuritySettings + +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from gns3server.config import Config +from gns3server.services.authentication import AuthService +import gns3server.db.models as models +from gns3server.services import auth_service +from gns3server.utils.request_utils import extract_client_info +from gns3server.db.repositories.api_keys import ApiKeysRepository +from gns3server.db.repositories.users import UsersRepository +from .projects import ( + list_projects_handler, get_project_handler, create_project_handler, + delete_project_handler, open_project_handler, close_project_handler, + get_project_stats_handler, update_project_handler, duplicate_project_handler, + get_project_readme_handler, update_project_readme_handler, + lock_project_handler, unlock_project_handler, + get_locked_project_handler, +) +from .server import ( + get_version_handler, get_statistics_handler, +) +# Symbol tools are disabled for now: they require a vision-capable model to +# be genuinely useful (the tools shuttle SVG content, which a text-only LLM +# cannot inspect or produce). Revisit later. +# from .symbols import ( +# get_symbols_handler, get_symbol_handler, +# get_symbol_dimensions_handler, get_default_symbols_handler, +# upload_symbol_handler, delete_symbol_handler, +# ) +from .appliances import ( + get_appliances_handler, get_appliance_handler, + install_appliance_handler, +) +from .images import ( + get_images_handler, get_image_handler, + delete_image_handler, prune_images_handler, + install_images_handler, +) +from .device_config import ( + device_config_send_handler, device_show_run_handler, + vpcs_config_set_handler, +) +from gns3server.agent.gns3_copilot.gns3_client.api_handlers import ( + get_nodes_handler, get_node_handler, start_node_handler, + stop_node_handler, suspend_node_handler, + create_node_handler, delete_node_handler, update_node_handler, + get_node_console_info_handler, + list_node_files_handler, get_node_file_handler, + write_node_file_handler, delete_node_file_handler, + start_all_nodes_handler, stop_all_nodes_handler, + suspend_all_nodes_handler, + duplicate_node_handler, isolate_node_handler, + unisolate_node_handler, get_node_links_handler, + get_links_handler, get_link_handler, available_filters_handler, + create_link_handler, + delete_link_handler, update_link_handler, + reset_link_handler, start_capture_handler, stop_capture_handler, + download_capture_file_handler, + link_marker_handler, marker_definition_handler, +) +from .templates import ( + list_templates_handler, get_template_handler, create_template_handler, + update_template_handler, delete_template_handler, +) +from .computes import ( + list_computes_handler, get_compute_handler, get_compute_images_handler, +) +from .snapshots import ( + get_snapshots_handler, create_snapshot_handler, + delete_snapshot_handler, restore_snapshot_handler, +) +from .drawings import ( + get_drawings_handler, create_drawing_handler, + get_drawing_handler, update_drawing_handler, delete_drawing_handler, +) + +log = logging.getLogger(__name__) + +# Suppress noisy telnet connection logs from device config tools. +logging.getLogger("telnetlib3").setLevel(logging.WARNING) + +# FastAPI app reference — used to lazily access app.state._db_engine for API key validation. +# The db engine is initialized during the lifespan startup, which runs AFTER +# register_starlette_routes() is called, so we cannot capture it at registration time. +_app = None + + +# ── Server ready state ──────────────────────────────────────────────── +# Tracks whether GNS3 server has completed initialization. +# MCP connections wait up to 5 seconds for startup to complete, then return +# 503 Service Unavailable if initialization is not complete to prevent +# "Received request before initialization was complete" errors. + +_mcp_ready_event = asyncio.Event() + + +def set_mcp_server_ready(ready: bool = True) -> None: + """ + Set MCP server ready state. + + Should be called after GNS3 startup completes (database, controller, etc.) + to allow MCP connections to proceed. + + Args: + ready: True to mark server as ready, False to mark as not ready + """ + if ready: + _mcp_ready_event.set() + log.info("MCP server is now ready to accept connections") + else: + _mcp_ready_event.clear() + + +async def wait_for_mcp_ready() -> bool: + """ + Wait until MCP server is ready before accepting connections. + + Returns: + True if server is ready, False if timeout reached + + Returns immediately if already ready. Otherwise waits with a timeout + and returns False if server does not become ready in time. + """ + if _mcp_ready_event.is_set(): + return True + + log.debug("MCP server not ready yet, waiting for initialization to complete...") + + try: + await asyncio.wait_for(_mcp_ready_event.wait(), timeout=5.0) + log.debug("MCP server is now ready, proceeding with connection") + return True + except asyncio.TimeoutError: + log.warning( + "MCP server ready check timed out after 5 seconds - " + "GNS3 server initialization may have issues" + ) + return False + + +# ── Per‑connection JWT token ───────────────────────────────────────── +# Set during SSE authentication, read by tool handlers running in the +# same asyncio task (contextvars propagate through asyncio.to_thread). + +_jwt_token_var: contextvars.ContextVar[str | None] = contextvars.ContextVar( + "mcp_jwt_token", default=None +) +# Username extracted during token validation — used by handlers to generate +# short-lived JWTs for download/console URLs without exposing the raw key. +_jwt_username_var: contextvars.ContextVar[str | None] = contextvars.ContextVar( + "mcp_jwt_username", default=None +) +# token_version extracted during token validation — short-lived JWTs minted for +# download/console URLs must carry the same version, or the revocation check +# (token_data.token_version != user.token_version) rejects them as "revoked". +_jwt_token_version_var: contextvars.ContextVar[int] = contextvars.ContextVar( + "mcp_jwt_token_version", default=0 +) + + +# ── Token validation ────────────────────────────────────────────────── + +async def _resolve_token(token: str) -> str | None: + """Validate a token (JWT or API key) and return the effective JWT to use. + + For JWT tokens, returns the token as-is. + For API keys, validates against the database and returns a fresh short-lived JWT. + + Returns None if the token is invalid. + """ + # API keys (gns3_...) are never valid JWTs — skip the JWT attempt for them + # so it doesn't log a spurious "JWT rejected" line on every API-key connection. + if not token.startswith("gns3_"): + try: + token_data = auth_service.get_token_data(token) + _jwt_username_var.set(token_data.username) + _jwt_token_version_var.set(token_data.token_version) + return token + except Exception: + pass + + # Try API key — format: gns3__ → O(1) lookup + if token.startswith("gns3_") and _app is not None: + db_engine = getattr(_app.state, "_db_engine", None) + if db_engine is not None: + try: + parts = token.split("_", 2) + if len(parts) == 3: + key_id = UUID(parts[1]) + secret = parts[2] + async with AsyncSession(db_engine, expire_on_commit=False) as db_session: + repo = ApiKeysRepository(db_session) + db_key = await repo.get_api_key(key_id) + if db_key and not db_key.revoked: + if await asyncio.to_thread(bcrypt.checkpw, secret.encode(), db_key.key_hash.encode()): + await repo.update_last_used(db_key.api_key_id) + user_repo = UsersRepository(db_session) + user = await user_repo.get_user(db_key.user_id) + if user: + _jwt_username_var.set(user.username) + _jwt_token_version_var.set(user.token_version) + fresh_token = auth_service.create_access_token(user.username, token_version=user.token_version) + return fresh_token + except Exception: + pass + + return None + + +# ── Server URL helper ───────────────────────────────────────────────── + +def _server_url() -> str: + cfg = Config.instance().settings + host = cfg.Server.host + if host in ("0.0.0.0", "::"): + try: + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: + s.settimeout(0.1) + s.connect(("8.8.8.8", 80)) + host = s.getsockname()[0] + except OSError: + host = "127.0.0.1" + scheme = "https" if cfg.Server.enable_ssl else "http" + return f"{scheme}://{host}:{cfg.Server.port}" + + +# ── FastMCP Server ──────────────────────────────────────────────────── + +def _create_mcp_server() -> FastMCP: + """Create MCP server with security settings from configuration.""" + cfg = Config.instance().settings.Server + + # Always pass an explicit TransportSecuritySettings to prevent FastMCP + # from auto-enabling protection when host is localhost (its default). + if cfg.mcp_enable_dns_rebinding_protection: + transport_security = TransportSecuritySettings( + enable_dns_rebinding_protection=True, + allowed_hosts=cfg.mcp_allowed_hosts or ["127.0.0.1:*", "localhost:*"], + allowed_origins=cfg.mcp_allowed_origins or ["http://127.0.0.1:*", "http://localhost:*"], + ) + else: + transport_security = TransportSecuritySettings( + enable_dns_rebinding_protection=False, + ) + + mcp = FastMCP("GNS3 MCP Server", transport_security=transport_security) + return mcp + + +mcp = _create_mcp_server() + + +# ── Tool handlers ───────────────────────────────────────────────────── + +def _run_handler_sync(handler, params: dict[str, Any]) -> list[dict[str, Any]]: + """Run a synchronous Gns3Connector handler in a thread.""" + ctx = { + "server_url": _server_url(), + "jwt_token": _jwt_token_var.get(), + "jwt_username": _jwt_username_var.get(), + "jwt_token_version": _jwt_token_version_var.get(), + } + result = handler(params, ctx) + return [{"type": "text", "text": json.dumps(result, ensure_ascii=False, default=str)}] + + +@mcp.tool() +async def project_list() -> list[dict[str, Any]]: + """List all GNS3 projects accessible to the current user.""" + return await asyncio.to_thread(_run_handler_sync, list_projects_handler, {}) + + +@mcp.tool() +async def project_get( + project_id: Annotated[str, Field(description="UUID of the project")], +) -> list[dict[str, Any]]: + """Get detailed information about a specific project.""" + return await asyncio.to_thread(_run_handler_sync, get_project_handler, {"project_id": project_id}) + + +@mcp.tool() +async def project_create( + name: Annotated[str, Field(description="Project name")], +) -> list[dict[str, Any]]: + """Create a new GNS3 project. auto_close is set to False so the project stays open when clients disconnect.""" + params = {"name": name, "auto_close": False} + return await asyncio.to_thread(_run_handler_sync, create_project_handler, params) + + +@mcp.tool() +async def project_delete( + project_id: Annotated[str, Field(description="UUID of the project to delete")], +) -> list[dict[str, Any]]: + """Delete a GNS3 project permanently.""" + return await asyncio.to_thread(_run_handler_sync, delete_project_handler, {"project_id": project_id}) + + +@mcp.tool() +async def project_open( + project_id: Annotated[str, Field(description="UUID of the project to open")], +) -> list[dict[str, Any]]: + """Open a closed GNS3 project.""" + return await asyncio.to_thread(_run_handler_sync, open_project_handler, {"project_id": project_id}) + +@mcp.tool() +async def project_close( + project_id: Annotated[str, Field(description="UUID of the project to close")], +) -> list[dict[str, Any]]: + """Close an open GNS3 project.""" + return await asyncio.to_thread(_run_handler_sync, close_project_handler, {"project_id": project_id}) + +@mcp.tool() +async def project_stats( + project_id: Annotated[str, Field(description="UUID of the project to get statistics for")], +) -> list[dict[str, Any]]: + """Get statistics (nodes, links, snapshots, drawings) for a project.""" + return await asyncio.to_thread(_run_handler_sync, get_project_stats_handler, {"project_id": project_id}) + + +@mcp.tool() +async def project_update( + project_id: Annotated[str, Field(description="UUID of the project to update")], + name: Annotated[str, Field(description="New project name")] = None, + auto_close: Annotated[bool, Field(description="Close project when last client leaves")] = None, + auto_open: Annotated[bool, Field(description="Project opens when GNS3 starts")] = None, + auto_start: Annotated[bool, Field(description="Project starts when opened")] = None, + scene_width: Annotated[int, Field(description="Width of the drawing area")] = None, + scene_height: Annotated[int, Field(description="Height of the drawing area")] = None, + zoom: Annotated[int, Field(description="Zoom of the drawing area")] = None, + show_layers: Annotated[bool, Field(description="Show layers on the drawing area")] = None, + snap_to_grid: Annotated[bool, Field(description="Snap to grid on the drawing area")] = None, + show_grid: Annotated[bool, Field(description="Show the grid on the drawing area")] = None, + grid_size: Annotated[int, Field(description="Grid size for the drawing area for nodes")] = None, + drawing_grid_size: Annotated[int, Field(description="Grid size for the drawing area for drawings")] = None, + show_interface_labels: Annotated[bool, Field(description="Show interface labels on the drawing area")] = None, +) -> list[dict[str, Any]]: + """Update a project's properties (name, auto_close, auto_open, etc.).""" + params = {"project_id": project_id} + local_vars = { + "name": name, "auto_close": auto_close, "auto_open": auto_open, "auto_start": auto_start, + "scene_width": scene_width, "scene_height": scene_height, "zoom": zoom, + "show_layers": show_layers, "snap_to_grid": snap_to_grid, "show_grid": show_grid, + "grid_size": grid_size, "drawing_grid_size": drawing_grid_size, "show_interface_labels": show_interface_labels, + } + for key, val in local_vars.items(): + if val is not None: + params[key] = val + return await asyncio.to_thread(_run_handler_sync, update_project_handler, params) + + +@mcp.tool() +async def project_duplicate( + project_id: Annotated[str, Field(description="UUID of the project to duplicate")], + name: Annotated[str, Field(description="New project name")], + reset_mac_addresses: Annotated[bool, Field(description="Reset MAC addresses for this project")] = False, +) -> list[dict[str, Any]]: + """Duplicate a project.""" + params = {"project_id": project_id, "name": name} + if reset_mac_addresses: + params["reset_mac_addresses"] = reset_mac_addresses + return await asyncio.to_thread(_run_handler_sync, duplicate_project_handler, params) + + +@mcp.tool() +async def project_readme_get( + project_id: Annotated[str, Field(description="UUID of the project")], +) -> list[dict[str, Any]]: + """Get the content of a project's README.md file — the project documentation (Markdown format).""" + return await asyncio.to_thread(_run_handler_sync, get_project_readme_handler, {"project_id": project_id}) + + +@mcp.tool() +async def project_readme_update( + project_id: Annotated[str, Field(description="UUID of the project")], + content: Annotated[str, Field(description="Content to write to README.md (Markdown format)")], +) -> list[dict[str, Any]]: + """Update or create a project's README.md file — the project documentation (Markdown format).""" + return await asyncio.to_thread(_run_handler_sync, update_project_readme_handler, {"project_id": project_id, "content": content}) + + +# ── Node tools ──────────────────────────────────────────────────────── + +@mcp.tool() +async def node_list( + project_id: Annotated[str, Field(description="UUID of the project")], + fields: Annotated[list[str] | None, Field(description="Optional: return only these fields per node. e.g. [\"name\",\"status\"]. Available: name, status, node_type, console, console_type, console_host, node_id, project_id, compute_id, symbol, x, y, z, locked, ports, properties, command_line, node_directory, label, tags, template_id, width, height, aux, aux_type")] = None, +) -> list[dict[str, Any]]: + """List all nodes in a project. Use fields=[] to return only what you need.""" + return await asyncio.to_thread(_run_handler_sync, get_nodes_handler, {"project_id": project_id, "fields": fields}) + + +@mcp.tool() +async def node_get( + project_id: Annotated[str, Field(description="UUID of the project")], + node_id: Annotated[str, Field(description="UUID of the node")], + fields: Annotated[list[str] | None, Field(description="Optional: return only these fields. e.g. [\"name\",\"status\"]. Available: name, status, node_type, console, console_type, console_host, node_id, project_id, compute_id, symbol, x, y, z, locked, ports, properties, command_line, node_directory, label, tags, template_id, width, height, aux, aux_type")] = None, +) -> list[dict[str, Any]]: + """Get detailed information about a specific node. Use fields=[] to return only what you need.""" + return await asyncio.to_thread(_run_handler_sync, get_node_handler, { + "project_id": project_id, "node_id": node_id, "fields": fields, + }) + +@mcp.tool() +async def node_start( + project_id: Annotated[str, Field(description="UUID of the project")], + node_id: Annotated[str | None, Field(description="Node UUID (single mode)")] = None, + node_ids: Annotated[list[str] | None, Field(description="Batch mode: [\"uuid1\",\"uuid2\"] — start multiple nodes in parallel")] = None, +) -> list[dict[str, Any]]: + """Start one or more nodes. Provide node_id for single, or node_ids for batch.""" + params = {"project_id": project_id} + if node_ids: + params["node_ids"] = node_ids + else: + params["node_id"] = node_id + return await asyncio.to_thread(_run_handler_sync, start_node_handler, params) + +@mcp.tool() +async def node_stop( + project_id: Annotated[str, Field(description="UUID of the project")], + node_id: Annotated[str | None, Field(description="Node UUID (single mode)")] = None, + node_ids: Annotated[list[str] | None, Field(description="Batch mode: [\"uuid1\",\"uuid2\"] — stop multiple nodes in parallel")] = None, +) -> list[dict[str, Any]]: + """Stop one or more nodes. Provide node_id for single, or node_ids for batch.""" + params = {"project_id": project_id} + if node_ids: + params["node_ids"] = node_ids + else: + params["node_id"] = node_id + return await asyncio.to_thread(_run_handler_sync, stop_node_handler, params) + +@mcp.tool() +async def node_suspend( + project_id: Annotated[str, Field(description="UUID of the project")], + node_id: Annotated[str | None, Field(description="Node UUID (single mode)")] = None, + node_ids: Annotated[list[str] | None, Field(description="Batch mode: [\"uuid1\",\"uuid2\"] — suspend multiple nodes in parallel")] = None, +) -> list[dict[str, Any]]: + """Suspend one or more nodes. Provide node_id for single, or node_ids for batch.""" + params = {"project_id": project_id} + if node_ids: + params["node_ids"] = node_ids + else: + params["node_id"] = node_id + return await asyncio.to_thread(_run_handler_sync, suspend_node_handler, params) + + +@mcp.tool() +async def node_create( + project_id: Annotated[str, Field(description="UUID of the project")], + template_id: Annotated[str | None, Field(description="Template UUID (required for single mode; used as default in batch mode)")] = None, + x: Annotated[int, Field(description="X coordinate (canvas center origin, right positive)")] = 0, + y: Annotated[int, Field(description="Y coordinate (canvas center origin, down positive)")] = 0, + compute_id: Annotated[str, Field(description="Compute ID (default: local)")] = "local", + nodes: Annotated[list | None, Field(description="Batch mode: [{name, template_id?, x?, y?, compute_id?}] — top-level template_id applies as default")] = None, + fields: Annotated[list[str] | None, Field(description="Response fields to include (default: [node_id, name, node_type, status, console]). " + "Available: compute_id, name, node_type, node_id, console, console_type, " + "console_auto_start, aux, aux_type, properties, label, symbol, x, y, z, " + "locked, port_name_format, port_segment_size, first_port_name, " + "custom_adapters, tags, template_id, project_id, node_directory, " + "status, command_line, width, height, ports, console_host")] = None, +) -> list[dict[str, Any]]: + """Create one or more nodes from templates. + + Single mode: provide template_id, x, y (optional compute_id) + Batch mode: provide nodes=[{name, template_id?, x?, y?, compute_id?}] — creates up to 100 in parallel. + Top-level template_id applies to all nodes; individual nodes can override. + Results are always returned in submission order; correlate nodes by node_id, not name. + When a node omits `name`, the server assigns a default name (R-1, R-2, ...) and console + port — such batches are created sequentially so those assignments follow submission order. + """ + if nodes is not None: + return await asyncio.to_thread(_run_handler_sync, create_node_handler, { + "project_id": project_id, "nodes": nodes, "fields": fields, + "template_id": template_id, + }) + return await asyncio.to_thread(_run_handler_sync, create_node_handler, { + "project_id": project_id, "template_id": template_id, + "x": x, "y": y, "compute_id": compute_id, "fields": fields, + }) + + +@mcp.tool() +async def node_delete( + project_id: Annotated[str, Field(description="UUID of the project")], + node_id: Annotated[str | None, Field(description="Node UUID (single mode)")] = None, + node_ids: Annotated[list[str] | None, Field(description="Batch mode: [\"uuid1\",\"uuid2\"] — delete multiple nodes in parallel")] = None, +) -> list[dict[str, Any]]: + """Delete one or more nodes from a project. Provide node_id for single, or node_ids for batch.""" + params = {"project_id": project_id} + if node_ids: + params["node_ids"] = node_ids + else: + params["node_id"] = node_id + return await asyncio.to_thread(_run_handler_sync, delete_node_handler, params) + + +@mcp.tool() +async def node_update( + project_id: Annotated[str, Field(description="UUID of the project")], + node_id: Annotated[str, Field(description="UUID of the node to update")], + **kwargs: Any, +) -> list[dict[str, Any]]: + """Update a node's properties (name, position, etc.).""" + params = {"project_id": project_id, "node_id": node_id, **kwargs} + return await asyncio.to_thread(_run_handler_sync, update_node_handler, params) + + +@mcp.tool() +async def node_console( + project_id: Annotated[str, Field(description="UUID of the project containing the node")], + node_id: Annotated[str, Field(description="UUID of the node to get console info for")], +) -> list[dict[str, Any]]: + """Get WebSocket console connection info for a node. + + Returns the console type (telnet/ssh/vnc) and a ready-to-run websocat + command with a short-lived access token (10 min) already embedded. + + IMPORTANT — copy the returned values EXACTLY: + - Run the returned "command" string verbatim; it already contains the + full URL with token. NEVER construct or edit the URL yourself, and + NEVER copy the token by hand — a mistyped token is rejected. + - The token expires after token_ttl_seconds (10 min): call this tool + again to get a fresh one; do not reuse an old URL. + + Sending device commands after connecting: + > timeout 10 websocat -t --no-close "ws://..." <<< $'\\r\\nenable\\r\\nshow version\\r\\nexit\\r\\n' + + - Use \\r\\n (not \\n) to match console protocol line endings + - Use $'...' format for escape sequences in bash + - --no-close keeps the WebSocket open after stdin (heredoc) hits EOF, so + device output is not cut off before it arrives + - Set a timeout to prevent hanging connections + """ + return await asyncio.to_thread(_run_handler_sync, get_node_console_info_handler, { + "project_id": project_id, "node_id": node_id, + }) + + +# ── Link tools ──────────────────────────────────────────────────────── + +@mcp.tool() +async def link_list( + project_id: Annotated[str, Field(description="UUID of the project")], + fields: Annotated[list[str] | None, Field(description="Optional: return only these fields. e.g. [\"link_id\",\"nodes\"]. Available: link_id, project_id, link_type, nodes, suspend, filters, capturing, capture_file_name, link_style")] = None, +) -> list[dict[str, Any]]: + """List all links in a project. Use fields=[] to return only what you need.""" + return await asyncio.to_thread(_run_handler_sync, get_links_handler, {"project_id": project_id, "fields": fields}) + + +@mcp.tool() +async def link_get( + project_id: Annotated[str, Field(description="UUID of the project")], + link_id: Annotated[str, Field(description="UUID of the link")], +) -> list[dict[str, Any]]: + """Get detailed information about a specific link.""" + return await asyncio.to_thread(_run_handler_sync, get_link_handler, {"project_id": project_id, "link_id": link_id}) + + +@mcp.tool() +async def link_create( + project_id: Annotated[str, Field(description="UUID of the project")], + nodes: Annotated[list | None, Field(description="Single mode: [{node_id, adapter_number, port_number}] or compact [id, ad, pt, id, ad, pt]")] = None, + link_type: Annotated[str, Field(description="Link type - ethernet or serial")] = "ethernet", + filters: Annotated[dict | None, Field(description="Optional packet filters")] = None, + links: Annotated[list | None, Field(description="Batch mode: [{nodes, link_type?, filters?}] — nodes supports compact [id, ad, pt, id, ad, pt] format")] = None, + fields: Annotated[list[str] | None, Field(description="Response fields to include (default: [link_id, link_type, nodes]). " + "Available: link_id, project_id, link_type, nodes, suspend, " + "link_style, filters, show_filters_icon, capturing, " + "capture_file_name, capture_file_path, capture_compute_id, wireshark")] = None, +) -> list[dict[str, Any]]: + """Create one or more links between nodes. + + Single mode: provide nodes, link_type (optional filters) + Batch mode: provide links=[{nodes, link_type?, filters?}] — up to 100 in parallel + """ + if links: + return await asyncio.to_thread(_run_handler_sync, create_link_handler, { + "project_id": project_id, "links": links, "fields": fields, + }) + params = {"project_id": project_id, "nodes": nodes, "link_type": link_type, "fields": fields} + if filters: + params["filters"] = filters + return await asyncio.to_thread(_run_handler_sync, create_link_handler, params) + + +@mcp.tool() +async def link_delete( + project_id: Annotated[str, Field(description="UUID of the project")], + link_id: Annotated[str | None, Field(description="Link UUID (single mode)")] = None, + link_ids: Annotated[list[str] | None, Field(description="Batch mode: [\"uuid1\",\"uuid2\"] — delete multiple links in parallel")] = None, +) -> list[dict[str, Any]]: + """Delete one or more links from a project.""" + params = {"project_id": project_id} + if link_ids: + params["link_ids"] = link_ids + else: + params["link_id"] = link_id + return await asyncio.to_thread(_run_handler_sync, delete_link_handler, params) + + +@mcp.tool() +async def link_update( + project_id: Annotated[str, Field(description="UUID of the project")], + link_id: Annotated[str, Field(description="UUID of the link to update")], + **kwargs: Any, +) -> list[dict[str, Any]]: + """Update a link's properties (suspend, filters, etc.). + + Supported kwargs: + - suspend: boolean - Suspend or resume the link + - filters: dict - Packet filters (must use array format): + * frequency_drop: [N] - Drop every Nth packet (N: -1 to 32767) + * packet_loss: [rate] - Packet loss percentage (rate: 0 to 100) + * delay: [ms, jitter] - Latency and jitter in milliseconds + * corrupt: [rate] - Packet corruption percentage (rate: 0 to 100) + * bpf: [expression] - Berkeley Packet Filter expression + + Example filters: + {"filters": {"frequency_drop": [10]}} + {"filters": {"delay": [100, 10]}} + {"filters": {"packet_loss": [5]}} + {"filters": {"delay": [50, 5], "packet_loss": [2]}} + + To clear all filters: {"filters": {}} + + Filters are applied **bidirectionally** — a packet crossing the link twice + (e.g. ping round-trip) is filtered in both directions independently. + For example, packet_loss: [50] gives ~75% observed loss (1 - 0.5²), not 50%. + ARP frames also pass through filters; at high loss/corrupt rates, pre-set + static ARP entries to avoid false "Destination Host Unreachable" errors. + """ + params = {"project_id": project_id, "link_id": link_id, **kwargs} + return await asyncio.to_thread(_run_handler_sync, update_link_handler, params) + + +@mcp.tool() +async def link_available_filters( + project_id: Annotated[str, Field(description="UUID of the project")], + link_id: Annotated[str, Field(description="UUID of the link")], +) -> list[dict[str, Any]]: + """List the packet filter types available for a link (frequency_drop, packet_loss, delay, corrupt, bpf) + with their parameters. Use before setting filters with link_update.""" + return await asyncio.to_thread(_run_handler_sync, available_filters_handler, { + "project_id": project_id, "link_id": link_id, + }) + + +# ── Template tools ──────────────────────────────────────────── + +@mcp.tool() +async def template_list( + fields: Annotated[list[str] | None, Field(description="Response fields to include (default: [template_id, name, template_type, category, default_name_format]). " + "Available: template_id, name, version, category, default_name_format, symbol, " + "template_type, compute_id, usage, tags, builtin, created_at, updated_at")] = None, +) -> list[dict[str, Any]]: + """List all available templates on the server.""" + return await asyncio.to_thread(_run_handler_sync, list_templates_handler, {"fields": fields}) + + +@mcp.tool() +async def template_get( + template_id: Annotated[str | None, Field(description="Template UUID (optional if name is provided)")] = None, + name: Annotated[str | None, Field(description="Template name (optional if template_id is provided)")] = None, +) -> list[dict[str, Any]]: + """Get detailed information about a specific template.""" + return await asyncio.to_thread(_run_handler_sync, get_template_handler, { + "template_id": template_id, "name": name, + }) + + +@mcp.tool() +async def template_create( + name: Annotated[str, Field(description="Template name")], + template_type: Annotated[str, Field(description="Template type (e.g. qemu, docker, dynamips)")], + compute_id: Annotated[str, Field(description="Compute ID (default: local)")] = "local", + image: Annotated[str | None, Field(description="Docker image name or Dynamips IOS image path (required for docker/dynamips)")] = None, +) -> list[dict[str, Any]]: + """Create a new template. + + Template-type-specific required parameters: + docker: image is required (e.g. "ubuntu:latest") + dynamips: image is required (path to .image file) + iou: needs 'path' (IOL image path) — set via template_update after creation + qemu: needs 'hda_disk_image' or 'qemu_path' — set via template_update after creation + """ + params = {"name": name, "template_type": template_type, "compute_id": compute_id} + if image: + params["image"] = image + return await asyncio.to_thread(_run_handler_sync, create_template_handler, params) + + +@mcp.tool() +async def template_update( + template_id: Annotated[str | None, Field(description="Template UUID (optional if name is provided)")] = None, + name: Annotated[str | None, Field(description="Template name (optional if template_id is provided)")] = None, + **kwargs: Any, +) -> list[dict[str, Any]]: + """Update an existing template's properties.""" + params = {"template_id": template_id, "name": name, **kwargs} + return await asyncio.to_thread(_run_handler_sync, update_template_handler, params) + + +@mcp.tool() +async def template_delete( + template_id: Annotated[str | None, Field(description="Template UUID (optional if name is provided)")] = None, + name: Annotated[str | None, Field(description="Template name (optional if template_id is provided)")] = None, +) -> list[dict[str, Any]]: + """Delete a template.""" + return await asyncio.to_thread(_run_handler_sync, delete_template_handler, { + "template_id": template_id, "name": name, + }) + + +# ── Compute tools ───────────────────────────────────────────────────── + +@mcp.tool() +async def compute_list() -> list[dict[str, Any]]: + """List all remotely registered compute nodes (returns only database entries, does NOT include the built-in local compute). + + For the local compute info, use server_statistics instead. + """ + return await asyncio.to_thread(_run_handler_sync, list_computes_handler, {}) + + +@mcp.tool() +async def compute_get( + compute_id: Annotated[str, Field(description="Compute ID: 'local' (default) for the built-in local compute, or a compute UUID from compute_list")] = "local", +) -> list[dict[str, Any]]: + """Get detailed information about a compute node. + + Accepts 'local' for the built-in local compute or a UUID from compute_list + for a registered remote compute. + """ + return await asyncio.to_thread(_run_handler_sync, get_compute_handler, {"compute_id": compute_id}) + + +@mcp.tool() +async def compute_images( + emulator: Annotated[str, Field(description="Emulator type (e.g. qemu, iou, docker)")], + compute_id: Annotated[str, Field(description="Compute ID: 'local' (default) for the built-in local compute, or a compute UUID from compute_list")] = "local", +) -> list[dict[str, Any]]: + """List available images for an emulator on a compute node. + + Accepts 'local' for the built-in local compute or a UUID from compute_list + for a registered remote compute. + """ + return await asyncio.to_thread(_run_handler_sync, get_compute_images_handler, { + "emulator": emulator, "compute_id": compute_id, + }) + + +# ── Node file tools ──────────────────────────────────────────────────── + + +@mcp.tool() +async def node_file_list( + project_id: Annotated[str, Field(description="UUID of the project")], + node_id: Annotated[str, Field(description="UUID of the node")], + path: Annotated[str, Field(description="Subdirectory path within node directory (optional)")] = "", + recursive: Annotated[bool, Field(description="Recursively list all files (optional, default: false)")] = False, +) -> list[dict[str, Any]]: + """List files in a node directory with metadata (name, size, type, modified time). + + Use this first to check file sizes before reading files with get_node_file. + Large config files should be read in chunks using offset/limit. + """ + return await asyncio.to_thread(_run_handler_sync, list_node_files_handler, { + "project_id": project_id, "node_id": node_id, "path": path, "recursive": recursive, + }) + + +@mcp.tool() +async def node_file_get( + project_id: Annotated[str, Field(description="UUID of the project")], + node_id: Annotated[str, Field(description="UUID of the node")], + file_path: Annotated[str, Field(description="Path to the file within the node directory")], + offset: Annotated[int, Field(description="Line offset to start reading from (optional, default: 0)")] = 0, + limit: Annotated[int, Field(description="Maximum number of lines to return (optional, default: 200)")] = 200, +) -> list[dict[str, Any]]: + """Read a text file from a node directory line-by-line with offset/limit support. + + Best practice: + 1. First call list_node_files to see the file size before deciding to read. + 2. Start with offset=0, limit=200 to preview the file. + 3. If metadata.has_more is true, read more by increasing offset. + Large files (>50KB) are auto-truncated; check the metadata.truncated flag. + For binary files, check the file type via list_node_files first. + """ + return await asyncio.to_thread(_run_handler_sync, get_node_file_handler, { + "project_id": project_id, "node_id": node_id, "file_path": file_path, + "offset": offset, "limit": limit, + }) + + +@mcp.tool() +async def node_file_write( + project_id: Annotated[str, Field(description="UUID of the project")], + node_id: Annotated[str, Field(description="UUID of the node")], + file_path: Annotated[str, Field(description="Path to the file within the node directory")], + content: Annotated[str, Field(description="Content to write to the file")], +) -> list[dict[str, Any]]: + """Write content to a file in a node directory. Creates the file if it doesn't exist. Overwrites existing content.""" + return await asyncio.to_thread(_run_handler_sync, write_node_file_handler, { + "project_id": project_id, "node_id": node_id, "file_path": file_path, "content": content, + }) + + +@mcp.tool() +async def node_file_delete( + project_id: Annotated[str, Field(description="UUID of the project")], + node_id: Annotated[str, Field(description="UUID of the node")], + file_path: Annotated[str, Field(description="Path to the file within the node directory")], +) -> list[dict[str, Any]]: + """Delete a file from a node directory. Cannot be undone.""" + return await asyncio.to_thread(_run_handler_sync, delete_node_file_handler, { + "project_id": project_id, "node_id": node_id, "file_path": file_path, + }) + + +# ── Node bulk / advanced tools ───────────────────────────────────────── + + +@mcp.tool() +async def node_start_all( + project_id: Annotated[str, Field(description="UUID of the project")], +) -> list[dict[str, Any]]: + """Start all nodes in a project.""" + return await asyncio.to_thread(_run_handler_sync, start_all_nodes_handler, { + "project_id": project_id, + }) + + +@mcp.tool() +async def node_stop_all( + project_id: Annotated[str, Field(description="UUID of the project")], +) -> list[dict[str, Any]]: + """Stop all nodes in a project.""" + return await asyncio.to_thread(_run_handler_sync, stop_all_nodes_handler, { + "project_id": project_id, + }) + + +@mcp.tool() +async def node_suspend_all( + project_id: Annotated[str, Field(description="UUID of the project")], +) -> list[dict[str, Any]]: + """Suspend all nodes in a project.""" + return await asyncio.to_thread(_run_handler_sync, suspend_all_nodes_handler, { + "project_id": project_id, + }) + + +@mcp.tool() +async def node_duplicate( + project_id: Annotated[str, Field(description="UUID of the project")], + node_id: Annotated[str, Field(description="UUID of the node to duplicate")], + x: Annotated[int, Field(description="X coordinate for the new node")] = 0, + y: Annotated[int, Field(description="Y coordinate for the new node")] = 0, + z: Annotated[int, Field(description="Z layer for the new node")] = 0, +) -> list[dict[str, Any]]: + """Duplicate a node in a project, creating a copy at a new position.""" + return await asyncio.to_thread(_run_handler_sync, duplicate_node_handler, { + "project_id": project_id, "node_id": node_id, "x": x, "y": y, "z": z, + }) + + +@mcp.tool() +async def node_isolate( + project_id: Annotated[str, Field(description="UUID of the project")], + node_id: Annotated[str, Field(description="UUID of the node to isolate")], +) -> list[dict[str, Any]]: + """Isolate a node by suspending all its attached links (network isolation).""" + return await asyncio.to_thread(_run_handler_sync, isolate_node_handler, { + "project_id": project_id, "node_id": node_id, + }) + + +@mcp.tool() +async def node_unisolate( + project_id: Annotated[str, Field(description="UUID of the project")], + node_id: Annotated[str, Field(description="UUID of the node to unisolate")], +) -> list[dict[str, Any]]: + """Un-isolate a node by resuming all its suspended links.""" + return await asyncio.to_thread(_run_handler_sync, unisolate_node_handler, { + "project_id": project_id, "node_id": node_id, + }) + + +@mcp.tool() +async def node_links( + project_id: Annotated[str, Field(description="UUID of the project")], + node_id: Annotated[str, Field(description="UUID of the node")], +) -> list[dict[str, Any]]: + """List all links connected to a specific node.""" + return await asyncio.to_thread(_run_handler_sync, get_node_links_handler, { + "project_id": project_id, "node_id": node_id, + }) + + +# ── Link capture / reset tools ──────────────────────────────────────── + + +@mcp.tool() +async def link_reset( + project_id: Annotated[str, Field(description="UUID of the project")], + link_id: Annotated[str | None, Field(description="Link UUID (single mode)")] = None, + link_ids: Annotated[list[str] | None, Field(description="Batch mode: [\"uuid1\",\"uuid2\"] — reset multiple links in parallel")] = None, +) -> list[dict[str, Any]]: + """Reset one or more links by tearing down and recreating the UDP connection. + + Use cases: + - Clear accumulated packet errors/drops from the link's UDP connection + - Force filter state (delay, packet loss, etc.) to restart fresh + - Recover a stuck or abnormal link state + + This restarts the filter state machines (e.g. frequency_drop counters) + while keeping the filter configuration intact. Filters are preserved but + their internal application state resets. + """ + params = {"project_id": project_id} + if link_ids: + params["link_ids"] = link_ids + else: + params["link_id"] = link_id + return await asyncio.to_thread(_run_handler_sync, reset_link_handler, params) + + +@mcp.tool() +async def link_capture_start( + project_id: Annotated[str, Field(description="UUID of the project")], + link_id: Annotated[str | None, Field(description="Link UUID (single mode)")] = None, + data_link_type: Annotated[str, Field(description="Data link type (default: DLT_EN10MB)")] = "DLT_EN10MB", + capture_file_name: Annotated[str | None, Field(description="Capture file name (optional)")] = None, + wireshark: Annotated[bool, Field(description="Open Wireshark automatically (default: false)")] = False, + link_ids: Annotated[list[str] | None, Field(description="Batch mode: [\"uuid1\",\"uuid2\"] — start capture on multiple links in parallel")] = None, +) -> list[dict[str, Any]]: + """Start packet capture on one or more links.""" + params = {"project_id": project_id, "data_link_type": data_link_type, "capture_file_name": capture_file_name, "wireshark": wireshark} + if link_ids: + params["link_ids"] = link_ids + else: + params["link_id"] = link_id + return await asyncio.to_thread(_run_handler_sync, start_capture_handler, params) + + +@mcp.tool() +async def link_capture_stop( + project_id: Annotated[str, Field(description="UUID of the project")], + link_id: Annotated[str | None, Field(description="Link UUID (single mode)")] = None, + link_ids: Annotated[list[str] | None, Field(description="Batch mode: [\"uuid1\",\"uuid2\"] — stop capture on multiple links in parallel")] = None, +) -> list[dict[str, Any]]: + """Stop packet capture on one or more links.""" + params = {"project_id": project_id} + if link_ids: + params["link_ids"] = link_ids + else: + params["link_id"] = link_id + return await asyncio.to_thread(_run_handler_sync, stop_capture_handler, params) + + +@mcp.tool() +async def link_capture_download( + project_id: Annotated[str, Field(description="UUID of the project")], + link_id: Annotated[str | None, Field(description="Link UUID (single mode)")] = None, + link_ids: Annotated[list[str] | None, Field(description="Batch mode: [\"uuid1\",\"uuid2\"] — get download URLs for multiple captures")] = None, +) -> list[dict[str, Any]]: + """Get download command(s) for PCAP capture file(s). + + Returns a ready-to-run curl command per link with a short-lived access + ticket (10 min) already embedded in the URL. + + IMPORTANT — copy the returned values EXACTLY: + - Run each returned "curl_command" verbatim; it already contains the + full URL with ticket. NEVER construct or edit the URL yourself, and + NEVER copy the ticket by hand — a mistyped ticket is rejected. + - The ticket expires after 10 minutes: call this tool again to get a + fresh one; do not reuse an old URL. + """ + params = {"project_id": project_id} + if link_ids: + params["link_ids"] = link_ids + else: + params["link_id"] = link_id + return await asyncio.to_thread(_run_handler_sync, download_capture_file_handler, params) + + +# ── Marker (traffic-insight) tools ───────────────────────────────────── + + +@mcp.tool() +async def link_marker( + project_id: Annotated[str, Field(description="UUID of the project")], + link_id: Annotated[str, Field(description="UUID of the link")], + action: Annotated[str, Field(description="Action: create, update, or delete")], + bpf: Annotated[str | None, Field(description="BPF expression, e.g. 'arp', 'icmp', 'tcp port 80' (required for create)")] = None, + marker_name: Annotated[str | None, Field(description="Marker name (required for update/delete actions)")] = None, + name: Annotated[str | None, Field(description="Custom marker name for create action (auto-generated if omitted)")] = None, + tag: Annotated[int | None, Field(description="Numeric tag for packet correlation")] = None, + enabled: Annotated[bool | None, Field(description="Enable or disable the marker (for update action)")] = None, + direction: Annotated[str | None, Field(description="Direction filter: 'tx' (capture node sending only), 'rx' (receiving only), or 'both' (no filter — on update this clears a previously set direction). Omit to leave unchanged on update.")] = None, + capture_node_id: Annotated[str | None, Field(description="UUID of the endpoint whose uBridge hosts the marker (the observer; tx/rx are from its perspective). Must be a link endpoint and marker-capable. Omit to auto-pick.")] = None, + color: Annotated[str | None, Field(description="Hex color for UI highlight, e.g. '#ff5722'")] = None, + highlight_duration: Annotated[int | None, Field(description="UI highlight duration in milliseconds")] = None, + data_link_type: Annotated[str | None, Field(description="pcap link-layer type for serial links (create-only): DLT_C_HDLC / DLT_PPP_SERIAL / DLT_FRELAY / DLT_ATM_RFC1483, matching the encapsulation on the serial link. Omit = DLT_EN10MB (Ethernet). Ignored on update — changing it would invalidate the capture file.")] = None, +) -> list[dict[str, Any]]: + """Manage traffic-insight markers on a link. + + A marker highlights packets matching a BPF expression as they cross the link. + Set action='create' to add a marker, 'update' to modify it, 'delete' to remove. + + Create requires: project_id, link_id, action='create', bpf + Update requires: project_id, link_id, action='update', marker_name, and at least one of (bpf, tag, enabled, direction, color, highlight_duration) + Delete requires: project_id, link_id, action='delete', marker_name + + To read current markers, use link_get — the response includes a 'markers' dict. + + NOTE: Markers named 'global-*' are inherited from project-level marker definitions + and cannot be modified or deleted via this tool. + """ + params = {"project_id": project_id, "link_id": link_id, "action": action} + for opt in ("bpf", "marker_name", "name", "tag", "enabled", "direction", "capture_node_id", "color", "highlight_duration", "data_link_type"): + val = locals().get(opt) + if val is not None: + params[opt] = val + return await asyncio.to_thread(_run_handler_sync, link_marker_handler, params) + + +@mcp.tool() +async def marker_definition( + project_id: Annotated[str, Field(description="UUID of the project")], + action: Annotated[str, Field(description="Action: create, update, delete, or list")], + bpf: Annotated[str | None, Field(description="BPF expression, e.g. 'arp', 'ospf', 'tcp port 22' (required for create)")] = None, + def_name: Annotated[str | None, Field(description="Definition name (required for update/delete actions)")] = None, + name: Annotated[str | None, Field(description="Custom definition name for create action (auto-generated if omitted)")] = None, + tag: Annotated[int | None, Field(description="Numeric tag for packet correlation")] = None, + color: Annotated[str | None, Field(description="Hex color for UI highlight, e.g. '#ff5722'")] = None, + highlight_duration: Annotated[int | None, Field(description="UI highlight duration in milliseconds")] = None, + data_link_type: Annotated[str | None, Field(description="pcap link-layer type for serial links (DLT_C_HDLC / DLT_PPP_SERIAL / DLT_FRELAY / DLT_ATM_RFC1483). Omit = Ethernet-only (serial links skipped); setting it also covers serial links with that encapsulation")] = None, +) -> list[dict[str, Any]]: + """Manage project-level marker definitions — traffic-insight rules that apply to ALL links. + + A marker definition is a global BPF rule. On create, it auto-fans out to every + link in the project as 'global-{name}'. Updates sync to all inherited copies. + On delete, 'global-{name}' is removed from every link. + + Create requires: project_id, action='create', bpf + Update requires: project_id, action='update', def_name, and at least one of (bpf, tag, color, highlight_duration, data_link_type) + Delete requires: project_id, action='delete', def_name + List requires: project_id, action='list' + + A definition has NO direction (tx/rx): it fans out to every link and auto-selects + its capture node on each, so a fixed direction has no consistent meaning. Encode + the direction you want in the BPF instead (e.g. 'icmp and icmp[icmptype]==8' for + echo requests only). For a capture-node-relative direction on a single link, use + the per-link `link_marker` tool. + + Common BPF examples: 'arp', 'icmp', 'ospf', 'tcp port 22', 'udp port 53' + """ + params = {"project_id": project_id, "action": action} + for opt in ("bpf", "def_name", "name", "tag", "color", "highlight_duration", "data_link_type"): + val = locals().get(opt) + if val is not None: + params[opt] = val + return await asyncio.to_thread(_run_handler_sync, marker_definition_handler, params) + + +# ── Snapshot tools ───────────────────────────────────────────────────── + + +@mcp.tool() +async def snapshot_list( + project_id: Annotated[str, Field(description="UUID of the project")], +) -> list[dict[str, Any]]: + """List all snapshots of a project.""" + return await asyncio.to_thread(_run_handler_sync, get_snapshots_handler, { + "project_id": project_id, + }) + + +@mcp.tool() +async def snapshot_create( + project_id: Annotated[str, Field(description="UUID of the project")], + name: Annotated[str, Field(description="Name for the new snapshot")], +) -> list[dict[str, Any]]: + """Create a new snapshot of a project. + + Prerequisite: All stoppable nodes (qemu, docker, dynamips, vpcs, iou, etc.) + must be stopped first. Use node_stop_all before creating a snapshot. + Cloud, NAT, and switch nodes are always-running and can be ignored. + """ + return await asyncio.to_thread(_run_handler_sync, create_snapshot_handler, { + "project_id": project_id, "name": name, + }) + + +@mcp.tool() +async def snapshot_delete( + project_id: Annotated[str, Field(description="UUID of the project")], + snapshot_id: Annotated[str, Field(description="UUID of the snapshot to delete")], +) -> list[dict[str, Any]]: + """Delete a snapshot from a project. Cannot be undone.""" + return await asyncio.to_thread(_run_handler_sync, delete_snapshot_handler, { + "project_id": project_id, "snapshot_id": snapshot_id, + }) + + +@mcp.tool() +async def snapshot_restore( + project_id: Annotated[str, Field(description="UUID of the project")], + snapshot_id: Annotated[str, Field(description="UUID of the snapshot to restore")], +) -> list[dict[str, Any]]: + """Restore a project to a previous snapshot state. The project may be closed and reopened.""" + return await asyncio.to_thread(_run_handler_sync, restore_snapshot_handler, { + "project_id": project_id, "snapshot_id": snapshot_id, + }) + + +# ── Drawing tools ────────────────────────────────────────────────────── + + +@mcp.tool() +async def drawing_list( + project_id: Annotated[str, Field(description="UUID of the project")], +) -> list[dict[str, Any]]: + """List all drawings (labels, shapes, images) on a project canvas.""" + return await asyncio.to_thread(_run_handler_sync, get_drawings_handler, { + "project_id": project_id, + }) + + +@mcp.tool() +async def drawing_create( + project_id: Annotated[str, Field(description="UUID of the project")], + svg: Annotated[str, Field(description="SVG content for the drawing")], + x: Annotated[int, Field(description="X coordinate (default: 0)")] = 0, + y: Annotated[int, Field(description="Y coordinate (default: 0)")] = 0, + z: Annotated[int, Field(description="Z layer (default: 0)")] = 0, + locked: Annotated[bool, Field(description="Lock the drawing (default: false)")] = False, + rotation: Annotated[int, Field(description="Rotation angle in degrees, -359 to 359 (default: 0)")] = 0, +) -> list[dict[str, Any]]: + """Create a new drawing (label, shape, or image) on a project canvas. + + GNS3 SVG rendering notes: + - MUST have a solid fill color (e.g. fill=\"#FF0000\") to render. + fill=\"none\" or fill=\"transparent\" will be invisible in the GUI. + - works correctly with or without fill. + - and work normally. + + SVG examples: + Text label: R1 + Rectangle: + Ellipse: + Line: + Dashed line: + """ + return await asyncio.to_thread(_run_handler_sync, create_drawing_handler, { + "project_id": project_id, "svg": svg, "x": x, "y": y, "z": z, + "locked": locked, "rotation": rotation, + }) + + +@mcp.tool() +async def drawing_get( + project_id: Annotated[str, Field(description="UUID of the project")], + drawing_id: Annotated[str, Field(description="UUID of the drawing")], +) -> list[dict[str, Any]]: + """Get detailed information about a specific drawing.""" + return await asyncio.to_thread(_run_handler_sync, get_drawing_handler, { + "project_id": project_id, "drawing_id": drawing_id, + }) + + +@mcp.tool() +async def drawing_update( + project_id: Annotated[str, Field(description="UUID of the project")], + drawing_id: Annotated[str, Field(description="UUID of the drawing")], + svg: Annotated[str | None, Field(description="New SVG content")] = None, + locked: Annotated[bool | None, Field(description="Lock or unlock the drawing")] = None, + x: Annotated[int | None, Field(description="New X coordinate")] = None, + y: Annotated[int | None, Field(description="New Y coordinate")] = None, + z: Annotated[int | None, Field(description="New Z layer")] = None, + rotation: Annotated[int | None, Field(description="Rotation angle in degrees, -359 to 359")] = None, +) -> list[dict[str, Any]]: + """Update a drawing's properties (svg, position, lock state, rotation, etc.).""" + params = {"project_id": project_id, "drawing_id": drawing_id} + local_vars = {"svg": svg, "locked": locked, "x": x, "y": y, "z": z, "rotation": rotation} + for key, val in local_vars.items(): + if val is not None: + params[key] = val + return await asyncio.to_thread(_run_handler_sync, update_drawing_handler, params) + + +@mcp.tool() +async def drawing_delete( + project_id: Annotated[str, Field(description="UUID of the project")], + drawing_id: Annotated[str, Field(description="UUID of the drawing to delete")], +) -> list[dict[str, Any]]: + """Delete a drawing from a project canvas. Cannot be undone.""" + return await asyncio.to_thread(_run_handler_sync, delete_drawing_handler, { + "project_id": project_id, "drawing_id": drawing_id, + }) + + +# ── Project lock tools ──────────────────────────────────────────────── + + +@mcp.tool() +async def project_lock( + project_id: Annotated[str, Field(description="UUID of the project")], +) -> list[dict[str, Any]]: + """Lock all drawings and nodes in a project to prevent accidental changes.""" + return await asyncio.to_thread(_run_handler_sync, lock_project_handler, { + "project_id": project_id, + }) + + +@mcp.tool() +async def project_unlock( + project_id: Annotated[str, Field(description="UUID of the project")], +) -> list[dict[str, Any]]: + """Unlock a project to allow editing of drawings and nodes.""" + return await asyncio.to_thread(_run_handler_sync, unlock_project_handler, { + "project_id": project_id, + }) + + +@mcp.tool() +async def project_locked( + project_id: Annotated[str, Field(description="UUID of the project")], +) -> list[dict[str, Any]]: + """Check whether a project is locked (preventing edits to drawings and nodes).""" + return await asyncio.to_thread(_run_handler_sync, get_locked_project_handler, { + "project_id": project_id, + }) + + +# ── Server info tools ───────────────────────────────────────────────── + + +@mcp.tool() +async def server_version() -> list[dict[str, Any]]: + """Get GNS3 server version information.""" + return await asyncio.to_thread(_run_handler_sync, get_version_handler, {}) + + +@mcp.tool() +async def server_statistics() -> list[dict[str, Any]]: + """Get GNS3 server statistics including computes, projects, nodes, and links.""" + return await asyncio.to_thread(_run_handler_sync, get_statistics_handler, {}) + + +# ── Symbol tools ────────────────────────────────────────────────────── +# +# Disabled for now: symbol handling requires a vision-capable model (the +# tools shuttle SVG content, which a text-only LLM cannot inspect or +# produce). Revisit later. +# +# @mcp.tool() +# async def symbol_list() -> list[dict[str, Any]]: +# """List all available symbols on the server.""" +# return await asyncio.to_thread(_run_handler_sync, get_symbols_handler, {}) +# +# +# @mcp.tool() +# async def symbol_get( +# symbol_id: Annotated[str, Field(description="Symbol ID (e.g. ':/symbols/router.svg')")], +# ) -> list[dict[str, Any]]: +# """Get a download URL for a symbol file (SVG). +# +# Returns a ready-to-run curl command with a short-lived access ticket +# (10 min) embedded in the URL. Run the returned "curl_command" verbatim — +# never reconstruct the URL or copy the ticket by hand. Re-call this tool +# once the ticket has expired. +# """ +# return await asyncio.to_thread(_run_handler_sync, get_symbol_handler, { +# "symbol_id": symbol_id, +# }) +# +# +# @mcp.tool() +# async def symbol_dimensions( +# symbol_id: Annotated[str, Field(description="Symbol ID to get dimensions for")], +# ) -> list[dict[str, Any]]: +# """Get the dimensions (width, height) of a symbol.""" +# return await asyncio.to_thread(_run_handler_sync, get_symbol_dimensions_handler, { +# "symbol_id": symbol_id, +# }) +# +# +# @mcp.tool() +# async def symbol_defaults() -> list[dict[str, Any]]: +# """Get the default symbol mapping for each node type.""" +# return await asyncio.to_thread(_run_handler_sync, get_default_symbols_handler, {}) +# +# +# @mcp.tool() +# async def symbol_upload( +# symbol_id: Annotated[str, Field(description="Symbol ID to upload (e.g. ':/symbols/my_symbol.svg')")], +# content: Annotated[str, Field(description="SVG content of the symbol")], +# ) -> list[dict[str, Any]]: +# """Upload or update a custom symbol on the server. Provide the SVG content as a string.""" +# return await asyncio.to_thread(_run_handler_sync, upload_symbol_handler, { +# "symbol_id": symbol_id, "content": content, +# }) +# +# +# @mcp.tool() +# async def symbol_delete( +# symbol_id: Annotated[str, Field(description="Symbol ID to delete (e.g. ':/symbols/my_custom_symbol.svg'). Use symbol_list to get existing IDs.")], +# ) -> list[dict[str, Any]]: +# """Delete a custom symbol from the server. +# +# NOTE: Only custom (user-uploaded) symbols can be deleted. +# Built-in symbols (starting with ':/symbols/') will be rejected with 403. +# Use symbol_list to see which symbols are available and their IDs. +# """ +# return await asyncio.to_thread(_run_handler_sync, delete_symbol_handler, { +# "symbol_id": symbol_id, +# }) + + +# ── Appliance tools ─────────────────────────────────────────────────── + + +@mcp.tool() +async def appliance_list( + fields: Annotated[list[str] | None, Field(description="Optional: return only these fields. e.g. [\"name\",\"category\"]. Available: name, category, description, vendor_name, product_name, status, availability, images, versions, tags, symbol, usage, builtin")] = None, +) -> list[dict[str, Any]]: + """List all available appliances (template library). Use fields=[] to return only what you need.""" + return await asyncio.to_thread(_run_handler_sync, get_appliances_handler, {"fields": fields} if fields else {}) + + +@mcp.tool() +async def appliance_get( + appliance_id: Annotated[str, Field(description="UUID of the appliance")], +) -> list[dict[str, Any]]: + """Get detailed information about a specific appliance.""" + return await asyncio.to_thread(_run_handler_sync, get_appliance_handler, { + "appliance_id": appliance_id, + }) + + +@mcp.tool() +async def appliance_install( + appliance_id: Annotated[str, Field(description="UUID of the appliance to install")], + version: Annotated[str | None, Field(description="Version to install (e.g. '2.7.0.356'). Required if the appliance has multiple versions. Use appliance_get to see available versions.")] = None, +) -> list[dict[str, Any]]: + """Create a template from a GNS3 appliance definition and return the created template. + + NOTE: This does NOT download images. Images must be placed in the + GNS3 images directory (e.g. ~/GNS3/images/) beforehand. + The appliance definition is read from local .gns3a files bundled with the server. + Use get_appliance first to see what images are required. + """ + return await asyncio.to_thread(_run_handler_sync, install_appliance_handler, { + "appliance_id": appliance_id, + "version": version, + }) + + +# ── Image tools ─────────────────────────────────────────────────────── + + +@mcp.tool() +async def image_list() -> list[dict[str, Any]]: + """List all images available on the server across all emulators.""" + return await asyncio.to_thread(_run_handler_sync, get_images_handler, {}) + + +@mcp.tool() +async def image_get( + image_id: Annotated[str, Field(description="ID or filename of the image")], +) -> list[dict[str, Any]]: + """Get detailed information about a specific image.""" + return await asyncio.to_thread(_run_handler_sync, get_image_handler, { + "image_id": image_id, + }) + + +@mcp.tool() +async def image_delete( + image_id: Annotated[str, Field(description="ID or filename of the image to delete")], +) -> list[dict[str, Any]]: + """Delete an image from the server. Cannot be undone.""" + return await asyncio.to_thread(_run_handler_sync, delete_image_handler, { + "image_id": image_id, + }) + + +@mcp.tool() +async def image_prune() -> list[dict[str, Any]]: + """Remove images not referenced by any template. + + NOTE: Only images that are not used by any template will be removed. + If all images are still referenced by templates, no images are deleted. + Use image_list to see which images exist and check if they are in use. + """ + return await asyncio.to_thread(_run_handler_sync, prune_images_handler, {}) + + +@mcp.tool() +async def image_install() -> list[dict[str, Any]]: + """Scan uploaded images and auto-create templates by matching image checksums against known appliance definitions. + + This is NOT for downloading images. Images must be uploaded first (via the GNS3 Web UI). + If an uploaded image matches a known appliance, a template is automatically created. + Returns {"created": [...], "skipped": [...]}: images already referenced by existing + templates are skipped, and no template is auto-created when one with the same name + already exists (regardless of version). + """ + return await asyncio.to_thread(_run_handler_sync, install_images_handler, {}) + + +# ── Device config tools ─────────────────────────────────────────────── +# These tools connect to network device consoles via telnet/SSH using +# Nornir + Netmiko. Devices must be started and have a device_type tag. +# +# Workflow: +# 1. node_list(project_id) → identify device names +# 2. node_start_all(project_id) → ensure devices are running +# 3. device_config_send(project_id, device_configs=[...]) → push config +# 4. device_show_run(project_id, device_commands=[...]) → verify + + +@mcp.tool() +async def device_config_send( + project_id: Annotated[str, Field(description="UUID of the project")], + device_configs: Annotated[list, Field( + description="List of device configs. Each entry: {\"device_name\": \"R1\", \"config_commands\": [\"int lo0\", \"ip add 1.1.1.1 255.255.255.255\"]}" + )], + template: Annotated[str | None, Field(description="Optional Jinja2 template. Use with vars in each device to reduce token usage for batch config. Example: \"interface lo{{ n }}\\nip address {{ ip }} 255.255.255.255\"")] = None, +) -> list[dict[str, Any]]: + """Send configuration commands to network devices via console (telnet/SSH). + + Two modes: + 1. Direct commands: each device has config_commands=[...] + 2. Jinja2 template: provide template + vars per device — template is rendered for each + Example: device_configs=[{\"device_name\": \"R1\", \"vars\": {\"n\": 0, \"ip\": \"1.1.1.1\"}}] + + Devices must be started first (use node_start or node_start_all). + Device type is auto-detected from the 'device_type:' tag on each node. + Common device types: cisco_ios_telnet, cisco_xr_telnet, huawei_telnet, gns3_huawei_telnet_ce + + Error contract: every failure is reported in-band as an entry with + status "failed" and an "error" message (per-device entries also carry + device_name and commands). + """ + params = {"project_id": project_id, "device_configs": device_configs} + if template is not None: + params["template"] = template + return await asyncio.to_thread(_run_handler_sync, device_config_send_handler, params) + + +@mcp.tool() +async def device_show_run( + project_id: Annotated[str, Field(description="UUID of the project")], + device_configs: Annotated[list, Field( + description="List of device commands. Each entry: {\"device_name\": \"R1\", \"commands\": [\"show ip int brief\", \"show running-config\"]}" + )], + template: Annotated[str | None, Field(description="Optional Jinja2 template. Use with vars per device. Example: \"show ip route {{ protocol }}\"")] = None, +) -> list[dict[str, Any]]: + """Run read-only diagnostic (show) commands on network devices via console. + + Two modes: + 1. Direct commands: each device has commands=[...] (read-only show/display/ping/traceroute only) + 2. Jinja2 template: provide template + vars per device + + Use this to inspect device status, view configurations, or verify changes. + For configuration changes use device_config_send instead. + + Prerequisites: + - Devices must be started first (use node_start or node_start_all). + - Each node must have a device_type: tag set in GNS3 + (e.g. device_type:cisco_ios_telnet, device_type:gns3_huawei_telnet_ce). + Nodes without this tag will fail with "device_type tag not found". + Docker/Linux nodes are not supported (use node_console instead). + + Error contract: every failure is reported in-band as an entry with + status "failed" and an "error" message (per-device entries also carry + device_name and commands). + """ + params = {"project_id": project_id, "device_configs": device_configs} + if template is not None: + params["template"] = template + return await asyncio.to_thread(_run_handler_sync, device_show_run_handler, params) + + +@mcp.tool() +async def vpcs_config_set( + project_id: Annotated[str, Field(description="UUID of the project")], + device_configs: Annotated[list, Field( + description="List of VPCS configs. Each entry: {\"device_name\": \"PC1\", \"commands\": [\"ip 10.0.0.1/24 10.0.0.254\", \"save\"]}" + )], +) -> list[dict[str, Any]]: + """Configure VPCS devices (set IP addresses, gateway, etc.). + + Only VPCS nodes are accepted: any other node type in device_configs fails + with a per-device error instead of typing VPCS syntax into its CLI. + Every failure is reported in-band as an entry with status "failed" + and an "error" message. + + VPCS-specific configuration commands: + - ip
/ Set IP and gateway + - save Save config to startup.vpc + - ping Test connectivity + """ + return await asyncio.to_thread(_run_handler_sync, vpcs_config_set_handler, { + "project_id": project_id, "device_configs": device_configs, + }) + + +# ── Auth‑wrapped SSE app ────────────────────────────────────────────── + +def _make_auth_wrapper(inner_app): + """Wrap the SSE app with JWT validation. + + Supports two ways to pass the token (checked in order): + 1. Authorization: Bearer header + 2. ?token= query parameter + + POST messages are passed through (authenticated by their session). + """ + + async def auth_wrapper(scope, receive, send): + # Wait for GNS3 server to complete initialization before accepting MCP connections + server_ready = await wait_for_mcp_ready() + if not server_ready: + # Server initialization timed out - return 503 Service Unavailable + client_info = extract_client_info(scope, auth_service) + log.warning( + f"Rejecting MCP connection - GNS3 server initialization not complete. " + f"Client: {client_info['host']}:{client_info['port']} ({client_info['user_info']}, Path: {client_info['path']})" + ) + response = Response( + "GNS3 server initialization not complete - please retry later", + status_code=503 + ) + await response(scope, receive, send) + return + + if scope["type"] == "http" and scope["method"] == "GET": + token = None + headers = dict(scope.get("headers", [])) + auth = headers.get(b"authorization", b"").decode() + if auth.startswith("Bearer "): + token = auth[7:] + if not token: + params = parse_qs(scope.get("query_string", b"").decode()) + tokens = params.get("token", []) + if tokens: + token = tokens[0] + if not token: + response = Response("Missing or invalid token", status_code=401) + await response(scope, receive, send) + return + resolved = await _resolve_token(token) + if not resolved: + response = Response("Missing or invalid token", status_code=401) + await response(scope, receive, send) + return + _jwt_token_var.set(resolved) + await inner_app(scope, receive, send) + + return auth_wrapper + + +# ── FastAPI router ──────────────────────────────────────────────────── + +router = APIRouter(prefix="/mcp", tags=["MCP"]) + + +@router.get("/") +async def mcp_root(): + """MCP service metadata.""" + return { + "name": "GNS3 MCP Server", + "version": "1.0.0", + "authentication": ["Authorization: Bearer ", "?token="], + "transports": { + "sse": "/v3/mcp/transport/sse", + }, + } + + +def register_starlette_routes(app): + """Mount MCP transports on the FastAPI app.""" + global _app + _app = app + sse_app = _make_auth_wrapper(mcp.sse_app(mount_path="")) + app.mount("/v3/mcp/transport", sse_app, name="mcp-sse") + log.info("MCP SSE server mounted at /v3/mcp/transport") + + # Log registered MCP tools for verification + tool_names = list(mcp._tool_manager._tools.keys()) + log.info("MCP tools registered (%d): %s", len(tool_names), ", ".join(sorted(tool_names))) diff --git a/gns3server/agent/mcp/appliances.py b/gns3server/agent/mcp/appliances.py new file mode 100644 index 000000000..ab867fe57 --- /dev/null +++ b/gns3server/agent/mcp/appliances.py @@ -0,0 +1,97 @@ +# +# Copyright (C) 2026 GNS3 Technologies Inc. +# Author: Yue Guobin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +MCP tool handlers for GNS3 appliance management. +""" + +from typing import Any + +import logging + +log = logging.getLogger(__name__) + + +# ── Helper ───────────────────────────────────────────────────────────────── + +def _get_connector(gns3_ctx: dict[str, Any]): + from gns3server.agent.gns3_copilot.gns3_client.connector import Gns3Connector + return Gns3Connector( + url=gns3_ctx["server_url"], + jwt_token=gns3_ctx["jwt_token"], + api_version=3, + verify=False, + ) + + +# ── Tool handlers ────────────────────────────────────────────────────────── + +VALID_APPLIANCE_FIELDS = { + "appliance_id", "name", "category", "description", "vendor_name", + "vendor_url", "product_name", "product_url", "documentation_url", + "status", "availability", "maintainer", "usage", "symbol", + "images", "versions", "tags", "builtin", + "first_port_name", "port_name_format", "port_segment_size", + "linked_clone", "docker", "iou", "dynamips", "qemu", +} + + +def get_appliances_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + conn = _get_connector(gns3_ctx) + appliances = conn.http_call("get", f"{conn.base_url}/appliances").json() + fields = params.get("fields") + if fields: + if not isinstance(fields, list): + return {"error": "fields must be a list, e.g. [\"name\", \"category\"]"} + invalid = [f for f in fields if f not in VALID_APPLIANCE_FIELDS] + if invalid: + return { + "error": f"Unknown fields: {invalid}", + "available_fields": sorted(VALID_APPLIANCE_FIELDS), + } + appliances = [{k: a[k] for k in fields if k in a} for a in appliances] + return {"appliances": appliances, "count": len(appliances)} + + +def get_appliance_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + appliance_id = params.get("appliance_id") + if not appliance_id: + return {"error": "appliance_id is required"} + conn = _get_connector(gns3_ctx) + return conn.http_call("get", f"{conn.base_url}/appliances/{appliance_id}").json() + + +def install_appliance_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + appliance_id = params.get("appliance_id") + if not appliance_id: + return {"error": "appliance_id is required"} + conn = _get_connector(gns3_ctx) + url = f"{conn.base_url}/appliances/{appliance_id}/install" + request_params = {} + version = params.get("version") + if version: + request_params["version"] = version + response = conn.http_call("post", url, params=request_params) + result = {"message": f"Appliance {appliance_id} installed"} + if response.content: + # the install endpoint returns the created template (201); tolerate an + # empty body in case an older server still replies with 204 + template = response.json() + result["template"] = { + k: template[k] for k in ("template_id", "name", "version", "template_type") if k in template + } + return result diff --git a/gns3server/agent/mcp/computes.py b/gns3server/agent/mcp/computes.py new file mode 100644 index 000000000..254dc53f1 --- /dev/null +++ b/gns3server/agent/mcp/computes.py @@ -0,0 +1,91 @@ +# +# Copyright (C) 2026 GNS3 Technologies Inc. +# Author: Yue Guobin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +MCP tool handlers for GNS3 compute management. +""" + +from typing import Any +import logging + +log = logging.getLogger(__name__) + + +def _get_connector(gns3_ctx: dict[str, Any]): + from gns3server.agent.gns3_copilot.gns3_client.connector import Gns3Connector + return Gns3Connector( + url=gns3_ctx["server_url"], + jwt_token=gns3_ctx["jwt_token"], + api_version=3, + verify=False, + ) + + +def list_computes_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + conn = _get_connector(gns3_ctx) + computes = conn.http_call("get", f"{conn.base_url}/computes").json() + return {"computes": computes, "count": len(computes)} + + +def get_compute_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + compute_id = params.get("compute_id") or "local" + conn = _get_connector(gns3_ctx) + return conn.http_call("get", f"{conn.base_url}/computes/{compute_id}").json() + + +def get_compute_images_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + emulator = params.get("emulator") + compute_id = params.get("compute_id") or "local" + if not emulator: + return {"error": "emulator is required (e.g. qemu, iou, docker)"} + conn = _get_connector(gns3_ctx) + images = conn.http_call("get", f"{conn.base_url}/computes/{compute_id}/{emulator}/images").json() + return {"images": images, "count": len(images)} + + +COMPUTE_TOOLS = [ + { + "name": "list_computes", + "description": "List all compute nodes available to the server", + "parameters": {"type": "object", "properties": {}}, + "handler": list_computes_handler, + }, + { + "name": "get_compute", + "description": "Get detailed information about a compute node", + "parameters": { + "type": "object", + "properties": { + "compute_id": {"type": "string", "description": "Compute ID (default: local)"}, + }, + }, + "handler": get_compute_handler, + }, + { + "name": "get_compute_images", + "description": "List available images for an emulator on a compute node", + "parameters": { + "type": "object", + "properties": { + "emulator": {"type": "string", "description": "Emulator type (e.g. qemu, iou, docker)"}, + "compute_id": {"type": "string", "description": "Compute ID (default: local)"}, + }, + "required": ["emulator"], + }, + "handler": get_compute_images_handler, + }, +] diff --git a/gns3server/agent/mcp/device_config.py b/gns3server/agent/mcp/device_config.py new file mode 100644 index 000000000..990f4b097 --- /dev/null +++ b/gns3server/agent/mcp/device_config.py @@ -0,0 +1,156 @@ +# +# Copyright (C) 2026 GNS3 Technologies Inc. +# Author: Yue Guobin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +MCP tool handlers for device configuration via Nornir + Netmiko. + +These tools connect to network device consoles via telnet/SSH and execute +configuration or diagnostic commands. Device connection info is automatically +discovered from the project topology using the device's tags for device_type. + +Prerequisites: + - Device must be started (use node_start / node_start_all) + - Device must have a 'device_type:' tag set in GNS3 + (right-click → Configure → Tags → add 'device_type:cisco_ios_telnet') + - Device must have a console port assigned +""" + +import json +import logging +from typing import Any + +from jinja2 import Template as JinjaTemplate, TemplateError as JinjaError + +log = logging.getLogger(__name__) + + +def _render_template(template: str, device_configs: list[dict], commands_field: str = "config_commands") -> list[dict]: + """Render a Jinja2 template for each device's vars into the specified commands field. + + Entries with the same device_name are merged into a single entry + so they share one Nornir session and avoid output fragmentation. + + Each device in device_configs can have: + - "vars": dict of template variables (rendered into commands_field) + - commands_field: existing commands merged after rendering if present + + Args: + commands_field: field name for the rendered commands, e.g. "config_commands", "commands" + """ + try: + jinja = JinjaTemplate(template) + except JinjaError as e: + # a syntactically invalid template must not escape as a raw exception + error_msg = f"Template rendering failed: {e}" + log.error(error_msg) + return [{"status": "failed", "error": error_msg}] + merged: dict[str, dict] = {} + for dev in device_configs: + name = dev.get("device_name") + if not name: + continue + vars_data = dev.get("vars", {}) + if name not in merged: + merged[name] = {"device_name": name, commands_field: list(dev.get(commands_field, []))} + entry = merged[name] + if vars_data: + try: + output = jinja.render(**vars_data) + lines = [l for l in output.splitlines() if l.strip()] + entry[commands_field].extend(lines) + except JinjaError as e: + error_msg = f"Template rendering failed for '{name}': {e}" + log.error(error_msg) + return [{"status": "failed", "error": error_msg}] + return list(merged.values()) + + +# ── Tool handlers ────────────────────────────────────────────────────────── + +def device_config_send_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> list[dict[str, Any]]: + """Send configuration commands to network devices via console.""" + project_id = params.get("project_id") + device_configs = params.get("device_configs") + template = params.get("template") + if not project_id or not device_configs: + return [{"status": "failed", "error": "project_id and device_configs are required"}] + + if template: + device_configs = _render_template(template, device_configs, commands_field="config_commands") + if len(device_configs) == 1 and "error" in device_configs[0]: + return device_configs + + from gns3server.agent.gns3_copilot.tools_v2.config_tools_nornir import ExecuteMultipleDeviceConfigCommands + + tool = ExecuteMultipleDeviceConfigCommands() + input_data = json.dumps({ + "project_id": project_id, + "device_configs": device_configs, + }) + return tool._run( + input_data, + jwt_token=gns3_ctx["jwt_token"], + url=gns3_ctx["server_url"], + ) + + +def device_show_run_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> list[dict[str, Any]]: + """Run read-only diagnostic (show) commands on network devices.""" + project_id = params.get("project_id") + device_configs = params.get("device_configs") + template = params.get("template") + if not project_id or not device_configs: + return [{"status": "failed", "error": "project_id and device_configs (list of {device_name, commands}) are required"}] + + if template: + device_configs = _render_template(template, device_configs, commands_field="commands") + if len(device_configs) == 1 and "error" in device_configs[0]: + return device_configs + + from gns3server.agent.gns3_copilot.tools_v2.display_tools_nornir import ExecuteMultipleDeviceCommands + + tool = ExecuteMultipleDeviceCommands() + input_data = json.dumps({ + "project_id": project_id, + "device_configs": device_configs, + }) + return tool._run( + input_data, + jwt_token=gns3_ctx["jwt_token"], + url=gns3_ctx["server_url"], + ) + + +def vpcs_config_set_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> list[dict[str, Any]]: + """Configure VPCS devices (set IP, gateway, etc.).""" + project_id = params.get("project_id") + device_configs = params.get("device_configs") + if not project_id or not device_configs: + return [{"status": "failed", "error": "project_id and device_configs are required"}] + + from gns3server.agent.gns3_copilot.tools_v2.vpcs_tools_netmiko import VPCSCommands + + tool = VPCSCommands() + input_data = json.dumps({ + "project_id": project_id, + "device_configs": device_configs, + }) + return tool._run( + input_data, + jwt_token=gns3_ctx["jwt_token"], + url=gns3_ctx["server_url"], + ) diff --git a/gns3server/agent/mcp/drawings.py b/gns3server/agent/mcp/drawings.py new file mode 100644 index 000000000..e9135b0f9 --- /dev/null +++ b/gns3server/agent/mcp/drawings.py @@ -0,0 +1,96 @@ +# +# Copyright (C) 2026 GNS3 Technologies Inc. +# Author: Yue Guobin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +MCP tool handlers for GNS3 drawing management. +""" + +from typing import Any + +import logging + +log = logging.getLogger(__name__) + + +# ── Helper ───────────────────────────────────────────────────────────────── + +def _get_connector(gns3_ctx: dict[str, Any]): + from gns3server.agent.gns3_copilot.gns3_client.connector import Gns3Connector + return Gns3Connector( + url=gns3_ctx["server_url"], + jwt_token=gns3_ctx["jwt_token"], + api_version=3, + verify=False, + ) + + +# ── Tool handlers ────────────────────────────────────────────────────────── + +def get_drawings_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + conn = _get_connector(gns3_ctx) + drawings = conn.http_call("get", f"{conn.base_url}/projects/{project_id}/drawings").json() + return {"drawings": drawings, "count": len(drawings)} + + +def create_drawing_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + svg = params.get("svg") + if not project_id or not svg: + return {"error": "project_id and svg are required"} + conn = _get_connector(gns3_ctx) + data = { + "svg": svg, + "x": params.get("x", 0), + "y": params.get("y", 0), + "z": params.get("z", 0), + "locked": params.get("locked", False), + "rotation": params.get("rotation", 0), + } + result = conn.http_call("post", f"{conn.base_url}/projects/{project_id}/drawings", json_data=data).json() + return {"message": "Drawing created", "drawing": result} + + +def get_drawing_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + drawing_id = params.get("drawing_id") + if not project_id or not drawing_id: + return {"error": "project_id and drawing_id are required"} + conn = _get_connector(gns3_ctx) + return conn.http_call("get", f"{conn.base_url}/projects/{project_id}/drawings/{drawing_id}").json() + + +def update_drawing_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + drawing_id = params.get("drawing_id") + if not project_id or not drawing_id: + return {"error": "project_id and drawing_id are required"} + conn = _get_connector(gns3_ctx) + data = {k: v for k, v in params.items() if k not in ("project_id", "drawing_id") and v is not None} + return conn.http_call("put", f"{conn.base_url}/projects/{project_id}/drawings/{drawing_id}", json_data=data).json() + + +def delete_drawing_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + drawing_id = params.get("drawing_id") + if not project_id or not drawing_id: + return {"error": "project_id and drawing_id are required"} + conn = _get_connector(gns3_ctx) + conn.http_call("delete", f"{conn.base_url}/projects/{project_id}/drawings/{drawing_id}") + return {"message": f"Drawing {drawing_id} deleted", "drawing_id": drawing_id} diff --git a/gns3server/agent/mcp/images.py b/gns3server/agent/mcp/images.py new file mode 100644 index 000000000..3fcbfd17d --- /dev/null +++ b/gns3server/agent/mcp/images.py @@ -0,0 +1,80 @@ +# +# Copyright (C) 2026 GNS3 Technologies Inc. +# Author: Yue Guobin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +MCP tool handlers for GNS3 image management. +""" + +from typing import Any + +import logging + +log = logging.getLogger(__name__) + + +# ── Helper ───────────────────────────────────────────────────────────────── + +def _get_connector(gns3_ctx: dict[str, Any]): + from gns3server.agent.gns3_copilot.gns3_client.connector import Gns3Connector + return Gns3Connector( + url=gns3_ctx["server_url"], + jwt_token=gns3_ctx["jwt_token"], + api_version=3, + verify=False, + ) + + +# ── Tool handlers ────────────────────────────────────────────────────────── + +def get_images_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + conn = _get_connector(gns3_ctx) + images = conn.http_call("get", f"{conn.base_url}/images").json() + return {"images": images, "count": len(images)} + + +def get_image_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + image_id = params.get("image_id") + if not image_id: + return {"error": "image_id is required"} + conn = _get_connector(gns3_ctx) + return conn.http_call("get", f"{conn.base_url}/images/{image_id}").json() + + +def delete_image_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + image_id = params.get("image_id") + if not image_id: + return {"error": "image_id is required"} + conn = _get_connector(gns3_ctx) + conn.http_call("delete", f"{conn.base_url}/images/{image_id}") + return {"message": f"Image {image_id} deleted", "image_id": image_id} + + +def prune_images_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + conn = _get_connector(gns3_ctx) + # Returns 204 No Content on success (empty body, no .json()) + conn.http_call("delete", f"{conn.base_url}/images/prune") + return {"message": "Unused images pruned"} + + +def install_images_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + conn = _get_connector(gns3_ctx) + response = conn.http_call("post", f"{conn.base_url}/images/install") + if response.content: + # the install endpoint reports which templates were created or skipped + return response.json() + # tolerate an empty body in case an older server still replies with 204 + return {"message": "Image installation completed"} diff --git a/gns3server/agent/mcp/projects.py b/gns3server/agent/mcp/projects.py new file mode 100644 index 000000000..e095354a1 --- /dev/null +++ b/gns3server/agent/mcp/projects.py @@ -0,0 +1,334 @@ +# +# Copyright (C) 2026 GNS3 Technologies Inc. +# Author: Yue Guobin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +MCP tools for GNS3 project management. + +Tool handlers receive (params, gns3_ctx) and call GNS3's REST API +via Gns3Connector (from gns3_copilot.gns3_client.connector). +""" + +from typing import Any + +import logging + +log = logging.getLogger(__name__) + + +# ── Helper ───────────────────────────────────────────────────────────────── + +def _get_connector(gns3_ctx: dict[str, Any]): + """Create a Gns3Connector from the GNS3 context dict.""" + from gns3server.agent.gns3_copilot.gns3_client.connector import Gns3Connector + return Gns3Connector( + url=gns3_ctx["server_url"], + jwt_token=gns3_ctx["jwt_token"], + api_version=3, + verify=False, + ) + + +# ── Tool handlers ────────────────────────────────────────────────────────── + +def list_projects_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + conn = _get_connector(gns3_ctx) + projects = conn.http_call("get", f"{conn.base_url}/projects").json() + return {"projects": projects, "count": len(projects)} + + +def get_project_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + conn = _get_connector(gns3_ctx) + project = conn.http_call("get", f"{conn.base_url}/projects/{project_id}").json() + if project is None: + return {"error": f"Project '{project_id}' not found"} + return project + + +def create_project_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + name = params.get("name") + if not name: + return {"error": "name is required"} + conn = _get_connector(gns3_ctx) + json_data: dict[str, Any] = {"name": name} + if params.get("auto_close") is not None: + json_data["auto_close"] = params["auto_close"] + return conn.http_call("post", f"{conn.base_url}/projects", json_data=json_data).json() + + +def delete_project_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + conn = _get_connector(gns3_ctx) + conn.http_call("delete", f"{conn.base_url}/projects/{project_id}") + return {"message": f"Project '{project_id}' deleted", "project_id": project_id} + + +def open_project_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + conn = _get_connector(gns3_ctx) + url = f"{conn.base_url}/projects/{project_id}/open" + return conn.http_call("post", url).json() + + +def close_project_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + conn = _get_connector(gns3_ctx) + url = f"{conn.base_url}/projects/{project_id}/close" + conn.http_call("post", url) + return {"message": f"Project '{project_id}' closed", "project_id": project_id} + + +def get_project_stats_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + conn = _get_connector(gns3_ctx) + url = f"{conn.base_url}/projects/{project_id}/stats" + return conn.http_call("get", url).json() + + +def update_project_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + conn = _get_connector(gns3_ctx) + kwargs = {k: v for k, v in params.items() if k != "project_id" and v is not None} + return conn.http_call("put", f"{conn.base_url}/projects/{project_id}", json_data=kwargs).json() + + +def duplicate_project_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + name = params.get("name") + if not name: + return {"error": "name is required"} + conn = _get_connector(gns3_ctx) + kwargs = {k: v for k, v in params.items() if k not in ("project_id",) and v is not None} + return conn.http_call("post", f"{conn.base_url}/projects/{project_id}/duplicate", json_data=kwargs).json() + + +def get_project_readme_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + conn = _get_connector(gns3_ctx) + try: + url = f"{conn.base_url}/projects/{project_id}/files/README.txt" + content = conn.http_call("get", url).text + return {"project_id": project_id, "file": "README.txt", "content": content} + except Exception as e: + if "404" in str(e): + return {"project_id": project_id, "file": "README.txt", "content": None, "message": "README.txt does not exist yet"} + raise + + +def update_project_readme_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + content = params.get("content") + if content is None: + return {"error": "content is required"} + conn = _get_connector(gns3_ctx) + url = f"{conn.base_url}/projects/{project_id}/files/README.txt" + conn.http_call("post", url, data=content, headers={"Content-Type": "text/plain"}) + return {"message": "README.txt updated", "project_id": project_id} + + +def lock_project_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + conn = _get_connector(gns3_ctx) + conn.http_call("post", f"{conn.base_url}/projects/{project_id}/lock") + return {"message": f"Project {project_id} locked", "project_id": project_id} + + +def unlock_project_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + conn = _get_connector(gns3_ctx) + conn.http_call("post", f"{conn.base_url}/projects/{project_id}/unlock") + return {"message": f"Project {project_id} unlocked", "project_id": project_id} + + +def get_locked_project_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + conn = _get_connector(gns3_ctx) + locked = conn.http_call("get", f"{conn.base_url}/projects/{project_id}/locked").json() + return {"project_id": project_id, "locked": locked} + + +# ── Tool definitions (consumed by mcp/__init__.py) ───────────────────────── + +PROJECT_TOOLS = [ + { + "name": "list_projects", + "description": "List all GNS3 projects accessible to the current user", + "parameters": {"type": "object", "properties": {}}, + "handler": list_projects_handler, + }, + { + "name": "get_project", + "description": "Get detailed information about a specific project", + "parameters": { + "type": "object", + "properties": { + "project_id": {"type": "string", "description": "Project UUID"}, + }, + "required": ["project_id"], + }, + "handler": get_project_handler, + }, + { + "name": "create_project", + "description": "Create a new GNS3 project", + "parameters": { + "type": "object", + "properties": { + "name": {"type": "string", "description": "Project name"}, + "description": {"type": "string", "description": "Optional project description"}, + }, + "required": ["name"], + }, + "handler": create_project_handler, + }, + { + "name": "delete_project", + "description": "Delete a GNS3 project permanently", + "parameters": { + "type": "object", + "properties": { + "project_id": {"type": "string", "description": "UUID of the project to delete"}, + }, + "required": ["project_id"], + }, + "handler": delete_project_handler, + }, + { + "name": "open_project", + "description": "Open a closed GNS3 project", + "parameters": { + "type": "object", + "properties": { + "project_id": {"type": "string", "description": "Project UUID"}, + }, + "required": ["project_id"], + }, + "handler": open_project_handler, + }, + { + "name": "close_project", + "description": "Close an open GNS3 project", + "parameters": { + "type": "object", + "properties": { + "project_id": {"type": "string", "description": "Project UUID"}, + }, + "required": ["project_id"], + }, + "handler": close_project_handler, + }, + { + "name": "get_project_stats", + "description": "Get statistics (nodes, links, snapshots, drawings) for a project", + "parameters": { + "type": "object", + "properties": { + "project_id": {"type": "string", "description": "Project UUID"}, + }, + "required": ["project_id"], + }, + "handler": get_project_stats_handler, + }, + { + "name": "update_project", + "description": "Update a project's properties (name, auto_close, auto_open, etc.)", + "parameters": { + "type": "object", + "properties": { + "project_id": {"type": "string", "description": "Project UUID"}, + "name": {"type": "string", "description": "New project name"}, + "auto_close": {"type": "boolean", "description": "Close project when last client leaves"}, + "auto_open": {"type": "boolean", "description": "Project opens when GNS3 starts"}, + "auto_start": {"type": "boolean", "description": "Project starts when opened"}, + "scene_width": {"type": "integer", "description": "Width of the drawing area"}, + "scene_height": {"type": "integer", "description": "Height of the drawing area"}, + "zoom": {"type": "integer", "description": "Zoom of the drawing area"}, + "show_layers": {"type": "boolean", "description": "Show layers on the drawing area"}, + "snap_to_grid": {"type": "boolean", "description": "Snap to grid on the drawing area"}, + "show_grid": {"type": "boolean", "description": "Show the grid on the drawing area"}, + "grid_size": {"type": "integer", "description": "Grid size for the drawing area for nodes"}, + "drawing_grid_size": {"type": "integer", "description": "Grid size for the drawing area for drawings"}, + "show_interface_labels": {"type": "boolean", "description": "Show interface labels on the drawing area"}, + }, + "required": ["project_id"], + }, + "handler": update_project_handler, + }, + { + "name": "duplicate_project", + "description": "Duplicate a project", + "parameters": { + "type": "object", + "properties": { + "project_id": {"type": "string", "description": "UUID of the project to duplicate"}, + "name": {"type": "string", "description": "New project name"}, + "reset_mac_addresses": {"type": "boolean", "description": "Reset MAC addresses for this project"}, + }, + "required": ["project_id", "name"], + }, + "handler": duplicate_project_handler, + }, + { + "name": "get_project_readme", + "description": "Get the content of a project's README.md file (project documentation, Markdown format)", + "parameters": { + "type": "object", + "properties": { + "project_id": {"type": "string", "description": "Project UUID"}, + }, + "required": ["project_id"], + }, + "handler": get_project_readme_handler, + }, + { + "name": "update_project_readme", + "description": "Update or create a project's README.md file (project documentation, Markdown format)", + "parameters": { + "type": "object", + "properties": { + "project_id": {"type": "string", "description": "Project UUID"}, + "content": {"type": "string", "description": "Content to write to README.md (Markdown format)"}, + }, + "required": ["project_id", "content"], + }, + "handler": update_project_readme_handler, + }, +] diff --git a/gns3server/agent/mcp/server.py b/gns3server/agent/mcp/server.py new file mode 100644 index 000000000..0f8ac4abb --- /dev/null +++ b/gns3server/agent/mcp/server.py @@ -0,0 +1,50 @@ +# +# Copyright (C) 2026 GNS3 Technologies Inc. +# Author: Yue Guobin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +MCP tool handlers for GNS3 server information. +""" + +from typing import Any + +import logging + +log = logging.getLogger(__name__) + + +# ── Helper ───────────────────────────────────────────────────────────────── + +def _get_connector(gns3_ctx: dict[str, Any]): + from gns3server.agent.gns3_copilot.gns3_client.connector import Gns3Connector + return Gns3Connector( + url=gns3_ctx["server_url"], + jwt_token=gns3_ctx["jwt_token"], + api_version=3, + verify=False, + ) + + +# ── Tool handlers ────────────────────────────────────────────────────────── + +def get_version_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + conn = _get_connector(gns3_ctx) + return conn.http_call("get", f"{conn.base_url}/version").json() + + +def get_statistics_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + conn = _get_connector(gns3_ctx) + return conn.http_call("get", f"{conn.base_url}/statistics").json() diff --git a/gns3server/agent/mcp/snapshots.py b/gns3server/agent/mcp/snapshots.py new file mode 100644 index 000000000..f428292b9 --- /dev/null +++ b/gns3server/agent/mcp/snapshots.py @@ -0,0 +1,79 @@ +# +# Copyright (C) 2026 GNS3 Technologies Inc. +# Author: Yue Guobin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +MCP tool handlers for GNS3 snapshot management. +""" + +from typing import Any + +import logging + +log = logging.getLogger(__name__) + + +# ── Helper ───────────────────────────────────────────────────────────────── + +def _get_connector(gns3_ctx: dict[str, Any]): + from gns3server.agent.gns3_copilot.gns3_client.connector import Gns3Connector + return Gns3Connector( + url=gns3_ctx["server_url"], + jwt_token=gns3_ctx["jwt_token"], + api_version=3, + verify=False, + ) + + +# ── Tool handlers ────────────────────────────────────────────────────────── + +def get_snapshots_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + if not project_id: + return {"error": "project_id is required"} + conn = _get_connector(gns3_ctx) + snapshots = conn.http_call("get", f"{conn.base_url}/projects/{project_id}/snapshots").json() + return {"snapshots": snapshots, "count": len(snapshots)} + + +def create_snapshot_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + name = params.get("name") + if not project_id or not name: + return {"error": "project_id and name are required"} + conn = _get_connector(gns3_ctx) + result = conn.http_call("post", f"{conn.base_url}/projects/{project_id}/snapshots", json_data={"name": name}).json() + return {"message": f"Snapshot '{name}' created", "snapshot": result} + + +def delete_snapshot_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + snapshot_id = params.get("snapshot_id") + if not project_id or not snapshot_id: + return {"error": "project_id and snapshot_id are required"} + conn = _get_connector(gns3_ctx) + conn.http_call("delete", f"{conn.base_url}/projects/{project_id}/snapshots/{snapshot_id}") + return {"message": f"Snapshot {snapshot_id} deleted", "snapshot_id": snapshot_id} + + +def restore_snapshot_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + project_id = params.get("project_id") + snapshot_id = params.get("snapshot_id") + if not project_id or not snapshot_id: + return {"error": "project_id and snapshot_id are required"} + conn = _get_connector(gns3_ctx) + result = conn.http_call("post", f"{conn.base_url}/projects/{project_id}/snapshots/{snapshot_id}/restore").json() + return {"message": f"Snapshot {snapshot_id} restored", "project": result} diff --git a/gns3server/agent/mcp/symbols.py b/gns3server/agent/mcp/symbols.py new file mode 100644 index 000000000..96f2841d6 --- /dev/null +++ b/gns3server/agent/mcp/symbols.py @@ -0,0 +1,108 @@ +# +# Copyright (C) 2026 GNS3 Technologies Inc. +# Author: Yue Guobin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +MCP tool handlers for GNS3 symbol management. +""" + +from typing import Any + +import logging + +from gns3server.services import access_ticket_service + +log = logging.getLogger(__name__) + + +# ── Helper ───────────────────────────────────────────────────────────────── + +def _get_connector(gns3_ctx: dict[str, Any]): + from gns3server.agent.gns3_copilot.gns3_client.connector import Gns3Connector + return Gns3Connector( + url=gns3_ctx["server_url"], + jwt_token=gns3_ctx["jwt_token"], + api_version=3, + verify=False, + ) + + +# ── Tool handlers ────────────────────────────────────────────────────────── + +def get_symbols_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + conn = _get_connector(gns3_ctx) + symbols = conn.http_call("get", f"{conn.base_url}/symbols").json() + return {"symbols": symbols, "count": len(symbols)} + + +def get_symbol_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + symbol_id = params.get("symbol_id") + if not symbol_id: + return {"error": "symbol_id is required"} + path = f"/v3/symbols/{symbol_id}/raw" + download_url = f"{gns3_ctx['server_url']}{path}" + username = gns3_ctx.get("jwt_username") + # short-lived ticket bound to this exact path — LLM clients retyping curl + # commands corrupted the long Bearer JWT this used to embed + ticket = access_ticket_service.mint( + username, token_version=gns3_ctx.get("jwt_token_version", 0), path=path + ) if username else None + if ticket: + download_url += f"?token={ticket}" + result = { + "symbol_id": symbol_id, + "download_url": download_url, + "note": "Symbol files are SVG images.", + } + if ticket: + safe_name = symbol_id.replace(':', '').replace('/', '_') + result["curl_command"] = f"curl -L -o '{safe_name}.svg' '{download_url}'" + result["note"] += " The download URL includes a 10-minute ticket." + return result + + +def get_symbol_dimensions_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + symbol_id = params.get("symbol_id") + if not symbol_id: + return {"error": "symbol_id is required"} + conn = _get_connector(gns3_ctx) + return conn.http_call("get", f"{conn.base_url}/symbols/{symbol_id}/dimensions").json() + + +def get_default_symbols_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + conn = _get_connector(gns3_ctx) + symbols = conn.http_call("get", f"{conn.base_url}/symbols/default_symbols").json() + return {"default_symbols": symbols} + + +def upload_symbol_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + symbol_id = params.get("symbol_id") + content = params.get("content") + if not symbol_id or content is None: + return {"error": "symbol_id and content (SVG data) are required"} + conn = _get_connector(gns3_ctx) + url = f"{conn.base_url}/symbols/{symbol_id}/raw" + conn.http_call("post", url, data=content, headers={"Content-Type": "image/svg+xml"}) + return {"message": f"Symbol {symbol_id} uploaded", "symbol_id": symbol_id} + + +def delete_symbol_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + symbol_id = params.get("symbol_id") + if not symbol_id: + return {"error": "symbol_id is required"} + conn = _get_connector(gns3_ctx) + conn.http_call("delete", f"{conn.base_url}/symbols/{symbol_id}") + return {"message": f"Symbol {symbol_id} deleted", "symbol_id": symbol_id} diff --git a/gns3server/agent/mcp/templates.py b/gns3server/agent/mcp/templates.py new file mode 100644 index 000000000..7c0f678bb --- /dev/null +++ b/gns3server/agent/mcp/templates.py @@ -0,0 +1,229 @@ +# +# Copyright (C) 2026 GNS3 Technologies Inc. +# Author: Yue Guobin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +MCP tool handlers for GNS3 template management. + +Handlers receive (params, gns3_ctx) and call GNS3's REST API +via Gns3Connector (from gns3_copilot.gns3_client.connector). +""" + +from typing import Any + +import logging + +log = logging.getLogger(__name__) + + +# ── Helper ───────────────────────────────────────────────────────────────── + +def _get_connector(gns3_ctx: dict[str, Any]): + from gns3server.agent.gns3_copilot.gns3_client.connector import Gns3Connector + return Gns3Connector( + url=gns3_ctx["server_url"], + jwt_token=gns3_ctx["jwt_token"], + api_version=3, + verify=False, + ) + + +# ── Tool handlers ────────────────────────────────────────────────────────── + +VALID_TEMPLATE_FIELDS = { + "template_id", "name", "version", "category", "default_name_format", + "symbol", "template_type", "compute_id", "usage", "tags", "builtin", + "created_at", "updated_at", +} + +TEMPLATE_DEFAULT_FIELDS = ["template_id", "name", "template_type", "category", "default_name_format"] + + +def _filter_templates(templates, fields): + """Filter each template to only include requested fields.""" + if not fields: + fields = TEMPLATE_DEFAULT_FIELDS + if isinstance(templates, dict): + return {k: templates[k] for k in fields if k in templates} + return [{k: t[k] for k in fields if k in t} for t in templates] + + +def list_templates_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + conn = _get_connector(gns3_ctx) + templates = conn.http_call("get", f"{conn.base_url}/templates").json() + fields = params.get("fields") + if fields: + if not isinstance(fields, list): + return {"error": "fields must be a list, e.g. [\"template_id\", \"name\"]"} + invalid = [f for f in fields if f not in VALID_TEMPLATE_FIELDS] + if invalid: + return { + "error": f"Unknown fields: {invalid}", + "available_fields": sorted(VALID_TEMPLATE_FIELDS), + } + templates = _filter_templates(templates, fields) + return {"templates": templates, "count": len(templates)} + + +def get_template_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + template_id = params.get("template_id") + name = params.get("name") + + if not template_id and not name: + return {"error": "template_id or name is required"} + + conn = _get_connector(gns3_ctx) + + if template_id: + template = conn.http_call("get", f"{conn.base_url}/templates/{template_id}").json() + else: + # Find template by name + all_templates = conn.http_call("get", f"{conn.base_url}/templates").json() + matches = [t for t in all_templates if t.get("name") == name] + if not matches: + return {"error": f"Template '{name}' not found"} + template = matches[0] + + return template + + +def create_template_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + name = params.get("name") + template_type = params.get("template_type") + if not name or not template_type: + return {"error": "name and template_type are required"} + + conn = _get_connector(gns3_ctx) + data = { + "name": name, + "template_type": template_type, + "compute_id": params.get("compute_id", "local"), + } + # Pass through optional template-type-specific fields (image, qemu_path, etc.) + for key in ("image",): + if key in params: + data[key] = params[key] + return conn.http_call("post", f"{conn.base_url}/templates", json_data=data).json() + + +def update_template_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + template_id = params.get("template_id") + name = params.get("name") + + if not template_id and not name: + return {"error": "template_id or name is required"} + + conn = _get_connector(gns3_ctx) + + # Resolve name to ID if needed + if not template_id and name: + all_templates = conn.http_call("get", f"{conn.base_url}/templates").json() + matches = [t for t in all_templates if t.get("name") == name] + if not matches: + return {"error": f"Template '{name}' not found"} + template_id = matches[0]["template_id"] + + update_data = {k: v for k, v in params.items() if k not in ("template_id", "name", "kwargs")} + # Support nested kwargs from MCP clients + if "kwargs" in params and isinstance(params["kwargs"], dict): + update_data = params["kwargs"] + + return conn.http_call("put", f"{conn.base_url}/templates/{template_id}", json_data=update_data).json() + + +def delete_template_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: + template_id = params.get("template_id") + name = params.get("name") + + if not template_id and not name: + return {"error": "template_id or name is required"} + + conn = _get_connector(gns3_ctx) + + if not template_id and name: + all_templates = conn.http_call("get", f"{conn.base_url}/templates").json() + matches = [t for t in all_templates if t.get("name") == name] + if not matches: + return {"error": f"Template '{name}' not found"} + template_id = matches[0]["template_id"] + + conn.http_call("delete", f"{conn.base_url}/templates/{template_id}") + return {"message": f"Template deleted"} + + +# ── Tool definitions ─────────────────────────────────────────────────────── + +TEMPLATE_TOOLS = [ + { + "name": "list_templates", + "description": "List all available templates on the server", + "parameters": { + "type": "object", + "properties": {}, + }, + "handler": list_templates_handler, + }, + { + "name": "get_template", + "description": "Get detailed information about a specific template", + "parameters": { + "type": "object", + "properties": { + "template_id": {"type": "string", "description": "Template UUID"}, + "name": {"type": "string", "description": "Template name"}, + }, + }, + "handler": get_template_handler, + }, + { + "name": "create_template", + "description": "Create a new template", + "parameters": { + "type": "object", + "properties": { + "name": {"type": "string", "description": "Template name"}, + "template_type": {"type": "string", "description": "Template type (e.g. qemu, docker, dynamips)"}, + "compute_id": {"type": "string", "description": "Compute ID (optional, default: local)"}, + }, + "required": ["name", "template_type"], + }, + "handler": create_template_handler, + }, + { + "name": "update_template", + "description": "Update an existing template's properties", + "parameters": { + "type": "object", + "properties": { + "template_id": {"type": "string", "description": "Template UUID"}, + "name": {"type": "string", "description": "Template name"}, + }, + }, + "handler": update_template_handler, + }, + { + "name": "delete_template", + "description": "Delete a template", + "parameters": { + "type": "object", + "properties": { + "template_id": {"type": "string", "description": "Template UUID"}, + "name": {"type": "string", "description": "Template name"}, + }, + }, + "handler": delete_template_handler, + }, +] diff --git a/gns3server/agent/web_wireshark/docker/Dockerfile b/gns3server/agent/web_wireshark/docker/Dockerfile index 3f3f39a2e..88efc2419 100644 --- a/gns3server/agent/web_wireshark/docker/Dockerfile +++ b/gns3server/agent/web_wireshark/docker/Dockerfile @@ -15,6 +15,7 @@ RUN sed -i 's|http://deb.debian.org/debian|http://mirrors.aliyun.com/debian|g' / && sed -i 's|http://security.debian.org/debian-security|http://mirrors.aliyun.com/debian-security|g' /etc/apt/sources.list.d/debian.sources # Add xpra official repository +COPY pin-xpra /etc/apt/preferences.d/ RUN apt-get update && apt-get install -y \ ca-certificates \ wget \ @@ -24,11 +25,11 @@ RUN apt-get update && apt-get install -y \ && apt-get update # Install xpra and dependencies -# Lock xpra version for reproducible builds +# Locking of xpra version is done in /etc/apt/preferences.d/pin-xpra RUN apt-get install -y \ wireshark-common \ wireshark \ - xpra=6.4.3* \ + xpra \ xpra-x11 \ xvfb \ curl \ diff --git a/gns3server/agent/web_wireshark/docker/pin-xpra b/gns3server/agent/web_wireshark/docker/pin-xpra new file mode 100644 index 000000000..f2b561657 --- /dev/null +++ b/gns3server/agent/web_wireshark/docker/pin-xpra @@ -0,0 +1,14 @@ +Explanation: Lock Xpra packages to v6.4 +Package: xpra* +Pin: version 6.4* +Pin-Priority: 1000 + +Explanation: xpra-html5 uses different version scheme, lock it to v19 +Package: xpra-html5 +Pin: version 19* +Pin-Priority: 1000 + +Explanation: Block the installation of other xpra versions +Package: xpra* +Pin: version * +Pin-Priority: -1 diff --git a/gns3server/api/routes/compute/cloud_nodes.py b/gns3server/api/routes/compute/cloud_nodes.py index 8453ba0d8..a5f2bb6a9 100644 --- a/gns3server/api/routes/compute/cloud_nodes.py +++ b/gns3server/api/routes/compute/cloud_nodes.py @@ -184,6 +184,8 @@ async def update_cloud_nio( nio.filters.clear() if nio_data.filters: nio.filters = nio_data.filters + # NIO type is a Union (Ethernet/TAP/UDP); only UDPNIO carries markers. + nio.markers = getattr(nio_data, "markers", None) or {} await node.update_nio(port_number, nio) return nio.asdict() @@ -253,3 +255,83 @@ async def stream_pcap_file( nio = node.get_nio(port_number) stream = Builtin.instance().stream_pcap_file(nio, node.project.id) return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap") + + +@router.put( + "/{node_id}/markers/{marker_name}" +) +async def toggle_cloud_marker( + marker_name: str, + toggle_data: schemas.MarkerToggle, + node: Cloud = Depends(dep_node) +) -> dict: + """ + Toggle a marker filter on/off without an NIO rebuild (ubridge contract §3.2). + """ + + if not any(n == marker_name for (n, lid) in node._marker_filter_bridges): + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Marker '{marker_name}' is not installed on this node", + ) + await node._ubridge_set_marker_filter_state(marker_name, toggle_data.enabled) + return {"marker_name": marker_name, "enabled": toggle_data.enabled} + + +@router.post( + "/{node_id}/markers/pause", + status_code=status.HTTP_204_NO_CONTENT +) +async def pause_cloud_markers(node: Cloud = Depends(dep_node)) -> None: + + await node._ubridge_marker_pause() + + +@router.post( + "/{node_id}/markers/resume", + status_code=status.HTTP_204_NO_CONTENT +) +async def resume_cloud_markers(node: Cloud = Depends(dep_node)) -> None: + + await node._ubridge_marker_resume() + + +@router.delete( + "/{node_id}/adapters/{adapter_number}/ports/{port_number}/markers/{marker_name}", + status_code=status.HTTP_204_NO_CONTENT +) +async def delete_cloud_marker_capture( + *, + marker_name: str, + adapter_number: int = Path(..., ge=0, le=0), + port_number: int, + link_id: str = "", + node: Cloud = Depends(dep_node) +) -> None: + """ + Delete a marker's capture pcap (called by the controller when the marker is + removed) so the file is cleaned up even with the node stopped. Also drops + the marker from the port NIO's cached spec so a node restart won't reinstall + it (and recreate an empty pcap). + """ + + nio = node.get_nio(port_number) + await node.delete_marker_capture(marker_name, link_id, nio) + + +@router.put("/{node_id}/markers/{marker_name}/rebuild") +async def rebuild_cloud_marker( + marker_name: str, + rebuild_data: schemas.MarkerRebuild, + node: Cloud = Depends(dep_node) +) -> dict: + """ + Re-install a single marker filter with new BPF/tag/direction (delete + add, + no bridge reset) so sibling markers' pcaps stay open. + """ + + await node.rebuild_marker_filter( + marker_name, rebuild_data.link_id, rebuild_data.bpf, + rebuild_data.tag, rebuild_data.direction, rebuild_data.enabled, + ) + return {"marker_name": marker_name} diff --git a/gns3server/api/routes/compute/compute.py b/gns3server/api/routes/compute/compute.py index ba6156fc0..1a862787c 100644 --- a/gns3server/api/routes/compute/compute.py +++ b/gns3server/api/routes/compute/compute.py @@ -21,7 +21,9 @@ API routes for compute. import os import psutil +import cpuinfo +from functools import lru_cache from gns3server.config import Config from gns3server.utils.cpu_percent import CpuPercent from gns3server.version import __version__ @@ -42,6 +44,11 @@ from typing import Optional, List router = APIRouter() +@lru_cache(maxsize=1) +def get_cpu_model() -> str: + return cpuinfo.get_cpu_info().get("brand_raw", "") + + @router.post("/projects/{project_id}/ports/udp", status_code=status.HTTP_201_CREATED) def allocate_udp_port(project_id: UUID) -> dict: """ @@ -55,6 +62,27 @@ def allocate_udp_port(project_id: UUID) -> dict: return {"udp_port": udp_port} +@router.post("/projects/{project_id}/ports/udp/batch", status_code=status.HTTP_201_CREATED) +def batch_allocate_udp_ports(project_id: UUID, body: dict) -> dict: + """ + Allocate multiple UDP ports on the compute in a single call. + + Used during project loading to pre-allocate all required UDP ports + before creating links, reducing HTTP round-trips. + """ + + count = body.get("count", 1) + try: + count = max(1, min(int(count), 10000)) + except (ValueError, TypeError): + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="count must be a positive integer") + pm = ProjectManager.instance() + project = pm.get_project(str(project_id)) + m = PortManager.instance() + udp_ports = [m.get_free_udp_port(project) for _ in range(count)] + return {"udp_ports": udp_ports} + + @router.get("/network/interfaces") def network_interfaces() -> List[dict]: """ @@ -98,10 +126,15 @@ def compute_statistics() -> dict: swap_free = psutil.swap_memory().free swap_used = psutil.swap_memory().used cpu_percent = int(CpuPercent.get()) - load_average_percent = [int(x / psutil.cpu_count() * 100) for x in psutil.getloadavg()] + cpu_count = psutil.cpu_count(logical=True) or 1 + cpu_count_physical = psutil.cpu_count(logical=False) + raw_load_average = psutil.getloadavg() + load_average = [round(x, 2) for x in raw_load_average] + load_average_percent = [round(x / cpu_count * 100, 2) for x in raw_load_average] memory_percent = int(psutil.virtual_memory().percent) swap_percent = int(psutil.swap_memory().percent) - disk_usage_percent = int(psutil.disk_usage(get_default_project_directory()).percent) + disk_usage = psutil.disk_usage(get_default_project_directory()) + disk_usage_percent = int(disk_usage.percent) except psutil.Error as e: raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) # raise HTTPConflict(text="Psutil error detected: {}".format(e)) @@ -114,9 +147,16 @@ def compute_statistics() -> dict: "swap_free": swap_free, "swap_used": swap_used, "cpu_usage_percent": cpu_percent, + "cpu_count": cpu_count, + "cpu_count_physical": cpu_count_physical, + "cpu_model": get_cpu_model(), "memory_usage_percent": memory_percent, "swap_usage_percent": swap_percent, "disk_usage_percent": disk_usage_percent, + "disk_total": disk_usage.total, + "disk_used": disk_usage.used, + "disk_free": disk_usage.free, + "load_average": load_average, "load_average_percent": load_average_percent, } diff --git a/gns3server/api/routes/compute/docker_nodes.py b/gns3server/api/routes/compute/docker_nodes.py index 1d9e98530..be55ec0f4 100644 --- a/gns3server/api/routes/compute/docker_nodes.py +++ b/gns3server/api/routes/compute/docker_nodes.py @@ -20,7 +20,7 @@ API routes for Docker nodes. import os -from fastapi import APIRouter, WebSocket, Depends, Body, status +from fastapi import APIRouter, WebSocket, Depends, Body, status, HTTPException from fastapi.encoders import jsonable_encoder from fastapi.responses import StreamingResponse from uuid import UUID @@ -29,7 +29,6 @@ from typing import Union from gns3server import schemas from gns3server.compute.docker import Docker from gns3server.compute.docker.docker_vm import DockerVM - from .dependencies.authentication import compute_authentication, ws_compute_authentication responses = {404: {"model": schemas.ErrorMessage, "description": "Could not find project or Docker node"}} @@ -79,9 +78,20 @@ async def create_docker_node(project_id: UUID, node_data: schemas.DockerCreate) aux_type=node_data.pop("aux_type", "none"), extra_hosts=node_data.get("extra_hosts"), extra_volumes=node_data.get("extra_volumes"), + extra_configs=node_data.get("extra_configs"), memory=node_data.get("memory", 0), cpus=node_data.get("cpus", 0), ) + # Pop keys already consumed by create_node above so the setattr + # fallback loop below only applies truly extra keys and does not + # re-trigger console/aux port setter logging. + for key in ( + "console", "console_type", "console_resolution", "console_http_port", + "console_http_path", "aux", "aux_type", "start_command", "environment", + "adapters", "mac_address", "extra_hosts", "extra_volumes", "extra_configs", + "memory", "cpus", + ): + node_data.pop(key, None) for name, value in node_data.items(): if name != "node_id": if hasattr(container, name) and getattr(container, name) != value: @@ -129,6 +139,7 @@ async def update_docker_node(node_data: schemas.DockerUpdate, node: DockerVM = D "custom_adapters", "extra_hosts", "extra_volumes", + "extra_configs", "memory", "cpus", ] @@ -166,10 +177,12 @@ async def start_docker_node(node: DockerVM = Depends(dep_node)) -> None: ) async def stop_docker_node(node: DockerVM = Depends(dep_node)) -> None: """ - Stop a Docker node. + Stop a Docker node. This is the explicit user stop — the only path that + asks for a graceful SIGTERM shutdown (vendor NOS override); internal + paths (delete/update/close) keep the immediate kill. """ - await node.stop() + await node.stop(graceful=True) @router.post( @@ -235,6 +248,7 @@ async def delete_docker_node(node: DockerVM = Depends(dep_node)) -> None: """ await node.delete() + await node.project.remove_node(node) @router.post( @@ -292,6 +306,7 @@ async def update_docker_node_nio( nio.filters.clear() if nio_data.filters: nio.filters = nio_data.filters + nio.markers = nio_data.markers or {} await node.adapter_update_nio_binding(adapter_number, nio) return nio.asdict() @@ -407,3 +422,89 @@ async def vnc_console_ws( async def reset_console(node: DockerVM = Depends(dep_node)) -> None: await node.reset_console() + + +@router.put( + "/{node_id}/markers/{marker_name}", + dependencies=[Depends(compute_authentication)] +) +async def toggle_docker_marker( + marker_name: str, + toggle_data: schemas.MarkerToggle, + node: DockerVM = Depends(dep_node) +) -> dict: + """ + Toggle a marker filter on/off without an NIO rebuild (ubridge contract §3.2). + """ + + if not any(n == marker_name for (n, lid) in node._marker_filter_bridges): + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Marker '{marker_name}' is not installed on this node", + ) + await node._ubridge_set_marker_filter_state(marker_name, toggle_data.enabled) + return {"marker_name": marker_name, "enabled": toggle_data.enabled} + + +@router.post( + "/{node_id}/markers/pause", + status_code=status.HTTP_204_NO_CONTENT, + dependencies=[Depends(compute_authentication)] +) +async def pause_docker_markers(node: DockerVM = Depends(dep_node)) -> None: + + await node._ubridge_marker_pause() + + +@router.post( + "/{node_id}/markers/resume", + status_code=status.HTTP_204_NO_CONTENT, + dependencies=[Depends(compute_authentication)] +) +async def resume_docker_markers(node: DockerVM = Depends(dep_node)) -> None: + + await node._ubridge_marker_resume() + + +@router.delete( + "/{node_id}/adapters/{adapter_number}/ports/{port_number}/markers/{marker_name}", + status_code=status.HTTP_204_NO_CONTENT, + dependencies=[Depends(compute_authentication)] +) +async def delete_docker_marker_capture( + marker_name: str, + adapter_number: int, + port_number: int, + link_id: str = "", + node: DockerVM = Depends(dep_node) +) -> None: + """ + Delete a marker's capture pcap (called by the controller when the marker is + removed) so the file is cleaned up even with the node stopped. Also drops + the marker from the port NIO's cached spec so a node restart won't reinstall + it (and recreate an empty pcap). + """ + + nio = node.get_nio(adapter_number) + await node.delete_marker_capture(marker_name, link_id, nio) + + +@router.put( + "/{node_id}/markers/{marker_name}/rebuild", + dependencies=[Depends(compute_authentication)] +) +async def rebuild_docker_marker( + marker_name: str, + rebuild_data: schemas.MarkerRebuild, + node: DockerVM = Depends(dep_node) +) -> dict: + """ + Re-install a single marker filter with new BPF/tag/direction (delete + add, + no bridge reset) so sibling markers' pcaps stay open. + """ + + await node.rebuild_marker_filter( + marker_name, rebuild_data.link_id, rebuild_data.bpf, + rebuild_data.tag, rebuild_data.direction, rebuild_data.enabled, + ) + return {"marker_name": marker_name} diff --git a/gns3server/api/routes/compute/dynamips_nodes.py b/gns3server/api/routes/compute/dynamips_nodes.py index 08eebb0d7..41314f8a1 100644 --- a/gns3server/api/routes/compute/dynamips_nodes.py +++ b/gns3server/api/routes/compute/dynamips_nodes.py @@ -20,7 +20,7 @@ API routes for Dynamips nodes. import os -from fastapi import APIRouter, WebSocket, Body, Depends, status +from fastapi import APIRouter, WebSocket, Body, Depends, status, HTTPException from fastapi.encoders import jsonable_encoder from fastapi.responses import StreamingResponse from typing import List, Union @@ -64,7 +64,6 @@ async def create_router(project_id: UUID, node_data: schemas.DynamipsCreate) -> dynamips_manager = Dynamips.instance() platform = node_data.platform - print(node_data.chassis, platform in DEFAULT_CHASSIS) if not node_data.chassis and platform in DEFAULT_CHASSIS: chassis = DEFAULT_CHASSIS[platform] else: @@ -235,6 +234,7 @@ async def update_nio( nio.filters.clear() if nio_data.filters: nio.filters = nio_data.filters + nio.markers = nio_data.markers or {} await node.slot_update_nio_binding(adapter_number, port_number, nio) return nio.asdict() @@ -366,3 +366,89 @@ async def console_ws( async def reset_console(node: Router = Depends(dep_node)) -> None: await node.reset_console() + + +@router.put( + "/{node_id}/markers/{marker_name}", + dependencies=[Depends(compute_authentication)] +) +async def toggle_dynamips_marker( + marker_name: str, + toggle_data: schemas.MarkerToggle, + node: Router = Depends(dep_node) +) -> dict: + """ + Toggle a marker filter on/off without an NIO rebuild (ubridge contract §3.2). + """ + + if not any(n == marker_name for (n, lid) in node._marker_filter_bridges): + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Marker '{marker_name}' is not installed on this node", + ) + await node._ubridge_set_marker_filter_state(marker_name, toggle_data.enabled) + return {"marker_name": marker_name, "enabled": toggle_data.enabled} + + +@router.post( + "/{node_id}/markers/pause", + status_code=status.HTTP_204_NO_CONTENT, + dependencies=[Depends(compute_authentication)] +) +async def pause_dynamips_markers(node: Router = Depends(dep_node)) -> None: + + await node._ubridge_marker_pause() + + +@router.post( + "/{node_id}/markers/resume", + status_code=status.HTTP_204_NO_CONTENT, + dependencies=[Depends(compute_authentication)] +) +async def resume_dynamips_markers(node: Router = Depends(dep_node)) -> None: + + await node._ubridge_marker_resume() + + +@router.delete( + "/{node_id}/adapters/{adapter_number}/ports/{port_number}/markers/{marker_name}", + status_code=status.HTTP_204_NO_CONTENT, + dependencies=[Depends(compute_authentication)] +) +async def delete_dynamips_marker_capture( + marker_name: str, + adapter_number: int, + port_number: int, + link_id: str = "", + node: Router = Depends(dep_node) +) -> None: + """ + Delete a marker's capture pcap (called by the controller when the marker is + removed) so the file is cleaned up even with the node stopped. Also drops + the marker from the port NIO's cached spec so a node restart won't reinstall + it (and recreate an empty pcap). + """ + + nio = node.get_nio(adapter_number, port_number) + await node.delete_marker_capture(marker_name, link_id, nio) + + +@router.put( + "/{node_id}/markers/{marker_name}/rebuild", + dependencies=[Depends(compute_authentication)] +) +async def rebuild_dynamips_marker( + marker_name: str, + rebuild_data: schemas.MarkerRebuild, + node: Router = Depends(dep_node) +) -> dict: + """ + Re-install a single marker filter with new BPF/tag/direction (delete + add, + no bridge reset) so sibling markers' pcaps stay open. + """ + + await node.rebuild_marker_filter( + marker_name, rebuild_data.link_id, rebuild_data.bpf, + rebuild_data.tag, rebuild_data.direction, rebuild_data.enabled, + ) + return {"marker_name": marker_name} diff --git a/gns3server/api/routes/compute/ethernet_switch_nodes.py b/gns3server/api/routes/compute/ethernet_switch_nodes.py index a8f755047..d6d253ca9 100644 --- a/gns3server/api/routes/compute/ethernet_switch_nodes.py +++ b/gns3server/api/routes/compute/ethernet_switch_nodes.py @@ -16,6 +16,10 @@ """ API routes for Ethernet switch nodes. + +The Ethernet switch is a builtin node backed by a Linux kernel bridge driven +through uBridge's ``brctl`` module (see +``gns3server.compute.builtin.nodes.ethernet_switch``). """ import os @@ -25,8 +29,8 @@ from fastapi.encoders import jsonable_encoder from fastapi.responses import StreamingResponse from uuid import UUID -from gns3server.compute.dynamips import Dynamips -from gns3server.compute.dynamips.nodes.ethernet_switch import EthernetSwitch +from gns3server.compute.builtin import Builtin +from gns3server.compute.builtin.nodes.ethernet_switch import EthernetSwitch from gns3server import schemas responses = {404: {"model": schemas.ErrorMessage, "description": "Could not find project or Ethernet switch node"}} @@ -39,8 +43,8 @@ def dep_node(project_id: UUID, node_id: UUID) -> EthernetSwitch: Dependency to retrieve a node. """ - dynamips_manager = Dynamips.instance() - node = dynamips_manager.get_node(str(node_id), project_id=str(project_id)) + builtin_manager = Builtin.instance() + node = builtin_manager.get_node(str(node_id), project_id=str(project_id)) return node @@ -55,10 +59,9 @@ async def create_ethernet_switch(project_id: UUID, node_data: schemas.EthernetSw Create a new Ethernet switch. """ - # Use the Dynamips Ethernet switch to simulate this node - dynamips_manager = Dynamips.instance() + builtin_manager = Builtin.instance() node_data = jsonable_encoder(node_data, exclude_unset=True) - node = await dynamips_manager.create_node( + node = await builtin_manager.create_node( node_data.pop("name"), str(project_id), node_data.get("node_id"), @@ -67,7 +70,7 @@ async def create_ethernet_switch(project_id: UUID, node_data: schemas.EthernetSw node_type="ethernet_switch", ports=node_data.get("ports_mapping"), ) - + node.usage = node_data.get("usage", "") return node.asdict() @@ -86,7 +89,7 @@ async def duplicate_ethernet_switch( Duplicate an Ethernet switch. """ - new_node = await Dynamips.instance().duplicate_node(node.id, str(destination_node_id)) + new_node = await Builtin.instance().duplicate_node(node.id, str(destination_node_id)) return new_node.asdict() @@ -101,7 +104,9 @@ async def update_ethernet_switch( node_data = jsonable_encoder(node_data, exclude_unset=True) if "name" in node_data and node.name != node_data["name"]: - await node.set_name(node_data["name"]) + node.name = node_data["name"] + if "usage" in node_data: + node.usage = node_data["usage"] if "ports_mapping" in node_data: node.ports_mapping = node_data["ports_mapping"] await node.update_port_settings() @@ -117,7 +122,7 @@ async def delete_ethernet_switch(node: EthernetSwitch = Depends(dep_node)) -> No Delete an Ethernet switch. """ - await Dynamips.instance().delete_node(node.id) + await Builtin.instance().delete_node(node.id) @router.post("/{node_id}/start", status_code=status.HTTP_204_NO_CONTENT) @@ -182,11 +187,38 @@ async def create_ethernet_switch_nio( node: EthernetSwitch = Depends(dep_node) ) -> schemas.UDPNIO: - nio = await Dynamips.instance().create_nio(node, jsonable_encoder(nio_data, exclude_unset=True)) + nio = Builtin.instance().create_nio(jsonable_encoder(nio_data, exclude_unset=True)) await node.add_nio(nio, port_number) return nio.asdict() +@router.put( + "/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio", + status_code=status.HTTP_201_CREATED, + response_model=schemas.UDPNIO, +) +async def update_ethernet_switch_nio( + *, + adapter_number: int = Path(..., ge=0, le=0), + port_number: int, + nio_data: schemas.UDPNIO, + node: EthernetSwitch = Depends(dep_node) +) -> schemas.UDPNIO: + """ + Update a NIO (Network Input/Output) on the node: re-apply the packet + filters and traffic-insight markers carried by the NIO onto the port's + uBridge relay. The adapter number on the switch is always 0. + """ + + nio = node.get_nio(port_number) + nio.filters.clear() + if nio_data.filters: + nio.filters = nio_data.filters + nio.markers = nio_data.markers or {} + await node.update_nio(port_number, nio) + return nio.asdict() + + @router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio", status_code=status.HTTP_204_NO_CONTENT) async def delete_ethernet_switch_nio( *, @@ -199,8 +231,7 @@ async def delete_ethernet_switch_nio( The adapter number on the switch is always 0. """ - nio = await node.remove_nio(port_number) - await nio.delete() + await node.remove_nio(port_number) @router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/start") @@ -251,5 +282,77 @@ async def stream_pcap_file( """ nio = node.get_nio(port_number) - stream = Dynamips.instance().stream_pcap_file(nio, node.project.id) + stream = Builtin.instance().stream_pcap_file(nio, node.project.id) return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap") + + +@router.put("/{node_id}/markers/{marker_name}") +async def toggle_ethernet_switch_marker( + marker_name: str, + toggle_data: schemas.MarkerToggle, + node: EthernetSwitch = Depends(dep_node) +) -> dict: + """ + Toggle a marker filter on/off without an NIO rebuild (ubridge contract §3.2). + """ + + if not any(n == marker_name for (n, lid) in node._marker_filter_bridges): + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Marker '{marker_name}' is not installed on this node", + ) + await node._ubridge_set_marker_filter_state(marker_name, toggle_data.enabled) + return {"marker_name": marker_name, "enabled": toggle_data.enabled} + + +@router.post("/{node_id}/markers/pause", status_code=status.HTTP_204_NO_CONTENT) +async def pause_ethernet_switch_markers(node: EthernetSwitch = Depends(dep_node)) -> None: + + await node._ubridge_marker_pause() + + +@router.post("/{node_id}/markers/resume", status_code=status.HTTP_204_NO_CONTENT) +async def resume_ethernet_switch_markers(node: EthernetSwitch = Depends(dep_node)) -> None: + + await node._ubridge_marker_resume() + + +@router.delete( + "/{node_id}/adapters/{adapter_number}/ports/{port_number}/markers/{marker_name}", + status_code=status.HTTP_204_NO_CONTENT, +) +async def delete_ethernet_switch_marker_capture( + *, + marker_name: str, + adapter_number: int = Path(..., ge=0, le=0), + port_number: int, + link_id: str = "", + node: EthernetSwitch = Depends(dep_node) +) -> None: + """ + Delete a marker's capture pcap (called by the controller when the marker is + removed) so the file is cleaned up even with the switch stopped. Also drops + the marker from the port NIO's cached spec so a switch restart won't + reinstall it (and recreate an empty pcap). The adapter number is always 0. + """ + + nio = node.get_nio(port_number) + await node.delete_marker_capture(marker_name, link_id, nio) + + +@router.put("/{node_id}/markers/{marker_name}/rebuild") +async def rebuild_ethernet_switch_marker( + marker_name: str, + rebuild_data: schemas.MarkerRebuild, + node: EthernetSwitch = Depends(dep_node) +) -> dict: + """ + Re-install a single marker filter with new BPF/tag/direction (delete + add, + no bridge reset) so sibling markers' pcaps stay open. + """ + + await node.rebuild_marker_filter( + marker_name, rebuild_data.link_id, rebuild_data.bpf, + rebuild_data.tag, rebuild_data.direction, rebuild_data.enabled, + ) + return {"marker_name": marker_name} diff --git a/gns3server/api/routes/compute/images.py b/gns3server/api/routes/compute/images.py index 3c3293934..2bde73906 100644 --- a/gns3server/api/routes/compute/images.py +++ b/gns3server/api/routes/compute/images.py @@ -21,7 +21,7 @@ API routes for images. import os import urllib.parse -from fastapi import APIRouter, Request, status, Response, HTTPException +from fastapi import APIRouter, Body, Request, status, Response, HTTPException from fastapi.responses import FileResponse from typing import List @@ -43,6 +43,16 @@ async def get_docker_images() -> List[dict]: return await docker_manager.list_images() +@router.post("/docker/images/pull", status_code=status.HTTP_204_NO_CONTENT) +async def pull_docker_image(image: str = Body(..., embed=True, min_length=1, pattern=r"^\S+$")) -> None: + """ + Pull or update a Docker image. + """ + + docker_manager = Docker.instance() + await docker_manager.pull_image(image, force=True) + + @router.get("/dynamips/images") async def get_dynamips_images() -> List[dict]: """ diff --git a/gns3server/api/routes/compute/iou_nodes.py b/gns3server/api/routes/compute/iou_nodes.py index 5be4fdd23..b826b21d9 100644 --- a/gns3server/api/routes/compute/iou_nodes.py +++ b/gns3server/api/routes/compute/iou_nodes.py @@ -254,6 +254,8 @@ async def update_iou_node_nio( nio.filters.clear() if nio_data.filters: nio.filters = nio_data.filters + # NIO type is a Union (Ethernet/TAP/UDP); only UDPNIO carries markers. + nio.markers = getattr(nio_data, "markers", None) or {} await node.adapter_update_nio_binding(adapter_number, port_number, nio) return nio.asdict() @@ -286,7 +288,7 @@ async def start_iou_node_capture( """ pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name) - await node.start_capture(adapter_number, port_number, pcap_file_path) + await node.start_capture(adapter_number, port_number, pcap_file_path, node_capture_data.data_link_type) return {"pcap_file_path": str(pcap_file_path)} @@ -344,3 +346,89 @@ async def console_ws( async def reset_console(node: IOUVM = Depends(dep_node)) -> None: await node.reset_console() + + +@router.put( + "/{node_id}/markers/{marker_name}", + dependencies=[Depends(compute_authentication)] +) +async def toggle_iou_marker( + marker_name: str, + toggle_data: schemas.MarkerToggle, + node: IOUVM = Depends(dep_node) +) -> dict: + """ + Toggle a marker filter on/off without an NIO rebuild (ubridge contract §3.2). + """ + + if not any(n == marker_name for (n, lid) in node._marker_filter_bridges): + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Marker '{marker_name}' is not installed on this node", + ) + await node._ubridge_set_marker_filter_state(marker_name, toggle_data.enabled) + return {"marker_name": marker_name, "enabled": toggle_data.enabled} + + +@router.post( + "/{node_id}/markers/pause", + status_code=status.HTTP_204_NO_CONTENT, + dependencies=[Depends(compute_authentication)] +) +async def pause_iou_markers(node: IOUVM = Depends(dep_node)) -> None: + + await node._ubridge_marker_pause() + + +@router.post( + "/{node_id}/markers/resume", + status_code=status.HTTP_204_NO_CONTENT, + dependencies=[Depends(compute_authentication)] +) +async def resume_iou_markers(node: IOUVM = Depends(dep_node)) -> None: + + await node._ubridge_marker_resume() + + +@router.delete( + "/{node_id}/adapters/{adapter_number}/ports/{port_number}/markers/{marker_name}", + status_code=status.HTTP_204_NO_CONTENT, + dependencies=[Depends(compute_authentication)] +) +async def delete_iou_marker_capture( + marker_name: str, + adapter_number: int, + port_number: int, + link_id: str = "", + node: IOUVM = Depends(dep_node) +) -> None: + """ + Delete a marker's capture pcap (called by the controller when the marker is + removed) so the file is cleaned up even with the node stopped. Also drops + the marker from the port NIO's cached spec so a node restart won't reinstall + it (and recreate an empty pcap). + """ + + nio = node.get_nio(adapter_number, port_number) + await node.delete_marker_capture(marker_name, link_id, nio) + + +@router.put( + "/{node_id}/markers/{marker_name}/rebuild", + dependencies=[Depends(compute_authentication)] +) +async def rebuild_iou_marker( + marker_name: str, + rebuild_data: schemas.MarkerRebuild, + node: IOUVM = Depends(dep_node) +) -> dict: + """ + Re-install a single marker filter with new BPF/tag/direction (delete + add, + no bridge reset) so sibling markers' pcaps stay open. + """ + + await node.rebuild_marker_filter( + marker_name, rebuild_data.link_id, rebuild_data.bpf, + rebuild_data.tag, rebuild_data.direction, rebuild_data.enabled, + ) + return {"marker_name": marker_name} diff --git a/gns3server/api/routes/compute/projects.py b/gns3server/api/routes/compute/projects.py index 711e78416..7da980a08 100644 --- a/gns3server/api/routes/compute/projects.py +++ b/gns3server/api/routes/compute/projects.py @@ -19,13 +19,16 @@ API routes for projects. """ import os +import shutil import urllib.parse +import inspect +import asyncio import logging log = logging.getLogger() -from fastapi import APIRouter, Depends, HTTPException, Request, Response, status +from fastapi import APIRouter, Depends, HTTPException, Request, Response, status, Query from fastapi.encoders import jsonable_encoder from fastapi.responses import FileResponse from typing import List @@ -33,6 +36,7 @@ from uuid import UUID from gns3server.compute.project_manager import ProjectManager from gns3server.compute.project import Project +from gns3server.compute.base_manager import BaseManager from gns3server.utils.path import is_safe_path from gns3server import schemas @@ -129,58 +133,189 @@ async def delete_compute_project(project: Project = Depends(dep_project)) -> Non await project.delete() ProjectManager.instance().remove_project(project.id) -# @Route.get( -# r"/projects/{project_id}/notifications", -# description="Receive notifications about the project", -# parameters={ -# "project_id": "Project UUID", -# }, -# status_codes={ -# 200: "End of stream", -# 404: "The project doesn't exist" -# }) -# async def notification(request, response): -# -# pm = ProjectManager.instance() -# project = pm.get_project(request.match_info["project_id"]) -# -# response.content_type = "application/json" -# response.set_status(200) -# response.enable_chunked_encoding() -# -# response.start(request) -# queue = project.get_listen_queue() -# ProjectHandler._notifications_listening.setdefault(project.id, 0) -# ProjectHandler._notifications_listening[project.id] += 1 -# await response.write("{}\n".format(json.dumps(ProjectHandler._getPingMessage())).encode("utf-8")) -# while True: -# try: -# (action, msg) = await asyncio.wait_for(queue.get(), 5) -# if hasattr(msg, "asdict"): -# msg = json.dumps({"action": action, "event": msg.asdict()}, sort_keys=True) -# else: -# msg = json.dumps({"action": action, "event": msg}, sort_keys=True) -# log.debug("Send notification: %s", msg) -# await response.write(("{}\n".format(msg)).encode("utf-8")) -# except asyncio.TimeoutError: -# await response.write("{}\n".format(json.dumps(ProjectHandler._getPingMessage())).encode("utf-8")) -# project.stop_listen_queue(queue) -# if project.id in ProjectHandler._notifications_listening: -# ProjectHandler._notifications_listening[project.id] -= 1 -# def _getPingMessage(cls): -# """ -# Ping messages are regularly sent to the client to -# keep the connection open. We send with it some information about server load. -# -# :returns: hash -# """ -# stats = {} -# # Non blocking call in order to get cpu usage. First call will return 0 -# stats["cpu_usage_percent"] = CpuPercent.get(interval=None) -# stats["memory_usage_percent"] = psutil.virtual_memory().percent -# stats["disk_usage_percent"] = psutil.disk_usage(get_default_project_directory()).percent -# return {"action": "ping", "event": stats} +async def _add_nio_binding(node, adapter_number, port_number, nio): + """ + Unified NIO-binding dispatch across node types. Each node type exposes a + different method signature, so centralise the fan-out here for the batch + endpoint. Dispatch keys off the manager class name (only dynamips/iou/qemu + carry a ``_NODE_TYPE`` attribute, so it can't be used universally). + """ + + manager_name = type(node.manager).__name__ + # Adapter-based nodes: docker / qemu / vmware / virtualbox take + # (adapter_number, nio); iou additionally takes port_number. + if manager_name in ("Docker", "Qemu", "VMware", "VirtualBox"): + await node.adapter_add_nio_binding(adapter_number, nio) + elif manager_name == "IOU": + await node.adapter_add_nio_binding(adapter_number, port_number, nio) + elif manager_name == "VPCS": + await node.port_add_nio_binding(port_number, nio) + elif manager_name == "Dynamips": + # Dynamips routers use slot_add_nio_binding(slot, port, nio); + # Dynamips switches/hubs use add_nio(nio, port_number). + if hasattr(node, "slot_add_nio_binding"): + await node.slot_add_nio_binding(adapter_number, port_number, nio) + else: + await node.add_nio(nio, port_number) + elif manager_name == "Builtin": + # ethernet_switch / ethernet_hub / cloud / nat: add_nio(nio, port_number) + await node.add_nio(nio, port_number) + else: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"Batch NIO creation not supported for node type '{manager_name}'", + ) + + +def _get_existing_nio(node, adapter_number, port_number): + """ + Fetch the already-bound NIO for a port, preserving its UDP endpoints + (lport/rhost/rport) so a marker/filter update only changes markers/filters. + Dispatch keys off the manager class name, mirroring _add_nio_binding. + """ + + manager_name = type(node.manager).__name__ + if manager_name in ("Docker", "Qemu", "VMware", "VirtualBox"): + return node.get_nio(adapter_number) + elif manager_name == "IOU": + return node.get_nio(adapter_number, port_number) + elif manager_name in ("VPCS", "Builtin"): + return node.get_nio(port_number) + elif manager_name == "Dynamips": + # Dynamips routers expose NIOs via the slot/adapter; switches/hubs + # via get_nio(port). + if hasattr(node, "get_nio"): + import inspect as _inspect + if len(_inspect.signature(node.get_nio).parameters) >= 2: + return node.get_nio(adapter_number, port_number) + return node.get_nio(port_number) + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"Dynamips node '{node.name}' has no get_nio for batch update", + ) + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"Batch NIO update not supported for node type '{manager_name}'", + ) + + +async def _update_nio_binding(node, adapter_number, port_number, nio): + """ + Re-apply a NIO binding (filters + markers) to a started node's uBridge. + Dispatch keys off the manager class name, mirroring _add_nio_binding. + """ + + manager_name = type(node.manager).__name__ + if manager_name in ("Docker", "Qemu", "VMware", "VirtualBox"): + await node.adapter_update_nio_binding(adapter_number, nio) + elif manager_name == "IOU": + await node.adapter_update_nio_binding(adapter_number, port_number, nio) + elif manager_name == "VPCS": + await node.port_update_nio_binding(port_number, nio) + elif manager_name == "Dynamips": + if hasattr(node, "slot_update_nio_binding"): + await node.slot_update_nio_binding(adapter_number, port_number, nio) + else: + await node.update_nio(port_number, nio) + elif manager_name == "Builtin": + await node.update_nio(port_number, nio) + else: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"Batch NIO update not supported for node type '{manager_name}'", + ) + + +@router.post( + "/projects/{project_id}/nios/batch", + status_code=status.HTTP_201_CREATED, +) +async def create_batch_nios( + project_id: UUID, + batch: schemas.BatchNIOCreate, + project: Project = Depends(dep_project), +) -> dict: + """ + Create many NIO bindings across nodes in a single request. + + Used by the controller during project open to avoid one HTTP round-trip per + NIO. Each entry resolves its node via the project, builds the NIO through + the node's manager, and binds it. Nodes that are not started perform the + binding in memory; started nodes additionally wire uBridge. + """ + + # Group entries by node so different nodes' uBridge processes are wired + # in parallel (each node has its own AF_UNIX socket). Within a node + # entries are serial to respect the per-node uBridge command lock. + # This is what makes builtin L2 nodes (ethernet_switch/hub/cloud/nat) + # start their uBridge concurrently during project open instead of one + # at a time (~0.5s each for fork + socket connect). + per_node = {} + for entry in batch.nios: + per_node.setdefault(entry.node_id, []).append(entry) + + async def _create_one_node(node_id, entries): + node = project.get_node(node_id) + # Dynamips.create_nio(self, node, nio_settings) is async and takes + # an extra positional 'node'; other managers' create_nio(nio_settings) + # is synchronous. Detect once per node via bound-method parameter + # count (standard == 1, Dynamips == 2) and await the async variant. + sig = inspect.signature(node.manager.create_nio) + dynamips_style = len(sig.parameters) >= 2 + for entry in entries: + nio_settings = jsonable_encoder(entry.nio, exclude_unset=True) + if dynamips_style: + nio = await node.manager.create_nio(node, nio_settings) + else: + nio = node.manager.create_nio(nio_settings) + await _add_nio_binding(node, entry.adapter_number, entry.port_number, nio) + + await asyncio.gather( + *[_create_one_node(nid, ents) for nid, ents in per_node.items()] + ) + return {"added": len(batch.nios)} + + +@router.put( + "/projects/{project_id}/nios/batch", + status_code=status.HTTP_200_OK, +) +async def update_batch_nios( + project_id: UUID, + batch: schemas.BatchNIOCreate, + project: Project = Depends(dep_project), +) -> dict: + """ + Update many NIO bindings (filters + markers) across nodes in a single + request, re-applying them to uBridge on started nodes. + + Used by the controller when a project-level marker definition changes and + must fan out to every affected link — replacing one PUT /nio round-trip per + link end with one round-trip per compute. Each entry fetches the already- + bound NIO (preserving its UDP endpoints), overlays the new markers/filters, + and re-binds it. + """ + + # Group entries by node so that different nodes' uBridge processes are + # updated in parallel (each node has its own AF_UNIX socket). Within a + # node entries are serial to respect the per-node uBridge command lock. + per_node = {} + for entry in batch.nios: + per_node.setdefault(entry.node_id, []).append(entry) + + async def _update_one_node(node_id, entries): + node = project.get_node(node_id) + for e in entries: + nio = _get_existing_nio(node, e.adapter_number, e.port_number) + nio.filters = e.nio.filters or {} + nio.markers = e.nio.markers or {} + await _update_nio_binding(node, e.adapter_number, e.port_number, nio) + + await asyncio.gather( + *[_update_one_node(nid, ents) for nid, ents in per_node.items()] + ) + return {"updated": len(batch.nios)} @router.get("/projects/{project_id}/files", response_model=List[schemas.ProjectFile]) @@ -196,14 +331,16 @@ async def get_compute_project_files(project: Project = Depends(dep_project)) -> async def get_compute_node_files( node_type: str, node_id: str, - project: Project = Depends(dep_project) + project: Project = Depends(dep_project), + path: str = Query("", description="Subdirectory path within node directory"), + recursive: bool = Query(False, description="Recursively list all files") ) -> List[schemas.NodeFile]: """ Return files belonging to a specific node with detailed metadata. """ node_path = f"project-files/{node_type}/{node_id}" - return await project.list_node_files(node_path) + return await project.list_node_files(node_path, subpath=path, recursive=recursive) @router.get("/projects/{project_id}/files/{file_path:path}") @@ -238,20 +375,49 @@ async def write_compute_project_file( # Raise error if user try to escape if not is_safe_path(path, project.path): - raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Path is outside the project directory") path = os.path.join(project.path, path) try: os.makedirs(os.path.dirname(path), exist_ok=True) - try: - with open(path, "wb+") as f: - async for chunk in request.stream(): - f.write(chunk) - except (UnicodeEncodeError, OSError) as e: - pass # FIXME + with open(path, "wb+") as f: + async for chunk in request.stream(): + f.write(chunk) except FileNotFoundError: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) except PermissionError: - raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=f"Permission denied writing to '{path}'") + except OSError as e: + log.error(f"Error writing file '{path}': {e}") + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) + + +@router.delete("/projects/{project_id}/files/{file_path:path}", status_code=status.HTTP_204_NO_CONTENT) +async def delete_compute_project_file( + file_path: str, + project: Project = Depends(dep_project) +) -> None: + + file_path = urllib.parse.unquote(file_path) + path = os.path.normpath(file_path) + + if not is_safe_path(path, project.path): + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Path is outside the project directory") + + path = os.path.join(project.path, path) + if not os.path.exists(path): + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) + + try: + if os.path.isdir(path): + shutil.rmtree(path) + else: + os.remove(path) + except FileNotFoundError: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) + except PermissionError: + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=f"Permission denied deleting '{path}'") + except OSError as e: + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) diff --git a/gns3server/api/routes/compute/qemu_nodes.py b/gns3server/api/routes/compute/qemu_nodes.py index 623ae8dae..efd6f7d74 100644 --- a/gns3server/api/routes/compute/qemu_nodes.py +++ b/gns3server/api/routes/compute/qemu_nodes.py @@ -20,7 +20,7 @@ API routes for Qemu nodes. import os -from fastapi import APIRouter, WebSocket, Depends, Body, Path, status +from fastapi import APIRouter, WebSocket, Depends, Body, Path, status, HTTPException from fastapi.encoders import jsonable_encoder from fastapi.responses import StreamingResponse from typing import Union @@ -30,7 +30,6 @@ from gns3server import schemas from gns3server.compute import qemu from gns3server.compute.qemu import Qemu from gns3server.compute.qemu.qemu_vm import QemuVM - from .dependencies.authentication import compute_authentication, ws_compute_authentication import logging @@ -84,7 +83,7 @@ async def create_qemu_node(project_id: UUID, node_data: schemas.QemuCreate) -> s for disk_index, drive in enumerate(drives): disk_image_backing_file = node_data.get(f"hd{drive}_disk_image_backing_file") if disk_image_backing_file: - log.info(f"Updating disk image for drive {drive} with backing file {disk_image_backing_file}") + log.debug(f"Updating disk image for drive {drive} with backing file {disk_image_backing_file}") node_data[f"hd{drive}_disk_image"] = disk_image_backing_file for name, value in node_data.items(): @@ -321,6 +320,7 @@ async def update_qemu_node_nio( if nio_data.filters: nio.filters = nio_data.filters nio.suspend = nio_data.suspend + nio.markers = nio_data.markers or {} await node.adapter_update_nio_binding(adapter_number, nio) return nio.asdict() @@ -438,3 +438,89 @@ async def vnc_console_ws( async def reset_console(node: QemuVM = Depends(dep_node)) -> None: await node.reset_console() + + +@router.put( + "/{node_id}/markers/{marker_name}", + dependencies=[Depends(compute_authentication)] +) +async def toggle_qemu_marker( + marker_name: str, + toggle_data: schemas.MarkerToggle, + node: QemuVM = Depends(dep_node) +) -> dict: + """ + Toggle a marker filter on/off without an NIO rebuild (ubridge contract §3.2). + """ + + if not any(n == marker_name for (n, lid) in node._marker_filter_bridges): + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Marker '{marker_name}' is not installed on this node", + ) + await node._ubridge_set_marker_filter_state(marker_name, toggle_data.enabled) + return {"marker_name": marker_name, "enabled": toggle_data.enabled} + + +@router.post( + "/{node_id}/markers/pause", + status_code=status.HTTP_204_NO_CONTENT, + dependencies=[Depends(compute_authentication)] +) +async def pause_qemu_markers(node: QemuVM = Depends(dep_node)) -> None: + + await node._ubridge_marker_pause() + + +@router.post( + "/{node_id}/markers/resume", + status_code=status.HTTP_204_NO_CONTENT, + dependencies=[Depends(compute_authentication)] +) +async def resume_qemu_markers(node: QemuVM = Depends(dep_node)) -> None: + + await node._ubridge_marker_resume() + + +@router.delete( + "/{node_id}/adapters/{adapter_number}/ports/{port_number}/markers/{marker_name}", + status_code=status.HTTP_204_NO_CONTENT, + dependencies=[Depends(compute_authentication)] +) +async def delete_qemu_marker_capture( + marker_name: str, + adapter_number: int, + port_number: int = Path(..., ge=0, le=0), + link_id: str = "", + node: QemuVM = Depends(dep_node) +) -> None: + """ + Delete a marker's capture pcap (called by the controller when the marker is + removed) so the file is cleaned up even with the node stopped. Also drops + the marker from the port NIO's cached spec so a node restart won't reinstall + it (and recreate an empty pcap). + """ + + nio = node.get_nio(adapter_number) + await node.delete_marker_capture(marker_name, link_id, nio) + + +@router.put( + "/{node_id}/markers/{marker_name}/rebuild", + dependencies=[Depends(compute_authentication)] +) +async def rebuild_qemu_marker( + marker_name: str, + rebuild_data: schemas.MarkerRebuild, + node: QemuVM = Depends(dep_node) +) -> dict: + """ + Re-install a single marker filter with new BPF/tag/direction (delete + add, + no bridge reset) so sibling markers' pcaps stay open. + """ + + await node.rebuild_marker_filter( + marker_name, rebuild_data.link_id, rebuild_data.bpf, + rebuild_data.tag, rebuild_data.direction, rebuild_data.enabled, + ) + return {"marker_name": marker_name} diff --git a/gns3server/api/routes/compute/vpcs_nodes.py b/gns3server/api/routes/compute/vpcs_nodes.py index c51f0254d..546fabdb0 100644 --- a/gns3server/api/routes/compute/vpcs_nodes.py +++ b/gns3server/api/routes/compute/vpcs_nodes.py @@ -29,7 +29,6 @@ from uuid import UUID from gns3server import schemas from gns3server.compute.vpcs import VPCS from gns3server.compute.vpcs.vpcs_vm import VPCSVM - from .dependencies.authentication import compute_authentication, ws_compute_authentication responses = {404: {"model": schemas.ErrorMessage, "description": "Could not find project or VMware node"}} @@ -240,6 +239,7 @@ async def update_vpcs_node_nio( nio.filters.clear() if nio_data.filters: nio.filters = nio_data.filters + nio.markers = nio_data.markers or {} await node.port_update_nio_binding(port_number, nio) return nio.asdict() @@ -303,6 +303,7 @@ async def stop_vpcs_node_capture( await node.stop_capture(port_number) + @router.get( "/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/stream", dependencies=[Depends(compute_authentication)] @@ -344,3 +345,90 @@ async def console_ws( async def reset_console(node: VPCSVM = Depends(dep_node)) -> None: await node.reset_console() + + +@router.put( + "/{node_id}/markers/{marker_name}", + dependencies=[Depends(compute_authentication)] +) +async def toggle_vpcs_marker( + marker_name: str, + toggle_data: schemas.MarkerToggle, + node: VPCSVM = Depends(dep_node) +) -> dict: + """ + Toggle a marker filter on/off without an NIO rebuild (ubridge contract §3.2). + """ + + if not any(n == marker_name for (n, lid) in node._marker_filter_bridges): + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Marker '{marker_name}' is not installed on this node", + ) + await node._ubridge_set_marker_filter_state(marker_name, toggle_data.enabled) + return {"marker_name": marker_name, "enabled": toggle_data.enabled} + + +@router.post( + "/{node_id}/markers/pause", + status_code=status.HTTP_204_NO_CONTENT, + dependencies=[Depends(compute_authentication)] +) +async def pause_vpcs_markers(node: VPCSVM = Depends(dep_node)) -> None: + + await node._ubridge_marker_pause() + + +@router.post( + "/{node_id}/markers/resume", + status_code=status.HTTP_204_NO_CONTENT, + dependencies=[Depends(compute_authentication)] +) +async def resume_vpcs_markers(node: VPCSVM = Depends(dep_node)) -> None: + + await node._ubridge_marker_resume() + + +@router.delete( + "/{node_id}/adapters/{adapter_number}/ports/{port_number}/markers/{marker_name}", + status_code=status.HTTP_204_NO_CONTENT, + dependencies=[Depends(compute_authentication)] +) +async def delete_vpcs_marker_capture( + *, + marker_name: str, + adapter_number: int = Path(..., ge=0, le=0), + port_number: int, + link_id: str = "", + node: VPCSVM = Depends(dep_node) +) -> None: + """ + Delete a marker's capture pcap (called by the controller when the marker is + removed) so the file is cleaned up even with the node stopped. Also drops + the marker from the port NIO's cached spec so a node restart won't reinstall + it (and recreate an empty pcap). + """ + + nio = node.get_nio(port_number) + await node.delete_marker_capture(marker_name, link_id, nio) + + +@router.put( + "/{node_id}/markers/{marker_name}/rebuild", + dependencies=[Depends(compute_authentication)] +) +async def rebuild_vpcs_marker( + marker_name: str, + rebuild_data: schemas.MarkerRebuild, + node: VPCSVM = Depends(dep_node) +) -> dict: + """ + Re-install a single marker filter with new BPF/tag/direction (delete + add, + no bridge reset) so sibling markers' pcaps stay open. + """ + + await node.rebuild_marker_filter( + marker_name, rebuild_data.link_id, rebuild_data.bpf, + rebuild_data.tag, rebuild_data.direction, rebuild_data.enabled, + ) + return {"marker_name": marker_name} diff --git a/gns3server/api/routes/controller/__init__.py b/gns3server/api/routes/controller/__init__.py index f34d342c3..25334f81b 100644 --- a/gns3server/api/routes/controller/__init__.py +++ b/gns3server/api/routes/controller/__init__.py @@ -39,7 +39,7 @@ else: async def ai_not_available(path: str = ""): raise HTTPException( status_code=status.HTTP_501_NOT_IMPLEMENTED, - detail="AI Copilot is not available. Install AI dependencies with: pip install gns3-server[ai-copilot]" + detail="AI Copilot is not available. Install AI dependencies with: pip install gns3-server[ai-features]" ) from . import controller @@ -60,6 +60,9 @@ from . import roles from . import acl from . import pools from . import privileges +from . import api_keys +from . import netmiko +from . import settings from .dependencies.authentication import get_current_active_user @@ -70,6 +73,12 @@ router.include_router( tags=["Controller"] ) +router.include_router( + settings.router, + prefix="/settings", + tags=["Server settings"] +) + router.include_router( users.router, prefix="/access/users", @@ -158,6 +167,12 @@ router.include_router( tags=["Appliances"] ) +router.include_router( + netmiko.router, + prefix="/netmiko", + tags=["Netmiko"] +) + router.include_router( pools.router, prefix="/pools", @@ -192,3 +207,9 @@ router.include_router( dependencies=[Depends(get_current_active_user)], tags=["GNS3 Copilot"] ) + +router.include_router( + api_keys.router, + dependencies=[Depends(get_current_active_user)], + tags=["API Keys"] +) diff --git a/gns3server/api/routes/controller/acl.py b/gns3server/api/routes/controller/acl.py index f4b0e8897..3ec65e874 100644 --- a/gns3server/api/routes/controller/acl.py +++ b/gns3server/api/routes/controller/acl.py @@ -22,9 +22,10 @@ API routes for ACL. import re from fastapi import APIRouter, Depends, Request, status -from fastapi.routing import APIRoute +from fastapi.routing import APIRoute, _IncludedRouter +from starlette.routing import BaseRoute, Mount from uuid import UUID -from typing import List +from typing import Iterator, List, Sequence from gns3server import schemas @@ -38,7 +39,6 @@ from gns3server.db.repositories.users import UsersRepository from gns3server.db.repositories.rbac import RbacRepository from gns3server.db.repositories.images import ImagesRepository from gns3server.db.repositories.templates import TemplatesRepository -from gns3server.db.repositories.pools import ResourcePoolsRepository from .dependencies.database import get_repository from .dependencies.rbac import has_privilege @@ -49,6 +49,39 @@ log = logging.getLogger(__name__) router = APIRouter() +def _join_paths(prefix: str, path: str) -> str: + + if not prefix: + return path + + normalized_prefix = prefix + while normalized_prefix.endswith("/"): + normalized_prefix = normalized_prefix[:-1] + + normalized_path = path + while normalized_path.startswith("/"): + normalized_path = normalized_path[1:] + + return f"{normalized_prefix}/{normalized_path}".rstrip("/") + + +def _iter_route_paths(routes: Sequence[BaseRoute], prefix: str = "", include_mounted_routes=False) -> Iterator[str]: + for route in routes: + if isinstance(route, _IncludedRouter): + include_prefix = route.include_context.prefix or "" + yield from _iter_route_paths(route.original_router.routes, _join_paths(prefix, include_prefix), include_mounted_routes) + continue + + if isinstance(route, APIRoute): + yield _join_paths(prefix, route.path) + continue + + if isinstance(route, Mount) and include_mounted_routes: + mounted_routes = getattr(route, "routes", None) + if isinstance(mounted_routes, Sequence): + yield from _iter_route_paths(mounted_routes, _join_paths(prefix, route.path), include_mounted_routes) + + @router.get( "/endpoints", status_code=status.HTTP_201_CREATED, @@ -58,8 +91,7 @@ async def endpoints( users_repo: UsersRepository = Depends(get_repository(UsersRepository)), rbac_repo: RbacRepository = Depends(get_repository(RbacRepository)), images_repo: ImagesRepository = Depends(get_repository(ImagesRepository)), - templates_repo: TemplatesRepository = Depends(get_repository(TemplatesRepository)), - pools_repo: ResourcePoolsRepository = Depends(get_repository(ResourcePoolsRepository)) + templates_repo: TemplatesRepository = Depends(get_repository(TemplatesRepository)) ) -> List[dict]: """ List all endpoints to be used in ACL entries. @@ -141,11 +173,9 @@ async def endpoints( for template in templates: add_to_endpoints(f"/templates/{template.template_id}", f'Template "{template.name}"', "template") - # resource pools - add_to_endpoints("/pools", "All resource pools", "pool") - pools = await pools_repo.get_resource_pools() - for pool in pools: - add_to_endpoints(f"/pools/{pool.resource_pool_id}", f'Resource pool "{pool.name}"', "pool") + # Resource pools are not included in "all endpoints" to prevent accidental access + # They must be explicitly configured for team sharing + return endpoints @@ -183,19 +213,21 @@ async def create_ace( Required privilege: ACE.Allocate """ - for route in request.app.routes: - if isinstance(route, APIRoute): + for route_path in _iter_route_paths(request.app.routes, include_mounted_routes=True): + print(route_path) - # remove the prefix (e.g. "/v3") from the route path - route_path = re.sub(r"^/v[0-9]", "", route.path) - # replace route path ID parameters by a UUID regex - route_path = re.sub(r"{\w+_id}", "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}", route_path) - # replace remaining route path parameters by a word matching regex - route_path = re.sub(r"/{[\w:]+}", r"/\\w+", route_path) + for route_path in _iter_route_paths(request.app.routes, include_mounted_routes=True): - if re.fullmatch(route_path, ace_create.path): - log.info(f"Creating ACE for route path {route_path}") - return await rbac_repo.create_ace(ace_create) + # remove the prefix (e.g. "/v3") from the route path + normalized_path = re.sub(r"^/v[0-9]", "", route_path) + # replace route path ID parameters by a UUID regex + normalized_path = re.sub(r"{\w+_id}", "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}", normalized_path) + # replace remaining route path parameters by a word matching regex + normalized_path = re.sub(r"/{[\w:]+}", r"/\\w+", normalized_path) + + if re.fullmatch(normalized_path, ace_create.path): + log.info(f"Creating ACE for route path {route_path}") + return await rbac_repo.create_ace(ace_create) raise ControllerBadRequestError(f"Path '{ace_create.path}' doesn't match any existing endpoint") diff --git a/gns3server/api/routes/controller/api_keys.py b/gns3server/api/routes/controller/api_keys.py new file mode 100644 index 000000000..235cbe7de --- /dev/null +++ b/gns3server/api/routes/controller/api_keys.py @@ -0,0 +1,149 @@ +# +# Copyright (C) 2026 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +API routes for API key management. +""" + +import secrets +import bcrypt +from uuid import uuid4, UUID + +from fastapi import APIRouter, Depends, status, HTTPException + +from gns3server import schemas +from gns3server.db.repositories.api_keys import ApiKeysRepository +from .dependencies.database import get_repository +from .dependencies.authentication import get_current_active_user + +import logging + +log = logging.getLogger(__name__) + +router = APIRouter(prefix="/access/api-keys", tags=["API Keys"]) + +API_KEY_PREFIX = "gns3_" +API_KEY_BYTES = 32 + + +def _generate_api_key(api_key_id: UUID = None) -> tuple[str, str, str, UUID]: + if api_key_id is None: + api_key_id = uuid4() + random_bytes = secrets.token_hex(API_KEY_BYTES) + raw_key = f"gns3_{api_key_id}_{random_bytes}" + # Only hash the random secret part, so auth can extract api_key_id and do O(1) lookup + key_hash = bcrypt.hashpw(random_bytes.encode(), bcrypt.gensalt()).decode() + key_prefix = raw_key[:8] + return raw_key, key_hash, key_prefix, api_key_id + + +@router.post("", status_code=status.HTTP_201_CREATED) +async def create_api_key( + api_key_data: schemas.ApiKeyCreate, + current_user: schemas.User = Depends(get_current_active_user), + api_keys_repo: ApiKeysRepository = Depends(get_repository(ApiKeysRepository)), +) -> dict: + """Create a new API key. The full key is returned only once.""" + + raw_key, key_hash, key_prefix, new_key_id = _generate_api_key() + db_key = await api_keys_repo.create_api_key( + api_key_id=new_key_id, + user_id=current_user.user_id, + name=api_key_data.name, + key_hash=key_hash, + key_prefix=key_prefix, + ) + return { + "api_key_id": str(db_key.api_key_id), + "api_key": raw_key, + "name": db_key.name, + "key_prefix": db_key.key_prefix, + "created_at": db_key.created_at.isoformat() if db_key.created_at else None, + } + + +@router.get("") +async def list_api_keys( + current_user: schemas.User = Depends(get_current_active_user), + api_keys_repo: ApiKeysRepository = Depends(get_repository(ApiKeysRepository)), +) -> list[dict]: + """List all API keys for the current user.""" + + keys = await api_keys_repo.get_api_keys_by_user(current_user.user_id) + return [ + { + "api_key_id": str(k.api_key_id), + "name": k.name, + "key_prefix": k.key_prefix, + "created_at": k.created_at.isoformat() if k.created_at else None, + "last_used_at": k.last_used_at.isoformat() if k.last_used_at else None, + "revoked": k.revoked, + } + for k in keys + ] + + +@router.post("/{api_key_id}/revoke", status_code=status.HTTP_200_OK) +async def revoke_api_key( + api_key_id: UUID, + current_user: schemas.User = Depends(get_current_active_user), + api_keys_repo: ApiKeysRepository = Depends(get_repository(ApiKeysRepository)), +) -> dict: + """Revoke an API key. It will immediately stop working, but can be restored.""" + + key = await api_keys_repo.get_api_key(api_key_id) + if not key: + raise HTTPException(status_code=404, detail="API key not found") + if key.user_id != current_user.user_id: + raise HTTPException(status_code=403, detail="Cannot modify another user's API key") + + await api_keys_repo.revoke_api_key(api_key_id) + return {"message": f"API key '{key.name}' revoked"} + + +@router.post("/{api_key_id}/restore", status_code=status.HTTP_200_OK) +async def restore_api_key( + api_key_id: UUID, + current_user: schemas.User = Depends(get_current_active_user), + api_keys_repo: ApiKeysRepository = Depends(get_repository(ApiKeysRepository)), +) -> dict: + """Restore a previously revoked API key.""" + + key = await api_keys_repo.get_api_key(api_key_id) + if not key: + raise HTTPException(status_code=404, detail="API key not found") + if key.user_id != current_user.user_id: + raise HTTPException(status_code=403, detail="Cannot modify another user's API key") + + await api_keys_repo.restore_api_key(api_key_id) + return {"message": f"API key '{key.name}' restored"} + + +@router.delete("/{api_key_id}", status_code=status.HTTP_204_NO_CONTENT) +async def delete_api_key( + api_key_id: UUID, + current_user: schemas.User = Depends(get_current_active_user), + api_keys_repo: ApiKeysRepository = Depends(get_repository(ApiKeysRepository)), +) -> None: + """Permanently delete an API key. Cannot be undone.""" + + key = await api_keys_repo.get_api_key(api_key_id) + if not key: + raise HTTPException(status_code=404, detail="API key not found") + if key.user_id != current_user.user_id: + raise HTTPException(status_code=403, detail="Cannot delete another user's API key") + + await api_keys_repo.delete_api_key(api_key_id) diff --git a/gns3server/api/routes/controller/appliances.py b/gns3server/api/routes/controller/appliances.py index 1ca32d8e9..142818f66 100644 --- a/gns3server/api/routes/controller/appliances.py +++ b/gns3server/api/routes/controller/appliances.py @@ -122,7 +122,8 @@ def add_appliance_version(appliance_id: UUID, appliance_version: Union[schemas.A @router.post( "/{appliance_id}/install", - status_code=status.HTTP_204_NO_CONTENT, + response_model=schemas.Template, + status_code=status.HTTP_201_CREATED, dependencies=[Depends(has_privilege("Appliance.Allocate"))] ) async def install_appliance( @@ -132,15 +133,15 @@ async def install_appliance( templates_repo: TemplatesRepository = Depends(get_repository(TemplatesRepository)), current_user: schemas.User = Depends(get_current_active_user), rbac_repo: RbacRepository = Depends(get_repository(RbacRepository)) -) -> None: +) -> schemas.Template: """ - Install an appliance. + Install an appliance and return the created template. Required privilege: Appliance.Allocate """ controller = Controller.instance() - await controller.appliance_manager.install_appliance( + return await controller.appliance_manager.install_appliance( appliance_id, version, images_repo, diff --git a/gns3server/api/routes/controller/computes.py b/gns3server/api/routes/controller/computes.py index 7385026bf..59c347ef7 100644 --- a/gns3server/api/routes/controller/computes.py +++ b/gns3server/api/routes/controller/computes.py @@ -18,7 +18,7 @@ API routes for computes. """ -from fastapi import APIRouter, Depends, status +from fastapi import APIRouter, Body, Depends, status from typing import Any, List, Union, Optional from uuid import UUID @@ -165,6 +165,25 @@ async def docker_get_images(compute_id: Union[str, UUID]) -> List[schemas.Comput return result +@router.post( + "/{compute_id}/docker/images/pull", + status_code=status.HTTP_204_NO_CONTENT, + dependencies=[Depends(has_privilege("Compute.Modify"))] +) +async def docker_pull_image( + compute_id: Union[str, UUID], + image: str = Body(..., embed=True, min_length=1, pattern=r"^\S+$") +) -> None: + """ + Pull or update a Docker image on a compute. + + Required privilege: Compute.Modify + """ + + compute = Controller.instance().get_compute(str(compute_id)) + await compute.forward("POST", "docker", "images/pull", data={"image": image}) + + @router.get("/{compute_id}/virtualbox/vms", response_model=List[schemas.ComputeVirtualBoxVM]) async def virtualbox_vms(compute_id: Union[str, UUID]) -> List[schemas.ComputeVirtualBoxVM]: """ diff --git a/gns3server/api/routes/controller/controller.py b/gns3server/api/routes/controller/controller.py index 4f1a3e862..da00957a4 100644 --- a/gns3server/api/routes/controller/controller.py +++ b/gns3server/api/routes/controller/controller.py @@ -17,6 +17,8 @@ import asyncio import signal import os +import time +import psutil from fastapi import APIRouter, Request, Depends, WebSocket, WebSocketDisconnect, status from fastapi.responses import StreamingResponse @@ -42,6 +44,13 @@ log = logging.getLogger(__name__) router = APIRouter() +def get_server_uptime_seconds() -> int: + try: + return max(0, int(time.time() - psutil.Process().create_time())) + except psutil.Error: + return 0 + + @router.get( "/version", response_model=schemas.Version, @@ -246,6 +255,7 @@ async def statistics() -> dict: webwireshark_stats = await collect_webwireshark_stats(projects) return { + "uptime_seconds": get_server_uptime_seconds(), "computes": compute_statistics, "projects": project_stats, "nodes": node_stats, diff --git a/gns3server/api/routes/controller/dependencies/authentication.py b/gns3server/api/routes/controller/dependencies/authentication.py index 3e06345d1..d363c996e 100644 --- a/gns3server/api/routes/controller/dependencies/authentication.py +++ b/gns3server/api/routes/controller/dependencies/authentication.py @@ -14,28 +14,50 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import asyncio +import hashlib import logging +import bcrypt from fastapi import Request, Query, Depends, HTTPException, WebSocket, status from fastapi.security import OAuth2PasswordBearer from typing import Optional +from uuid import UUID from gns3server import schemas +import gns3server.db.models as models +from gns3server.db.repositories.api_keys import ApiKeysRepository from gns3server.db.repositories.users import UsersRepository from gns3server.db.repositories.rbac import RbacRepository -from gns3server.services import auth_service +from gns3server.schemas.controller.tokens import TokenData +from gns3server.services import auth_service, access_ticket_service +from gns3server.services.access_tickets import TICKET_PREFIX from .database import get_repository log = logging.getLogger(__name__) oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/v3/access/users/login", auto_error=False) +def _reject_refresh_token(token_data) -> None: + """Reject tokens with type == 'refresh' — they must not grant API access.""" + + if token_data.token_use == "refresh": + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Refresh tokens cannot be used for API access", + headers={"WWW-Authenticate": "Bearer"}, + ) + + async def get_user_from_token( + request: Request, bearer_token: str = Depends(oauth2_scheme), user_repo: UsersRepository = Depends(get_repository(UsersRepository)), + api_keys_repo: ApiKeysRepository = Depends(get_repository(ApiKeysRepository)), token: Optional[str] = Query(None, include_in_schema=False) ) -> schemas.User: + if bearer_token: # bearer token is used first, then any token passed as a URL parameter token = bearer_token @@ -47,7 +69,67 @@ async def get_user_from_token( headers={"WWW-Authenticate": "Bearer"}, ) + if token.startswith(TICKET_PREFIX): + # Access tickets ("gns3t_…"): short-lived credentials bound to one + # exact resource path, minted by the MCP download tools (capture + # files, symbols). redeem_for_path() confines the ticket to that + # path, so it cannot be replayed against any other resource. + ticket = access_ticket_service.redeem_for_path(token, request.url.path) + if ticket is None: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Invalid or expired access ticket", + headers={"WWW-Authenticate": "Bearer"}, + ) + token_data = TokenData( + username=ticket.username, + token_version=ticket.token_version, + token_use="access", + ) + user = await user_repo.get_user_by_username(token_data.username) + if user is None: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Could not validate credentials", + headers={"WWW-Authenticate": "Bearer"}, + ) + if token_data.token_version != user.token_version: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail=f"Token has been revoked for '{token_data.username}'", + headers={"WWW-Authenticate": "Bearer"}, + ) + return user + + # API Key authentication — format: gns3__ + # Direct lookup by UUID avoids O(n) scan of all keys. + if token.startswith("gns3_"): + parts = token.split("_", 2) + if len(parts) != 3: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid API key format") + try: + key_id = UUID(parts[1]) + except ValueError: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid API key format") + secret = parts[2] + db_key = await api_keys_repo.get_api_key(key_id) + if not db_key or db_key.revoked: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid API key") + if not await asyncio.to_thread(bcrypt.checkpw, secret.encode(), db_key.key_hash.encode()): + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid API key") + await api_keys_repo.update_last_used(db_key.api_key_id) + user = await user_repo.get_user(db_key.user_id) + if not user or not user.is_active: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Not an active user", + headers={"WWW-Authenticate": "Bearer"}, + ) + return user + + # JWT authentication token_data = auth_service.get_token_data(token) + _reject_refresh_token(token_data) user = await user_repo.get_user_by_username(token_data.username) if user is None: raise HTTPException( @@ -102,7 +184,26 @@ async def get_current_active_user_from_websocket( await websocket.accept(subprotocol=subprotocol) try: - token_data = auth_service.get_token_data(token) + if token.startswith(TICKET_PREFIX): + # Node-bound access tickets ("gns3t_…"): short-lived credentials + # for one node's console endpoints, minted by the node_console + # MCP tool. redeem() confines them to the route matching their + # binding, so they cannot authenticate the notification/wireshark + # sockets that share this dependency. + ticket = access_ticket_service.redeem(token, websocket.path_params) + if ticket is None: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Invalid or expired console ticket", + ) + token_data = TokenData( + username=ticket.username, + token_version=ticket.token_version, + token_use="access", + ) + else: + token_data = auth_service.get_token_data(token) + _reject_refresh_token(token_data) user = await user_repo.get_user_by_username(token_data.username) if user is None: @@ -129,7 +230,14 @@ async def get_current_active_user_from_websocket( return user except HTTPException as e: - err_msg = f"Could not authenticate while connecting to controller WebSocket: {e.detail}" + # Fingerprint the received token so clients can compare it against the fingerprint + # returned when the token was issued (e.g. token_sha256_prefix from the + # node_console_info MCP tool) and detect copy corruption on their side. + token_sha256_prefix = hashlib.sha256(token.encode()).hexdigest()[:8] + err_msg = ( + f"Could not authenticate while connecting to controller WebSocket: {e.detail} " + f"(received token sha256 prefix: {token_sha256_prefix})" + ) websocket_error = {"action": "log.error", "event": {"message": err_msg}} await websocket.send_json(websocket_error) log.error(err_msg) diff --git a/gns3server/api/routes/controller/dependencies/database.py b/gns3server/api/routes/controller/dependencies/database.py index 2e859106b..e5338ad75 100644 --- a/gns3server/api/routes/controller/dependencies/database.py +++ b/gns3server/api/routes/controller/dependencies/database.py @@ -22,6 +22,8 @@ from sqlalchemy.ext.asyncio import AsyncSession from gns3server.db.repositories.base import BaseRepository + + async def get_db_session(request: HTTPConnection) -> AsyncSession: async with AsyncSession(request.app.state._db_engine, expire_on_commit=False) as session: diff --git a/gns3server/api/routes/controller/dependencies/rbac.py b/gns3server/api/routes/controller/dependencies/rbac.py index e67953486..41f4cbd0d 100644 --- a/gns3server/api/routes/controller/dependencies/rbac.py +++ b/gns3server/api/routes/controller/dependencies/rbac.py @@ -52,6 +52,10 @@ def has_privilege_on_websocket( current_user: schemas.User = Depends(get_current_active_user_from_websocket), rbac_repo: RbacRepository = Depends(get_repository(RbacRepository)) ): + # Authentication may have failed and closed the socket inside the auth + # dependency, returning None — bail out before touching the user object. + if current_user is None: + return None if not current_user.is_superadmin: path = re.sub(r"^/v[0-9]", "", websocket.url.path) # remove the prefix (e.g. "/v3") from URL path log.debug(f"Checking user {current_user.username} has privilege {privilege_name} on '{path}'") diff --git a/gns3server/api/routes/controller/images.py b/gns3server/api/routes/controller/images.py index 26b9a2ed2..70c49b098 100644 --- a/gns3server/api/routes/controller/images.py +++ b/gns3server/api/routes/controller/images.py @@ -206,19 +206,24 @@ async def prune_images( @router.post( "/install", - status_code=status.HTTP_204_NO_CONTENT, + status_code=status.HTTP_200_OK, dependencies=[Depends(has_privilege("Image.Allocate"))] ) async def install_images( images_repo: ImagesRepository = Depends(get_repository(ImagesRepository)), templates_repo: TemplatesRepository = Depends(get_repository(TemplatesRepository)) -) -> None: +) -> dict: """ Attempt to automatically create templates based on image checksums. + Returns the list of created templates and the list of skipped + candidates (with the reason why they were skipped). + Required privilege: Image.Allocate """ + created = [] + skipped = [] skip_images = get_builtin_disks() images = await images_repo.get_images() for image in images: @@ -229,8 +234,12 @@ async def install_images( if templates: # the image is already used by a template log.warning(f"Image '{image.path}' is used by one or more templates") + skipped.append({ + "name": image.filename, + "reason": "image is already used by one or more templates", + }) continue - await Controller.instance().appliance_manager.install_appliances_from_image( + results = await Controller.instance().appliance_manager.install_appliances_from_image( image.path, image.checksum, images_repo, @@ -239,6 +248,12 @@ async def install_images( None, os.path.dirname(image.path) ) + for result in results: + if result.get("status") == "created": + created.append({k: v for k, v in result.items() if k != "status"}) + else: + skipped.append({k: v for k, v in result.items() if k != "status"}) + return {"created": created, "skipped": skipped} @router.get( diff --git a/gns3server/api/routes/controller/links.py b/gns3server/api/routes/controller/links.py index 2a6eec200..bf6c5467f 100644 --- a/gns3server/api/routes/controller/links.py +++ b/gns3server/api/routes/controller/links.py @@ -27,12 +27,12 @@ from fastapi import APIRouter, Depends, Request, status, WebSocket from fastapi.responses import FileResponse, StreamingResponse from fastapi.encoders import jsonable_encoder from typing import List, Union -from uuid import UUID +from uuid import UUID, uuid4 from gns3server.controller import Controller from gns3server.controller.controller_error import ControllerError from gns3server.db.repositories.rbac import RbacRepository -from gns3server.controller.link import Link +from gns3server.controller.link import Link, _UNSET from gns3server.utils.http_client import HTTPClient from gns3server.utils.port_allocator import link_id_to_port from gns3server.utils.websocket_to_websocket import websocket_proxy @@ -107,6 +107,8 @@ async def create_link(project_id: UUID, link_data: schemas.LinkCreate) -> schema await link.update_link_style(link_data["link_style"]) if "suspend" in link_data: await link.update_suspend(link_data["suspend"]) + if "show_filters_icon" in link_data: + await link.update_show_filters_icon(link_data["show_filters_icon"]) try: for node in link_data["nodes"]: await link.add_node( @@ -171,6 +173,8 @@ async def update_link(link_data: schemas.LinkUpdate, link: Link = Depends(dep_li await link.update_link_style(link_data["link_style"]) if "suspend" in link_data: await link.update_suspend(link_data["suspend"]) + if "show_filters_icon" in link_data: + await link.update_show_filters_icon(link_data["show_filters_icon"]) if "nodes" in link_data: await link.update_nodes(link_data["nodes"]) return link.asdict() @@ -387,27 +391,30 @@ async def web_wireshark_websocket( # Get container IP manager = WebWiresharkManager() - container_ip = await manager.get_container_ip(container_name) + try: + container_ip = await manager.get_container_ip(container_name) - if not container_ip: - log.error(f"Container {container_name} not found in wireshark network") - await websocket.close(code=status.WS_1011_INTERNAL_ERROR) - return + if not container_ip: + log.error(f"Container {container_name} not found in wireshark network") + await websocket.close(code=status.WS_1011_INTERNAL_ERROR) + return - # Build container WebSocket URL - container_ws_url = f"ws://{container_ip}:{xpra_port}" - log.info(f"Proxying WebSocket to container: {container_ws_url}") + # Build container WebSocket URL + container_ws_url = f"ws://{container_ip}:{xpra_port}" + log.info(f"Proxying WebSocket to container: {container_ws_url}") - # Get client's requested subprotocols from request headers - scope = websocket.scope - headers = dict(scope.get("headers", [])) - requested_protocols_header = headers.get(b"sec-websocket-protocol", b"") - requested_protocols = [p.decode().strip() for p in requested_protocols_header.split(b",") if p.strip()] - log.info(f"Client requested subprotocols: {requested_protocols}") + # Get client's requested subprotocols from request headers + scope = websocket.scope + headers = dict(scope.get("headers", [])) + requested_protocols_header = headers.get(b"sec-websocket-protocol", b"") + requested_protocols = [p.decode().strip() for p in requested_protocols_header.split(b",") if p.strip()] + log.info(f"Client requested subprotocols: {requested_protocols}") - # The WebSocket connection has already been accepted by the authentication dependency - # with the proper subprotocol. Now we just proxy data to the backend. - await websocket_proxy(websocket, container_ws_url, requested_protocols) + # The WebSocket connection has already been accepted by the authentication dependency + # with the proper subprotocol. Now we just proxy data to the backend. + await websocket_proxy(websocket, container_ws_url, requested_protocols) + finally: + await manager.close() except Exception as e: log.error(f"Error in WebSocket proxy for link {link_id}: {e}") @@ -417,6 +424,101 @@ async def web_wireshark_websocket( pass +@router.get( + "/{link_id}/markers", + dependencies=[Depends(has_privilege("Link.Audit"))] +) +async def get_markers(link: Link = Depends(dep_link)) -> dict: + """ + Return all traffic-insight markers configured on this link. + + Required privilege: Link.Audit + """ + + return link.markers + + +@router.post( + "/{link_id}/markers", + status_code=status.HTTP_201_CREATED, + dependencies=[Depends(has_privilege("Link.Modify"))] +) +async def create_marker( + marker_data: schemas.MarkerCreate, + link: Link = Depends(dep_link) +) -> dict: + """ + Attach a traffic-insight marker to the link. + On BPF match uBridge emits MARK signals and appends packets to a pcap. + + Required privilege: Link.Modify + """ + + # Auto-generate a link-unique name when the caller omits one. The short + # uuid suffix avoids the collision that `marker-{link.id[:8]}` alone would + # cause on the second anonymous marker on the same link (start_marker + # rejects duplicate names). + if marker_data.name and marker_data.name.lower().startswith("global"): + raise ControllerError('Names starting with "global" are reserved for inherited markers') + name = marker_data.name or f"marker-{link.id[:8]}-{uuid4().hex[:4]}" + await link.start_marker( + name=name, + bpf=marker_data.bpf, + tag=marker_data.tag, + direction=marker_data.direction, + capture_node_id=marker_data.capture_node_id, + color=marker_data.color, + highlight_duration=marker_data.highlight_duration, + data_link_type=marker_data.data_link_type, + ) + return link.markers.get(name, {}) + + +@router.delete( + "/{link_id}/markers/{marker_name}", + status_code=status.HTTP_204_NO_CONTENT, + dependencies=[Depends(has_privilege("Link.Modify"))] +) +async def delete_marker( + marker_name: str, + link: Link = Depends(dep_link) +) -> None: + """ + Remove a traffic-insight marker from the link. + + Required privilege: Link.Modify + """ + + await link.stop_marker(marker_name) + + +@router.put( + "/{link_id}/markers/{marker_name}", + dependencies=[Depends(has_privilege("Link.Modify"))] +) +async def update_marker( + marker_name: str, + marker_data: schemas.MarkerUpdate, + link: Link = Depends(dep_link) +) -> dict: + """ + Update a traffic-insight marker (change BPF, tag, or enabled). + + Required privilege: Link.Modify + """ + + await link.update_marker( + name=marker_name, + bpf=marker_data.bpf if marker_data.bpf else None, + tag=marker_data.tag, + direction=marker_data.direction if "direction" in marker_data.model_fields_set else _UNSET, + color=marker_data.color, + enabled=marker_data.enabled, + highlight_duration=marker_data.highlight_duration, + ) + return link.markers.get(marker_name, {}) + + @router.get( "/{link_id}/iface", response_model=Union[schemas.UDPPortInfo, schemas.EthernetPortInfo], diff --git a/gns3server/api/routes/controller/llm_model_configs.py b/gns3server/api/routes/controller/llm_model_configs.py index a567bd1ef..e6c5b4338 100644 --- a/gns3server/api/routes/controller/llm_model_configs.py +++ b/gns3server/api/routes/controller/llm_model_configs.py @@ -66,13 +66,13 @@ def _filter_api_key_from_config(config: dict) -> dict: ) async def get_user_llm_model_configs( user_id: UUID, - current_user: schemas.User = Depends(has_privilege("User.Audit")), + current_user: schemas.User = Depends(has_privilege("LLMConfig.Audit")), llm_repo: LLMModelConfigsRepository = Depends(get_repository(LLMModelConfigsRepository)) ) -> schemas.LLMModelConfigInheritedResponse: """ Get user's effective LLM model configurations (own + inherited from groups). - Required privilege: User.Audit + Required privilege: LLMConfig.Audit """ try: @@ -97,7 +97,7 @@ async def get_user_llm_model_configs( @router.get( "/users/{user_id}/llm-model-configs/own", response_model=List[schemas.LLMModelConfigResponse], - dependencies=[Depends(has_privilege("User.Audit"))] + dependencies=[Depends(has_privilege("LLMConfig.Audit"))] ) async def get_user_own_llm_model_configs( user_id: UUID, @@ -106,7 +106,7 @@ async def get_user_own_llm_model_configs( """ Get user's own LLM model configurations (excluding inherited ones). - Required privilege: User.Audit + Required privilege: LLMConfig.Audit """ try: @@ -137,7 +137,7 @@ async def get_user_own_llm_model_configs( @router.get( "/users/{user_id}/llm-model-configs/default", response_model=schemas.LLMModelConfigResponse, - dependencies=[Depends(has_privilege("User.Audit"))] + dependencies=[Depends(has_privilege("LLMConfig.Audit"))] ) async def get_user_default_llm_model_config( user_id: UUID, @@ -146,7 +146,7 @@ async def get_user_default_llm_model_config( """ Get user's default LLM model configuration. - Required privilege: User.Audit + Required privilege: LLMConfig.Audit """ try: @@ -183,7 +183,7 @@ async def get_user_default_llm_model_config( "/users/{user_id}/llm-model-configs", response_model=schemas.LLMModelConfigResponse, status_code=status.HTTP_201_CREATED, - dependencies=[Depends(has_privilege("User.Modify"))] + dependencies=[Depends(has_privilege("LLMConfig.Modify"))] ) async def create_user_llm_model_config( user_id: UUID, @@ -194,7 +194,7 @@ async def create_user_llm_model_config( """ Create a new LLM model configuration for a user. - Required privilege: User.Modify + Required privilege: LLMConfig.Modify IMPORTANT: context_limit is REQUIRED. Unit is K tokens (e.g., 128 = 128K = 128,000 tokens). Please check your model provider's documentation for the current context window size. @@ -249,7 +249,7 @@ async def create_user_llm_model_config( @router.put( "/users/{user_id}/llm-model-configs/{config_id}", response_model=schemas.LLMModelConfigResponse, - dependencies=[Depends(has_privilege("User.Modify"))] + dependencies=[Depends(has_privilege("LLMConfig.Modify"))] ) async def update_user_llm_model_config( user_id: UUID, @@ -261,7 +261,7 @@ async def update_user_llm_model_config( Update a user's LLM model configuration. Supports optimistic locking via expected_version field. - Required privilege: User.Modify + Required privilege: LLMConfig.Modify """ try: @@ -317,7 +317,7 @@ async def update_user_llm_model_config( @router.delete( "/users/{user_id}/llm-model-configs/{config_id}", status_code=status.HTTP_204_NO_CONTENT, - dependencies=[Depends(has_privilege("User.Modify"))] + dependencies=[Depends(has_privilege("LLMConfig.Modify"))] ) async def delete_user_llm_model_config( user_id: UUID, @@ -327,7 +327,7 @@ async def delete_user_llm_model_config( """ Delete a user's LLM model configuration. - Required privilege: User.Modify + Required privilege: LLMConfig.Modify """ try: @@ -350,7 +350,7 @@ async def delete_user_llm_model_config( @router.put( "/users/{user_id}/llm-model-configs/default/{config_id}", response_model=schemas.LLMModelConfigResponse, - dependencies=[Depends(has_privilege("User.Modify"))] + dependencies=[Depends(has_privilege("LLMConfig.Modify"))] ) async def set_user_default_llm_model_config( user_id: UUID, @@ -360,7 +360,7 @@ async def set_user_default_llm_model_config( """ Set a user's default LLM model configuration. - Required privilege: User.Modify + Required privilege: LLMConfig.Modify """ try: diff --git a/gns3server/api/routes/controller/netmiko.py b/gns3server/api/routes/controller/netmiko.py new file mode 100644 index 000000000..1fd2b907d --- /dev/null +++ b/gns3server/api/routes/controller/netmiko.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# +# Copyright (C) 2020 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +API routes for Netmiko metadata. +""" + +from typing import Optional + +from fastapi import APIRouter, Depends, HTTPException, status + +from gns3server import schemas + +from .dependencies.authentication import get_current_active_user + +import logging + +log = logging.getLogger(__name__) + + +router = APIRouter() + +# Computed once per process: the list only changes if the installed +# Netmiko library changes, which requires a server restart anyway. +_device_types_cache: Optional[schemas.NetmikoDeviceTypeList] = None + + +def _load_netmiko_device_types() -> schemas.NetmikoDeviceTypeList: + """ + Build the list of device types supported by the installed Netmiko library. + + Imports Netmiko and the GNS3-copilot custom drivers (which register + additional 'gns3_*' device types into Netmiko's CLASS_MAPPER on import), + then filters out the '_ssh' aliases and the 'autodetect' + pseudo device type. + + Raises: + ImportError: If Netmiko is not installed (ai-features extra). + """ + + import importlib + + import netmiko + # "from netmiko import ssh_dispatcher" is shadowed by a function of the same + # name in netmiko's __init__, so import the module through importlib + sd = importlib.import_module("netmiko.ssh_dispatcher") + + # Importing the package auto-registers all custom drivers (in case nothing + # imported them yet); failures are logged by the package itself, do not + # fail the whole endpoint. + try: + from gns3server.agent.gns3_copilot.utils import custom_netmiko # noqa: F401 + except Exception as e: + log.warning(f"Could not register GNS3-copilot custom Netmiko drivers: {e}") + + # Custom drivers all use the 'gns3_' prefix by convention, which is more + # reliable than diffing CLASS_MAPPER around the import: the drivers may + # already be registered when the copilot package got imported at startup. + device_types = [ + schemas.NetmikoDeviceType(name=name, telnet="_telnet" in name, custom=name.startswith("gns3_")) + for name in sorted(sd.CLASS_MAPPER.keys()) + if not name.endswith("_ssh") and name != "autodetect" + ] + return schemas.NetmikoDeviceTypeList(netmiko_version=netmiko.__version__, device_types=device_types) + + +@router.get( + "/device_types", + response_model=schemas.NetmikoDeviceTypeList, + dependencies=[Depends(get_current_active_user)] +) +def get_netmiko_device_types() -> schemas.NetmikoDeviceTypeList: + """ + Return the device types supported by the Netmiko library installed on this server. + + Required privilege: None (authenticated users only) + """ + + global _device_types_cache + if _device_types_cache is None: + try: + _device_types_cache = _load_netmiko_device_types() + except ImportError: + raise HTTPException( + status_code=status.HTTP_501_NOT_IMPLEMENTED, + detail="Netmiko is not available. Install AI dependencies with: pip install gns3-server[ai-features]" + ) + return _device_types_cache diff --git a/gns3server/api/routes/controller/nodes.py b/gns3server/api/routes/controller/nodes.py index 0ee624ce4..ed17a703e 100644 --- a/gns3server/api/routes/controller/nodes.py +++ b/gns3server/api/routes/controller/nodes.py @@ -24,6 +24,7 @@ import ipaddress from fastapi import APIRouter, Depends, WebSocket, WebSocketDisconnect, Request, Response, status, Query, HTTPException from fastapi.encoders import jsonable_encoder +from fastapi.responses import StreamingResponse from fastapi.routing import APIRoute from typing import List, Callable, Optional from uuid import UUID @@ -242,14 +243,25 @@ async def reload_all_nodes(project: Project = Depends(dep_project)) -> None: raise +# Node types that need live host interface data from compute +_HOST_INTERFACE_NODE_TYPES = {"cloud", "nat"} + + @router.get("/{node_id}", response_model=schemas.Node, dependencies=[Depends(has_privilege("Node.Audit"))]) -def get_node(node: Node = Depends(dep_node)) -> schemas.Node: +async def get_node(node: Node = Depends(dep_node)) -> schemas.Node: """ Return a node from a given project. Required privilege: Node.Audit """ + if node.node_type in _HOST_INTERFACE_NODE_TYPES: + try: + response = await node.get() + await node.parse_node_response(response.json) + except Exception: + # If compute is unreachable, still return cached data + log.warning(f"Could not refresh node {node.id} from compute, returning cached data") return node.asdict() @@ -360,14 +372,13 @@ async def suspend_node(node: Node = Depends(dep_node)) -> None: """ Suspend a node. + Node types without suspend support return a 405 error instead of a + silent no-op, so the caller cannot mistake it for a suspended node. + Required privilege: Node.PowerMgmt """ - try: - await node.suspend() - except HTTPException as e: - if not e.status_code == status.HTTP_405_METHOD_NOT_ALLOWED: - raise + await node.suspend() @router.post( @@ -523,17 +534,30 @@ async def delete_disk_image( @router.get("/{node_id}/files", response_model=List[schemas.NodeFile], dependencies=[Depends(has_privilege("Node.Audit"))]) -async def list_node_files(node: Node = Depends(dep_node)) -> List[schemas.NodeFile]: +async def list_node_files( + node: Node = Depends(dep_node), + path: str = Query("", description="Subdirectory path within node directory"), + recursive: bool = Query(False, description="Recursively list all files") +) -> List[schemas.NodeFile]: """ List files in a node directory with detailed metadata. + By default lists only the current directory level (non-recursive). + Use recursive=true for a full recursive listing. + Required privilege: Node.Audit """ node_type = node.node_type + url = f"/projects/{node.project.id}/nodes/{node_type}/{node.id}/files" + params = {} + if path: + params["path"] = path + if recursive: + params["recursive"] = "true" res = await node.compute.http_query( - "GET", - f"/projects/{node.project.id}/nodes/{node_type}/{node.id}/files", + "GET", url, + params=params if params else None, timeout=None ) return res.json @@ -550,14 +574,32 @@ async def get_file(file_path: str, node: Node = Depends(dep_node)) -> Response: path = force_unix_path(file_path) # Raise error if user try to escape - if path[0] == ".": + if path.startswith(".."): raise ControllerForbiddenError("It is forbidden to get a file outside the project directory") node_type = node.node_type path = f"/project-files/{node_type}/{node.id}/{path}" - res = await node.compute.http_query("GET", f"/projects/{node.project.id}/files{path}", timeout=None, raw=True) - return Response(res.body, media_type="application/octet-stream", status_code=res.status) + compute_resp = await node.compute.http_query( + "GET", f"/projects/{node.project.id}/files{path}", + timeout=None, stream=True + ) + + async def streamer(): + try: + async for chunk in compute_resp.content.iter_chunked(65536): + yield chunk + except (IOError, OSError, asyncio.TimeoutError) as e: + log.error(f"Error streaming file '{path}' from compute: {e}") + raise + finally: + compute_resp.close() + + return StreamingResponse( + streamer(), + media_type="application/octet-stream", + status_code=compute_resp.status, + ) @router.post( @@ -575,15 +617,43 @@ async def post_file(file_path: str, request: Request, node: Node = Depends(dep_n path = force_unix_path(file_path) # Raise error if user try to escape - if path[0] == ".": + if path.startswith(".."): raise ControllerForbiddenError("Cannot write outside the node directory") node_type = node.node_type path = f"/project-files/{node_type}/{node.id}/{path}" - data = await request.body() # FIXME: are we handling timeout or large files correctly? - await node.compute.http_query("POST", f"/projects/{node.project.id}/files{path}", data=data, timeout=None, raw=True) - # FIXME: response with correct status code (from compute) + # Stream request body directly to compute node + await node.compute.http_query( + "POST", f"/projects/{node.project.id}/files{path}", + data=request.stream(), timeout=None + ) + + +@router.delete( + "/{node_id}/files/{file_path:path}", + status_code=status.HTTP_204_NO_CONTENT, + dependencies=[Depends(has_privilege("Node.Modify"))] +) +async def delete_node_file(file_path: str, node: Node = Depends(dep_node)) -> None: + """ + Delete a file from the node directory. + + Required privilege: Node.Modify + """ + + path = force_unix_path(file_path) + + if path.startswith(".."): + raise ControllerForbiddenError("It is forbidden to delete a file outside the project directory") + + node_type = node.node_type + path = f"/project-files/{node_type}/{node.id}/{path}" + + await node.compute.http_query( + "DELETE", f"/projects/{node.project.id}/files{path}", + timeout=None + ) @router.websocket("/{node_id}/console/ws") @@ -623,13 +693,19 @@ async def ws_console( async def ws_receive(ws_console_compute): """ Receive WebSocket data from client and forward to compute console WebSocket. + Text frames carry terminal data; binary frames carry client control + messages (e.g. terminal size), forwarded as-is. """ try: while True: - data = await websocket.receive_text() - if data: - await ws_console_compute.send_str(data) + msg = await websocket.receive() + if msg["type"] == "websocket.disconnect": + break + if "text" in msg and msg["text"]: + await ws_console_compute.send_str(msg["text"]) + elif "bytes" in msg and msg["bytes"]: + await ws_console_compute.send_bytes(msg["bytes"]) except WebSocketDisconnect: await ws_console_compute.close() log.info( @@ -660,6 +736,12 @@ async def ws_console( await websocket.send_bytes(msg.data) elif msg.type == aiohttp.WSMsgType.ERROR: break + except WebSocketDisconnect: + # the client disconnected while the compute was still streaming console output + log.info( + f"Client {websocket.client.host}:{websocket.client.port} has disconnected from controller" + f" console WebSocket" + ) except aiohttp.ClientError as e: log.error(f"Client error received when forwarding to compute console WebSocket: {e}") @@ -735,6 +817,12 @@ async def vnc_console( await websocket.send_bytes(msg.data) elif msg.type == aiohttp.WSMsgType.ERROR: break + except WebSocketDisconnect: + # the client disconnected while the compute was still streaming VNC console output + log.info( + f"Client {websocket.client.host}:{websocket.client.port} has disconnected from controller" + f" VNC console WebSocket" + ) except aiohttp.ClientError as e: log.error(f"Client error received when forwarding to compute VNC console WebSocket: {e}") diff --git a/gns3server/api/routes/controller/pools.py b/gns3server/api/routes/controller/pools.py index 6c8e865fa..0d79e39bc 100644 --- a/gns3server/api/routes/controller/pools.py +++ b/gns3server/api/routes/controller/pools.py @@ -147,10 +147,38 @@ async def delete_resource_pool( if not resource_pool: raise ControllerNotFoundError(f"Resource pool '{resource_pool_id}' not found") + # Check if there are any ACE configurations using this resource pool path + pool_path = f"/pools/{resource_pool_id}" + using_aces = await rbac_repo.get_aces_for_path(pool_path) + + if using_aces: + # Build detailed error message with ACE information + ace_details = [] + for ace in using_aces: + identifier = "" + if ace.ace_type == "user" and ace.user: + identifier = f"User '{ace.user.username}'" + elif ace.ace_type == "group" and ace.group: + identifier = f"Group '{ace.group.name}'" + else: + identifier = f"{ace.ace_type.capitalize()} '{ace.user_id or ace.group_id}'" + + if ace.role: + identifier += f" with role '{ace.role.name}'" + + ace_details.append(f"- {identifier}") + + error_message = ( + f"Resource pool '{resource_pool.name}' cannot be deleted because it is being used by {len(using_aces)} ACE configuration(s):\n" + + "\n".join(ace_details) + + f"\n\nPlease delete the ACE configuration(s) for resource pool '{resource_pool.name}' first." + ) + raise ControllerBadRequestError(error_message) + success = await pools_repo.delete_resource_pool(resource_pool_id) if not success: raise ControllerError(f"Resource pool '{resource_pool_id}' could not be deleted") - await rbac_repo.delete_all_ace_starting_with_path(f"/pools/{resource_pool_id}") + await rbac_repo.delete_all_ace_starting_with_path(pool_path) @router.get( diff --git a/gns3server/api/routes/controller/projects.py b/gns3server/api/routes/controller/projects.py index 99861230c..f9012a8ef 100644 --- a/gns3server/api/routes/controller/projects.py +++ b/gns3server/api/routes/controller/projects.py @@ -40,6 +40,7 @@ from uuid import UUID from gns3server import schemas from gns3server.controller import Controller from gns3server.controller.project import Project +from gns3server.controller.link import _UNSET from gns3server.controller.controller_error import ControllerError, ControllerBadRequestError from gns3server.controller.import_project import import_project as import_controller_project from gns3server.controller.export_project import export_project as export_controller_project @@ -76,7 +77,6 @@ def dep_project(project_id: UUID) -> Project: async def get_projects( current_user: schemas.User = Depends(get_current_active_user), rbac_repo: RbacRepository = Depends(get_repository(RbacRepository)), - pools_repo: ResourcePoolsRepository = Depends(get_repository(ResourcePoolsRepository)) ) -> List[schemas.Project]: """ Return all projects. @@ -86,19 +86,32 @@ async def get_projects( controller = Controller.instance() projects = [] + seen_project_ids = set() # track seen projects to avoid duplicates if current_user.is_superadmin: # super admin sees all projects return [p.asdict() for p in controller.projects.values()] - elif await rbac_repo.check_user_has_privilege(current_user.user_id, "/projects", "Project.Audit"): - # user with Project.Audit privilege on '/projects' sees all projects except those in resource pools - project_ids_in_pools = [str(r.resource_id) for r in await pools_repo.get_resources() if r.resource_type == "project"] - projects.extend([p.asdict() for p in controller.projects.values() if p.id not in project_ids_in_pools]) - # user with Project.Audit privilege on resource pools sees the projects in these pools - user_pool_resources = await rbac_repo.get_user_pool_resources(current_user.user_id, "Project.Audit") - project_ids_in_pools = [str(r.resource_id) for r in user_pool_resources if r.resource_type == "project"] - projects.extend([p.asdict() for p in controller.projects.values() if p.id in project_ids_in_pools]) + # Batch ACE + resource pool check (3 DB queries regardless of project count) + all_project_ids = list(controller.projects.keys()) + direct_ace_ids, pool_accessible_ids = await rbac_repo.get_accessible_project_ids( + current_user.user_id, "Project.Audit", all_project_ids + ) + + # Step 2: Filter direct ACE projects by created_by + # Direct project sharing is only available through resource pools + for p in controller.projects.values(): + if p.id in direct_ace_ids and p.created_by == current_user.username: + if p.id not in seen_project_ids: + projects.append(p.asdict()) + seen_project_ids.add(p.id) + + # Step 3: Resource pool projects (no created_by filter) + for p in controller.projects.values(): + if p.id in pool_accessible_ids: + if p.id not in seen_project_ids: + projects.append(p.asdict()) + seen_project_ids.add(p.id) return projects @@ -191,6 +204,161 @@ def get_project_stats(project: Project = Depends(dep_project)) -> dict: return project.stats() +@router.get("/{project_id}/markers", dependencies=[Depends(has_privilege("Project.Audit"))]) +def get_project_markers(project: Project = Depends(dep_project)) -> dict: + """ + Return all traffic-insight markers across every link in the project. + + Each entry is keyed ``"{link_id}/{marker_name}"`` and carries the + marker's BPF, tag, color, enabled flag, plus its parent ``link_id`` + and capture-side ``node_id`` for frontend filtering / grouping. + + Required privilege: Project.Audit + """ + + return project.markers + + +# --------------------------------------------------------------------------- +# Project-level marker definitions (global rules inherited by every link) +# --------------------------------------------------------------------------- + +@router.get( + "/{project_id}/marker-definitions", + dependencies=[Depends(has_privilege("Project.Audit"))] +) +def get_marker_definitions(project: Project = Depends(dep_project)) -> dict: + """ + Return all project-level marker definitions with their bound link IDs. + + Required privilege: Project.Audit + """ + + result = {} + for name, d in project.marker_definitions.items(): + # Collect which links currently carry an inherited copy. + bound = [ + lid for lid, link in project.links.items() + if f"global-{name}" in link.markers + and link.markers[f"global-{name}"].get("inherited_from") == name + ] + result[name] = {**d, "link_ids": bound} + return result + + +@router.post( + "/{project_id}/marker-definitions", + status_code=status.HTTP_201_CREATED, + dependencies=[Depends(has_privilege("Project.Modify"))] +) +async def create_marker_definition( + def_data: schemas.MarkerDefinitionCreate, + project: Project = Depends(dep_project) +) -> dict: + """ + Create a project-level marker definition and fan out to every link. + + Required privilege: Project.Modify + """ + + if def_data.name and def_data.name.lower().startswith("global"): + raise ControllerError('Names starting with "global" are reserved for inherited markers') + name = def_data.name or f"def-{project.id[:8]}" + await project.create_marker_definition( + name=name, + bpf=def_data.bpf, + tag=def_data.tag, + direction=def_data.direction, + color=def_data.color, + highlight_duration=def_data.highlight_duration, + data_link_type=def_data.data_link_type, + ) + return project.marker_definitions.get(name, {}) + + +@router.put( + "/{project_id}/marker-definitions/{def_name}", + dependencies=[Depends(has_privilege("Project.Modify"))] +) +async def update_marker_definition( + def_name: str, + def_data: schemas.MarkerDefinitionCreate, + project: Project = Depends(dep_project) +) -> dict: + """ + Update a marker definition and sync all inherited copies on every link. + + Required privilege: Project.Modify + """ + + await project.update_marker_definition( + name=def_name, + bpf=def_data.bpf if def_data.bpf else None, + tag=def_data.tag, + direction=def_data.direction if "direction" in def_data.model_fields_set else _UNSET, + color=def_data.color, + highlight_duration=def_data.highlight_duration, + data_link_type=def_data.data_link_type if "data_link_type" in def_data.model_fields_set else _UNSET, + ) + return project.marker_definitions.get(def_name, {}) + + +@router.post( + "/{project_id}/marker-definitions/{def_name}/pause", + status_code=status.HTTP_204_NO_CONTENT, + dependencies=[Depends(has_privilege("Project.Modify"))] +) +async def pause_marker_definition( + def_name: str, + project: Project = Depends(dep_project) +) -> None: + """ + Pause a definition: toggle off every inherited ``global-{def_name}`` copy + on every link (uBridge ``enable_packet_filter off``, instant — no NIO + rebuild). The definition's ``paused`` flag is persisted, so links created + later inherit it already paused. + + Required privilege: Project.Modify + """ + + await project.pause_marker_definition(def_name) + + +@router.post( + "/{project_id}/marker-definitions/{def_name}/resume", + status_code=status.HTTP_204_NO_CONTENT, + dependencies=[Depends(has_privilege("Project.Modify"))] +) +async def resume_marker_definition( + def_name: str, + project: Project = Depends(dep_project) +) -> None: + """Resume a paused definition (toggle on every inherited copy). + + Required privilege: Project.Modify + """ + + await project.resume_marker_definition(def_name) + + +@router.delete( + "/{project_id}/marker-definitions/{def_name}", + status_code=status.HTTP_204_NO_CONTENT, + dependencies=[Depends(has_privilege("Project.Modify"))] +) +async def delete_marker_definition( + def_name: str, + project: Project = Depends(dep_project) +) -> None: + """ + Delete a marker definition and remove all inherited copies from every link. + + Required privilege: Project.Modify + """ + + await project.delete_marker_definition(def_name) + + @router.post( "/{project_id}/close", status_code=status.HTTP_204_NO_CONTENT, @@ -317,6 +485,38 @@ async def project_ws_notifications( await project.close() +@router.websocket("/{project_id}/notifications/markers/ws") +async def project_marker_ws_notifications( + project_id: UUID, + websocket: WebSocket, + current_user: schemas.User = Depends(has_privilege_on_websocket("Project.Audit")) +) -> None: + """ + Receive marker notifications (e.g. marker.match) for a project on a + dedicated WebSocket, separate from the main project stream so high-frequency + marker.matches do not block topology events (node.*/link.*). + + Required privilege: Project.Audit + """ + + if current_user is None: + return + + controller = Controller.instance() + project = controller.get_project(str(project_id)) + + log.info(f"New client has connected to the marker notification stream for project ID '{project.id}' (WebSocket method)") + try: + with controller.notification.project_marker_queue(project.id) as queue: + while True: + notification = await queue.get_json(5) + await websocket.send_text(notification) + except (ConnectionClosed, WebSocketDisconnect): + log.info(f"Client has disconnected from the marker notification stream for project ID '{project.id}' (WebSocket method)") + except WebSocketException as e: + log.warning(f"Error while sending marker event to WebSocket client: {e}") + + @router.get("/{project_id}/export", dependencies=[Depends(has_privilege("Project.Audit"))]) async def export_project( project: Project = Depends(dep_project), @@ -546,6 +746,21 @@ async def get_file(file_path: str, project: Project = Depends(dep_project)) -> F return FileResponse(path, media_type="application/octet-stream") +@router.get("/{project_id}/gns3file", dependencies=[Depends(has_privilege("Project.Audit"))]) +async def get_project_gns3_file(project: Project = Depends(dep_project)) -> FileResponse: + """ + Return the .gns3 topology file of a project. + + Required privilege: Project.Audit + """ + + path = project.topology_file + if not os.path.exists(path): + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) + + return FileResponse(path, media_type="application/json") + + @router.post( "/{project_id}/files/{file_path:path}", status_code=status.HTTP_204_NO_CONTENT, @@ -599,9 +814,11 @@ async def create_node_from_template( """ template = await TemplatesService(templates_repo).get_template(template_id) + controller = Controller.instance() project = controller.get_project(str(project_id)) + node = await project.add_node_from_template( - template, x=template_usage.x, y=template_usage.y, compute_id=template_usage.compute_id + template, x=template_usage.x, y=template_usage.y, name=template_usage.name, compute_id=template_usage.compute_id ) return node.asdict() diff --git a/gns3server/api/routes/controller/settings.py b/gns3server/api/routes/controller/settings.py new file mode 100644 index 000000000..9924435ac --- /dev/null +++ b/gns3server/api/routes/controller/settings.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python +# +# Copyright (C) 2026 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +API routes for managing the server settings (gns3_server.conf). +""" + +from fastapi import APIRouter, Depends, HTTPException + +from pydantic import ValidationError + +from gns3server import schemas +from gns3server.config import Config, ConfigConflictError +from gns3server.controller import Controller +from gns3server.controller.controller_error import ControllerBadRequestError, ControllerError +from gns3server.schemas.controller.settings import SECRET_MASK + +from .dependencies.rbac import has_privilege + +import logging + +log = logging.getLogger(__name__) + +router = APIRouter() + +# Settings that are only consumed at startup (or once, by singletons) and +# therefore require a server restart to take effect: +# - host/port/protocol/SSL are bound when the server starts +# - paths are used to initialize controller resources +# - port ranges are read once by the PortManager singleton +# - default admin credentials are only used to seed the users database +# - builtin templates/appliances and the skills repository are installed at startup +RESTART_REQUIRED = frozenset({ + "Server.host", + "Server.port", + "Server.protocol", + "Server.enable_ssl", + "Server.certfile", + "Server.certkey", + "Server.secrets_dir", + "Server.images_path", + "Server.projects_path", + "Server.appliances_path", + "Server.symbols_path", + "Server.configs_path", + "Server.resources_path", + "Server.console_start_port_range", + "Server.console_end_port_range", + "Server.vnc_console_start_port_range", + "Server.vnc_console_end_port_range", + "Server.udp_start_port_range", + "Server.udp_end_port_range", + "Server.enable_builtin_templates", + "Server.install_builtin_appliances", + "Server.skills_repo_url", + "Server.skills_repo_branch", + "Server.skills_auto_update", + "Server.ubridge_path", + "Controller.default_admin_username", + "Controller.default_admin_password", +}) + +# never expose these sections (deprecated) nor the secret managed outside +# the configuration file; must match the response model in schemas.controller.settings +_DUMP_EXCLUDE = { + "VirtualBox": True, + "VMware": True, + "Controller": {"jwt_secret_key": True}, +} + + +def _current_settings_response() -> dict: + + settings = Config.instance().settings + return settings.model_dump(mode="json", exclude=_DUMP_EXCLUDE) + + +@router.get("", response_model=schemas.SettingsResponse, + dependencies=[Depends(has_privilege("Server.Audit"))], + responses={401: {"model": schemas.ErrorMessage}, 403: {"model": schemas.ErrorMessage}}) +async def get_server_settings() -> schemas.SettingsResponse: + """ + Return the server settings. + + The values reflect the running configuration (which may include command + line overrides). Secret fields are masked. + """ + + return schemas.SettingsResponse.model_validate(_current_settings_response()) + + +@router.put("", response_model=schemas.SettingsUpdateResponse, + dependencies=[Depends(has_privilege("Server.Modify"))], + responses={ + 400: {"model": schemas.ErrorMessage}, + 401: {"model": schemas.ErrorMessage}, + 403: {"model": schemas.ErrorMessage}, + 409: {"model": schemas.ErrorMessage}, + 422: {"model": schemas.ErrorMessage}, + }) +async def update_server_settings(settings_update: schemas.SettingsUpdate) -> schemas.SettingsUpdateResponse: + """ + Update the server settings and persist them to the configuration file. + + Only the submitted options are modified. A JSON null removes an option + from the configuration file (restoring its default). Secret fields set + to an empty string or left at their masked value are considered unchanged. + """ + + changes = { + section: options + for section, options in settings_update.model_dump(exclude_unset=True).items() + if options + } + + # masked or empty secrets mean "unchanged": never write them back + for section, option in (("Server", "compute_password"), ("Controller", "default_admin_password")): + if section in changes and changes[section].get(option) in ("", SECRET_MASK): + del changes[section][option] + + if not changes: + # nothing to change, don't touch the file + data = _current_settings_response() + data["restart_required"] = [] + return schemas.SettingsUpdateResponse.model_validate(data) + + try: + changed = Config.instance().update_config(changes) + except ValidationError as e: + raise ControllerBadRequestError(f"Invalid server settings: {e}") + except ConfigConflictError as e: + raise HTTPException(status_code=409, detail=str(e)) + except OSError as e: + raise ControllerError(f"Could not write the configuration file: {e}") + + restart_required = sorted(set(changed) & RESTART_REQUIRED) + + # only send metadata, never settings values (they may contain secrets) + controller = Controller.instance() + if controller is not None: + controller.notification.controller_emit( + "settings.updated", + {"changed": changed, "restart_required": restart_required} + ) + + data = _current_settings_response() + data["restart_required"] = restart_required + return schemas.SettingsUpdateResponse.model_validate(data) diff --git a/gns3server/api/routes/controller/templates.py b/gns3server/api/routes/controller/templates.py index a8d0be8a3..05fa6482c 100644 --- a/gns3server/api/routes/controller/templates.py +++ b/gns3server/api/routes/controller/templates.py @@ -35,7 +35,7 @@ from gns3server.db.repositories.templates import TemplatesRepository from gns3server.services.templates import TemplatesService from gns3server.db.repositories.rbac import RbacRepository from gns3server.db.repositories.images import ImagesRepository -from gns3server.controller.controller_error import ControllerError +from gns3server.controller.controller_error import ControllerError, ControllerBadRequestError from gns3server.utils.images import get_builtin_disks from .dependencies.authentication import get_current_active_user @@ -230,3 +230,60 @@ async def duplicate_template( template = await TemplatesService(templates_repo).duplicate_template(template_id) return template + +@router.get( + "/{template_id}/base-config/{filename}", + dependencies=[Depends(has_privilege("Template.Audit"))] +) +async def get_base_config( + template_id: UUID, + filename: str, + templates_repo: TemplatesRepository = Depends(get_repository(TemplatesRepository)), +): + + service = TemplatesService(templates_repo) + await service.get_template(template_id) + content = service.get_file(str(template_id), filename) + + return { + "template_id": str(template_id), + "filename": os.path.basename(filename), + "content": content + } + + +@router.put( + "/{template_id}/base-config/{filename}", + dependencies=[Depends(has_privilege("Template.Modify"))] +) +async def update_base_config( + template_id: UUID, + filename: str, + body: dict, + templates_repo: TemplatesRepository = Depends(get_repository(TemplatesRepository)), +): + if not body or "content" not in body: + raise ControllerBadRequestError("Missing 'content' field") + + service = TemplatesService(templates_repo) + await service.get_template(template_id) + service.update_file(str(template_id), filename, body["content"]) + + return { + "template_id": str(template_id), + "filename": os.path.basename(filename), + "content": body["content"] + } + + +@router.get( + "/{template_id}/base-configs", + dependencies=[Depends(has_privilege("Template.Audit"))] +) +async def list_base_configs( + template_id: UUID, + templates_repo: TemplatesRepository = Depends(get_repository(TemplatesRepository)), +): + service = TemplatesService(templates_repo) + await service.get_template(template_id) + return service.list_files(str(template_id)) diff --git a/gns3server/api/routes/controller/users.py b/gns3server/api/routes/controller/users.py index 1a19da98f..97fedd47f 100644 --- a/gns3server/api/routes/controller/users.py +++ b/gns3server/api/routes/controller/users.py @@ -67,7 +67,8 @@ async def login( token = schemas.Token( access_token=auth_service.create_access_token(user.username, token_version=user.token_version), - token_type="bearer" + token_type="bearer", + refresh_token=auth_service.create_refresh_token(user.username, token_version=user.token_version), ) return token @@ -92,11 +93,55 @@ async def authenticate( token = schemas.Token( access_token=auth_service.create_access_token(user.username, token_version=user.token_version), - token_type="bearer" + token_type="bearer", + refresh_token=auth_service.create_refresh_token(user.username, token_version=user.token_version), ) return token +@router.post("/refresh", response_model=schemas.Token) +async def refresh_access_token( + request: schemas.RefreshTokenRequest, + users_repo: UsersRepository = Depends(get_repository(UsersRepository)), +) -> schemas.Token: + """ + Exchange a refresh token for a new access token. + + Public endpoint — the refresh token itself proves identity. Respects the + user's token_version, so logout (which increments it) invalidates all + outstanding refresh tokens. Refresh tokens are stateless JWTs with a + longer expiry (default 30 days). Stolen tokens remain valid until their + `exp` or until logout — no replay protection without a server-side table. + """ + + token_data = auth_service.get_token_data(request.refresh_token) + if token_data.token_use != "refresh": + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Invalid refresh token", + headers={"WWW-Authenticate": "Bearer"}, + ) + user = await users_repo.get_user_by_username(token_data.username) + if user is None or not user.is_active: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Could not validate credentials", + headers={"WWW-Authenticate": "Bearer"}, + ) + if token_data.token_version != user.token_version: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail=f"Token has been revoked for '{token_data.username}'", + headers={"WWW-Authenticate": "Bearer"}, + ) + + return schemas.Token( + access_token=auth_service.create_access_token(user.username, token_version=user.token_version), + token_type="bearer", + refresh_token=auth_service.create_refresh_token(user.username, token_version=user.token_version), + ) + + @router.post("/logout", status_code=status.HTTP_204_NO_CONTENT) async def logout( current_user: schemas.User = Depends(get_current_active_user), diff --git a/gns3server/api/server.py b/gns3server/api/server.py index 196ddb504..3b3218380 100644 --- a/gns3server/api/server.py +++ b/gns3server/api/server.py @@ -47,6 +47,24 @@ from gns3server.api.routes import controller, index from gns3server.api.routes.compute import compute_api from gns3server.core import tasks +# MCP is an optional feature — import only if dependencies are installed +from gns3server.agent import MCP_AVAILABLE + +if MCP_AVAILABLE: + from gns3server.agent import mcp + _mcp_router = mcp.router +else: + from fastapi import APIRouter + + _mcp_router = APIRouter(prefix="/mcp", tags=["MCP"]) + + @_mcp_router.api_route("/{path:path}", methods=["GET", "POST", "DELETE", "PATCH", "PUT"]) + async def mcp_not_available(path: str = ""): + raise HTTPException( + status_code=status.HTTP_501_NOT_IMPLEMENTED, + detail="MCP is not available. Install AI dependencies with: pip install gns3-server[ai-features]" + ) + import logging log = logging.getLogger(__name__) @@ -76,11 +94,18 @@ def get_application() -> FastAPI: application.mount("/static", StaticFiles(packages=[('gns3server', 'static')], html=True), name="static") application.mount("/v3/compute", compute_api, name="compute") + # Register MCP routes (stub returns 501 if MCP dependencies are not installed) + application.include_router(_mcp_router, prefix="/v3", tags=["MCP"]) + return application app = get_application() +# Register MCP SSE transport routes (Starlette-level, for raw ASGI access) +if MCP_AVAILABLE: + mcp.register_starlette_routes(app) + # Monkey Patch uvicorn signal handler to detect the application is shutting down app.state.exiting = False unicorn_exit_handler = UvicornServer.handle_exit @@ -208,15 +233,3 @@ async def validation_exception_handler(request: Request, exc: RequestValidationE status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, content={"message": str(exc)} ) - -# FIXME: do not use this middleware since it creates issue when using StreamingResponse -# see https://starlette-context.readthedocs.io/en/latest/middleware.html#why-are-there-two-middlewares-that-do-the-same-thing - -# @app.middleware("http") -# async def add_extra_headers(request: Request, call_next): -# start_time = time.time() -# response = await call_next(request) -# process_time = time.time() - start_time -# response.headers["X-Process-Time"] = str(process_time) -# response.headers["X-GNS3-Server-Version"] = f"{__version__}" -# return response diff --git a/gns3server/appliances/armbian.gns3a b/gns3server/appliances/armbian.gns3a new file mode 100644 index 000000000..221fe7083 --- /dev/null +++ b/gns3server/appliances/armbian.gns3a @@ -0,0 +1,58 @@ +{ + "appliance_id": "b3b90fde-143a-4129-8031-ccbba73c5e02", + "name": "armbian", + "category": "guest", + "description": "A highly optimized base operating system specialized for single board computers (SBCs) and its extensive build framework.", + "vendor_name": "The Armbian team", + "vendor_url": "https://armbian.com/", + "documentation_url": "https://docs.armbian.com/", + "product_name": "Armbian UEFI x86", + "product_url": "https://armbian.com/boards/uefi-x86", + "registry_version": 6, + "status": "stable", + "maintainer": "GNS3 Team", + "maintainer_email": "developers@gns3.net", + "usage": "By first login you create root password and new sudo user.\n\nBoot disk from UEFI shell, type: FS0:EFI\\BOOT\\BOOTX64 and press ", + "port_name_format": "Ethernet{0}", + "qemu": { + "adapter_type": "virtio-net-pci", + "adapters": 2, + "ram": 256, + "hda_disk_interface": "virtio", + "arch": "x86_64", + "console_type": "spice+agent", + "boot_priority": "c", + "kvm": "require", + "options": "-nographic" + }, + "images": [ + { + "filename": "OVMF-edk2-stable202305.fd", + "version": "stable202305", + "md5sum": "6c4cf1519fec4a4b95525d9ae562963a", + "filesize": 4194304, + "download_url": "https://sourceforge.net/projects/gns-3/files/Qemu%20Appliances/", + "direct_download_url": "https://sourceforge.net/projects/gns-3/files/Qemu%20Appliances/OVMF-edk2-stable202305.fd.zip/download", + "compression": "zip" + }, + { + "filename": "Armbian_26.5.1_Uefi-x86_trixie_cloud_6.18.32_minimal.img.qcow2", + "version": "Armbian 26.5.1 Minimal (CLI)", + "md5sum": "7f4c915668718d6135406de5a6c4fc30", + "filesize": 877920512, + "download_url": "https://armbian.com/boards/uefi-x86", + "direct_download_url": "https://armbian.atomonetworks.com/dl/uefi-x86/archive/Armbian_26.5.1_Uefi-x86_trixie_cloud_6.18.32_minimal.img.qcow2.xz", + "compression": "xz" + } + + ], + "versions": [ + { + "name": "Armbian 26.5.1 Minimal (CLI)", + "images": { + "bios_image": "OVMF-edk2-stable202305.fd", + "hda_disk_image": "Armbian_26.5.1_Uefi-x86_trixie_cloud_6.18.32_minimal.img.qcow2" + } + } + ] +} diff --git a/gns3server/appliances/cisco-iou-l2.gns3a b/gns3server/appliances/cisco-iou-l2.gns3a index dd5966dd2..5537447ad 100644 --- a/gns3server/appliances/cisco-iou-l2.gns3a +++ b/gns3server/appliances/cisco-iou-l2.gns3a @@ -21,6 +21,12 @@ "startup_config": "iou_l2_base_startup-config.txt" }, "images": [ + { + "filename": "x86_64_crb_linux_l2-adventerprisek9-ms.17.18.2.iol", + "version": "17.18.2", + "md5sum": "0e460cbcfa9e0b76ca400162928fd989", + "filesize": 246471208 + }, { "filename": "x86_64_crb_linux_l2-adventerprisek9-ms.17.16.1a.iol", "version": "17.16.1a", @@ -41,6 +47,12 @@ } ], "versions": [ + { + "name": "17.18.2", + "images": { + "image": "x86_64_crb_linux_l2-adventerprisek9-ms.17.18.2.iol" + } + }, { "name": "17.16.1a", "images": { diff --git a/gns3server/appliances/cisco-iou-l3.gns3a b/gns3server/appliances/cisco-iou-l3.gns3a index ae8273616..a8da82002 100644 --- a/gns3server/appliances/cisco-iou-l3.gns3a +++ b/gns3server/appliances/cisco-iou-l3.gns3a @@ -21,6 +21,12 @@ "startup_config": "iou_l3_base_startup-config.txt" }, "images": [ + { + "filename": "x86_64_crb_linux-adventerprisek9-ms.17.18.2.iol", + "version": "17.18.2", + "md5sum": "34bf876c5b3c60f3ef4eb28eb8a176d7", + "filesize": 293729288 + }, { "filename": "x86_64_crb_linux-adventerprisek9-ms.17.16.1a.iol", "version": "17.16.1a", @@ -41,6 +47,12 @@ } ], "versions": [ + { + "name": "17.18.2", + "images": { + "image": "x86_64_crb_linux-adventerprisek9-ms.17.18.2.iol" + } + }, { "name": "17.16.1a", "images": { diff --git a/gns3server/appliances/fortimanager.gns3a b/gns3server/appliances/fortimanager.gns3a index b5de0248e..ae51af861 100644 --- a/gns3server/appliances/fortimanager.gns3a +++ b/gns3server/appliances/fortimanager.gns3a @@ -51,250 +51,12 @@ "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" }, { - "filename": "FMG_VM64_KVM-v7.4.3-build2487-FORTINET.out.kvm.qcow2", - "version": "7.4.3", - "md5sum": "b01d9f86aa27c538407d518df1326863", - "filesize": 346107904, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v7.4.2-build2397-FORTINET.out.kvm.qcow2", - "version": "7.4.2", - "md5sum": "36371fbf06210ded57c00b2ff290f2c5", - "filesize": 322514944, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v7.4.1-build2308-FORTINET.out.kvm.qcow2", - "version": "7.4.1", - "md5sum": "e542cc8f2d8f46e9c32b783bf31bef39", - "filesize": 309387264, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v7.2.5-build1574-FORTINET.out.kvm.qcow2", - "version": "7.2.5", - "md5sum": "754326845096afd909ec45d98f8d5a83", - "filesize": 278401024, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v7.2.4-build1460-FORTINET.out.kvm.qcow2", - "version": "7.2.4", - "md5sum": "98fa9830d9ecb5911a703d03b80026b6", - "filesize": 261992448, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v7.2.2-build1334-FORTINET.out.kvm.qcow2", - "version": "7.2.2", - "md5sum": "2ff1298257321cd485d2cad91d6ce510", - "filesize": 246083584, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v7.2.1-build1215-FORTINET.out.kvm.qcow2", - "version": "7.2.1", - "md5sum": "1a3eeff1204fa8f4243773f7521e12b5", - "filesize": 242814976, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v7.0.12-build0623-FORTINET.out.kvm.qcow2", - "version": "7.0.12", - "md5sum": "5b6f6a2b8bc00e56337aa7023a9025cf", - "filesize": 249520128, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v7.0.11-build0595-FORTINET.out.kvm.qcow2", - "version": "7.0.11", - "md5sum": "7b166222136e26190159f37cccbaab6e", - "filesize": 249360384, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v7.0.9-build0489-FORTINET.out.kvm.qcow2", - "version": "7.0.9", - "md5sum": "dbeb6a79b6e421000573dbbbdb50b8b5", - "filesize": 247955456, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v7.0.6-build0372-FORTINET.out.kvm.qcow2", - "version": "7.0.6", - "md5sum": "dfa4df9e976ed87e73cb9601a8a70323", - "filesize": 239190016, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v7.0.5-build0365-FORTINET.out.kvm.qcow2", - "version": "7.0.5", - "md5sum": "e8b9c992784cea766b52a427a5fe0279", - "filesize": 237535232, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v6.4.14-build2660-FORTINET.out.kvm.qcow2", - "version": "6.4.14", - "md5sum": "0fe56e363b166c07b710bde795e36049", - "filesize": 219430912, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v6.4.12-build2610-FORTINET.out.kvm.qcow2", - "version": "6.4.12", - "md5sum": "36c0dc531d921e5f1e1e09b030f7c813", - "filesize": 219455488, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v6-build2288-FORTINET.out.kvm.qcow2", - "version": "6.4.5", - "md5sum": "bd2791984b03f55a6825297e83c6576a", - "filesize": 117014528, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v6-build2253-FORTINET.out.kvm.qcow2", - "version": "6.4.4", - "md5sum": "3554a47fde2dc91d17eec16fd0dc10a3", - "filesize": 116621312, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v6-build1183-FORTINET.out.kvm.qcow2", - "version": "6.2.2", - "md5sum": "f5051a8fe49d916bb554b9bae32a1eb4", - "filesize": 139145216, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v6-build1050-FORTINET.out.kvm.qcow2", - "version": "6.2.0", - "md5sum": "c19d2527f91ad1bbafbde5bf08487867", - "filesize": 126894080, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v6-build0349-FORTINET.out.kvm.qcow2", - "version": "6.0.6", - "md5sum": "d03f024c948ba6e2bb9e66c11ca8f34c", - "filesize": 112553984, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v6-build0255-FORTINET.out.kvm.qcow2", - "version": "6.0.3", - "md5sum": "5f34d52d9289b0be2a4c04943446ea39", - "filesize": 115703808, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v6-build0205-FORTINET.out.kvm.qcow2", - "version": "6.0.2", - "md5sum": "8f748649c537d9b5466b24c5b4e62017", - "filesize": 116981760, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v6-build0092-FORTINET.out.kvm.qcow2", - "version": "6.0.0", - "md5sum": "73bfe1bc70124521a524d857646b9c2e", - "filesize": 119066624, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v5-build1631-FORTINET.out.kvm.qcow2", - "version": "5.6.2", - "md5sum": "c81cc247e8eb03249b475fe0e847653e", - "filesize": 106946560, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v5-build1619-FORTINET.out.kvm.qcow2", - "version": "5.6.1", - "md5sum": "8cc553842564d232af295d6a0c784c1f", - "filesize": 106831872, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v5-build1557-FORTINET.out.kvm.qcow2", - "version": "5.6.0", - "md5sum": "f8bd600796f894f4ca1ea2d6b4066d3d", - "filesize": 108363776, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v5-build1225-FORTINET.out.kvm.qcow2", - "version": "5.4.4", - "md5sum": "53bc6e320fe7bde5d2b636bde95a910c", - "filesize": 89911296, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v5-build1187-FORTINET.out.kvm.qcow2", - "version": "5.4.3", - "md5sum": "53602c776d215d98e32163a10804fc49", - "filesize": 87425024, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v5-build1151-FORTINET.out.kvm.qcow2", - "version": "5.4.2", - "md5sum": "8e131ad40009c740f3efdee6dc3a0ac3", - "filesize": 86437888, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v5-build1082-FORTINET.out.kvm.qcow2", - "version": "5.4.1", - "md5sum": "fc1815410f3f0536e2e3a9c1c5c07f41", - "filesize": 83124224, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v5-build1019-FORTINET.out.kvm.qcow2", - "version": "5.4.0", - "md5sum": "1cfb22671cb372d8bf3e47b9c3c55ded", - "filesize": 77541376, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v5-build0786-FORTINET.out.kvm.qcow2", - "version": "5.2.10", - "md5sum": "377fe38bf07bc2435608e5b65f780f07", - "filesize": 64962560, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v5-build0780-FORTINET.out.kvm.qcow2", - "version": "5.2.9", - "md5sum": "04268e779d3d5e6c928c6fd638423c52", - "filesize": 65007616, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v5-build0777-FORTINET.out.kvm.qcow2", - "version": "5.2.8", - "md5sum": "6dbf148ace9bf309ad383757afd75fad", - "filesize": 65011712, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "FMG_VM64_KVM-v5-build0757-FORTINET.out.kvm.qcow2", - "version": "5.2.7", - "md5sum": "d37dbaa49d7522324681eeba19f7699b", - "filesize": 65056768, - "download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx" - }, - { - "filename": "empty30G.qcow2", + "filename": "empty500G.qcow2", "version": "1.0", - "md5sum": "3411a599e822f2ac6be560a26405821a", - "filesize": 197120, + "md5sum": "658c825441b9b3080ba00f9eec002eaa", + "filesize": 204608, "download_url": "https://sourceforge.net/projects/gns-3/files/Empty%20Qemu%20disk/", - "direct_download_url": "https://sourceforge.net/projects/gns-3/files/Empty%20Qemu%20disk/empty30G.qcow2/download" + "direct_download_url": "https://sourceforge.net/projects/gns-3/files/Empty%20Qemu%20disk/empty500G.qcow2/download" } ], "versions": [ @@ -302,259 +64,21 @@ "name": "7.4.6", "images": { "hda_disk_image": "FMG_VM64_KVM-v7.4.6.M-build2588-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" + "hdb_disk_image": "empty500G.qcow2" } }, { "name": "7.4.5", "images": { "hda_disk_image": "FMG_VM64_KVM-v7.4.5.M-build2553-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" + "hdb_disk_image": "empty500G.qcow2" } }, { "name": "7.4.4", "images": { "hda_disk_image": "FMG_VM64_KVM-v7.4.4.F-build2550-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "7.4.3", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v7.4.3-build2487-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "7.4.2", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v7.4.2-build2397-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "7.4.1", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v7.4.1-build2308-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "7.2.5", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v7.2.5-build1574-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "7.2.4", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v7.2.4-build1460-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "7.2.2", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v7.2.2-build1334-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "7.2.1", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v7.2.1-build1215-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "7.0.12", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v7.0.12-build0623-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "7.0.11", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v7.0.11-build0595-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "7.0.9", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v7.0.9-build0489-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "7.0.6", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v7.0.6-build0372-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "7.0.5", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v7.0.5-build0365-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "6.4.14", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v6.4.14-build2660-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "6.4.12", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v6.4.12-build2610-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "6.4.5", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v6-build2288-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "6.4.4", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v6-build2253-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "6.2.2", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v6-build1183-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "6.2.0", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v6-build1050-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "6.0.6", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v6-build0349-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "6.0.3", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v6-build0255-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "6.0.2", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v6-build0205-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "6.0.0", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v6-build0092-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "5.6.2", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v5-build1631-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "5.6.1", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v5-build1619-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "5.6.0", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v5-build1557-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "5.4.4", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v5-build1225-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "5.4.3", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v5-build1187-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "5.4.2", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v5-build1151-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "5.4.1", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v5-build1082-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "5.4.0", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v5-build1019-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "5.2.10", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v5-build0786-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "5.2.9", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v5-build0780-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "5.2.8", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v5-build0777-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" - } - }, - { - "name": "5.2.7", - "images": { - "hda_disk_image": "FMG_VM64_KVM-v5-build0757-FORTINET.out.kvm.qcow2", - "hdb_disk_image": "empty30G.qcow2" + "hdb_disk_image": "empty500G.qcow2" } } ] diff --git a/gns3server/appliances/frr-docker-lite.gns3a b/gns3server/appliances/frr-docker-lite.gns3a new file mode 100644 index 000000000..9c4d84062 --- /dev/null +++ b/gns3server/appliances/frr-docker-lite.gns3a @@ -0,0 +1,24 @@ +{ + "appliance_id": "7c3303b4-ad66-4cd4-8677-05c26f370c0d", + "name": "FRRDockerLite", + "category": "router", + "symbol": "router.svg", + "description": "FRRouting (FRR) is an IP routing protocol suite for Linux and Unix platforms which includes protocol daemons for BGP, IS-IS, LDP, OSPF, PIM, and RIP.\n\nThis is the minimal Docker variant: it runs only zebra with OSPFv2 and OSPFv3 (ospfd + ospf6d), for the lowest memory footprint when you just need OSPF.", + "vendor_name": "FRRouting Project", + "vendor_url": "https://frrouting.org", + "documentation_url": "https://docs.frrouting.org/", + "product_name": "FRR", + "registry_version": 4, + "status": "experimental", + "availability": "free", + "maintainer": "GNS3 Team", + "maintainer_email": "developers@gns3.net", + "usage": "OSPF-only variant. The console opens directly in the FRR CLI (vtysh). Type 'exit' to reach a Linux shell; run 'vtysh' to return. Save your config with 'write memory'.", + "docker": { + "adapters": 8, + "image": "gns3/frr:latest", + "console_type": "telnet", + "start_command": "/usr/lib/frr/docker-start", + "environment": "FRR_PROFILE=lite,\nTERM=xterm" + } +} diff --git a/gns3server/appliances/frr-docker.gns3a b/gns3server/appliances/frr-docker.gns3a new file mode 100644 index 000000000..0ae4fa4a2 --- /dev/null +++ b/gns3server/appliances/frr-docker.gns3a @@ -0,0 +1,24 @@ +{ + "appliance_id": "813e6041-2693-422b-9d2d-9ab2b4f61247", + "name": "FRRDocker", + "category": "router", + "symbol": "router.svg", + "description": "FRRouting (FRR) is an IP routing protocol suite for Linux and Unix platforms which includes protocol daemons for BGP, IS-IS, LDP, OSPF, PIM, and RIP.\n\nThis Docker version runs the complete FRR protocol suite — BGP, OSPFv2/v3, RIP/RIPng, IS-IS, LDP, PIM/PIM6, NHRP, EIGRP, Babel, BFD, VRRP, PBR and static routing — and is ready to route within seconds. For a minimal OSPF-only image, use the FRRDockerLite appliance.", + "vendor_name": "FRRouting Project", + "vendor_url": "https://frrouting.org", + "documentation_url": "https://docs.frrouting.org/", + "product_name": "FRR", + "registry_version": 4, + "status": "experimental", + "availability": "free", + "maintainer": "GNS3 Team", + "maintainer_email": "developers@gns3.net", + "usage": "The console opens directly in the FRR CLI (vtysh). Type 'exit' to reach a Linux shell; run 'vtysh' to return to the routing CLI. Save your config with 'write memory'.", + "docker": { + "adapters": 8, + "image": "gns3/frr:latest", + "console_type": "telnet", + "start_command": "/usr/lib/frr/docker-start", + "environment": "FRR_PROFILE=full,\nTERM=xterm" + } +} diff --git a/gns3server/appliances/infix.gns3a b/gns3server/appliances/infix.gns3a index 736863ba7..481cba0fd 100644 --- a/gns3server/appliances/infix.gns3a +++ b/gns3server/appliances/infix.gns3a @@ -132,9 +132,37 @@ "md5sum": "24cd1006734993dab338e5c75f80b875", "version": "26.03.0", "direct_download_url": "https://github.com/kernelkit/infix/releases/download/v26.03.0/infix-x86_64-v26.03.0.qcow2" + }, + { + "filename": "infix-x86_64-v26.05.0.qcow2", + "filesize": 330039296, + "md5sum": "60f7c36c389c33ab108acc021f41ccd5", + "version": "26.05.0", + "direct_download_url": "https://github.com/kernelkit/infix/releases/download/v26.05.0/infix-x86_64-v26.05.0.qcow2" + }, + { + "filename": "infix-x86_64-v26.06.0.qcow2", + "filesize": 363593728, + "md5sum": "79ca8bd8534bbaa1af0ab874a49d6f4c", + "version": "26.06.0", + "direct_download_url": "https://github.com/kernelkit/infix/releases/download/v26.06.0/infix-x86_64-v26.06.0.qcow2" } ], "versions": [ + { + "name": "26.06.0", + "images": { + "bios_image": "OVMF-edk2-stable202305.fd", + "hda_disk_image": "infix-x86_64-v26.06.0.qcow2" + } + }, + { + "name": "26.05.0", + "images": { + "bios_image": "OVMF-edk2-stable202305.fd", + "hda_disk_image": "infix-x86_64-v26.05.0.qcow2" + } + }, { "name": "26.03.0", "images": { diff --git a/gns3server/appliances/ubuntu-docker.gns3a b/gns3server/appliances/ubuntu-docker.gns3a index 9cde03e79..c69a1405d 100644 --- a/gns3server/appliances/ubuntu-docker.gns3a +++ b/gns3server/appliances/ubuntu-docker.gns3a @@ -14,7 +14,7 @@ "symbol": "linux_guest.svg", "docker": { "adapters": 1, - "image": "gns3/ubuntu:noble", + "image": "gns3/ubuntu:resolute", "console_type": "telnet" } } diff --git a/gns3server/appliances/vyos.gns3a b/gns3server/appliances/vyos.gns3a index f89c82044..46cdac418 100644 --- a/gns3server/appliances/vyos.gns3a +++ b/gns3server/appliances/vyos.gns3a @@ -50,6 +50,13 @@ } ], "images": [ + { + "filename": "vyos-1.5.1-kvm-amd64.qcow2", + "version": "1.5.1", + "md5sum": "816ec7c3699a9e4f19e2b8765fd3d7eb", + "filesize": 667549696, + "download_url": "https://support.vyos.io/" + }, { "filename": "vyos-1.5.0-kvm-amd64.qcow2", "version": "1.5.0", @@ -57,6 +64,13 @@ "filesize": 618528768, "download_url": "https://support.vyos.io/" }, + { + "filename": "vyos-1.4.5-kvm-amd64.qcow2", + "version": "1.4.5", + "md5sum": "06ccf7e3ed3f948a23c995133b5fbfce", + "filesize": 557645824, + "download_url": "https://support.vyos.io/" + }, { "filename": "vyos-1.4.4-kvm-amd64.qcow2", "version": "1.4.4", @@ -164,6 +178,13 @@ } ], "versions": [ + { + "name": "1.5.1", + "settings": "1.5 x86_64", + "images": { + "hda_disk_image": "vyos-1.5.1-kvm-amd64.qcow2" + } + }, { "name": "1.5.0", "settings": "1.5 x86_64", @@ -171,6 +192,12 @@ "hda_disk_image": "vyos-1.5.0-kvm-amd64.qcow2" } }, + { + "name": "1.4.5", + "images": { + "hda_disk_image": "vyos-1.4.5-kvm-amd64.qcow2" + } + }, { "name": "1.4.4", "images": { diff --git a/gns3server/appliances/westermo-weos.gns3a b/gns3server/appliances/westermo-weos.gns3a index 6ebce85d7..dbcf3a7eb 100644 --- a/gns3server/appliances/westermo-weos.gns3a +++ b/gns3server/appliances/westermo-weos.gns3a @@ -38,6 +38,13 @@ "filesize": 17825792, "direct_download_url": "https://dropzone.westermo.com/file.aspx?id=e6a7676d-85a7-4374-8961-c68aacb74921" }, + { + "filename": "WeOS-zero-5.29.0.disk", + "version": "5.29.0", + "filesize": 81788928, + "md5sum": "f464de7b2b424f4a8ad7c650108fee3d", + "direct_download_url": "https://dropzone.westermo.com/file.aspx?id=52655074-0fab-4119-ba01-b68200a733ab" + }, { "filename": "WeOS-zero-5.28.0.disk", "version": "5.28.0", @@ -54,6 +61,13 @@ } ], "versions": [ + { + "name": "5.29.0", + "images": { + "hda_disk_image": "WeOS-zero-5.29.0.disk", + "hdb_disk_image": "Config-zero-1.0.0.disk" + } + }, { "name": "5.28.0", "images": { diff --git a/gns3server/compute/base_manager.py b/gns3server/compute/base_manager.py index 076b802d1..8ef27265c 100644 --- a/gns3server/compute/base_manager.py +++ b/gns3server/compute/base_manager.py @@ -356,6 +356,7 @@ class BaseManager: raise ComputeError(f"Could not create an UDP connection to {rhost}:{rport}: {e}") nio = NIOUDP(lport, rhost, rport) nio.filters = nio_settings.get("filters", {}) + nio.markers = nio_settings.get("markers", {}) nio.suspend = nio_settings.get("suspend", False) elif nio_settings["type"] == "nio_tap": tap_device = nio_settings["tap_device"] diff --git a/gns3server/compute/base_node.py b/gns3server/compute/base_node.py index 8e7681d9e..65b816e5c 100644 --- a/gns3server/compute/base_node.py +++ b/gns3server/compute/base_node.py @@ -19,6 +19,9 @@ import os import stat import shutil import asyncio +import contextlib +import json +import struct import tempfile import psutil import platform @@ -100,6 +103,13 @@ class BaseNode: self._internal_aux_port = None self._custom_adapters = [] self._ubridge_require_privileged_access = False + # marker filter name -> uBridge bridge_name (recorded at apply time so + # _ubridge_set_marker_filter_state can toggle on/off without an NIO rebuild). + self._marker_filter_bridges = {} + # Parallel store of the installed marker spec (bpf/tag/direction/enabled/...) + # keyed by (name, link_id) so _ubridge_apply_markers can reconcile: detect + # deletions and field changes instead of being add-only. + self._marker_specs = {} if self._console is not None: # use a previously allocated console port @@ -322,7 +332,7 @@ class BaseNode: Creates the node. """ - log.info("{module}: {name} [{id}] created".format(module=self.manager.module_name, name=self.name, id=self.id)) + log.debug("{module}: {name} [{id}] created".format(module=self.manager.module_name, name=self.name, id=self.id)) async def delete(self): """ @@ -371,7 +381,7 @@ class BaseNode: if self._closed: return False - log.info( + log.debug( "{module}: '{name}' [{id}]: is closing".format(module=self.manager.module_name, name=self.name, id=self.id) ) @@ -513,7 +523,7 @@ class BaseNode: log.warning(f"Cannot open console WebSocket: node {self.name} is not started") return - if self._console_type not in ("telnet", "ssh"): + if self._console_type not in ("telnet", "ssh", "docker_exec"): await websocket.close(code=1000) log.warning( f"Cannot open console WebSocket: node {self.name} console type '{self._console_type}' " @@ -554,6 +564,51 @@ class BaseNode: log.warning(f"Cannot connect to node {self.name} console server: {e}") return + def _parse_terminal_size_message(data: bytes): + """ + Binary control frames sent by WebSocket console clients to propagate + their terminal geometry: {"cols": int, "rows": int}. Terminal data + travels as text frames (xterm.js AttachAddon), so binary frames are + an unambiguous side channel. Returns (cols, rows) or None. + """ + + try: + message = json.loads(data.decode("utf-8")) + except (UnicodeDecodeError, ValueError): + return None + if not isinstance(message, dict): + return None + cols, rows = message.get("cols"), message.get("rows") + if ( + isinstance(cols, int) and not isinstance(cols, bool) + and isinstance(rows, int) and not isinstance(rows, bool) + and 2 <= cols <= 5000 + and 2 <= rows <= 100000 + ): + return cols, rows + return None + + async def resize_console(cols: int, rows: int) -> None: + """ + Propagate a client terminal resize to the node console stream: + SSH channels use a pty request update, telnet-based consoles + (including docker_exec) speak a NAWS subnegotiation to the console + telnet server, which resizes the underlying stream (e.g. the + docker exec pty). + """ + + if self._console_type == "ssh": + with contextlib.suppress(AttributeError): + ssh_process.change_terminal_size(cols, rows) + else: + telnet_writer.write( + bytes([255, 251, 31]) # IAC WILL NAWS + + bytes([255, 250, 31]) # IAC SB NAWS + + struct.pack("!HH", cols, rows).replace(b"\xff", b"\xff\xff") + + bytes([255, 240]) # IAC SE + ) + await telnet_writer.drain() + async def ws_forward(telnet_writer): try: @@ -564,6 +619,14 @@ class BaseNode: if "text" in msg and msg["text"]: data = msg["text"].encode() elif "bytes" in msg and msg["bytes"]: + size = _parse_terminal_size_message(msg["bytes"]) + if size is not None: + log.debug( + f"Console WebSocket client {websocket.client.host}:{websocket.client.port}" + f" resized terminal to {size[0]}x{size[1]}" + ) + await resize_console(*size) + continue data = msg["bytes"] else: continue @@ -577,10 +640,20 @@ class BaseNode: async def telnet_forward(telnet_reader): - while not telnet_reader.at_eof(): - data = await telnet_reader.read(1024) - if data: - await websocket.send_bytes(data) + try: + while not telnet_reader.at_eof(): + data = await telnet_reader.read(1024) + if data: + await websocket.send_bytes(data) + except WebSocketDisconnect: + # the client disconnected while node output was still streaming: + # normal end of the session, not an error. Starlette raises + # WebSocketDisconnect (whose str() is empty) from send once the + # peer is gone, which used to surface as a message-less warning. + log.info( + f"Client {websocket.client.host}:{websocket.client.port} has disconnected from compute" + f" console WebSocket while node output was being forwarded" + ) # keep forwarding websocket data in both direction if sys.version_info >= (3, 11, 0): @@ -594,7 +667,7 @@ class BaseNode: if task.exception(): log.warning( f"Exception while forwarding WebSocket data to " - f"{self._console_type.upper()} server: {task.exception()}" + f"{self._console_type.upper()} server: {task.exception()!r}" ) for task in pending: task.cancel() @@ -665,8 +738,16 @@ class BaseNode: data = await vnc_reader.read(65536) # Larger buffer for VNC frames if data: await websocket.send_bytes(data) + except WebSocketDisconnect: + # the browser disconnected while VNC frames were still streaming + # (starlette raises WebSocketDisconnect with an empty str() from + # send once the peer is gone — not an error) + log.info( + f"Client {websocket.client.host}:{websocket.client.port} has disconnected from compute " + f"VNC console WebSocket while frames were being forwarded" + ) except Exception as e: - log.warning(f"Exception while forwarding VNC data to WebSocket: {e}") + log.warning(f"Exception while forwarding VNC data to WebSocket: {e!r}") # Keep forwarding WebSocket data in both directions if sys.version_info >= (3, 11, 0): @@ -678,7 +759,7 @@ class BaseNode: done, pending = await asyncio.wait(aws, return_when=asyncio.FIRST_COMPLETED) for task in done: if task.exception(): - log.warning(f"Exception while forwarding WebSocket data to VNC server: {task.exception()}") + log.warning(f"Exception while forwarding WebSocket data to VNC server: {task.exception()!r}") for task in pending: task.cancel() @@ -926,27 +1007,72 @@ class BaseNode: raise NodeError("uBridge requires root access or the capability to interact with network adapters") server_host = self._manager.config.settings.Server.host + transport = self._manager.config.settings.Server.ubridge_control_transport if not self.ubridge: - self._ubridge_hypervisor = Hypervisor(self._project, self.ubridge_path, self.working_dir, server_host) - log.info(f"Starting new uBridge hypervisor {self._ubridge_hypervisor.host}:{self._ubridge_hypervisor.port}") + self._ubridge_hypervisor = Hypervisor( + self._project, self.ubridge_path, self.working_dir, transport, server_host, self.id + ) + log.debug(f"Starting new uBridge hypervisor at {self._ubridge_hypervisor.endpoint}") await self._ubridge_hypervisor.start() if self._ubridge_hypervisor: log.info( - f"Hypervisor {self._ubridge_hypervisor.host}:{self._ubridge_hypervisor.port} has successfully started" + f"Hypervisor at {self._ubridge_hypervisor.endpoint} has successfully started" ) await self._ubridge_hypervisor.connect() + # Tell this uBridge where to send MARK signals and which node id to + # tag them with. Marker is opt-in and inert until a `mark` filter is + # added, so this never disturbs the data plane. + await self._ubridge_configure_marker_sink() # save if privileged are required in case uBridge needs to be restarted in self._ubridge_send() self._ubridge_require_privileged_access = require_privileged_access + async def _ubridge_configure_marker_sink(self): + """ + Point this node's uBridge at the compute's marker UDP sink and tag its + signals with this node's id. Safe to call before any marker filter + exists — uBridge stays inert until a ``mark`` filter is configured. + + Old uBridge builds without the marker module are tolerated: the failure + is downgraded to a warning so node start is not blocked by an opt-in + observability feature. + """ + + from gns3server.compute.marker.marker_manager import MarkerManager + + manager = MarkerManager.instance() + if not manager.running or not manager.host or not manager.port: + return + if self._ubridge_hypervisor is None: + return + try: + # Talk to the hypervisor directly, NOT via _ubridge_send: this runs + # inside _start_ubridge, which is reached THROUGH _ubridge_send when + # uBridge starts lazily (e.g. linking a stopped node). _ubridge_send's + # lock is non-reentrant, so calling it again here would deadlock on + # the held ___ubridge_send_lock. uBridge is already running and + # connected at this point, so the raw hypervisor send is safe. + await self._ubridge_hypervisor.send(f"marker sink {manager.host} {manager.port}") + await self._ubridge_hypervisor.send(f"marker node {self._id}") + except UbridgeError: + log.warning( + "uBridge does not support the marker module; traffic insight disabled for node %r", + self.name, + ) + async def _stop_ubridge(self): """ Stops uBridge. """ if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running(): - log.info(f"Stopping uBridge hypervisor {self._ubridge_hypervisor.host}:{self._ubridge_hypervisor.port}") + log.debug(f"Stopping uBridge hypervisor at {self._ubridge_hypervisor.endpoint}") await self._ubridge_hypervisor.stop() self._ubridge_hypervisor = None + # uBridge is gone, so every marker filter (and its in-bridge state) is + # gone too — clear the map so the next apply re-installs them all rather + # than skipping them as "already installed". + self._marker_filter_bridges.clear() + self._marker_specs.clear() async def add_ubridge_udp_connection(self, bridge_name, source_nio, destination_nio): """ @@ -983,10 +1109,12 @@ class BaseNode: await self._ubridge_send(f"bridge start {bridge_name}") await self._ubridge_apply_filters(bridge_name, destination_nio.filters) + await self._ubridge_apply_markers(bridge_name, destination_nio) async def update_ubridge_udp_connection(self, bridge_name, source_nio, destination_nio): if destination_nio: await self._ubridge_apply_filters(bridge_name, destination_nio.filters) + await self._ubridge_apply_markers(bridge_name, destination_nio) async def ubridge_delete_bridge(self, name): """ @@ -1042,6 +1170,283 @@ class BaseNode: ) i += 1 + @staticmethod + def _marker_linktype(data_link_type): + """ + Normalize a GNS3 pcap data-link type (e.g. ``DLT_C_HDLC``) to the bare + uBridge ``linktype`` token (``C_HDLC``) by stripping the ``DLT_`` prefix. + Returns ``None`` for Ethernet (``DLT_EN10MB`` / unset) so the ``linktype`` + keyword is omitted and uBridge defaults to EN10MB. Values come straight + from ``SerialPort.data_link_types`` (the single source of truth); uBridge + resolves them with ``pcap_datalink_name_to_val``, which is case-sensitive + and expects the canonical uppercase form. + """ + if not data_link_type: + return None + dlt = data_link_type.upper() + if dlt.startswith("DLT_"): + dlt = dlt[4:] + return None if dlt == "EN10MB" else dlt + + async def _ubridge_add_marker_filter(self, bridge_name, name, bpf, pcap_path, tag=None, link_id=None, direction=None, data_link_type=None): + """ + Attach a `mark` packet filter to a uBridge bridge for traffic insight. + + On BPF match uBridge (a) emits a UDP MARK signal to the configured sink + and (b) appends the packet to ``pcap_path``. Unlike the impairment + filters, this is an observability tap: it never drops or alters traffic, + and it is added/removed on its own (not via reset_packet_filters) so the + pcap is not closed/reopened on unrelated filter changes. + + :param bridge_name: uBridge bridge carrying the link's traffic + :param name: stable, gns3server-chosen filter name (pcap identity + echoed in signals) + :param bpf: libpcap BPF expression + :param pcap_path: absolute path ubridge appends matched packets to + :param tag: optional correlation id echoed in MARK signals + """ + + # mark [tag ] [pcap ] — tag/pcap keyword pairs, any order. + # name travels from the controller REST layer (MarkerCreate schema) but is + # validated here too as defense-in-depth against hand-edited topology files. + # Note: "global-*" names are legitimate here — they come from project-level + # marker definitions (inherit_marker). The prefix is only forbidden at the + # user-facing schema layer, not at the uBridge boundary. + _MARKER_NAME_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.-]*$") + # Defense-in-depth vs hand-edited topology: the user-facing name is capped + # at 32 by the schema; inherited copies carry a ``global-`` prefix (≤ 39), + # so allow up to 48 here. + if not _MARKER_NAME_RE.match(name) or len(name) > 48: + raise UbridgeError(f"Invalid marker name: {name!r}") + cmd = 'bridge add_packet_filter {bridge} {name} mark "{bpf}"'.format( + bridge=bridge_name, name=name, bpf=bpf + ) + if tag is not None: + cmd += f" tag {tag}" + # Per-link attribution (contract §3.2): when one ubridge bridge serves + # several GNS3 links (e.g. IOU's per-node bridge), bridge+filter collide, + # so the link id is the only way to tell signals — and pcap files — apart. + if link_id: + cmd += f" link {link_id}" + if direction is not None: + cmd += f" dir {direction}" + linktype = self._marker_linktype(data_link_type) + if linktype is not None: + cmd += f" linktype {linktype}" + cmd += ' pcap "{path}"'.format(path=pcap_path) + # Let BPF compile errors propagate — the marker is the user's intent, so a + # bad expression must surface instead of being silently dropped. + await self._ubridge_send(cmd) + + async def delete_marker_capture(self, name, link_id, nio=None): + """ + Remove a marker from uBridge (fine-grained ``delete_packet_filter`` — NOT + reset_packet_filters, so sibling markers' pcaps aren't closed/reopened) + and delete its capture pcap. Called by the controller when a marker is + removed; safe with the node stopped (filter removal is skipped, the file + is still unlinked). IOU overrides ``_ubridge_delete_marker_filter`` for + its ``iol_bridge`` command shape. + + ``nio`` is the port NIO whose cached ``nio.markers`` carries this marker + spec; it is dropped here so a later node start / NIO reapply + (``_ubridge_apply_markers``) does not reinstall the marker. Without this, + deleting a marker while the node is stopped left the spec in + ``nio.markers``, and starting the node recreated an empty pcap. + """ + if nio is not None and getattr(nio, "markers", None): + nio.markers.pop(name, None) + bridge_name = self._marker_filter_bridges.pop((name, link_id), None) + self._marker_specs.pop((name, link_id), None) + if bridge_name is not None: + await self._ubridge_delete_marker_filter(bridge_name, name) + try: + markers_dir = self.project.markers_working_directory() + pcap_path = os.path.join(markers_dir, f"{self._id}_{link_id}_{name}.pcap") + os.remove(pcap_path) + except FileNotFoundError: + pass + except OSError as e: + log.warning("Could not remove marker pcap for '%s' on link %s: %s", name, link_id, e) + + async def _ubridge_delete_marker_filter(self, bridge_name, name): + """ + Remove a single marker filter from uBridge with ``delete_packet_filter`` + (not a bridge-wide reset) so other markers keep their pcaps open. A no-op + when uBridge isn't running — the pcap cleanup in the caller still proceeds. + """ + if not (self._ubridge_hypervisor and self._ubridge_hypervisor.is_running()): + return + try: + await self._ubridge_send(f"bridge delete_packet_filter {bridge_name} {name}") + except UbridgeError as e: + log.warning("Could not remove marker filter '%s' from %s: %s", name, bridge_name, e) + + async def rebuild_marker_filter(self, name, link_id, bpf, tag=None, direction=None, enabled=True): + """ + Re-install a single marker filter with new params (delete + add), without + a bridge-wide reset — so sibling markers keep their pcaps open. uBridge + reopens the marker's own pcap on re-add (a new capture session for the + new BPF), which is expected. No-op if the marker isn't installed (node + stopped) — the next NIO reapply picks up the updated ``_markers``. + + IOU needs no override: this calls ``_ubridge_delete_marker_filter`` / + ``_ubridge_add_marker_filter`` / ``_ubridge_set_marker_filter_state``, + all of which IOU already overrides for ``iol_bridge``. + """ + bridge_name = self._marker_filter_bridges.get((name, link_id)) + if bridge_name is None: + return + await self._ubridge_delete_marker_filter(bridge_name, name) + pcap_path = os.path.join(self.project.markers_working_directory(), f"{self._id}_{link_id}_{name}.pcap") + await self._ubridge_add_marker_filter(bridge_name, name, bpf, pcap_path, tag, link_id, direction=direction) + if not enabled: + await self._ubridge_set_marker_filter_state(name, enabled=False) + + async def _ubridge_apply_markers(self, bridge_name, nio): + """ + Reconcile the traffic-insight markers carried by *nio* onto bridge + *bridge_name* with what is already installed there. + + uBridge's ``reset_packet_filters`` preserves mark filters (contract), so + a plain re-add would duplicate them; instead this diffs the desired + ``nio.markers`` against the installed ``_marker_specs``: + + * installed but no longer desired → delete filter + unlink pcap + * desired with changed bpf/tag/direction/data_link_type → rebuild + (delete + add; the marker's own pcap reopens for the new BPF) + * desired with only ``enabled`` changed → instant on/off toggle + (sibling and own pcap stay open) + * desired and unchanged → skip + * desired and new → add + + Called from ``add_ubridge_udp_connection`` (fresh bridge, empty maps → + installs all) and ``update_ubridge_udp_connection`` / the batch NIO + update path (incremental reconcile). + """ + from gns3server.compute.marker.marker_manager import MarkerManager + + markers = nio.markers if hasattr(nio, 'markers') else {} + manager = MarkerManager.instance() + markers_dir = self.project.markers_working_directory() + desired = {(name, spec.get("link_id", "")): spec for name, spec in markers.items()} + + # 1. Remove installed markers that are no longer desired (marker/def delete). + # Scope to THIS bridge: the map is node-wide and also holds markers + # installed on this node's other links/NIOs. Without this guard, + # reconciling one NIO would delete every other link's markers + pcaps + # (desired only carries the current NIO's markers) — a regression. + for key in list(self._marker_filter_bridges): + if self._marker_filter_bridges[key] != bridge_name: + continue + if key not in desired: + mname, link_id = key + installed_bridge = self._marker_filter_bridges.pop(key) + self._marker_specs.pop(key, None) + await self._ubridge_delete_marker_filter(installed_bridge, mname) + try: + os.remove(os.path.join(markers_dir, f"{self._id}_{link_id}_{mname}.pcap")) + except FileNotFoundError: + pass + except OSError as e: + log.warning("Could not remove marker pcap for '%s' on link %s: %s", mname, link_id, e) + manager.unregister(self._id, mname) + + # 2. Add newly-desired markers; rebuild ones whose filter fields changed. + rebuild_fields = ("bpf", "tag", "direction", "data_link_type") + for (name, link_id), spec in desired.items(): + bpf = spec.get("bpf", "") + tag = spec.get("tag") + enabled = spec.get("enabled", True) + if (name, link_id) in self._marker_filter_bridges: + installed_spec = self._marker_specs.get((name, link_id)) + if installed_spec is None: + # Installed but no recorded spec (legacy / pre-reconcile state): + # cannot diff, skip to avoid a duplicate add. + continue + if any(installed_spec.get(f) != spec.get(f) for f in rebuild_fields): + # A filter field changed → rebuild (delete + re-add). + installed_bridge = self._marker_filter_bridges.get((name, link_id)) + await self._ubridge_delete_marker_filter(installed_bridge, name) + elif installed_spec.get("enabled", True) != enabled: + # Only the on/off state changed → instant toggle, pcap preserved. + await self._ubridge_set_marker_filter_state(name, enabled) + self._marker_specs[(name, link_id)] = spec + continue + else: + continue # unchanged + pcap_path = os.path.join(markers_dir, f"{self._id}_{link_id}_{name}.pcap") + try: + await self._ubridge_add_marker_filter(bridge_name, name, bpf, pcap_path, tag, link_id, + direction=spec.get("direction"), + data_link_type=spec.get("data_link_type")) + except UbridgeError as e: + # Swallow BPF compile errors (warn + skip) so a single bad + # expression can't break link creation / node restart — mirrors + # _ubridge_apply_filters, which does the same for packet filters. + if "syntax error" in str(e).lower() or "compile filter" in str(e).lower(): + message = f"Warning: ignoring marker '{name}' due to BPF syntax error: {e}" + log.warning(message) + self.project.emit("log.warning", {"message": message}) + continue + raise + # A disabled marker is installed but turned off (a paused tap), not + # dropped — so the UI can flip it back on instantly with + # enable_packet_filter, no NIO rebuild (ubridge contract §3.2). + if not enabled: + try: + await self._ubridge_send(f"bridge enable_packet_filter {bridge_name} {name} off") + except UbridgeError as e: + # Old ubridge without enable_packet_filter: leave it installed + # (on) rather than fail the whole link/marker apply. + log.warning(f"Could not turn marker '{name}' off on {bridge_name}: {e}") + manager.register(str(self.project.id), self._id, name, link_id, tag) + # Remember which bridge hosts this filter so an instant on/off toggle + # (no NIO rebuild) can resolve it by name alone, and keep the spec so + # the next reconcile can detect changes. + self._marker_filter_bridges[name, link_id] = bridge_name + self._marker_specs[name, link_id] = spec + + async def _ubridge_set_marker_filter_state(self, name, enabled): + """ + Toggle an installed marker filter on/off with a single uBridge command + (``bridge enable_packet_filter … on|off``) — no NIO reset/reapply, so the + pcap identity and emitted counter are preserved (ubridge contract §3.2). + The bridge is resolved from the (name, link_id)→bridge map populated at + apply time; entries are iterated so a node that hosts the same marker name + on several links (e.g. IOU with one IOL-BRIDGE per node) toggles every + copy. IOU overrides this for its ``iol_bridge`` command shape. + + :param name: marker filter name + :param enabled: True = on (signal+pcap), False = off (paused tap) + """ + + state = "on" if enabled else "off" + for (n, lid), bridge_name in list(self._marker_filter_bridges.items()): + if n == name: + await self._ubridge_send(f"bridge enable_packet_filter {bridge_name} {name} {state}") + + async def _ubridge_marker_pause(self): + """ + Pause all marker signal+pcap emission on this node's uBridge + (``marker pause``). Keeps the sink open so ``resume`` is instant. Safe + on old ubridge builds (the error is downgraded to a warning). Called by + the project-level pause fan-out. + """ + + if self._ubridge_hypervisor: + try: + await self._ubridge_hypervisor.send("marker pause") + except UbridgeError as e: + log.warning(f"Could not pause markers on node {self._id}: {e}") + + async def _ubridge_marker_resume(self): + """Resume marker signal+pcap emission (``marker resume``).""" + + if self._ubridge_hypervisor: + try: + await self._ubridge_hypervisor.send("marker resume") + except UbridgeError as e: + log.warning(f"Could not resume markers on node {self._id}: {e}") + async def _add_ubridge_ethernet_connection(self, bridge_name, ethernet_interface, block_host_traffic=False): """ Creates a connection with an Ethernet interface in uBridge. diff --git a/gns3server/compute/builtin/nodes/cloud.py b/gns3server/compute/builtin/nodes/cloud.py index 43d3bcc1f..b01f2b03d 100644 --- a/gns3server/compute/builtin/nodes/cloud.py +++ b/gns3server/compute/builtin/nodes/cloud.py @@ -82,8 +82,20 @@ class Cloud(BaseNode): host_interfaces = [] network_interfaces = gns3server.utils.interfaces.interfaces() for interface in network_interfaces: + # Hide GNS3 internal bridges (e.g. EthernetSwitch kernel bridges) + if interface["name"].lower().startswith("gns3"): + continue host_interfaces.append( - {"name": interface["name"], "type": interface["type"], "special": interface["special"]} + { + "name": interface["name"], + "type": interface["type"], + "special": interface["special"], + "ip_addresses": interface.get("ip_addresses", []), + "status": interface.get("status", "down"), + "speed": interface.get("speed", 0), + "mtu": interface.get("mtu", 0), + "flags": interface.get("flags", []), + } ) return { @@ -216,7 +228,7 @@ class Cloud(BaseNode): """ await self.start() - log.info(f'Cloud "{self._name}" [{self._id}] has been created') + log.debug(f'Cloud "{self._name}" [{self._id}] has been created') async def start(self): """ @@ -249,7 +261,7 @@ class Cloud(BaseNode): self.manager.port_manager.release_udp_port(nio.lport, self._project) await self._stop_ubridge() - log.info(f'Cloud "{self._name}" [{self._id}] has been closed') + log.debug(f'Cloud "{self._name}" [{self._id}] has been closed') async def _is_wifi_adapter_osx(self, adapter_name): """ @@ -303,6 +315,7 @@ class Cloud(BaseNode): ) await self._ubridge_apply_filters(bridge_name, nio.filters) + await self._ubridge_apply_markers(bridge_name, nio) if port_info["type"] in ("ethernet", "tap"): if not self.manager.has_privileged_access(self.ubridge_path): @@ -317,7 +330,7 @@ class Cloud(BaseNode): f"Interface '{port_info['interface']}' could not be found on this system, please update '{self.name}'" ) - if sys.platform.startswith("linux"): + if sys.platform.startswith("linux") or sys.platform.startswith("openbsd"): await self._add_linux_ethernet(port_info, bridge_name) elif sys.platform.startswith("darwin"): await self._add_osx_ethernet(port_info, bridge_name) @@ -416,7 +429,7 @@ class Cloud(BaseNode): if port_number in self._nios: raise NodeError(f"Port {port_number} isn't free") - log.info( + log.debug( 'Cloud "{name}" [{id}]: NIO {nio} bound to port {port}'.format( name=self._name, id=self._id, nio=nio, port=port_number ) @@ -443,6 +456,7 @@ class Cloud(BaseNode): bridge_name = f"{self._id}-{port_number}" if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running(): await self._ubridge_apply_filters(bridge_name, nio.filters) + await self._ubridge_apply_markers(bridge_name, nio) async def _delete_ubridge_connection(self, port_number): """ @@ -471,7 +485,7 @@ class Cloud(BaseNode): if isinstance(nio, NIOUDP): self.manager.port_manager.release_udp_port(nio.lport, self._project) - log.info( + log.debug( 'Cloud "{name}" [{id}]: NIO {nio} removed from port {port}'.format( name=self._name, id=self._id, nio=nio, port=port_number ) @@ -521,7 +535,7 @@ class Cloud(BaseNode): await self._ubridge_send( 'bridge start_capture {name} "{output_file}"'.format(name=bridge_name, output_file=output_file) ) - log.info( + log.debug( "Cloud '{name}' [{id}]: starting packet capture on port {port_number}".format( name=self.name, id=self.id, port_number=port_number ) @@ -541,7 +555,7 @@ class Cloud(BaseNode): bridge_name = f"{self._id}-{port_number}" await self._ubridge_send(f"bridge stop_capture {bridge_name}") - log.info( + log.debug( "Cloud'{name}' [{id}]: stopping packet capture on port {port_number}".format( name=self.name, id=self.id, port_number=port_number ) diff --git a/gns3server/compute/builtin/nodes/ethernet_hub.py b/gns3server/compute/builtin/nodes/ethernet_hub.py index fc601ff01..4ef0e27e9 100644 --- a/gns3server/compute/builtin/nodes/ethernet_hub.py +++ b/gns3server/compute/builtin/nodes/ethernet_hub.py @@ -53,7 +53,7 @@ class EthernetHub(BaseNode): """ super().create() - log.info(f'Ethernet hub "{self._name}" [{self._id}] has been created') + log.debug(f'Ethernet hub "{self._name}" [{self._id}] has been created') async def delete(self): """ diff --git a/gns3server/compute/builtin/nodes/ethernet_switch.py b/gns3server/compute/builtin/nodes/ethernet_switch.py index d4a4863a3..5c66cbad5 100644 --- a/gns3server/compute/builtin/nodes/ethernet_switch.py +++ b/gns3server/compute/builtin/nodes/ethernet_switch.py @@ -14,14 +14,45 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import asyncio +""" +Ethernet switch backed by a Linux kernel bridge driven through uBridge's +``brctl`` module. + +The historical GNS3 Ethernet switch was an emulated L2 device inside Dynamips +(``ethsw``). This implementation replaces it with a *real* Linux kernel bridge: +one bridge per switch node, managed over uBridge's hypervisor socket. Each +switch port is a persistent TAP that plays two roles at once -- uBridge holds +its file descriptor as a ``nio_tap`` relay endpoint, and the same TAP is +enslaved to the kernel bridge as a port. This dual-role TAP is exactly the +pattern the Cloud node already uses for host bridges (see +``cloud.py::_add_linux_ethernet``). + +Data path (UDP link mode):: + + peer --UDP-- ubridge[nio_udp <-> nio_tap(tap)] --tap-- kernel bridge --tap-- ... (other ports) + +The kernel bridge performs MAC learning/forwarding and VLAN filtering; uBridge +is only the per-port UDP transport (uBridge is strictly a 2-NIO pipe, it cannot +be the switch). ESW ``access``/``dot1q``/``qinq`` port modes are composed from +the ``brctl`` VLAN primitives here -- see ``_apply_port_vlan``. +""" from ...base_node import BaseNode +from ...nios.nio_udp import NIOUDP +from ...error import NodeError +from gns3server.compute.ubridge.ubridge_error import UbridgeError import logging log = logging.getLogger(__name__) +# VLAN ethertypes the Linux kernel bridge can realise. ``brctl setvlanproto`` +# accepts only 0x8100 (802.1Q) and 0x88a8 (802.1ad). The GNS3 schema also allows +# the legacy 0x9100/0x9200 QinQ ethertypes; the kernel bridge cannot do those, so +# configuring them on a qinq port is rejected. +_SUPPORTED_VLAN_ETHERTYPE = {"0x8100", "0x88a8"} +_QINQ_ETHERTYPE = "0x88a8" + class EthernetSwitch(BaseNode): @@ -32,11 +63,101 @@ class EthernetSwitch(BaseNode): :param node_id: Node identifier :param project: Project instance :param manager: Parent VM Manager + :param ports: initial switch ports """ - def __init__(self, name, node_id, project, manager): + def __init__(self, name, node_id, project, manager, console=None, console_type=None, ports=None): - super().__init__(name, node_id, project, manager) + super().__init__(name, node_id, project, manager, console=console, console_type=console_type or "none") + # The switch has no console; ``console_type="none"`` makes BaseNode skip + # reserving a TCP console port entirely. + self._ubridge_require_privileged_access = True + + self._nios = {} + self._tap_by_port = {} # port_number -> kernel TAP enslaved to the bridge + self._bridge_name = None # kernel bridge interface name (allocated on start) + self._bridge_created = False + self._bridge_proto_set = False # whether ``brctl setvlanproto`` has been applied + # Idempotency flag for start(). Decoupled from ``status`` so the node can + # report "started" (always-on, like the ESW) while ``duplicate_node`` still + # sees status "stopped" and refuses only genuinely running stateful nodes. + self._started = False + + if ports is None: + # 8 access ports in VLAN 1 by default, matching the historical ESW. + self._ports_mapping = [] + for port_number in range(0, 8): + self._ports_mapping.append( + {"port_number": port_number, "name": f"Ethernet{port_number}", "type": "access", "vlan": 1} + ) + else: + self._ports_mapping = self._normalize_ports(ports) + + # ------------------------------------------------------------------ # + # helpers + # ------------------------------------------------------------------ # + + @staticmethod + def _normalize_ports(ports): + """Assign sequential port numbers/names like the Dynamips ESW did.""" + port_number = 0 + normalized = [] + for port in ports: + port = dict(port) + port["name"] = f"Ethernet{port_number}" + port["port_number"] = port_number + normalized.append(port) + port_number += 1 + return normalized + + def _ubridge_bridge_name(self, port_number): + """Name of the per-port uBridge relay bridge (not a kernel interface).""" + return f"{self._id}-{port_number}" + + def _tap_name(self, port_number): + """Kernel TAP name for a port: ``-`` (host-unique via the bridge).""" + return f"{self._bridge_name}-{port_number}" + + def _port_settings(self, port_number): + for port in self._ports_mapping: + if port["port_number"] == port_number: + return port + return None + + # ------------------------------------------------------------------ # + # properties / serialisation + # ------------------------------------------------------------------ # + + @property + def nios(self): + return self._nios + + @property + def ports_mapping(self): + return self._ports_mapping + + @ports_mapping.setter + def ports_mapping(self, ports): + if ports != self._ports_mapping: + if len(self._nios) > 0 and len(ports) != len(self._ports_mapping): + raise NodeError("Cannot change the port count of a switch that is already connected.") + self._ports_mapping = self._normalize_ports(ports) + + @property + def console(self): + return self._console + + @console.setter + def console(self, console): + self._console = console + + @property + def console_type(self): + return self._console_type + + @console_type.setter + def console_type(self, console_type): + self._console_type = console_type def asdict(self): @@ -44,61 +165,375 @@ class EthernetSwitch(BaseNode): "name": self.name, "usage": self.usage, "node_id": self.id, - "project_id": self.project.id + "project_id": self.project.id, + "ports_mapping": self._ports_mapping, + "console": self.console, + "console_type": self.console_type, + # The switch is always-on once created (a kernel bridge), like the ESW. + "status": "started", } + # ------------------------------------------------------------------ # + # lifecycle + # ------------------------------------------------------------------ # + async def create(self): """ Creates this switch. """ - super().create() - log.info(f'Ethernet switch "{self._name}" [{self._id}] has been created') + await self.start() + log.debug(f'Ethernet switch "{self._name}" [{self._id}] has been created') + + async def start(self): + """ + Starts this switch: bring up uBridge, create the kernel bridge, and + re-wire any ports already bound before a restart. + """ + + if not self._started: + if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running(): + await self._stop_ubridge() + await self._start_ubridge(self._ubridge_require_privileged_access) + await self._ensure_bridge() + for port_number in self._nios: + if self._nios[port_number]: + try: + await self._add_ubridge_connection(self._nios[port_number], port_number) + except (UbridgeError, NodeError) as e: + self._started = False + raise e + self._started = True + + async def _ensure_bridge(self): + """ + Creates the per-node kernel bridge once and enables VLAN filtering. + Applies the bridge-level QinQ ethertype if any port needs it. + + The bridge name is deterministic: ``gns3`` + the first 6 hex chars of + this switch's UUID (kernel interface names are ≤ 15 chars). A stale + bridge from a previous crash is deleted first so ``brctl create`` never + hits EEXIST. + """ + + if self._bridge_created: + return + # deterministic short name — 10 chars, always fits the 15-char kernel cap + self._bridge_name = "gns3" + self._id.replace("-", "")[:6] + # crash recovery: best-effort delete any leftover bridge + try: + await self._ubridge_send(f'brctl delete "{self._bridge_name}"') + except UbridgeError: + pass # not found = nothing to clean + await self._ubridge_send(f'brctl create "{self._bridge_name}"') + # ``brctl create`` leaves the bridge DOWN; bring it UP so it forwards. + await self._ubridge_send(f'link set "{self._bridge_name}" up') + await self._ubridge_send(f'brctl vlanfiltering "{self._bridge_name}" on') + self._bridge_created = True + await self._apply_bridge_proto_if_needed() + + async def _apply_bridge_proto_if_needed(self): + """ + If any port is a QinQ port using the 802.1ad ethertype (0x88a8), switch + the whole bridge to that protocol. A Linux bridge has a single VLAN + protocol, so mixed QinQ ethertypes within one switch are not supported. + """ + + proto = None + for port in self._ports_mapping: + if port.get("type") == "qinq": + # normalise case: the schema carries uppercase (e.g. "0x88A8") but + # brctl setvlanproto wants lowercase hex + ethertype = port.get("ethertype", "0x8100").lower() + if ethertype not in _SUPPORTED_VLAN_ETHERTYPE: + raise NodeError( + f"VLAN ethertype {ethertype} is not supported by the Linux bridge " + f"(only 0x8100/0x88a8) for QinQ port {port['name']}" + ) + if ethertype == _QINQ_ETHERTYPE: + proto = _QINQ_ETHERTYPE + if proto and not self._bridge_proto_set: + await self._ubridge_send(f'brctl setvlanproto "{self._bridge_name}" {proto}') + self._bridge_proto_set = True async def delete(self): """ Deletes this switch. """ - raise NotImplementedError() + return await self.close() + + async def close(self): + """ + Closes this switch: release UDP ports, tear down the kernel bridge, stop uBridge. + """ + + if not (await super().close()): + return False + + for nio in self._nios.values(): + if nio and isinstance(nio, NIOUDP): + self.manager.port_manager.release_udp_port(nio.lport, self._project) + self._nios.clear() + self._tap_by_port.clear() + + if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running() and self._bridge_created: + try: + # Deleting the bridge releases its enslaved TAPs; uBridge destroys + # them when it stops below. + await self._ubridge_send(f'brctl delete "{self._bridge_name}"') + except UbridgeError as e: + log.warning(f'Could not delete kernel bridge "{self._bridge_name}": {e}') + self._bridge_created = False + self._bridge_proto_set = False + self._bridge_name = None + self._started = False + + await self._stop_ubridge() + log.debug(f'Ethernet switch "{self._name}" [{self._id}] has been closed') + return True + + # ------------------------------------------------------------------ # + # per-port wiring + # ------------------------------------------------------------------ # async def add_nio(self, nio, port_number): """ - Adds a NIO as new port on this switch. + Adds a NIO as a new port on this switch. :param nio: NIO instance to add :param port_number: port to allocate for the NIO """ - raise NotImplementedError() + if port_number in self._nios: + raise NodeError(f"Port {port_number} isn't free") + if not isinstance(nio, NIOUDP): + raise NodeError("Ethernet switch ports only support UDP NIOs") + + log.debug( + 'Ethernet switch "{name}" [{id}]: NIO {nio} bound to port {port}'.format( + name=self._name, id=self._id, nio=nio, port=port_number + ) + ) + try: + await self.start() + await self._add_ubridge_connection(nio, port_number) + self._nios[port_number] = nio + except (NodeError, UbridgeError) as e: + log.error('Cannot add NIO on Ethernet switch "{name}": {error}'.format(name=self._name, error=e)) + await self._stop_ubridge() + self.status = "stopped" + self._nios[port_number] = nio + self.project.emit("log.error", {"message": str(e)}) + + async def _add_ubridge_connection(self, nio, port_number): + """ + Wires one port: a per-port uBridge relay (nio_tap <-> nio_udp) whose TAP + is enslaved to the kernel bridge, with the port's VLAN mode applied. + """ + + port_settings = self._port_settings(port_number) + if port_settings is None: + raise NodeError(f"Port {port_number} doesn't exist on Ethernet switch '{self.name}'") + + ubridge_bridge = self._ubridge_bridge_name(port_number) + tap = self._tap_name(port_number) + + # per-port uBridge relay -- uBridge holds the TAP fd + await self._ubridge_send(f"bridge create {ubridge_bridge}") + await self._ubridge_send(f'bridge add_nio_tap {ubridge_bridge} "{tap}"') + # enslave the same TAP to the kernel bridge (the cloud.py::_add_linux_ethernet move) + await self._ubridge_send(f'brctl addif "{self._bridge_name}" "{tap}"') + # VLAN membership for this port's access/trunk/qinq mode + await self._apply_port_vlan(port_settings, tap) + # GNS3 link endpoint + await self._ubridge_send( + "bridge add_nio_udp {name} {lport} {rhost} {rport}".format( + name=ubridge_bridge, lport=nio.lport, rhost=nio.rhost, rport=nio.rport + ) + ) + await self._ubridge_apply_filters(ubridge_bridge, nio.filters) + await self._ubridge_apply_markers(ubridge_bridge, nio) + if nio.capturing: + await self._ubridge_send( + 'bridge start_capture {name} "{output_file}"'.format( + name=ubridge_bridge, output_file=nio.pcap_output_file + ) + ) + await self._ubridge_send(f"bridge start {ubridge_bridge}") + self._tap_by_port[port_number] = tap + + async def _delete_ubridge_connection(self, port_number): + """ + Tears down one port's wiring: release the TAP from the bridge and delete + the per-port uBridge relay. + """ + + tap = self._tap_by_port.pop(port_number, None) + ubridge_bridge = self._ubridge_bridge_name(port_number) + if tap is not None and self._bridge_created: + try: + await self._ubridge_send(f'brctl delif "{self._bridge_name}" "{tap}"') + except UbridgeError as e: + log.warning(f'Could not remove TAP "{tap}" from bridge "{self._bridge_name}": {e}') + try: + await self._ubridge_send(f"bridge delete {ubridge_bridge}") + except UbridgeError as e: + log.warning(f"Could not delete uBridge bridge {ubridge_bridge}: {e}") async def remove_nio(self, port_number): """ - Removes the specified NIO as member of this switch. + Removes the specified NIO from this switch. :param port_number: allocated port number - :returns: the NIO that was bound to the allocated port """ - raise NotImplementedError() + if port_number not in self._nios: + raise NodeError(f"Port {port_number} is not allocated") + + await self.stop_capture(port_number) + nio = self._nios[port_number] + if isinstance(nio, NIOUDP): + self.manager.port_manager.release_udp_port(nio.lport, self._project) + + log.debug( + 'Ethernet switch "{name}" [{id}]: NIO {nio} removed from port {port}'.format( + name=self._name, id=self._id, nio=nio, port=port_number + ) + ) + del self._nios[port_number] + if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running(): + await self._delete_ubridge_connection(port_number) + return nio + + def get_nio(self, port_number): + """ + Gets a port NIO binding. + + :param port_number: port number + :returns: NIO instance + """ + + if port_number not in self._nios: + raise NodeError(f"Port {port_number} is not connected") + return self._nios[port_number] + + async def update_nio(self, port_number, nio): + """ + Re-applies uBridge filters/markers for a port (called when a link is updated). + """ + + ubridge_bridge = self._ubridge_bridge_name(port_number) + if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running(): + await self._ubridge_apply_filters(ubridge_bridge, nio.filters) + await self._ubridge_apply_markers(ubridge_bridge, nio) + + # ------------------------------------------------------------------ # + # VLAN mode translation + # ------------------------------------------------------------------ # + + async def _reset_port_vlan(self, tap): + """ + Resets a port's VLAN membership to the kernel default (PVID 1, untagged) + by re-enslaving it. Used before re-applying a changed mode so stale VIDs + from the previous mode do not leak. + """ + + await self._ubridge_send(f'brctl delif "{self._bridge_name}" "{tap}"') + await self._ubridge_send(f'brctl addif "{self._bridge_name}" "{tap}"') + + async def _apply_port_vlan(self, port_settings, tap): + """ + Translates an ESW port mode into ``brctl`` VLAN primitives. The port must + already be enslaved to the bridge and carry the default PVID 1. + + - access VLAN N: drop default 1, add N as PVID + egress untagged. + - dot1q trunk (native N): drop default 1, admit all VIDs tagged, then mark + the native VLAN PVID + untagged. (The ESW model declares only the native + VLAN per trunk port, so the trunk admits all VIDs, like the emulated ESW.) + - qinq (outer N): the bridge-level protocol is set separately; the port + gets the service VLAN as PVID + untagged so customer frames are S-tagged. + """ + + br = self._bridge_name + port_type = port_settings["type"] + vlan = int(port_settings["vlan"]) + + if port_type == "access": + await self._ubridge_send(f'brctl vlan_del "{br}" "{tap}" 1') + await self._ubridge_send(f'brctl vlan_add "{br}" "{tap}" {vlan} pvid untagged') + elif port_type == "dot1q": + await self._ubridge_send(f'brctl vlan_del "{br}" "{tap}" 1') + await self._ubridge_send(f'brctl vlan_add "{br}" "{tap}" 1 vid 4094') + await self._ubridge_send(f'brctl vlan_add "{br}" "{tap}" {vlan} pvid untagged') + elif port_type == "qinq": + # setvlanproto is applied at the bridge level by _apply_bridge_proto_if_needed + await self._ubridge_send(f'brctl vlan_del "{br}" "{tap}" 1') + await self._ubridge_send(f'brctl vlan_add "{br}" "{tap}" {vlan} pvid untagged') + else: + raise NodeError(f"Unknown port type '{port_type}' on Ethernet switch '{self.name}'") + + async def update_port_settings(self): + """ + Re-applies port settings (called after ``ports_mapping`` is updated). For + ports already wired, reset then re-apply so a mode/VLAN change fully + replaces the previous VLAN membership. + """ + + await self._apply_bridge_proto_if_needed() + if not (self._ubridge_hypervisor and self._ubridge_hypervisor.is_running() and self._bridge_created): + return + for port_settings in self._ports_mapping: + port_number = port_settings["port_number"] + tap = self._tap_by_port.get(port_number) + if tap is None: + continue + await self._reset_port_vlan(tap) + await self._apply_port_vlan(port_settings, tap) + + # ------------------------------------------------------------------ # + # capture + # ------------------------------------------------------------------ # async def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"): """ - Starts a packet capture. + Starts a packet capture on a port (uBridge captures on the per-port relay). :param port_number: allocated port number :param output_file: PCAP destination file for the capture :param data_link_type: PCAP data link type (DLT_*), default is DLT_EN10MB """ - raise NotImplementedError() + nio = self.get_nio(port_number) + if nio.capturing: + raise NodeError(f"Packet capture is already activated on port {port_number}") + nio.start_packet_capture(output_file) + if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running(): + ubridge_bridge = self._ubridge_bridge_name(port_number) + await self._ubridge_send(f'bridge start_capture {ubridge_bridge} "{output_file}"') + log.debug( + 'Ethernet switch "{name}" [{id}]: starting packet capture on port {port}'.format( + name=self.name, id=self.id, port=port_number + ) + ) async def stop_capture(self, port_number): """ - Stops a packet capture. + Stops a packet capture on a port. :param port_number: allocated port number """ - raise NotImplementedError() + nio = self.get_nio(port_number) + if not nio.capturing: + return + nio.stop_packet_capture() + if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running(): + ubridge_bridge = self._ubridge_bridge_name(port_number) + await self._ubridge_send(f"bridge stop_capture {ubridge_bridge}") + log.debug( + 'Ethernet switch "{name}" [{id}]: stopping packet capture on port {port}'.format( + name=self.name, id=self.id, port=port_number + ) + ) diff --git a/gns3server/compute/builtin/nodes/nat.py b/gns3server/compute/builtin/nodes/nat.py index f833f4e50..a3b9b69d4 100644 --- a/gns3server/compute/builtin/nodes/nat.py +++ b/gns3server/compute/builtin/nodes/nat.py @@ -69,7 +69,7 @@ class Nat(Cloud): ) interface = interfaces[0] # take the first available interface containing the vmnet8 name - log.info(f"NAT node '{name}' configured to use NAT interface '{interface}'") + log.debug(f"NAT node '{name}' configured to use NAT interface '{interface}'") ports = [{"name": "nat0", "type": "ethernet", "interface": interface, "port_number": 0}] super().__init__(name, node_id, project, manager, ports=ports) @@ -87,6 +87,23 @@ class Nat(Cloud): return True def asdict(self): + + nat_interface = self._ports_mapping[0].get("interface", "") if self._ports_mapping else "" + + host_interfaces = [] + network_interfaces = gns3server.utils.interfaces.interfaces() + for interface in network_interfaces: + if interface["name"] == nat_interface: + host_interfaces.append( + { + "name": interface["name"], + "type": interface["type"], + "special": interface["special"], + "ip_addresses": interface.get("ip_addresses", []), + } + ) + break + return { "name": self.name, "usage": self.usage, @@ -94,4 +111,5 @@ class Nat(Cloud): "project_id": self.project.id, "status": "started", "ports_mapping": self.ports_mapping, + "interfaces": host_interfaces, } diff --git a/gns3server/compute/docker/__init__.py b/gns3server/compute/docker/__init__.py index 4a64a4d3a..ed37ca626 100644 --- a/gns3server/compute/docker/__init__.py +++ b/gns3server/compute/docker/__init__.py @@ -32,6 +32,7 @@ from gns3server.config import Config from gns3server.utils.asyncio import locking from gns3server.compute.base_manager import BaseManager from gns3server.compute.docker.docker_vm import DockerVM +from gns3server.compute.docker.vendor_docker_vm import VendorDockerVM from gns3server.compute.docker.docker_error import DockerError, DockerHttp304Error, DockerHttp404Error, DockerHttp409Error log = logging.getLogger(__name__) @@ -58,6 +59,17 @@ class Docker(BaseManager): self._connector = None self._session = None self._api_version = DOCKER_MINIMUM_API_VERSION + self._host_checked = False + + def _select_node_class(self, **kwargs): + """Select the node class based on console_type.""" + if kwargs.get("console_type") == "docker_exec": + return VendorDockerVM + return DockerVM + + async def create_node(self, name, project_id, node_id, *args, **kwargs): + self._NODE_CLASS = self._select_node_class(**kwargs) + return await super().create_node(name, project_id, node_id, *args, **kwargs) @staticmethod async def install_busybox(dst_dir): @@ -149,6 +161,62 @@ class Docker(BaseManager): log.warning("Using Docker client with the minimum API version {}".format(self._api_version)) log.info("Connected to Docker daemon version {} using API version {}".format(version, self._api_version)) + self._check_host_readiness() + + def _check_host_readiness(self): + """ + Best-effort, read-only check of kernel settings that heavy NOS containers + (e.g. Cisco XRd) need. The server runs unprivileged (only the setuid + ubridge helper gets root), so we cannot raise these limits ourselves -- + we only warn, with the exact commands to fix, when they are too low or + when FUSE support is missing. Runs at most once per process. + """ + + if self._host_checked: + return + self._host_checked = True + + # Thresholds recommended for running several heavy containers (sized for + # ~15 XRd-style nodes). Raising them is harmless; the stock Linux defaults + # (e.g. max_user_instances=128) are far too low and break such images. + thresholds = { + "fs.inotify.max_user_instances": 64000, + "fs.inotify.max_user_watches": 524288, + "fs.file-max": 1000000, + } + low = [] + for key, minimum in thresholds.items(): + try: + with open(f"/proc/sys/{key.replace('.', '/')}") as f: + current = int(f.read().strip()) + except (OSError, ValueError): + # One unreadable key must not discard the warnings already + # collected nor skip the FUSE check — skip just this key. + continue + if current < minimum: + low.append((key, current, minimum)) + + fuse_supported = False + try: + with open("/proc/filesystems") as f: + filesystems = {parts[-1] for parts in (line.split() for line in f) if parts} + fuse_supported = "fuse" in filesystems or "fuseblk" in filesystems + except OSError: + pass + + if low: + details = ", ".join(f"{k}={c} (need >={m})" for k, c, m in low) + raise_cmd = " ".join(f"{k}={m}" for k, _, m in low) + log.warning( + f"Low kernel limits for heavy Docker containers ({details}). " + f"Some NOS images (e.g. Cisco XRd) may fail to start. Raise once: " + f"'sudo sysctl -w {raise_cmd}' and persist it under /etc/sysctl.d/." + ) + if not fuse_supported: + log.warning( + "FUSE filesystem support is not available in the kernel. " + "Containers that need it (e.g. Cisco XRd) will fail. Load it: 'sudo modprobe fuse'." + ) def connector(self): @@ -260,19 +328,21 @@ class Docker(BaseManager): return connection @locking - async def pull_image(self, image, progress_callback=None): + async def pull_image(self, image, progress_callback=None, force=False): """ Pulls an image from the Docker repository :params image: Image name :params progress_callback: A function that receive a log message about image download progress + :params force: Pull the image even if it is already available locally """ - try: - await self.query("GET", f"images/{image}/json") - return # We already have the image skip the download - except DockerHttp404Error: - pass + if not force: + try: + await self.query("GET", f"images/{image}/json") + return # We already have the image skip the download + except DockerHttp404Error: + pass if progress_callback: progress_callback(f"Pulling '{image}' from Docker repository") @@ -285,29 +355,45 @@ class Docker(BaseManager): ) # The pull api will stream status via an HTTP JSON stream content = "" - while True: - try: - chunk = await response.content.read(CHUNK_SIZE) - except aiohttp.ServerDisconnectedError: - log.error(f"Disconnected from server while pulling Docker image '{image}' from Docker repository") - break - except asyncio.TimeoutError: - log.error("Timeout while pulling Docker image '{}' from Docker repository".format(image)) - break - if not chunk: - break - content += chunk.decode("utf-8") + try: + while True: + try: + chunk = await response.content.read(CHUNK_SIZE) + except aiohttp.ServerDisconnectedError as e: + raise DockerError( + f"Disconnected while pulling Docker image '{image}' from Docker repository" + ) from e + except asyncio.TimeoutError as e: + raise DockerError( + f"Timeout while pulling Docker image '{image}' from Docker repository" + ) from e + if not chunk: + break + content += chunk.decode("utf-8") + + try: + while True: + content = content.lstrip(" \r\n\t") + answer, index = json.JSONDecoder().raw_decode(content) + if not isinstance(answer, dict): + raise DockerError(f"Invalid response while pulling Docker image '{image}'") + error_detail = answer.get("errorDetail") + error = answer.get("error") + if not error and isinstance(error_detail, dict): + error = error_detail.get("message") + if error: + raise DockerError(error) + if "progress" in answer and progress_callback: + progress_callback("Pulling image {}:{}: {}".format(image, answer["id"], answer["progress"])) + content = content[index:] + except ValueError: # Partial JSON + pass + + if content.strip(): + raise DockerError(f"Invalid response while pulling Docker image '{image}'") + finally: + response.close() - try: - while True: - content = content.lstrip(" \r\n\t") - answer, index = json.JSONDecoder().raw_decode(content) - if "progress" in answer and progress_callback: - progress_callback("Pulling image {}:{}: {}".format(image, answer["id"], answer["progress"])) - content = content[index:] - except ValueError: # Partial JSON - pass - response.close() if progress_callback: progress_callback(f"Success pulling image {image}") diff --git a/gns3server/compute/docker/docker_vm.py b/gns3server/compute/docker/docker_vm.py index 5219ad4c8..666844875 100644 --- a/gns3server/compute/docker/docker_vm.py +++ b/gns3server/compute/docker/docker_vm.py @@ -69,6 +69,30 @@ class DockerVM(BaseNode): :param extra_volumes: Additional directories to make persistent """ + # systemd units masked by GNS3_MASK_UDEV=1: the udev daemon, its activation + # sockets and the coldplug/settle triggers. Masking them stops a privileged + # systemd container from replaying device events on the host. + _UDEV_UNITS = ( + "systemd-udevd.service", + "systemd-udevd-control.socket", + "systemd-udevd-kernel.socket", + "systemd-udev-trigger.service", + "systemd-udev-settle.service", + ) + + # udevadm binary paths also null-bound by GNS3_MASK_UDEV=1. NOS startup + # scripts call udevadm directly -- Cisco XRd's xr_startup.sh runs + # `udevadm trigger --action=add --parent-match=` (USB license + # dongle probing), which synthesizes uevents into the host kernel from a + # privileged container and reconnects host USB devices. Masking the units + # alone does not stop this; the binary must be neutralized too. XRd boots + # fine without udevadm (interfaces are pre-created by GNS3). + _UDEVADM_PATHS = ( + "/bin/udevadm", + "/sbin/udevadm", + "/usr/bin/udevadm", + ) + def __init__( self, name, @@ -89,6 +113,7 @@ class DockerVM(BaseNode): console_http_path="/", extra_hosts=None, extra_volumes=[], + extra_configs=None, memory=0, cpus=0, ): @@ -104,8 +129,10 @@ class DockerVM(BaseNode): if ":" not in image: image = f"{image}:latest" self._image = image - self._start_command = start_command - self._environment = environment + # assign through the property setters so creation and updates apply + # the same value normalization (e.g. "" -> None) + self.start_command = start_command + self.environment = environment self._cid = None self._ethernet_adapters = [] self._temporary_directory = None @@ -113,11 +140,12 @@ class DockerVM(BaseNode): self._vnc_process = None self._vncconfig_process = None self._console_resolution = console_resolution - self._console_http_path = console_http_path + self.console_http_path = console_http_path self._console_http_port = console_http_port self._console_websocket = None - self._extra_hosts = extra_hosts + self.extra_hosts = extra_hosts self._extra_volumes = extra_volumes or [] + self._extra_configs = extra_configs or [] self._memory = memory self._cpus = cpus self._permissions_fixed = True @@ -164,6 +192,7 @@ class DockerVM(BaseNode): "node_directory": self.working_path, "extra_hosts": self.extra_hosts, "extra_volumes": self.extra_volumes, + "extra_configs": self.extra_configs, "memory": self.memory, "cpus": self.cpus, } @@ -228,7 +257,7 @@ class DockerVM(BaseNode): else: self._mac_address = mac_address - log.info('Docker container "{name}" [{id}]: MAC address changed to {mac_addr}'.format( + log.debug('Docker container "{name}" [{id}]: MAC address changed to {mac_addr}'.format( name=self._name, id=self._id, mac_addr=self._mac_address) @@ -261,7 +290,9 @@ class DockerVM(BaseNode): @console_http_path.setter def console_http_path(self, path): - self._console_http_path = path + # the canonical "no path" value is "/" so that "", None and "/" + # all compare equal in the update diff + self._console_http_path = path or "/" @property def console_http_port(self): @@ -277,7 +308,8 @@ class DockerVM(BaseNode): @environment.setter def environment(self, command): - self._environment = command + # "" and None are the same "no environment variables" value + self._environment = command or None @property def extra_hosts(self): @@ -285,7 +317,8 @@ class DockerVM(BaseNode): @extra_hosts.setter def extra_hosts(self, extra_hosts): - self._extra_hosts = extra_hosts + # "" and None are the same "no extra hosts" value + self._extra_hosts = extra_hosts or None @property def extra_volumes(self): @@ -295,6 +328,14 @@ class DockerVM(BaseNode): def extra_volumes(self, extra_volumes): self._extra_volumes = extra_volumes + @property + def extra_configs(self): + return self._extra_configs + + @extra_configs.setter + def extra_configs(self, extra_configs): + self._extra_configs = extra_configs or [] + @property def memory(self): return self._memory @@ -338,6 +379,51 @@ class DockerVM(BaseNode): result = await self.manager.query("GET", f"images/{self._image}/json") return result + def _persistent_volume_list(self, image_info, include_network_config=True): + """ + The in-container paths that get a persistent volume mount: GNS3's + /etc/network, every VOLUME declared by the image and the node's + extra_volumes. Overlapping paths are de-duplicated so that a path + covered by a more general volume is not mounted twice. + + :param include_network_config: include GNS3's hardcoded /etc/network + volume (consumed by init.sh; subclasses that skip init.sh pass + False so the list matches the mounts they actually create). + """ + + for volume in self._extra_volumes: + if not volume.strip() or volume[0] != "/" or volume.find("..") >= 0: + raise DockerError( + f"Persistent volume '{volume}' has invalid format. It must start with a '/' and not contain '..'." + ) + volumes = [] + if include_network_config: + volumes.append("/etc/network") + volumes.extend((image_info.get("Config", {}).get("Volumes") or {}).keys()) + volumes.extend(self._extra_volumes) + + deduped = [] + # define lambdas for validation checks + nf = lambda x: re.sub(r"//+", "/", (x if x.endswith("/") else x + "/")) + generalises = lambda v1, v2: nf(v2).startswith(nf(v1)) + for volume in volumes: + # remove any mount that is equal or more specific, then append this one + deduped = list(filter(lambda v: not generalises(volume, v), deduped)) + # if there is nothing more general, append this mount + if not [v for v in deduped if generalises(v, volume)]: + deduped.append(volume) + return deduped + + async def _prepare_volumes(self, image_info): + """ + Hook: prepare persistent volumes before the container (and its + mounts) are created. The default implementation does nothing — + init.sh performs the first-copy seeding inside the container at + boot. Subclasses that skip init.sh override this to seed the host + directories from the image instead, so their mounts can be bound + directly at the real in-container paths from the very first process. + """ + def _mount_binds(self, image_info): """ :returns: Return the path that we need to map to local folders @@ -348,7 +434,7 @@ class DockerVM(BaseNode): except OSError as e: raise DockerError(f"Cannot access resources: {e}") - log.info(f'Mount resources from "{resources_path}"') + log.debug(f'Mount resources from "{resources_path}"') binds = [{ "Type": "bind", "Source": resources_path, @@ -361,26 +447,7 @@ class DockerVM(BaseNode): self._create_network_config() except OSError as e: raise DockerError(f"Could not create network config in the container: {e}") - volumes = ["/etc/network"] - - volumes.extend((image_info.get("Config", {}).get("Volumes") or {}).keys()) - for volume in self._extra_volumes: - if not volume.strip() or volume[0] != "/" or volume.find("..") >= 0: - raise DockerError( - f"Persistent volume '{volume}' has invalid format. It must start with a '/' and not contain '..'." - ) - volumes.extend(self._extra_volumes) - - self._volumes = [] - # define lambdas for validation checks - nf = lambda x: re.sub(r"//+", "/", (x if x.endswith("/") else x + "/")) - generalises = lambda v1, v2: nf(v2).startswith(nf(v1)) - for volume in volumes: - # remove any mount that is equal or more specific, then append this one - self._volumes = list(filter(lambda v: not generalises(volume, v), self._volumes)) - # if there is nothing more general, append this mount - if not [v for v in self._volumes if generalises(v, volume)]: - self._volumes.append(volume) + self._volumes = self._persistent_volume_list(image_info) for volume in self._volumes: source = os.path.join(self.working_dir, os.path.relpath(volume, "/")) @@ -391,6 +458,39 @@ class DockerVM(BaseNode): "Target": "/gns3volumes{}".format(volume) }) + # Inject extra config files: write each to the node working directory and + # bind-mount it read-only at its target path. Single-file binds are applied + # at create time, so this works for the generic init.sh path AND for vendor + # nodes that skip init.sh (the NOS reads its startup config from the mount). + for cfg in self._extra_configs: + target = cfg["target"] if isinstance(cfg, dict) else cfg.target + content = cfg["content"] if isinstance(cfg, dict) else cfg.content + if not target.startswith("/") or target.endswith("/") or ".." in target.split("/"): + raise DockerError( + f"Extra config target '{target}' must be an absolute file path and not contain '..'." + ) + for volume in self._volumes: + # A single-file bind gets covered by the volume's bind mount at + # start (init.sh or the vendor volume bridge), so the injected + # content would never be seen — or worse, frozen at whatever + # the first-start seed copied. + if target == volume or target.startswith(volume.rstrip("/") + "/"): + log.warning( + "Extra config target '%s' on container '%s' is shadowed by persisted volume '%s' " + "and will not take effect; pick a target outside persisted volumes.", + target, self._name, volume, + ) + host_path = os.path.join(self.working_dir, "configs", target.lstrip("/")) + os.makedirs(os.path.dirname(host_path), exist_ok=True) + with open(host_path, "w") as f: + f.write(content) + binds.append({ + "Type": "bind", + "Source": host_path, + "Target": target, + "ReadOnly": True, + }) + return binds def _create_network_config(self): @@ -411,6 +511,11 @@ class DockerVM(BaseNode): f.write("""# # This is a sample network config, please uncomment lines to configure the network # +# NOTE: at boot /gns3/init.sh applies this file with BusyBox ifup ("ifup -a -f"). +# BusyBox ifupdown only brings up "auto" stanzas and requires separate +# "address " and "netmask " lines: CIDR notation such as +# "address 10.0.0.1/24" is rejected and the interface stays unconfigured. +# # Uncomment this line to load custom interface files # source /etc/network/interfaces.d/* @@ -433,6 +538,16 @@ class DockerVM(BaseNode): """.format(adapter=adapter, hostname=self._name)) return path + def _prepare_init_and_interface_env(self, params): + """ + Prepare the init-script entrypoint and GNS3_MAX_ETHERNET env var. + May be overridden by subclasses (e.g. VendorDockerVM) to skip init.sh + or rename injected interfaces. + """ + params["Entrypoint"].insert(0, "/gns3/init.sh") # FIXME /gns3/init.sh is not found? + # Give the information to the container on how many interface should be inside + params["Env"].append(f"GNS3_MAX_ETHERNET=eth{self.adapters - 1}") + async def create(self): """ Creates the Docker container. @@ -460,6 +575,10 @@ class DockerVM(BaseNode): f"(max available is {available_cpus} CPUs)" ) + # Prepare persistent volume content before the container and its + # mounts are created (no-op for the init.sh path). + await self._prepare_volumes(image_infos) + params = { "Hostname": self._name, "Image": self._image, @@ -481,6 +600,68 @@ class DockerVM(BaseNode): "Entrypoint": image_infos.get("Config", {"Entrypoint": []}).get("Entrypoint"), } + # Optional /dev/shm size and host device mappings requested through the + # environment (GNS3_SHM_SIZE in MB, GNS3_DEVICES). These are native Docker + # HostConfig keys applied at create time, so they work whether or not + # init.sh runs -- heavy NOS containers such as Cisco XRd (which skips + # init.sh via the vendor/docker_exec path) rely on them. Only injected + # when set, so ordinary nodes keep the default Docker behaviour. + if self._environment: + for line in self._environment.splitlines(): + # Strip a trailing comma like the vendor-class parser does, so + # "GNS3_MASK_UDEV=1," composed from a comma-separated list + # still activates (values are never comma-separated here). + line = line.strip().rstrip(",") + if line.startswith("GNS3_SHM_SIZE="): + try: + params["HostConfig"]["ShmSize"] = int(line.split("=", 1)[1].strip()) * (1024 * 1024) + except ValueError: + pass + elif line.startswith("GNS3_DEVICES="): + devices = self._format_devices(line.split("=", 1)[1]) + if devices: + params["HostConfig"]["Devices"] = devices + elif line.startswith("GNS3_MASK_UDEV=") and \ + line.split("=", 1)[1].strip().lower() in ("1", "true", "yes"): + # A privileged systemd-based NOS container (e.g. Cisco XRd) + # runs systemd-udevd, which coldplugs every device it can see + # -- and in privileged mode that includes the HOST's USB/input/ + # audio/disk devices, reconnecting/muting them on every start. + # XRd doesn't need udev (interfaces are pre-created by GNS3), so + # bind /dev/null over the udev units to keep it from running. + for target in [f"/etc/systemd/system/{u}" for u in self._UDEV_UNITS] + list(self._UDEVADM_PATHS): + params["HostConfig"]["Mounts"].append({ + "Type": "bind", + "Source": "/dev/null", + "Target": target, + "ReadOnly": True, + }) + elif line.startswith("GNS3_MASK_SYSTEMD="): + # Generic form: comma/semicolon-separated unit names to mask + # the same way (bind /dev/null over /etc/systemd/system/). + for unit in line.split("=", 1)[1].replace(";", ",").split(","): + unit = unit.strip() + if unit and "/" not in unit and ".." not in unit: + params["HostConfig"]["Mounts"].append({ + "Type": "bind", + "Source": "/dev/null", + "Target": f"/etc/systemd/system/{unit}", + "ReadOnly": True, + }) + + # Overlapping bind targets (GNS3_MASK_UDEV together with a + # GNS3_MASK_SYSTEMD entry for the same unit, an extra_configs target + # equal to a masked unit, a unit named twice in the list) make Docker + # reject the create outright ("Duplicate mount point") — keep only + # the first occurrence of each target. + seen_targets = set() + deduped_mounts = [] + for mount in params["HostConfig"]["Mounts"]: + if mount["Target"] not in seen_targets: + seen_targets.add(mount["Target"]) + deduped_mounts.append(mount) + params["HostConfig"]["Mounts"] = deduped_mounts + if params["Entrypoint"] is None: params["Entrypoint"] = [] if self._start_command: @@ -494,10 +675,7 @@ class DockerVM(BaseNode): params["Cmd"] = [] if len(params["Cmd"]) == 0 and len(params["Entrypoint"]) == 0: params["Cmd"] = ["/bin/sh"] - params["Entrypoint"].insert(0, "/gns3/init.sh") # FIXME /gns3/init.sh is not found? - - # Give the information to the container on how many interface should be inside - params["Env"].append(f"GNS3_MAX_ETHERNET=eth{self.adapters - 1}") + self._prepare_init_and_interface_env(params) # Give the information to the container the list of volume path mounted params["Env"].append("GNS3_VOLUMES={}".format(":".join(self._volumes))) @@ -511,8 +689,18 @@ class DockerVM(BaseNode): variables = [] for var in variables: - formatted = self._format_env(variables, var.get("value", "")) - params["Env"].append("{}={}".format(var["name"], formatted)) + # Handle both Pydantic Variable objects and dictionaries + if hasattr(var, "name"): + # Pydantic Variable object + var_name = var.name + var_value = getattr(var, "value", "") + else: + # Dictionary format + var_name = var.get("name", "") + var_value = var.get("value", "") + + formatted = self._format_env(variables, var_value) + params["Env"].append("{}={}".format(var_name, formatted)) if self._environment: for e in self._environment.strip().split("\n"): @@ -572,16 +760,26 @@ class DockerVM(BaseNode): log.error(f"Failed to clean up conflicting container '{self.docker_name}': {e}") raise self._cid = result["Id"] - log.info(f"Docker container '{self._name}' [{self._id}] created") + log.debug(f"Docker container '{self._name}' [{self._id}] created") if self._cpus > 0: - log.info(f"CPU limit set to {self._cpus} CPUs") + log.debug(f"CPU limit set to {self._cpus} CPUs") if self._memory > 0: - log.info(f"Memory limit set to {self._memory} MB") + log.debug(f"Memory limit set to {self._memory} MB") return True def _format_env(self, variables, env): for variable in variables: - env = env.replace("${" + variable["name"] + "}", variable.get("value", "")) + # Handle both Pydantic Variable objects and dictionaries + if hasattr(variable, "name"): + # Pydantic Variable object + var_name = variable.name + var_value = getattr(variable, "value", "") + else: + # Dictionary format + var_name = variable.get("name", "") + var_value = variable.get("value", "") + + env = env.replace("${" + var_name + "}", var_value) return env def _format_extra_hosts(self, extra_hosts): @@ -598,6 +796,37 @@ class DockerVM(BaseNode): raise DockerError(f"Can't apply `ExtraHosts`, wrong format: {extra_hosts}") return "\n".join([f"{h[1]}\t{h[0]}" for h in hosts]) + def _format_devices(self, devices_value): + """ + Parse a GNS3_DEVICES value into Docker HostConfig Devices entries. + + Mirrors `docker run --device`: items are whitespace/comma-separated and + each is ``host[:container[:permissions]]`` (e.g. /dev/fuse, + /dev/fuse:/dev/fuse:rwm). Docker resolves type/major/minor from the host + node itself, so the device must exist on the host -- the host-readiness + check warns when /dev/fuse is missing (load the fuse module). + """ + + formatted = [] + for raw in devices_value.replace(",", " ").split(): + parts = raw.split(":") + if len(parts) == 1: + on_host = in_container = parts[0] + permissions = "rwm" + elif len(parts) == 2: + on_host, in_container = parts + permissions = "rwm" + elif len(parts) == 3: + on_host, in_container, permissions = parts + else: + continue + formatted.append({ + "PathOnHost": on_host, + "PathInContainer": in_container, + "CgroupPermissions": permissions, + }) + return formatted + async def update(self): """ Destroy and recreate the container with the new settings @@ -645,10 +874,17 @@ class DockerVM(BaseNode): if self._console_websocket: await self._console_websocket.close() self._console_websocket = None + self._cleanup_console_resources() await self._clean_servers() await self.manager.query("POST", f"containers/{self._cid}/start") await asyncio.sleep(0.5) # give the Docker container some time to start + # Fix host-side directory ownership after Docker (re)creates + # volume mount points as root (rootful Docker only). + # This allows the GNS3 process to write files into node directories + # while the container is running. Permissions are recorded and + # restored inside the container by init.sh on next startup. + # await self._fix_permissions() self._namespace = await self._get_namespace() await self._start_ubridge(require_privileged_access=True) @@ -668,22 +904,29 @@ class DockerVM(BaseNode): log.error(line) raise DockerError(logdata) - if self.console_type in ("telnet", "ssh"): - await self._start_console() - elif self.console_type == "http" or self.console_type == "https": - await self._start_http() + await self._start_console_server() if self.aux_type != "none": await self._start_aux() self._permissions_fixed = False self.status = "started" - log.info( + log.debug( "Docker container '{name}' [{image}] started listen for {console_type} on {console}".format( name=self._name, image=self._image, console=self.console, console_type=self.console_type ) ) + async def _start_console_server(self): + """ + Dispatch the console server start based on console_type. + May be overridden to add extra console types (e.g. docker_exec). + """ + if self.console_type in ("telnet", "ssh"): + await self._start_console() + elif self.console_type == "http" or self.console_type == "https": + await self._start_http() + async def _start_aux(self): """ Start an auxiliary console @@ -724,7 +967,7 @@ class DockerVM(BaseNode): """ state = await self._get_container_state() - log.info(f"Docker container '{self._name}' fix ownership, state = {state}") + log.debug(f"Docker container '{self._name}' fix ownership, state = {state}") if state == "stopped" or state == "exited": # We need to restart it to fix permissions await self.manager.query("POST", f"containers/{self._cid}/start") @@ -752,11 +995,19 @@ class DockerVM(BaseNode): ' && /gns3/bin/busybox chown {uid}:{gid} -R "{path}"'.format( uid=os.getuid(), gid=os.getgid(), path=volume ), + stderr=asyncio.subprocess.PIPE, ) except OSError as e: raise DockerError(f"Could not fix permissions for {volume}: {e}") await process.wait() - self._permissions_fixed = True + if process.returncode != 0: + stderr = (await process.stderr.read()).decode(errors="replace").strip() + log.error( + "Failed to fix permissions on '%s' for container '%s': %s", + volume, self._name, stderr or f"exit code {process.returncode}" + ) + else: + self._permissions_fixed = True async def _start_vnc_process(self, restart=False): """ @@ -976,7 +1227,14 @@ class DockerVM(BaseNode): """ await self.manager.query("POST", f"containers/{self._cid}/restart") - log.info("Docker container '{name}' [{image}] restarted".format(name=self._name, image=self._image)) + log.debug("Docker container '{name}' [{image}] restarted".format(name=self._name, image=self._image)) + + def _cleanup_console_resources(self): + """ + Clean up console resources before restart. + May be overridden (e.g. VendorDockerVM closes the exec pty socket). + """ + pass async def _clean_servers(self): """ @@ -989,15 +1247,21 @@ class DockerVM(BaseNode): await telnet_server.wait_closed() self._telnet_servers = [] - async def stop(self): + async def stop(self, graceful: bool = False): """ Stops this Docker container. + + :param graceful: request a graceful SIGTERM shutdown (honoured by the + vendor NOS override). The default immediate kill is used on the + internal paths (delete, update, close, crash cleanup), where the + container is force-deleted or recreated right after anyway. """ try: if self._console_websocket: await self._console_websocket.close() self._console_websocket = None + self._cleanup_console_resources() await self._clean_servers() await self._stop_ubridge() @@ -1014,12 +1278,11 @@ class DockerVM(BaseNode): await self._fix_permissions() state = await self._get_container_state() - if state != "stopped" or state != "exited": - # t=5 number of seconds to wait before killing the container + if state != "stopped" and state != "exited": try: - await self.manager.query("POST", f"containers/{self._cid}/stop", params={"t": 5}) - log.info(f"Docker container '{self._name}' [{self._image}] stopped") - except DockerHttp304Error: + await self._terminate_container(graceful=graceful) + log.debug(f"Docker container '{self._name}' [{self._image}] stopped") + except DockerHttp409Error: # Container is already stopped pass # Ignore runtime error because when closing the server @@ -1028,6 +1291,20 @@ class DockerVM(BaseNode): return self.status = "stopped" + async def _terminate_container(self, graceful: bool = False): + """ + Final termination of a still-running container: immediate SIGKILL. + GNS3 has already persisted container state (permissions via + _fix_permissions, /gns3volumes) before this point, and the business + process (often an interactive shell) ignores SIGTERM — a stop grace + period buys nothing but latency. Vendor NOS containers override this + with a graceful SIGTERM shutdown when asked (see VendorDockerVM); + the ``graceful`` flag is accepted here only for signature + compatibility. + """ + + await self.manager.query("POST", f"containers/{self._cid}/kill") + async def pause(self): """ Pauses this Docker container. @@ -1035,7 +1312,7 @@ class DockerVM(BaseNode): await self.manager.query("POST", f"containers/{self._cid}/pause") self.status = "suspended" - log.info(f"Docker container '{self._name}' [{self._image}] paused") + log.debug(f"Docker container '{self._name}' [{self._image}] paused") async def unpause(self): """ @@ -1044,7 +1321,7 @@ class DockerVM(BaseNode): await self.manager.query("POST", f"containers/{self._cid}/unpause") self.status = "started" - log.info(f"Docker container '{self._name}' [{self._image}] unpaused") + log.debug(f"Docker container '{self._name}' [{self._image}] unpaused") async def close(self): """ @@ -1096,7 +1373,7 @@ class DockerVM(BaseNode): # Container deletion failed - log warning but don't block project close # The stale container will be cleaned up when the project is opened again log.warning(f"Failed to delete Docker container '{self.docker_name}': {e}") - log.info("Docker container '{name}' [{image}] removed".format(name=self._name, image=self._image)) + log.debug("Docker container '{name}' [{image}] removed".format(name=self._name, image=self._image)) if release_nio_udp_ports: for adapter in self._ethernet_adapters: @@ -1109,6 +1386,13 @@ class DockerVM(BaseNode): log.debug(f"Docker error when closing: {str(e)}") return + def _get_container_ifname(self, adapter_number): + """ + Return the interface name used inside the container for *adapter_number*. + May be overridden to provide custom naming (e.g. mgmt0, e1-1). + """ + return f"eth{adapter_number}" + async def _add_ubridge_connection(self, nio, adapter_number): """ Creates a connection in uBridge. @@ -1157,17 +1441,16 @@ class DockerVM(BaseNode): log.warning(f"Could not set MAC address {mac_address} on interface {adapter.host_ifc}") - log.debug(f"Move container {self.name} adapter {adapter.host_ifc} to namespace {self._namespace}") + ifname = self._get_container_ifname(adapter_number) + log.debug(f"Move container {self.name} adapter {adapter.host_ifc} -> {ifname} in ns {self._namespace}") try: await self._ubridge_send( - "docker move_to_ns {ifc} {ns} eth{adapter}".format( - ifc=adapter.host_ifc, ns=self._namespace, adapter=adapter_number - ) + f"docker move_to_ns {adapter.host_ifc} {self._namespace} {ifname}" ) except UbridgeError as e: raise UbridgeNamespaceError(e) else: - log.info(f"Created adapter {adapter_number} with MAC address {mac_address} in namespace {self._namespace}") + log.debug(f"Created adapter {adapter_number} with MAC address {mac_address} in namespace {self._namespace}") if nio: await self._connect_nio(adapter_number, nio) @@ -1185,7 +1468,6 @@ class DockerVM(BaseNode): bridge_name=bridge_name, lport=nio.lport, rhost=nio.rhost, rport=nio.rport ) ) - if nio.capturing: await self._ubridge_send( 'bridge start_capture {bridge_name} "{pcap_file}"'.format( @@ -1194,6 +1476,7 @@ class DockerVM(BaseNode): ) await self._ubridge_send(f"bridge start {bridge_name}") await self._ubridge_apply_filters(bridge_name, nio.filters) + await self._ubridge_apply_markers(bridge_name, nio) async def adapter_add_nio_binding(self, adapter_number, nio): """ @@ -1216,7 +1499,7 @@ class DockerVM(BaseNode): await self._connect_nio(adapter_number, nio) adapter.add_nio(0, nio) - log.info( + log.debug( "Docker container '{name}' [{id}]: {nio} added to adapter {adapter_number}".format( name=self.name, id=self._id, nio=nio, adapter_number=adapter_number ) @@ -1234,7 +1517,7 @@ class DockerVM(BaseNode): bridge_name = f"bridge{adapter_number}" if bridge_name in self._bridges: await self._ubridge_apply_filters(bridge_name, nio.filters) - + await self._ubridge_apply_markers(bridge_name, nio) async def adapter_remove_nio_binding(self, adapter_number): """ Removes an adapter NIO binding. @@ -1266,7 +1549,7 @@ class DockerVM(BaseNode): adapter.remove_nio(0) - log.info( + log.debug( "Docker VM '{name}' [{id}]: {nio} removed from adapter {adapter_number}".format( name=self.name, id=self.id, nio=adapter.host_ifc, adapter_number=adapter_number ) @@ -1323,7 +1606,7 @@ class DockerVM(BaseNode): for adapter_number in range(0, adapters): self._ethernet_adapters.append(EthernetAdapter()) - log.info( + log.debug( 'Docker container "{name}" [{id}]: number of Ethernet adapters changed to {adapters}'.format( name=self._name, id=self._id, adapters=adapters ) @@ -1380,7 +1663,7 @@ class DockerVM(BaseNode): if self.status == "started" and self.ubridge: await self._start_ubridge_capture(adapter_number, output_file) - log.info( + log.debug( "Docker VM '{name}' [{id}]: starting packet capture on adapter {adapter_number}".format( name=self.name, id=self.id, adapter_number=adapter_number ) @@ -1400,7 +1683,7 @@ class DockerVM(BaseNode): if self.status == "started" and self.ubridge: await self._stop_ubridge_capture(adapter_number) - log.info( + log.debug( "Docker VM '{name}' [{id}]: stopping packet capture on adapter {adapter_number}".format( name=self.name, id=self.id, adapter_number=adapter_number ) diff --git a/gns3server/compute/docker/resources/init.sh b/gns3server/compute/docker/resources/init.sh index 05c6ee2e0..a9488f333 100755 --- a/gns3server/compute/docker/resources/init.sh +++ b/gns3server/compute/docker/resources/init.sh @@ -87,6 +87,11 @@ sed -n 's/^ *\(eth[0-9]*\):.*/\1/p' < /proc/net/dev | while read dev; do done # configure network interfaces +# NOTE: unless the image ships its own ifupdown in /sbin this is BusyBox ifup, +# which only brings up 'auto' stanzas and does NOT understand CIDR notation: +# 'address ' and 'netmask ' must be separate lines, otherwise the +# stanza fails with "don't have all variables for /inet" and the +# interface comes up unconfigured. ifup -a -f # continue normal docker startup diff --git a/gns3server/compute/docker/vendor_docker_vm.py b/gns3server/compute/docker/vendor_docker_vm.py new file mode 100644 index 000000000..6089663f2 --- /dev/null +++ b/gns3server/compute/docker/vendor_docker_vm.py @@ -0,0 +1,637 @@ +# +# Copyright (C) 2025 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Vendor NOS Docker container subclass. + +Provides support for vendor NOS containers (Nokia SR Linux, Arista cEOS, +Juniper cRPD, …) whose CLI is a separate TUI process not exposed on PID 1 +stdio, and whose boot model requires skipping GNS3's init.sh bootstrapping. + +The subclass is selected automatically when ``console_type == "docker_exec"``. +All vendor features are opt-in — without GNS3_* environment variables the +container behaves identically to DockerVM. +""" + +import asyncio +import contextlib +import json +import logging +import os +import shutil + +from gns3server.utils.asyncio.telnet_server import AsyncioTelnetServer +from gns3server.compute.docker.docker_vm import DockerVM +from gns3server.compute.docker.docker_error import DockerError, DockerHttp304Error, DockerHttp404Error + +log = logging.getLogger(__name__) + + +class VendorDockerVM(DockerVM): + """ + DockerVM subclass for vendor NOS containers. + + Opt-in features, activated by GNS3_-prefixed environment entries + (host-side only — GNS3_ entries are never forwarded into the container): + + * ``GNS3_SKIP_INIT=1`` — do not prepend /gns3/init.sh; the container runs + its own entrypoint (e.g. SR Linux's ``sr_linux``). Persistent volumes + are seeded host-side and bound directly at their real in-container + paths at create time (see ``_prepare_volumes`` / ``_mount_binds``), so + the NOS sees its saved configuration from the very first process — + no post-start mount pass that could race the NOS reading its config. + * ``GNS3_INTERFACE_NAMES=mgmt0,e1-1,e1-2`` — rename injected interfaces + (adapter order) instead of default ``eth{N}``. + * ``GNS3_CONSOLE_CMD=/opt/srlinux/bin/sr_cli`` — command run inside the + container by the ``docker_exec`` console (defaults to ``/bin/sh``). + * ``GNS3_CONSOLE_RESIZE=0`` — ignore client-driven console resizes + (WS terminal-size frames / telnet NAWS). Set for CLIs that page on the + PTY window size (IOS-XR): the exec PTY must stay at the tall + no-NAWS default for every client, including concurrent netmiko + sessions on the shared exec. + * ``GNS3_STOP_TIMEOUT=60`` — SIGTERM grace period in seconds when stopping + the container (default 60; Docker SIGKILLs once it expires). + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self._console_exec_writer = None + # Parsed eagerly so _get_container_ifname can return the right name, + # and re-parsed on every create() so a PUT to the node's environment + # takes effect on the next (re)create instead of the next reload. + self._parse_vendor_environment() + + def _parse_vendor_environment(self): + """ + (Re)parse the GNS3_* knobs from the current ``environment`` value, + resetting to defaults first so removed entries stop applying. + """ + + self._gns3_init = True + self._interface_names = [] + self._console_cmd = None + self._console_resize = True + self._stop_timeout = 60 + if self._environment: + for _line in self._environment.splitlines(): + _line = _line.strip().rstrip(",") + if _line.startswith("GNS3_SKIP_INIT="): + self._gns3_init = _line.split("=", 1)[1].strip().lower() not in ("1", "true", "yes") + elif _line.startswith("GNS3_INTERFACE_NAMES="): + self._interface_names = [ + n.strip() for n in _line.split("=", 1)[1].split(",") if n.strip() + ] + elif _line.startswith("GNS3_CONSOLE_CMD="): + self._console_cmd = _line.split("=", 1)[1].strip() + elif _line.startswith("GNS3_CONSOLE_RESIZE="): + self._console_resize = _line.split("=", 1)[1].strip().lower() not in ("0", "false", "no") + elif _line.startswith("GNS3_STOP_TIMEOUT="): + try: + timeout = int(_line.split("=", 1)[1].strip()) + # Ceiling is derived from the call chain, not arbitrary: + # the controller's stop request times out at 240 s + # (controller/node.py) and the Docker stop query gets + # this value +30 s as its HTTP timeout — so anything + # above 210 would abort upstream first. + if 1 <= timeout <= 210: + self._stop_timeout = timeout + except ValueError: + pass + + async def create(self): + # The environment may have changed since __init__ (PUT on the node) — + # re-parse the knobs so the recreated container picks them up. + self._parse_vendor_environment() + return await super().create() + + # ---- hook overrides --------------------------------------------------- + + def _mount_binds(self, image_info): + """ + Override: for SKIP_INIT containers, drop GNS3's hardcoded + /etc/network volume. It holds GNS3's own network config consumed by + init.sh's `ifup`; init.sh never runs for SKIP_INIT containers (the + NOS manages its own interfaces), so the mount would be dead weight. + Removes the bind, drops the volume from self._volumes (so + GNS3_VOLUMES and the vendor passes stay consistent) and deletes the + host-side skeleton directory the base class just created. + + Additionally, the persistent volumes are bound directly at their + real in-container paths instead of /gns3volumes. With + init.sh skipped there is no in-container mount pass, so a volume + bound at /gns3volumes would only be moved into place by a post-start + ``docker exec`` — racing the NOS reading its startup configuration + (an SR Linux node booted factory whenever the exec lost that race, + e.g. on the concurrent node starts of a project reload). Binding at + the real path is safe because the content is seeded host-side before + the container is created (see _prepare_volumes): the image's files + are never shadowed by an empty mount. + """ + binds = super()._mount_binds(image_info) + if self._gns3_init: + return binds + binds = [b for b in binds if b.get("Target") != "/gns3volumes/etc/network"] + self._volumes = [v for v in self._volumes if v != "/etc/network"] + shutil.rmtree(os.path.join(self.working_dir, "etc", "network"), ignore_errors=True) + with contextlib.suppress(OSError): + os.rmdir(os.path.join(self.working_dir, "etc")) + + # Re-target the volume binds from /gns3volumes to . + retargeted = [] + for bind in binds: + target = bind.get("Target", "") + if target.startswith("/gns3volumes"): + volume = target[len("/gns3volumes"):] + if volume in self._volumes: + bind = {**bind, "Target": volume} + retargeted.append(bind) + return retargeted + + async def _prepare_volumes(self, image_info): + """ + Override: for SKIP_INIT containers, seed every persistent volume's + host directory with the image's original content *before* the + container is created. This is the host-side replacement of init.sh's + first-copy: because the volume is then bound directly at its real + in-container path (see _mount_binds), the seed must exist first or + the NOS would boot with an empty config directory. + + ``.gns3_perms`` doubles as the seeded marker: a volume that has it + (every node that ever started, on any GNS3 version) is never + re-seeded — a re-seed would overwrite the node's saved + configuration with the factory image content. + """ + if self._gns3_init: + return + volumes = self._persistent_volume_list(image_info, include_network_config=False) + to_seed = [] + for volume in volumes: + host_dir = os.path.join(self.working_dir, os.path.relpath(volume, "/")) + os.makedirs(host_dir, exist_ok=True) + if not os.path.exists(os.path.join(host_dir, ".gns3_perms")): + to_seed.append((volume, host_dir)) + if not to_seed: + return + seed_cid = await self._create_seed_container() + try: + for volume, host_dir in to_seed: + await self._seed_volume_from_container(seed_cid, volume, host_dir) + # Write the marker only after the copy attempt, mirroring + # init.sh: a volume without it is (re)seeded on the next + # create(), so a partial seed self-heals. + open(os.path.join(host_dir, ".gns3_perms"), "a").close() + finally: + await self._remove_seed_container(seed_cid) + + async def _create_seed_container(self): + """ + A throwaway ``docker create`` container (nothing executes) used as + the copy source for seeding persistent volumes with the image's + original content. + """ + + try: + process = await asyncio.subprocess.create_subprocess_exec( + "docker", "create", self._image, + stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, + ) + except OSError as e: + raise DockerError(f"Could not seed persistent volumes for '{self._name}': {e}") + stdout, stderr = await process.communicate() + if process.returncode != 0: + raise DockerError( + f"Could not create a seeding container for image '{self._image}': " + f"{stderr.decode(errors='replace').strip()}" + ) + return stdout.decode().strip() + + async def _seed_volume_from_container(self, seed_cid, volume, host_dir): + """ + Copy one volume's original content from the seeding container to its + host directory with ``docker cp -a`` (preserves modes/ownership; no + dependency on tools inside the image). + """ + + try: + process = await asyncio.subprocess.create_subprocess_exec( + "docker", "cp", "-a", f"{seed_cid}:{volume}/.", host_dir + "/", + stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, + ) + except OSError as e: + raise DockerError(f"Could not seed persistent volume '{volume}' for '{self._name}': {e}") + _, stderr = await process.communicate() + if process.returncode != 0: + # A path the image does not contain (e.g. XRd's /xr-storage-shadow) + # is not an error: the volume starts empty. Same tolerance as + # init.sh's first copy (cp -a ... 2>/dev/null). + log.info( + "Persistent volume '%s' on '%s' not seedable from image '%s' (%s); starting empty", + volume, self._name, self._image, stderr.decode(errors="replace").strip(), + ) + return + log.info("Seeded persistent volume '%s' for '%s' from image '%s'", volume, self._name, self._image) + + async def _remove_seed_container(self, seed_cid): + """ + Best-effort removal of the seeding container. + """ + + try: + process = await asyncio.subprocess.create_subprocess_exec( + "docker", "rm", "-f", seed_cid, + stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, + ) + except OSError: + return + await process.communicate() + + def _prepare_init_and_interface_env(self, params): + """ + Override: conditionally prepend init.sh, and honour + GNS3_INTERFACE_NAMES (if set) for GNS3_MAX_ETHERNET. + """ + if self._gns3_init: + params["Entrypoint"].insert(0, "/gns3/init.sh") + + # Tell init.sh which last interface to wait for; honour the rename if any + # (no-op when init is skipped, but kept consistent). + if self._interface_names and self.adapters - 1 < len(self._interface_names): + last_ifname = self._interface_names[self.adapters - 1] + else: + last_ifname = f"eth{self.adapters - 1}" + params["Env"].append(f"GNS3_MAX_ETHERNET={last_ifname}") + + def _get_container_ifname(self, adapter_number): + """ + Override: honour GNS3_INTERFACE_NAMES (e.g. mgmt0, e1-1) in adapter + order; fall back to eth{N} for unlisted ports. + """ + if self._interface_names and adapter_number < len(self._interface_names): + return self._interface_names[adapter_number] + return f"eth{adapter_number}" + + def _cleanup_console_resources(self): + """ + Override: close the docker-exec pty socket, if any, so the next + restart or stop doesn't leak it. + """ + if self._console_exec_writer: + try: + self._console_exec_writer.close() + except Exception: + pass + self._console_exec_writer = None + + async def _terminate_container(self, graceful: bool = False): + """ + Override: vendor NOS containers run systemd and require a graceful + shutdown (e.g. Cisco XRd treats an abrupt SIGKILL as an unclean + shutdown). + + With ``graceful`` (explicit user stop), send SIGTERM and wait up to + ``GNS3_STOP_TIMEOUT`` seconds (default 60, 1-210 — the ceiling keeps + the +30 s HTTP margin inside the controller's 240 s stop budget) for + the services to stop; Docker SIGKILLs the container itself once the + grace period expires, so no fallback kill is needed. + + Without ``graceful`` (delete/update/close/crash cleanup), fall back to + the base immediate kill: those paths force-delete or recreate the + container right after anyway, so a grace period buys nothing but + latency. + """ + if not graceful: + await super()._terminate_container(graceful=False) + return + try: + response = await self.manager.http_query( + "POST", + f"containers/{self._cid}/stop", + params={"t": self._stop_timeout}, + timeout=self._stop_timeout + 30, + ) + response.close() + except DockerHttp304Error: + pass # already stopped + + async def start(self): + await super().start() + if self.status == "started" and not self._gns3_init: + # Persistent volumes are seeded and bound directly at create time + # (see _prepare_volumes / _mount_binds), so there is no post-start + # bridge to run. Fix host-side ownership right away so the + # controller can read project files while the node runs, and reset + # the "fixed" flag: files written by the container during runtime + # still need the stop-time pass. + await self._fix_permissions() + self._permissions_fixed = False + + async def _fix_permissions(self): + """ + Container-side override of DockerVM._fix_permissions for vendor NOS + containers. The persistent volumes are Docker bind mounts created + with the container (see _mount_binds), so the in-container paths + resolve to the host-side files for the container's whole lifetime — + no /gns3volumes aliasing is needed. + + The busybox script runs inside the container as root (a host-side + GNS3 process may be unprivileged and cannot chown root-owned files). + + Unlike the base implementation, a stopped/exited container is NOT + restarted just to fix permissions (vendor NOS images are heavy to + boot): the pass is skipped and the next start fixes ownership. + """ + try: + state = await self._get_container_state() + except DockerHttp404Error: + log.warning("Container '%s' does not exist, skipping permission fix", self._name) + return + if state == "stopped" or state == "exited": + log.info( + "Container '%s' is %s, skipping permission fix (next start will fix)", + self._name, state, + ) + return + + uid, gid = os.getuid(), os.getgid() + for volume in self._volumes: + target = volume + log.debug("Docker container '%s' fix ownership on %s", self._name, target) + try: + # chown prefers the container's own coreutils over /gns3/bin/busybox: + # busybox is static, and its chown dlopens NSS modules from the + # container, which mismatch the static glibc and abort (glibc + # "sym != NULL") on NOS images whose glibc differs from the host's + # (e.g. Cisco XRd). It falls back to busybox on minimal images that + # ship no chown. cp/chmod/find/stat don't use NSS, so stay busybox. + process = await asyncio.subprocess.create_subprocess_exec( + "docker", + "exec", + self._cid, + "/gns3/bin/busybox", + "sh", + "-c", + "(" + f'/gns3/bin/busybox find "{target}" -depth -print0' + f" | /gns3/bin/busybox xargs -0 /gns3/bin/busybox stat -c '%a:%u:%g:%n' > \"{target}/.gns3_perms\"" + ")" + f' && /gns3/bin/busybox chmod -R u+rX "{target}"' + f' && ( command -v chown >/dev/null 2>&1 && chown {uid}:{gid} -R "{target}" || /gns3/bin/busybox chown {uid}:{gid} -R "{target}" )', + stderr=asyncio.subprocess.PIPE, + ) + except OSError as e: + raise DockerError(f"Could not fix permissions for {volume}: {e}") + await process.wait() + if process.returncode != 0: + stderr = (await process.stderr.read()).decode(errors="replace").strip() + log.error( + "Failed to fix permissions on '%s' for container '%s': %s", + volume, self._name, stderr or f"exit code {process.returncode}", + ) + else: + self._permissions_fixed = True + + async def _start_console_server(self): + """ + Override: add the ``docker_exec`` console type alongside the + telnet/ssh/http types supported by the base class. + """ + if self.console_type == "docker_exec": + await self._start_docker_exec_console() + else: + await super()._start_console_server() + + # ---- docker_exec console implementation -------------------------------- + + async def _start_docker_exec_console(self): + """ + Start a console that runs a command inside the container via the Docker + exec API, bridged to a telnet server. Intended for vendor NOS containers + (e.g. Nokia SR Linux) whose CLI is a separate TUI process not exposed on + PID 1's stdio. + + The exec is created lazily on the first client connection (not when the + node starts) so the command's startup terminal probe has a real xterm.js + client to answer it (CPR / prompt_toolkit). The single exec is then + shared (broadcast) by all clients, matching GNS3's console model. + Command from GNS3_CONSOLE_CMD. + """ + + telnet = _LazyExecTelnetServer( + self, + self.manager, + self._cid, + self._console_cmd or "/bin/sh", + allow_resize=self._console_resize, + ) + try: + self._telnet_servers.append( + await telnet.start(self._manager.port_manager.console_host, self.console) + ) + except OSError as e: + raise DockerError( + f"Could not start console server on socket {self._manager.port_manager.console_host}:{self.console}: {e}" + ) + log.debug(f"Docker container '{self.name}' started docker_exec console (lazy) on {self.console}") + + +class _LazyExecTelnetServer(AsyncioTelnetServer): + """Telnet console whose docker exec (pty + command) is created lazily on + the first client connection and recreated if the upstream dies. + + Extracted to module level (rather than a closure inside + _start_docker_exec_console) so the reconnect/recreate logic is unit-testable. + + Lifecycle: the exec is created on the first connect. When the CLI exits + (quit / idle timeout / crash) the exec pty closes, the broadcast task ends, + and the *next* client connection recreates the exec — with a terminal + attached, so the CLI's startup CPR probe is answered. No ``while true`` + wrapper: that would restart the CLI mid-session with no client to answer + CPR, producing a blank/degraded screen on reconnect. + """ + + def __init__(self, vm, manager, cid, command, allow_resize=True): + super().__init__( + reader=None, + writer=None, + binary=True, + echo=False, + naws=True, + window_size_changed_callback=self._on_naws, + ) + self._vm = vm + self._manager = manager + self._cid = cid + self._command = command + self._allow_resize = allow_resize + self._exec_id = None + self._client_size = None # size received while no exec existed yet + self._broadcast_task = None + self._lock = asyncio.Lock() + self._log_name = f"docker_exec console '{vm.name}'" + + def _upstream_alive(self): + """True if the exec pty + broadcast task are still pumping.""" + if self._exec_id is None or self._writer is None: + return False + if self._writer.is_closing(): + return False + if self._broadcast_task is not None and self._broadcast_task.done(): + return False + return True + + async def _disconnect_client(self, network_writer): + await super()._disconnect_client(network_writer) + # When the last client leaves, restore the tall no-NAWS default: a + # browser client resizes the exec to its own geometry (WS terminal + # size control frames -> NAWS), and the next non-NAWS client (netmiko, + # bare telnet) connecting to the still-live exec would otherwise + # inherit it and hit PTY-window paging (the IOS-XR --More-- trap). + if self._exec_id and not await self._get_connections_snapshot(): + with contextlib.suppress(Exception): + self._client_size = None + await self._resize_exec(511, 10000) + + async def _resize_exec(self, columns, rows): + if not self._exec_id: + # No exec yet (first client still inside client_connected_hook): + # remember the size — the hook applies it right after creation + # instead of the tall default, so it doesn't get overwritten. + self._client_size = (columns, rows) + return + try: + await self._manager.query( + "POST", + f"exec/{self._exec_id}/resize", + params={"h": str(rows), "w": str(columns)}, + ) + except DockerError: + pass + + async def _on_naws(self, columns, rows): + # Client-driven resize (WS terminal-size control frames, telnet NAWS). + # Ignored for paging CLIs (GNS3_CONSOLE_RESIZE=0): with the exec shared + # by all clients, one browser resize would break concurrent netmiko + # sessions that rely on the tall no-paging geometry. + if not self._allow_resize: + return + await self._resize_exec(columns, rows) + + async def run(self, network_reader, network_writer): + """Catch and log any exception that kills the client session.""" + try: + await super().run(network_reader, network_writer) + except Exception as exc: + log.warning(f"{self._log_name}: client session terminated: {exc}", exc_info=True) + + async def _create_exec(self): + # create exec with a pty; run as root (vendor CLIs reject the image's + # default unprivileged user) and export TERM=xterm. + result = await self._manager.query( + "POST", + f"containers/{self._cid}/exec", + data={ + "AttachStdin": True, + "AttachStdout": True, + "AttachStderr": True, + "Tty": True, + "User": "root", + "Env": ["TERM=xterm"], + "Cmd": ["sh", "-c", self._command], + }, + ) + self._exec_id = result["Id"] + log.info(f"{self._log_name}: exec created ({self._exec_id})") + + # start the exec via a hijacked raw HTTP request on the Docker unix + # socket; with Tty:true the response body is a raw bidirectional pty + # byte stream (no multiplexing). + reader, writer = await asyncio.open_unix_connection(self._manager._server_url) + body = json.dumps({"Detach": False, "Tty": True}) + request = ( + f"POST /v{self._manager._api_version}/exec/{self._exec_id}/start HTTP/1.1\r\n" + "Host: docker\r\n" + "Connection: Upgrade\r\n" + "Upgrade: tcp\r\n" + "Content-Type: application/json\r\n" + f"Content-Length: {len(body)}\r\n\r\n{body}" + ).encode() + writer.write(request) + await writer.drain() + try: + headers = await asyncio.wait_for(reader.readuntil(b"\r\n\r\n"), timeout=5) + except (asyncio.IncompleteReadError, asyncio.TimeoutError) as e: + writer.close() + raise DockerError(f"Docker exec start failed: {e}") + status_line = headers.split(b"\r\n", 1)[0] + log.info(f"{self._log_name}: hijacked start -> {status_line.decode(errors='ignore')}") + if b" 101 " not in status_line and b" 200 " not in status_line: + writer.close() + raise DockerError(f"Docker exec start rejected: {status_line.decode(errors='ignore')}") + + # wire the exec stream as this server's upstream and start the broadcast + # task. AsyncioTelnetServer.start() only starts the broadcast when a + # reader is set at construction time, so with a lazy upstream we start + # it manually here. + self._reader = reader + self._writer = writer + self._vm._console_exec_writer = writer # for stop() cleanup + self._broadcast_task = asyncio.create_task(self._broadcast_from_upstream()) + log.info(f"{self._log_name}: broadcast task started, upstream wired, ready") + + async def client_connected_hook(self): + await super().client_connected_hook() + async with self._lock: + # (Re)create the exec if it was never created or has died (CLI + # exited → pty EOF → broadcast task ended). Doing this with a + # client attached means the CLI's startup CPR probe is answered by + # a real terminal. + if not self._upstream_alive(): + log.info(f"{self._log_name}: client connected, (re)creating exec") + # close a half-dead writer before replacing it + if self._writer is not None and not self._writer.is_closing(): + with contextlib.suppress(Exception): + self._writer.close() + try: + await self._create_exec() + except Exception as exc: + log.warning(f"{self._log_name}: failed to create exec: {exc}", exc_info=True) + raise + try: + # Tall/wide default geometry before any NAWS arrives: a + # 24-row PTY makes CLIs that page on the PTY window size + # (e.g. the IOS-XR pager) park at --More-- for clients + # that never negotiate NAWS (netmiko, bare telnet). + # Width 511 matches netmiko's 'terminal width 511'. + # A size already pushed by this client (WS terminal-size + # control frames -> NAWS, racing the exec creation) wins + # over the default. + if self._client_size: + await self._resize_exec(*self._client_size) + else: + await self._resize_exec(511, 10000) + except Exception: + pass + else: + log.info(f"{self._log_name}: client connected, reusing live exec") + # ask the TUI to (re)draw for the client that just connected. + if self._writer: + try: + self._writer.write(b"\x0c") # Ctrl-L -> TUI redraws + await self._writer.drain() + except Exception as exc: + log.warning(f"{self._log_name}: Ctrl-L write failed: {exc}") + log.info(f"{self._log_name}: client_connected_hook done") diff --git a/gns3server/compute/dynamips/__init__.py b/gns3server/compute/dynamips/__init__.py index 3d620b6a2..134ce83e0 100644 --- a/gns3server/compute/dynamips/__init__.py +++ b/gns3server/compute/dynamips/__init__.py @@ -333,9 +333,9 @@ class Dynamips(BaseManager): port_manager = PortManager.instance() hypervisor = Hypervisor(self._dynamips_path, working_dir, server_host, port, port_manager.console_host, bind_console_host) - log.info(f"Creating new hypervisor {hypervisor.host}:{hypervisor.port} with working directory {working_dir}") + log.debug(f"Creating new hypervisor {hypervisor.host}:{hypervisor.port} with working directory {working_dir}") await hypervisor.start() - log.info(f"Hypervisor {hypervisor.host}:{hypervisor.port} has successfully started") + log.debug(f"Hypervisor {hypervisor.host}:{hypervisor.port} has successfully started") await hypervisor.connect() return hypervisor @@ -376,6 +376,7 @@ class Dynamips(BaseManager): raise DynamipsError(f"Could not create an UDP connection to {rhost}:{rport}: {e}") nio = NIOUDP(node, lport, rhost, rport) nio.filters = nio_settings.get("filters", {}) + nio.markers = nio_settings.get("markers", {}) nio.suspend = nio_settings.get("suspend", False) elif nio_settings["type"] == "nio_generic_ethernet": ethernet_device = nio_settings["ethernet_device"] @@ -554,7 +555,7 @@ class Dynamips(BaseManager): :returns: relative path to the created config file """ - log.info(f"Creating config file {path}") + log.debug(f"Creating config file {path}") config_dir = os.path.dirname(path) try: os.makedirs(config_dir, exist_ok=True) diff --git a/gns3server/compute/dynamips/dynamips_hypervisor.py b/gns3server/compute/dynamips/dynamips_hypervisor.py index 187876eb2..997cfce60 100644 --- a/gns3server/compute/dynamips/dynamips_hypervisor.py +++ b/gns3server/compute/dynamips/dynamips_hypervisor.py @@ -90,12 +90,12 @@ class DynamipsHypervisor: if not connection_success: raise DynamipsError(f"Couldn't connect to hypervisor on {host}:{self._port} :{last_exception}") else: - log.info(f"Connected to Dynamips hypervisor on {host}:{self._port} after {time.time() - begin:.4f} seconds") + log.debug(f"Connected to Dynamips hypervisor on {host}:{self._port} after {time.time() - begin:.4f} seconds") try: version = await self.send("hypervisor version") self._version = version[0].split("-", 1)[0] - log.info("Dynamips version {} detected".format(self._version)) + log.debug("Dynamips version {} detected".format(self._version)) except IndexError: log.warning("Dynamips version could not be detected") self._version = "Unknown" diff --git a/gns3server/compute/dynamips/hypervisor.py b/gns3server/compute/dynamips/hypervisor.py index 517605f37..30d9a268b 100644 --- a/gns3server/compute/dynamips/hypervisor.py +++ b/gns3server/compute/dynamips/hypervisor.py @@ -120,14 +120,14 @@ class Hypervisor(DynamipsHypervisor): self._command = self._build_command() env = os.environ.copy() try: - log.info(f"Starting Dynamips: {self._command}") + log.debug(f"Starting Dynamips: {self._command}") self._stdout_file = os.path.join(self.working_dir, f"dynamips_i{self._id}_stdout.txt") - log.info(f"Dynamips process logging to {self._stdout_file}") + log.debug(f"Dynamips process logging to {self._stdout_file}") with open(self._stdout_file, "w", encoding="utf-8") as fd: self._process = await asyncio.create_subprocess_exec( *self._command, stdout=fd, stderr=subprocess.STDOUT, cwd=self._working_dir, env=env ) - log.info(f"Dynamips process started PID={self._process.pid}") + log.debug(f"Dynamips process started PID={self._process.pid}") self._started = True except (OSError, subprocess.SubprocessError) as e: log.error(f"Could not start Dynamips: {e}") @@ -139,7 +139,7 @@ class Hypervisor(DynamipsHypervisor): """ if self.is_running(): - log.info(f"Stopping Dynamips process PID={self._process.pid}") + log.debug(f"Stopping Dynamips process PID={self._process.pid}") await DynamipsHypervisor.stop(self) # give some time for the hypervisor to properly stop. # time to delete UNIX NIOs for instance. diff --git a/gns3server/compute/dynamips/nios/nio.py b/gns3server/compute/dynamips/nios/nio.py index 2872b89eb..5c5c9ec6d 100644 --- a/gns3server/compute/dynamips/nios/nio.py +++ b/gns3server/compute/dynamips/nios/nio.py @@ -40,6 +40,7 @@ class NIO: self._hypervisor = hypervisor self._name = name self._filters = {} + self._markers = {} self._suspended = False self._capturing = False self._pcap_output_file = "" @@ -303,6 +304,26 @@ class NIO: assert isinstance(new_filters, dict) self._filters = new_filters + @property + def markers(self): + """ + Returns the list of traffic-insight markers for this NIO. + + :returns: markers (dictionary) + """ + + return self._markers + + @markers.setter + def markers(self, new_markers): + """ + Set markers for this NIO. + + :param new_markers: markers (dictionary) + """ + + self._markers = new_markers + @property def capturing(self): """ diff --git a/gns3server/compute/dynamips/nios/nio_udp.py b/gns3server/compute/dynamips/nios/nio_udp.py index 47faacc43..6a7590cbd 100644 --- a/gns3server/compute/dynamips/nios/nio_udp.py +++ b/gns3server/compute/dynamips/nios/nio_udp.py @@ -73,7 +73,7 @@ class NIOUDP(NIO): ) ) - log.info( + log.debug( "NIO UDP {name} created with lport={lport}, rhost={rhost}, rport={rport}".format( name=self._name, lport=self._lport, rhost=self._rhost, rport=self._rport ) @@ -82,10 +82,12 @@ class NIOUDP(NIO): self._source_nio = nio_udp.NIOUDP(self._local_tunnel_rport, "127.0.0.1", self._local_tunnel_lport) self._destination_nio = nio_udp.NIOUDP(self._lport, self._rhost, self._rport) self._destination_nio.filters = self._filters + self._destination_nio.markers = self._markers await self._node.add_ubridge_udp_connection(self._bridge_name, self._source_nio, self._destination_nio) async def update(self): self._destination_nio.filters = self._filters + self._destination_nio.markers = self._markers await self._node.update_ubridge_udp_connection(self._bridge_name, self._source_nio, self._destination_nio) async def close(self): diff --git a/gns3server/compute/dynamips/nodes/router.py b/gns3server/compute/dynamips/nodes/router.py index e6e750f83..6a09cae5f 100644 --- a/gns3server/compute/dynamips/nodes/router.py +++ b/gns3server/compute/dynamips/nodes/router.py @@ -126,7 +126,7 @@ class Router(BaseNode): self._dynamips_id = dynamips_id manager.take_dynamips_id(project.id, dynamips_id) else: - log.info("Creating a new ghost IOS instance") + log.debug("Creating a new ghost IOS instance") if self._console: # Ghost VMs do not need a console port. self.console = None @@ -243,7 +243,7 @@ class Router(BaseNode): if not self._ghost_flag: - log.info( + log.debug( 'Router {platform} "{name}" [{id}] has been created'.format( name=self._name, platform=self._platform, id=self._id ) @@ -328,7 +328,7 @@ class Router(BaseNode): ) await self._hypervisor.send(f'vm start "{self._name}"') self.status = "started" - log.info(f'router "{self._name}" [{self._id}] has been started') + log.debug(f'router "{self._name}" [{self._id}] has been started') self._memory_watcher = FileWatcher(self._memory_files(), self._memory_changed, strategy="hash", delay=30) monitor_process(self._hypervisor.process, self._termination_callback) @@ -348,7 +348,7 @@ class Router(BaseNode): if self.status == "started": self.status = "stopped" - log.info("Dynamips hypervisor process has stopped, return code: %d", returncode) + log.debug("Dynamips hypervisor process has stopped, return code: %d", returncode) if returncode != 0: self.project.emit( "log.error", @@ -369,7 +369,7 @@ class Router(BaseNode): except DynamipsError as e: log.warning(f"Could not stop {self._name}: {e}") self.status = "stopped" - log.info(f'Router "{self._name}" [{self._id}] has been stopped') + log.debug(f'Router "{self._name}" [{self._id}] has been stopped') if self._memory_watcher: self._memory_watcher.close() self._memory_watcher = None @@ -393,7 +393,7 @@ class Router(BaseNode): if status == "running": await self._hypervisor.send(f'vm suspend "{self._name}"') self.status = "suspended" - log.info(f'Router "{self._name}" [{self._id}] has been suspended') + log.debug(f'Router "{self._name}" [{self._id}] has been suspended') async def resume(self): """ @@ -404,7 +404,7 @@ class Router(BaseNode): if status == "suspended": await self._hypervisor.send(f'vm resume "{self._name}"') self.status = "started" - log.info(f'Router "{self._name}" [{self._id}] has been resumed') + log.debug(f'Router "{self._name}" [{self._id}] has been resumed') async def is_running(self): """ @@ -545,7 +545,7 @@ class Router(BaseNode): await self._hypervisor.send(f'vm set_ios "{self._name}" "{image}"') - log.info( + log.debug( 'Router "{name}" [{id}]: has a new IOS image set: "{image}"'.format( name=self._name, id=self._id, image=image ) @@ -574,7 +574,7 @@ class Router(BaseNode): return await self._hypervisor.send(f'vm set_ram "{self._name}" {ram}') - log.info( + log.debug( 'Router "{name}" [{id}]: RAM updated from {old_ram}MB to {new_ram}MB'.format( name=self._name, id=self._id, old_ram=self._ram, new_ram=ram ) @@ -602,7 +602,7 @@ class Router(BaseNode): return await self._hypervisor.send(f'vm set_nvram "{self._name}" {nvram}') - log.info( + log.debug( 'Router "{name}" [{id}]: NVRAM updated from {old_nvram}KB to {new_nvram}KB'.format( name=self._name, id=self._id, old_nvram=self._nvram, new_nvram=nvram ) @@ -635,9 +635,9 @@ class Router(BaseNode): await self._hypervisor.send(f'vm set_ram_mmap "{self._name}" {flag}') if mmap: - log.info(f'Router "{self._name}" [{self._id}]: mmap enabled') + log.debug(f'Router "{self._name}" [{self._id}]: mmap enabled') else: - log.info(f'Router "{self._name}" [{self._id}]: mmap disabled') + log.debug(f'Router "{self._name}" [{self._id}]: mmap disabled') self._mmap = mmap @property @@ -664,9 +664,9 @@ class Router(BaseNode): await self._hypervisor.send(f'vm set_sparse_mem "{self._name}" {flag}') if sparsemem: - log.info(f'Router "{self._name}" [{self._id}]: sparse memory enabled') + log.debug(f'Router "{self._name}" [{self._id}]: sparse memory enabled') else: - log.info(f'Router "{self._name}" [{self._id}]: sparse memory disabled') + log.debug(f'Router "{self._name}" [{self._id}]: sparse memory disabled') self._sparsemem = sparsemem @property @@ -688,7 +688,7 @@ class Router(BaseNode): """ await self._hypervisor.send(f'vm set_clock_divisor "{self._name}" {clock_divisor}') - log.info( + log.debug( 'Router "{name}" [{id}]: clock divisor updated from {old_clock} to {new_clock}'.format( name=self._name, id=self._id, old_clock=self._clock_divisor, new_clock=clock_divisor ) @@ -722,7 +722,7 @@ class Router(BaseNode): else: await self._hypervisor.send(f'vm set_idle_pc_online "{self._name}" 0 {idlepc}') - log.info(f'Router "{self._name}" [{self._id}]: idle-PC set to {idlepc}') + log.debug(f'Router "{self._name}" [{self._id}]: idle-PC set to {idlepc}') self._idlepc = idlepc async def get_idle_pc_prop(self): @@ -741,10 +741,10 @@ class Router(BaseNode): was_auto_started = True await asyncio.sleep(20) # leave time to the router to boot - log.info(f'Router "{self._name}" [{self._id}] has started calculating Idle-PC values') + log.debug(f'Router "{self._name}" [{self._id}] has started calculating Idle-PC values') begin = time.time() idlepcs = await self._hypervisor.send(f'vm get_idle_pc_prop "{self._name}" 0') - log.info( + log.debug( 'Router "{name}" [{id}] has finished calculating Idle-PC values after {time:.4f} seconds'.format( name=self._name, id=self._id, time=time.time() - begin ) @@ -789,7 +789,7 @@ class Router(BaseNode): if is_running: # router is running await self._hypervisor.send(f'vm set_idle_max "{self._name}" 0 {idlemax}') - log.info( + log.debug( 'Router "{name}" [{id}]: idlemax updated from {old_idlemax} to {new_idlemax}'.format( name=self._name, id=self._id, old_idlemax=self._idlemax, new_idlemax=idlemax ) @@ -820,7 +820,7 @@ class Router(BaseNode): 'vm set_idle_sleep_time "{name}" 0 {idlesleep}'.format(name=self._name, idlesleep=idlesleep) ) - log.info( + log.debug( 'Router "{name}" [{id}]: idlesleep updated from {old_idlesleep} to {new_idlesleep}'.format( name=self._name, id=self._id, old_idlesleep=self._idlesleep, new_idlesleep=idlesleep ) @@ -849,7 +849,7 @@ class Router(BaseNode): 'vm set_ghost_file "{name}" "{ghost_file}"'.format(name=self._name, ghost_file=ghost_file) ) - log.info( + log.debug( 'Router "{name}" [{id}]: ghost file set to "{ghost_file}"'.format( name=self._name, id=self._id, ghost_file=ghost_file ) @@ -892,7 +892,7 @@ class Router(BaseNode): 'vm set_ghost_status "{name}" {ghost_status}'.format(name=self._name, ghost_status=ghost_status) ) - log.info( + log.debug( 'Router "{name}" [{id}]: ghost status set to {ghost_status}'.format( name=self._name, id=self._id, ghost_status=ghost_status ) @@ -923,7 +923,7 @@ class Router(BaseNode): 'vm set_exec_area "{name}" {exec_area}'.format(name=self._name, exec_area=exec_area) ) - log.info( + log.debug( 'Router "{name}" [{id}]: exec area updated from {old_exec}MB to {new_exec}MB'.format( name=self._name, id=self._id, old_exec=self._exec_area, new_exec=exec_area ) @@ -949,7 +949,7 @@ class Router(BaseNode): await self._hypervisor.send(f'vm set_disk0 "{self._name}" {disk0}') - log.info( + log.debug( 'Router "{name}" [{id}]: disk0 updated from {old_disk0}MB to {new_disk0}MB'.format( name=self._name, id=self._id, old_disk0=self._disk0, new_disk0=disk0 ) @@ -975,7 +975,7 @@ class Router(BaseNode): await self._hypervisor.send(f'vm set_disk1 "{self._name}" {disk1}') - log.info( + log.debug( 'Router "{name}" [{id}]: disk1 updated from {old_disk1}MB to {new_disk1}MB'.format( name=self._name, id=self._id, old_disk1=self._disk1, new_disk1=disk1 ) @@ -1000,9 +1000,9 @@ class Router(BaseNode): """ if auto_delete_disks: - log.info(f'Router "{self._name}" [{self._id}]: auto delete disks enabled') + log.debug(f'Router "{self._name}" [{self._id}]: auto delete disks enabled') else: - log.info(f'Router "{self._name}" [{self._id}]: auto delete disks disabled') + log.debug(f'Router "{self._name}" [{self._id}]: auto delete disks disabled') self._auto_delete_disks = auto_delete_disks async def set_console(self, console): @@ -1130,7 +1130,7 @@ class Router(BaseNode): ) ) - log.info( + log.debug( 'Router "{name}" [{id}]: MAC address updated from {old_mac} to {new_mac}'.format( name=self._name, id=self._id, old_mac=self._mac_addr, new_mac=mac_addr ) @@ -1160,7 +1160,7 @@ class Router(BaseNode): ) ) - log.info( + log.debug( 'Router "{name}" [{id}]: system ID updated from {old_id} to {new_id}'.format( name=self._name, id=self._id, old_id=self._system_id, new_id=system_id ) @@ -1218,7 +1218,7 @@ class Router(BaseNode): ) ) - log.info( + log.debug( 'Router "{name}" [{id}]: adapter {adapter} inserted into slot {slot_number}'.format( name=self._name, id=self._id, adapter=adapter, slot_number=slot_number ) @@ -1233,7 +1233,7 @@ class Router(BaseNode): 'vm slot_oir_start "{name}" {slot_number} 0'.format(name=self._name, slot_number=slot_number) ) - log.info( + log.debug( 'Router "{name}" [{id}]: OIR start event sent to slot {slot_number}'.format( name=self._name, id=self._id, slot_number=slot_number ) @@ -1279,7 +1279,7 @@ class Router(BaseNode): 'vm slot_oir_stop "{name}" {slot_number} 0'.format(name=self._name, slot_number=slot_number) ) - log.info( + log.debug( 'Router "{name}" [{id}]: OIR stop event sent to slot {slot_number}'.format( name=self._name, id=self._id, slot_number=slot_number ) @@ -1289,7 +1289,7 @@ class Router(BaseNode): 'vm slot_remove_binding "{name}" {slot_number} 0'.format(name=self._name, slot_number=slot_number) ) - log.info( + log.debug( 'Router "{name}" [{id}]: adapter {adapter} removed from slot {slot_number}'.format( name=self._name, id=self._id, adapter=adapter, slot_number=slot_number ) @@ -1331,7 +1331,7 @@ class Router(BaseNode): ) ) - log.info( + log.debug( 'Router "{name}" [{id}]: {wic} inserted into WIC slot {wic_slot_number}'.format( name=self._name, id=self._id, wic=wic, wic_slot_number=wic_slot_number ) @@ -1375,7 +1375,7 @@ class Router(BaseNode): ) ) - log.info( + log.debug( 'Router "{name}" [{id}]: {wic} removed from WIC slot {wic_slot_number}'.format( name=self._name, id=self._id, wic=adapter.wics[wic_slot_number], wic_slot_number=wic_slot_number ) @@ -1441,7 +1441,7 @@ class Router(BaseNode): ) ) - log.info( + log.debug( 'Router "{name}" [{id}]: NIO {nio_name} bound to port {slot_number}/{port_number}'.format( name=self._name, id=self._id, nio_name=nio.name, slot_number=slot_number, port_number=port_number ) @@ -1502,7 +1502,7 @@ class Router(BaseNode): await nio.close() adapter.remove_nio(port_number) - log.info( + log.debug( 'Router "{name}" [{id}]: NIO {nio_name} removed from port {slot_number}/{port_number}'.format( name=self._name, id=self._id, nio_name=nio.name, slot_number=slot_number, port_number=port_number ) @@ -1526,7 +1526,7 @@ class Router(BaseNode): ) ) - log.info( + log.debug( 'Router "{name}" [{id}]: NIO enabled on port {slot_number}/{port_number}'.format( name=self._name, id=self._id, slot_number=slot_number, port_number=port_number ) @@ -1581,7 +1581,7 @@ class Router(BaseNode): ) ) - log.info( + log.debug( 'Router "{name}" [{id}]: NIO disabled on port {slot_number}/{port_number}'.format( name=self._name, id=self._id, slot_number=slot_number, port_number=port_number ) @@ -1635,7 +1635,7 @@ class Router(BaseNode): ) ) await nio.start_packet_capture(output_file, data_link_type) - log.info( + log.debug( 'Router "{name}" [{id}]: starting packet capture on port {slot_number}/{port_number}'.format( name=self._name, id=self._id, nio_name=nio.name, slot_number=slot_number, port_number=port_number ) @@ -1675,7 +1675,7 @@ class Router(BaseNode): return await nio.stop_packet_capture() - log.info( + log.debug( 'Router "{name}" [{id}]: stopping packet capture on port {slot_number}/{port_number}'.format( name=self._name, id=self._id, nio_name=nio.name, slot_number=slot_number, port_number=port_number ) @@ -1748,7 +1748,7 @@ class Router(BaseNode): except OSError as e: raise DynamipsError(f"Could not amend the configuration {self.private_config_path}: {e}") - log.info(f'Router "{self._name}" [{self._id}]: renamed to "{new_name}"') + log.debug(f'Router "{self._name}" [{self._id}]: renamed to "{new_name}"') self._name = new_name async def extract_config(self): @@ -1788,7 +1788,7 @@ class Router(BaseNode): config = "!\n" + config.replace("\r", "") config_path = os.path.join(self._working_directory, startup_config) with open(config_path, "wb") as f: - log.info(f"saving startup-config to {startup_config}") + log.debug(f"saving startup-config to {startup_config}") f.write(config.encode("utf-8")) except (binascii.Error, OSError) as e: raise DynamipsError(f"Could not save the startup configuration {config_path}: {e}") @@ -1799,7 +1799,7 @@ class Router(BaseNode): config = base64.b64decode(private_config_base64).decode("utf-8", errors="replace") config_path = os.path.join(self._working_directory, private_config) with open(config_path, "wb") as f: - log.info(f"saving private-config to {private_config}") + log.debug(f"saving private-config to {private_config}") f.write(config.encode("utf-8")) except (binascii.Error, OSError) as e: raise DynamipsError(f"Could not save the private configuration {config_path}: {e}") @@ -1827,7 +1827,7 @@ class Router(BaseNode): await wait_run_in_executor(shutil.rmtree, self._working_directory) except OSError as e: log.warning(f"Could not delete file {e}") - log.info(f'Router "{self._name}" [{self._id}] has been deleted (including associated files)') + log.debug(f'Router "{self._name}" [{self._id}] has been deleted (including associated files)') def _memory_files(self): diff --git a/gns3server/compute/iou/iou_vm.py b/gns3server/compute/iou/iou_vm.py index ace86a8db..ff46bb4fb 100644 --- a/gns3server/compute/iou/iou_vm.py +++ b/gns3server/compute/iou/iou_vm.py @@ -54,9 +54,94 @@ import sys log = logging.getLogger(__name__) +class IOUL1KeepaliveProtocol(asyncio.DatagramProtocol): + """Handle IOU/IOL Layer 1 keepalives for connected interfaces.""" + + _header = struct.Struct("!HHBBBB") + _message_type = 3 + + def __init__(self, vm): + self._vm = vm + self.transport = None + + def connection_made(self, transport): + self.transport = transport + + @staticmethod + def encode_interface(adapter_number, port_number): + """Encode an IOU bay/unit for the L1 keepalive protocol.""" + + # IOU stores the zero-based unit in the high nibble and the + # zero-based bay in the low nibble. + return (port_number << 4) | adapter_number + + @staticmethod + def decode_interface(interface): + """Decode an L1 keepalive interface into an IOU bay/unit.""" + + return interface & 0x0F, interface >> 4 + + def datagram_received(self, data, address): + if len(data) != self._header.size: + log.debug('IOU "%s": ignored malformed L1 keepalive of %d bytes', self._vm.name, len(data)) + return + + destination, source, destination_interface, source_interface, message_type, channel = self._header.unpack(data) + if ( + destination != self._vm.l1_bridge_id + or source != self._vm.application_id + or message_type != self._message_type + or not self._vm.has_nio_for_iou_interface(source_interface) + ): + return + + response = self._header.pack( + source, + destination, + source_interface, + destination_interface, + message_type, + channel, + ) + try: + self.transport.sendto(response, self._vm.l1_iou_socket_path) + except OSError as e: + # IOU creates its endpoint during startup and removes it on stop. + # Dropping a keepalive during either transition is harmless. + log.debug('IOU "%s": could not send an L1 keepalive response: %s', self._vm.name, e) + + def send_keepalives(self): + """Tell IOU that every interface with an attached NIO has Layer 1 connectivity.""" + + for adapter_number, adapter in enumerate(self._vm.adapters): + for port_number, nio in adapter.ports.items(): + if nio is None: + continue + interface = self.encode_interface(adapter_number, port_number) + keepalive = self._header.pack( + self._vm.application_id, + self._vm.l1_bridge_id, + interface, + interface, + self._message_type, + 0, + ) + try: + self.transport.sendto(keepalive, self._vm.l1_iou_socket_path) + except OSError as e: + # The IOU endpoint does not exist until the image has started. + log.debug('IOU "%s": could not send an L1 keepalive: %s', self._vm.name, e) + + class IOUVM(BaseNode): module_name = "iou" + # Class-level caches shared across all IOU VM instances using the same image. + # These avoid redundant subprocess calls during project loading when multiple + # IOU nodes use the same image. + _loader_cache = {} # image path -> loader command list + _default_values_cache = {} # image path -> (ram, nvram) + """ IOU VM implementation. @@ -77,7 +162,7 @@ class IOUVM(BaseNode): super().__init__(name, node_id, project, manager, console=console, console_type=console_type) - log.info( + log.debug( 'IOU "{name}" [{id}]: assigned with application ID {application_id}'.format( name=self._name, id=self._id, application_id=application_id ) @@ -92,6 +177,8 @@ class IOUVM(BaseNode): self._lib_base = self.manager.get_images_directory() self._loader = None self._license_check = True + self._l1_keepalive_transport = None + self._l1_keepalive_task = None # IOU settings self._ethernet_adapters = [] @@ -104,7 +191,7 @@ class IOUVM(BaseNode): self._private_config = "" self._ram = 1024 # Megabytes self._application_id = application_id - self._l1_keepalives = False # used to overcome the always-up Ethernet interfaces (not supported by all IOSes). + self._l1_keepalives = False def _nvram_changed(self, path): """ @@ -151,7 +238,7 @@ class IOUVM(BaseNode): self._path = self.manager.get_abs_image_path(path, self.project.path) self._loader = None - log.info(f'IOU "{self._name}" [{self._id}]: IOU image updated to "{self._path}"') + log.debug(f'IOU "{self._name}" [{self._id}]: IOU image updated to "{self._path}"') @property def use_default_iou_values(self): @@ -173,15 +260,22 @@ class IOUVM(BaseNode): self._use_default_iou_values = state if state: - log.info(f'IOU "{self._name}" [{self._id}]: uses the default IOU image values') + log.debug(f'IOU "{self._name}" [{self._id}]: uses the default IOU image values') else: - log.info(f'IOU "{self._name}" [{self._id}]: does not use the default IOU image values') + log.debug(f'IOU "{self._name}" [{self._id}]: does not use the default IOU image values') async def update_default_iou_values(self): """ Finds the default RAM and NVRAM values for the IOU image. + Results are cached per image path to avoid redundant subprocess calls + when multiple IOU nodes use the same image. """ + # Check class-level cache for default values + if self._path in IOUVM._default_values_cache: + self._ram, self._nvram = IOUVM._default_values_cache[self._path] + return + await self._check_requirements() try: output = await gns3server.utils.asyncio.subprocess_check_output( @@ -193,6 +287,9 @@ class IOUVM(BaseNode): match = re.search(r"-m \s+Megabytes of router memory \(default ([0-9]+)MB\)", output) if match: self.ram = int(match.group(1)) + # Only cache on success, so a subsequent call with explicitly set + # ram/nvram values won't be overwritten by stale cached defaults + IOUVM._default_values_cache[self._path] = (self._ram, self._nvram) except (ValueError, OSError, subprocess.SubprocessError) as e: log.warning(f"could not find default RAM and NVRAM values for {os.path.basename(self._path)}: {e}") @@ -207,6 +304,13 @@ class IOUVM(BaseNode): if self._loader is not None: return # image already checked + + # Check class-level cache: if another IOU VM already verified this image, + # reuse its loader configuration to avoid redundant subprocess calls. + if self._path in IOUVM._loader_cache: + self._loader = IOUVM._loader_cache[self._path] + return + if not self._path: raise IOUError("IOU image is not configured") if not os.path.isfile(self._path) or not os.path.exists(self._path): @@ -252,6 +356,9 @@ class IOUVM(BaseNode): except (OSError, subprocess.SubprocessError) as e: log.warning(f"Could not use loader {loader}: {e}") + # Cache the loader result for other IOU VMs using the same image + IOUVM._loader_cache[self._path] = self._loader + def asdict(self): iou_vm_info = { @@ -323,7 +430,7 @@ class IOUVM(BaseNode): if self._ram == ram: return - log.info( + log.debug( 'IOU "{name}" [{id}]: RAM updated from {old_ram}MB to {new_ram}MB'.format( name=self._name, id=self._id, old_ram=self._ram, new_ram=ram ) @@ -352,7 +459,7 @@ class IOUVM(BaseNode): if self._nvram == nvram: return - log.info( + log.debug( 'IOU "{name}" [{id}]: NVRAM updated from {old_nvram}KB to {new_nvram}KB'.format( name=self._name, id=self._id, old_nvram=self._nvram, new_nvram=nvram ) @@ -467,7 +574,7 @@ class IOUVM(BaseNode): config = configparser.ConfigParser() try: - log.info(f"Checking IOU license in '{self.iourc_path}'") + log.debug(f"Checking IOU license in '{self.iourc_path}'") with open(self.iourc_path, encoding="utf-8") as f: config.read_file(f) except OSError as e: @@ -611,11 +718,15 @@ class IOUVM(BaseNode): raise IOUError(f"Could not create symbolic link: {e}") command = await self._build_command() + # Only start the responder when the capability probe actually + # enabled IOU's L1 protocol on the command line. + if "-l" in command: + await self._start_l1_keepalive_responder() try: if self._loader: - log.info(f"Starting IOU: {command} with loader {self._loader}") + log.debug(f"Starting IOU: {command} with loader {self._loader}") else: - log.info(f"Starting IOU: {command}") + log.debug(f"Starting IOU: {command}") self.command_line = " ".join(command) self._iou_process = await asyncio.create_subprocess_exec( *self._loader, *command, @@ -625,14 +736,16 @@ class IOUVM(BaseNode): cwd=self.working_dir, env=env, ) - log.info(f"IOU instance {self._id} started PID={self._iou_process.pid}") + log.debug(f"IOU instance {self._id} started PID={self._iou_process.pid}") self._started = True self.status = "started" callback = functools.partial(self._termination_callback, "IOU") gns3server.utils.asyncio.monitor_process(self._iou_process, callback) except FileNotFoundError as e: + self._stop_l1_keepalive_responder() raise IOUError(f"Could not start IOU: {e}: 32-bit binary support is probably not installed, it is recommended to use a 64-bit image instead") except (OSError, subprocess.SubprocessError) as e: + self._stop_l1_keepalive_responder() iou_stdout = self.read_iou_stdout() log.error(f"Could not start IOU {self._path}: {e}\n{iou_stdout}") raise IOUError(f"Could not start IOU {self._path}: {e}\n{iou_stdout}") @@ -720,6 +833,7 @@ class IOUVM(BaseNode): ) await self._ubridge_apply_filters(bay_id, unit_id, nio.filters) + await self._ubridge_apply_markers(bay_id, unit_id, nio) unit_id += 1 bay_id += 1 @@ -733,6 +847,7 @@ class IOUVM(BaseNode): """ self._terminate_process_iou() + self._stop_l1_keepalive_responder() if returncode != 0: if returncode == -11: message = 'IOU VM "{}" process has stopped with return code: {} (segfault). This could be an issue with the IOU image, using a different image may fix this.\n{}'.format( @@ -765,6 +880,7 @@ class IOUVM(BaseNode): Stops the IOU process. """ + self._stop_l1_keepalive_responder() await self._stop_ubridge() if self._nvram_watcher: self._nvram_watcher.close() @@ -804,7 +920,7 @@ class IOUVM(BaseNode): """ if self._iou_process: - log.info(f'Stopping IOU process for IOU VM "{self.name}" PID={self._iou_process.pid}') + log.debug(f'Stopping IOU process for IOU VM "{self.name}" PID={self._iou_process.pid}') try: self._iou_process.terminate() # Sometime the process can already be dead when we garbage collect @@ -863,10 +979,87 @@ class IOUVM(BaseNode): iou_id=self.application_id, ) ) - log.info("IOU {name} [id={id}]: NETMAP file created".format(name=self._name, id=self._id)) + log.debug("IOU {name} [id={id}]: NETMAP file created".format(name=self._name, id=self._id)) except OSError as e: raise IOUError(f"Could not create {netmap_path}: {e}") + @property + def l1_bridge_id(self): + return self.application_id + 512 + + @property + def l1_socket_directory(self): + # IOU hard-codes this directory independently from TMPDIR. + return os.path.join("/tmp", f"netl1{os.geteuid()}") + + @property + def l1_bridge_socket_path(self): + return os.path.join(self.l1_socket_directory, f"L1{self.l1_bridge_id}") + + @property + def l1_iou_socket_path(self): + return os.path.join(self.l1_socket_directory, f"L1{self.application_id}") + + def has_nio_for_iou_interface(self, interface): + """Return whether the IOU bay/unit encoded in one byte is connected.""" + + adapter_number, port_number = IOUL1KeepaliveProtocol.decode_interface(interface) + if adapter_number >= len(self._adapters): + return False + adapter = self._adapters[adapter_number] + return adapter.port_exists(port_number) and adapter.get_nio(port_number) is not None + + async def _start_l1_keepalive_responder(self): + """Create the bridge-side UNIX datagram endpoint used by IOU's ``-l`` option.""" + + if self._l1_keepalive_transport is not None: + return + + socket_directory = self.l1_socket_directory + try: + os.makedirs(socket_directory, mode=0o755, exist_ok=True) + if os.path.islink(socket_directory) or os.stat(socket_directory).st_uid != os.geteuid(): + raise IOUError(f"Unsafe IOU L1 keepalive directory '{socket_directory}'") + if os.path.lexists(self.l1_bridge_socket_path): + os.unlink(self.l1_bridge_socket_path) + loop = asyncio.get_running_loop() + transport, protocol = await loop.create_datagram_endpoint( + lambda: IOUL1KeepaliveProtocol(self), + local_addr=self.l1_bridge_socket_path, + family=socket.AF_UNIX, + ) + self._l1_keepalive_transport = transport + self._l1_keepalive_task = asyncio.create_task(self._send_l1_keepalives(protocol)) + log.debug( + 'IOU "%s" [%s]: L1 keepalive responder listening on %s', + self._name, + self._id, + self.l1_bridge_socket_path, + ) + except (OSError, RuntimeError) as e: + self._stop_l1_keepalive_responder() + raise IOUError(f"Could not start IOU L1 keepalive responder: {e}") + + async def _send_l1_keepalives(self, protocol): + while self._l1_keepalive_transport is not None: + protocol.send_keepalives() + await asyncio.sleep(1) + + def _stop_l1_keepalive_responder(self): + """Stop the L1 endpoint and remove its bridge-side socket.""" + + if self._l1_keepalive_task is not None: + self._l1_keepalive_task.cancel() + self._l1_keepalive_task = None + if self._l1_keepalive_transport is not None: + self._l1_keepalive_transport.close() + self._l1_keepalive_transport = None + try: + if os.path.lexists(self.l1_bridge_socket_path): + os.unlink(self.l1_bridge_socket_path) + except OSError as e: + log.warning('Could not remove IOU L1 keepalive socket "%s": %s', self.l1_bridge_socket_path, e) + async def _build_command(self): """ Command to start the IOU process. @@ -957,7 +1150,7 @@ class IOUVM(BaseNode): for _ in range(0, ethernet_adapters): self._ethernet_adapters.append(EthernetAdapter(interfaces=4)) - log.info( + log.debug( 'IOU "{name}" [{id}]: number of Ethernet adapters changed to {adapters}'.format( name=self._name, id=self._id, adapters=len(self._ethernet_adapters) ) @@ -987,7 +1180,7 @@ class IOUVM(BaseNode): for _ in range(0, serial_adapters): self._serial_adapters.append(SerialAdapter(interfaces=4)) - log.info( + log.debug( 'IOU "{name}" [{id}]: number of Serial adapters changed to {adapters}'.format( name=self._name, id=self._id, adapters=len(self._serial_adapters) ) @@ -1021,7 +1214,7 @@ class IOUVM(BaseNode): ) adapter.add_nio(port_number, nio) - log.info( + log.debug( 'IOU "{name}" [{id}]: {nio} added to {adapter_number}/{port_number}'.format( name=self._name, id=self._id, nio=nio, adapter_number=adapter_number, port_number=port_number ) @@ -1041,6 +1234,7 @@ class IOUVM(BaseNode): ) ) await self._ubridge_apply_filters(adapter_number, port_number, nio.filters) + await self._ubridge_apply_markers(adapter_number, port_number, nio) async def adapter_update_nio_binding(self, adapter_number, port_number, nio): """ @@ -1053,6 +1247,7 @@ class IOUVM(BaseNode): if self.ubridge: await self._ubridge_apply_filters(adapter_number, port_number, nio.filters) + await self._ubridge_apply_markers(adapter_number, port_number, nio) async def _ubridge_apply_filters(self, adapter_number, port_number, filters): """ @@ -1069,6 +1264,124 @@ class IOUVM(BaseNode): cmd = "iol_bridge add_packet_filter {} {}".format(location, filter) await self._ubridge_send(cmd) + async def _ubridge_apply_markers(self, adapter_number, port_number, nio): + """ + Reconcile traffic-insight markers on the IOL bridge (diff desired + ``nio.markers`` against installed ``_marker_specs``): delete removed, + rebuild changed, toggle on/off-only changes, add new, skip unchanged. + + IOU uses ``iol_bridge`` (not ``bridge``) and the ``add_packet_filter`` + command carries extra ``{bay} {unit}`` positional arguments between the + bridge name and the filter name — this override mirrors the pattern in + ``_ubridge_apply_filters`` above. + + :param adapter_number: bay id + :param port_number: unit id + :param nio: NIO instance carrying ``nio.markers`` + """ + from gns3server.compute.marker.marker_manager import MarkerManager + + markers = nio.markers if hasattr(nio, 'markers') else {} + manager = MarkerManager.instance() + markers_dir = self.project.markers_working_directory() + bridge_name = f"IOL-BRIDGE-{self.application_id + 512}" + location = "{bridge_name} {bay} {unit}".format( + bridge_name=bridge_name, bay=adapter_number, unit=port_number + ) + desired = {(name, spec.get("link_id", "")): spec for name, spec in markers.items()} + + # 1. Remove installed markers that are no longer desired. + # Scope to THIS port's IOL location — the map is node-wide and also + # holds markers on this IOU's other ports, which must not be deleted + # when reconciling a single NIO (see base_node for the same guard). + for key in list(self._marker_filter_bridges): + if self._marker_filter_bridges[key] != location: + continue + if key not in desired: + mname, link_id = key + installed_location = self._marker_filter_bridges.pop(key) + self._marker_specs.pop(key, None) + await self._ubridge_delete_marker_filter(installed_location, mname) + try: + os.remove(os.path.join(markers_dir, f"{self._id}_{link_id}_{mname}.pcap")) + except FileNotFoundError: + pass + except OSError as e: + log.warning("Could not remove marker pcap for '%s' on link %s: %s", mname, link_id, e) + manager.unregister(self._id, mname) + + # 2. Add / reconcile desired markers. + rebuild_fields = ("bpf", "tag", "direction", "data_link_type") + for (name, link_id), spec in desired.items(): + bpf = spec.get("bpf", "") + tag = spec.get("tag") + enabled = spec.get("enabled", True) + if (name, link_id) in self._marker_filter_bridges: + installed_spec = self._marker_specs.get((name, link_id)) + if installed_spec is None: + continue # installed (legacy, no spec) — skip to avoid dup + if any(installed_spec.get(f) != spec.get(f) for f in rebuild_fields): + installed_location = self._marker_filter_bridges.get((name, link_id)) + await self._ubridge_delete_marker_filter(installed_location, name) + elif installed_spec.get("enabled", True) != enabled: + await self._ubridge_set_marker_filter_state(name, enabled) + self._marker_specs[(name, link_id)] = spec + continue + else: + continue + pcap_path = os.path.join(markers_dir, f"{self._id}_{link_id}_{name}.pcap") + # iol_bridge add_packet_filter {br} {bay} {unit} {name} mark "{bpf}" [tag {id}] pcap "{path}" + cmd = 'iol_bridge add_packet_filter {loc} {name} mark "{bpf}"'.format( + loc=location, name=name, bpf=bpf + ) + if tag is not None: + cmd += f" tag {tag}" + if link_id: + cmd += f" link {link_id}" + direction = spec.get("direction") + if direction is not None: + cmd += f" dir {direction}" + linktype = self._marker_linktype(spec.get("data_link_type")) + if linktype is not None: + cmd += f" linktype {linktype}" + cmd += ' pcap "{path}"'.format(path=pcap_path) + try: + await self._ubridge_send(cmd) + except UbridgeError as e: + if "syntax error" in str(e).lower() or "compile filter" in str(e).lower(): + message = f"Warning: ignoring marker '{name}' due to BPF syntax error: {e}" + log.warning(message) + self.project.emit("log.warning", {"message": message}) + continue + raise + if not enabled: + try: + await self._ubridge_send(f"iol_bridge enable_packet_filter {location} {name} off") + except UbridgeError as e: + log.warning(f"Could not turn marker '{name}' off on {location}: {e}") + manager.register(str(self.project.id), self._id, name, link_id, tag) + self._marker_filter_bridges[name, link_id] = location + self._marker_specs[name, link_id] = spec + + async def _ubridge_set_marker_filter_state(self, name, enabled): + """IOU override: toggle every (name, link_id) entry via ``iol_bridge``.""" + + state = "on" if enabled else "off" + for (n, lid), location in list(self._marker_filter_bridges.items()): + if n == name: + await self._ubridge_send(f"iol_bridge enable_packet_filter {location} {name} {state}") + + async def _ubridge_delete_marker_filter(self, location, name): + """IOU override: remove a single marker filter via ``iol_bridge`` + (location = ``{bridge} {bay} {unit}``), not a bridge-wide reset.""" + + if not (self._ubridge_hypervisor and self._ubridge_hypervisor.is_running()): + return + try: + await self._ubridge_send(f"iol_bridge delete_packet_filter {location} {name}") + except UbridgeError as e: + log.warning("Could not remove marker filter '%s' from %s: %s", name, location, e) + async def adapter_remove_nio_binding(self, adapter_number, port_number): """ Removes an adapter NIO binding. @@ -1099,7 +1412,7 @@ class IOUVM(BaseNode): if isinstance(nio, NIOUDP): self.manager.port_manager.release_udp_port(nio.lport, self._project) adapter.remove_nio(port_number) - log.info( + log.debug( 'IOU "{name}" [{id}]: {nio} removed from {adapter_number}/{port_number}'.format( name=self._name, id=self._id, nio=nio, adapter_number=adapter_number, port_number=port_number ) @@ -1169,9 +1482,9 @@ class IOUVM(BaseNode): self._l1_keepalives = state if state: - log.info(f'IOU "{self._name}" [{self._id}]: has activated layer 1 keepalive messages') + log.debug(f'IOU "{self._name}" [{self._id}]: has activated layer 1 keepalive messages') else: - log.info(f'IOU "{self._name}" [{self._id}]: has deactivated layer 1 keepalive messages') + log.debug(f'IOU "{self._name}" [{self._id}]: has deactivated layer 1 keepalive messages') async def _enable_l1_keepalives(self, command): """ @@ -1181,8 +1494,9 @@ class IOUVM(BaseNode): """ env = os.environ.copy() - if "IOURC" not in os.environ: - env["IOURC"] = self.iourc_path + iourc_path = self.iourc_path + if "IOURC" not in os.environ and iourc_path: + env["IOURC"] = iourc_path try: output = await gns3server.utils.asyncio.subprocess_check_output( *self._loader, self._path, "-h", cwd=self.working_dir, env=env, stderr=True @@ -1405,7 +1719,7 @@ class IOUVM(BaseNode): try: config = startup_config_content.decode("utf-8", errors="replace") with open(config_path, "wb") as f: - log.info(f"saving startup-config to {config_path}") + log.debug(f"saving startup-config to {config_path}") f.write(config.encode("utf-8")) except (binascii.Error, OSError) as e: raise IOUError(f"Could not save the startup configuration {config_path}: {e}") @@ -1415,7 +1729,7 @@ class IOUVM(BaseNode): try: config = private_config_content.decode("utf-8", errors="replace") with open(config_path, "wb") as f: - log.info(f"saving private-config to {config_path}") + log.debug(f"saving private-config to {config_path}") f.write(config.encode("utf-8")) except (binascii.Error, OSError) as e: raise IOUError(f"Could not save the private configuration {config_path}: {e}") @@ -1439,7 +1753,7 @@ class IOUVM(BaseNode): ) nio.start_packet_capture(output_file, data_link_type) - log.info( + log.debug( 'IOU "{name}" [{id}]: starting packet capture on {adapter_number}/{port_number} to {output_file}'.format( name=self._name, id=self._id, @@ -1473,7 +1787,7 @@ class IOUVM(BaseNode): if not nio.capturing: return nio.stop_packet_capture() - log.info( + log.debug( 'IOU "{name}" [{id}]: stopping packet capture on {adapter_number}/{port_number}'.format( name=self._name, id=self._id, adapter_number=adapter_number, port_number=port_number ) diff --git a/gns3server/compute/marker/__init__.py b/gns3server/compute/marker/__init__.py new file mode 100644 index 000000000..8fdb2b775 --- /dev/null +++ b/gns3server/compute/marker/__init__.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# +# Copyright (C) 2024 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# +# Traffic-insight marker subsystem (compute side). +# +# ubridge's ``marker`` module is a passive tap: on a BPF match it emits a UDP +# ``MARK`` signal to a configured sink and/or appends the packet to a pcap. +# This package owns the compute-side UDP sink: one listener per compute process +# serves every ubridge on that host, disambiguated by ``node=``. diff --git a/gns3server/compute/marker/marker_listener.py b/gns3server/compute/marker/marker_listener.py new file mode 100644 index 000000000..40cdd97b6 --- /dev/null +++ b/gns3server/compute/marker/marker_listener.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# +# Copyright (C) 2024 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import asyncio +import logging + +log = logging.getLogger(__name__) + + +class MarkerListener(asyncio.DatagramProtocol): + """ + Receives ubridge ``MARK`` signal datagrams and turns each into a + ``marker.match`` notification. + + Signal format (one datagram per match, ASCII):: + + MARK node= filter= tag= len= [dir=]\\n + + The signal carries metadata only (no packet bytes). ``dir`` is optional and + additive: uBridge stamps it from the ingress NIO of the matched packet to + indicate travel direction relative to the capture node (the ``node=`` + above) — ``tx`` = the capture node is sending (ingressed on the device-side + NIO), ``rx`` = it is receiving (ingressed on the link-side NIO). Older + uBridge builds omit it, so the listener leaves ``dir`` unset and consumers + fall back to undirected rendering. Unknown keys are always ignored, so the + field ships safely with no version coupling. + + The compute-side + :class:`~gns3server.compute.marker.marker_manager.MarkerManager` registry + resolves ``(node_id, filter_name)`` to ``(project_id, link_id, tag)`` so the + event can be emitted on the right project-scoped notification stream. + """ + + def __init__(self, manager): + # MarkerManager owns this listener and the registry. + self._manager = manager + self.transport = None + self._received = 0 + self._errors = 0 + + def connection_made(self, transport): + self.transport = transport + + def datagram_received(self, data, addr): + self._received += 1 + try: + self._handle(data) + except Exception: + self._errors += 1 + # Never let a malformed datagram kill the listener. + log.exception("Failed to process MARK datagram from %s: %r", addr, data) + + def _handle(self, data): + line = data.decode("utf-8", errors="replace").strip() + if not line.startswith("MARK"): + return + + parts = line.split() + # parts[0] == "MARK"; parts[1] == "" + if len(parts) < 2: + return + + try: + ts = float(parts[1]) + except ValueError: + log.warning("Ignoring MARK signal with bad timestamp: %r", line) + return + + kv = {} + for token in parts[2:]: + if "=" in token: + key, value = token.split("=", 1) + kv[key] = value + + node_id = kv.get("node") + filter_name = kv.get("filter") + if not node_id or not filter_name: + return + + # "-" means the field was unset on the ubridge side (see contract §3.3). + link = kv.get("link") + tag = kv.get("tag") + length = kv.get("len") + # Travel direction relative to the capture node (the node= above): + # "tx" = capture node is sending (matched packet ingressed on the + # device-side NIO), "rx" = it is receiving (link-side NIO). Older + # uBridge builds omit dir; None here lets consumers render undirected. + direction = kv.get("dir") + + project_id, link_id, registered_tag = self._manager.lookup(node_id, filter_name) + if project_id is None: + log.warning( + "MARK signal for unregistered node=%s filter=%s, dropping", node_id, filter_name + ) + return + + # `link=` is the authoritative per-link id (opaque, set by gns3server at + # filter install time). It disambiguates signals that share a node+filter + # across several links; fall back to the registry's link only for legacy + # signals that carry no `link=`. + signal_link = link if link and link != "-" else None + + event = { + "project_id": project_id, + "node_id": node_id, + "link_id": signal_link or link_id, + "filter": filter_name, + # Prefer the value carried in the signal; fall back to the one we registered. + "tag": tag if tag and tag != "-" else registered_tag, + "ts": ts, + "len": int(length) if length and length.isdigit() else 0, + # Travel direction relative to the capture node (node_id above); + # None when the signal carries none (older uBridge) — undirected. + "dir": direction, + } + self._manager.emit_match(project_id, event) diff --git a/gns3server/compute/marker/marker_manager.py b/gns3server/compute/marker/marker_manager.py new file mode 100644 index 000000000..132a19812 --- /dev/null +++ b/gns3server/compute/marker/marker_manager.py @@ -0,0 +1,218 @@ +#!/usr/bin/env python +# +# Copyright (C) 2024 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import asyncio +import logging +import socket + +from gns3server.compute.marker.marker_listener import MarkerListener +from gns3server.compute.notification_manager import NotificationManager + +log = logging.getLogger(__name__) + + +class MarkerManager: + """ + Singleton owning the compute-side UDP sink for ubridge ``MARK`` signals and + the registry that maps each ``(node_id, filter_name)`` back to its + ``(project_id, link_id, tag)``. + + The registry is populated when a marker is created on a link (the compute + endpoint has project_id + node_id from its route path and link_id/name/tag + from the request body) and cleared when the marker is deleted or the project + closed. At signal time it is an O(1) lookup — no node-table scan, and the + signal payload is untouched. + + One listener per compute process serves every ubridge on that host; source + ubridges are disambiguated by ``node=`` (UUID, globally unique). + """ + + def __init__(self): + + self._listener = None + self._transport = None + self._host = None + self._port = None + # Flat lookup: (node_id, filter_name) -> {"project_id", "link_id", "tag"} + self._entries = {} + # Reverse index for O(1) per-project teardown: project_id -> set of keys + self._by_project = {} + + @property + def host(self): + """The host the UDP sink is reachable on (for ``marker sink``).""" + return self._host + + @property + def port(self): + """The UDP port the sink is bound on (for ``marker sink``).""" + return self._port + + @property + def running(self): + return self._transport is not None + + async def start(self, host="127.0.0.1", port=0): + """ + Bind the UDP sink. ``port=0`` lets the OS choose a free port, which is + then read back and exposed via :attr:`port` for ``marker sink`` commands. + """ + + if self.running: + return + loop = asyncio.get_running_loop() + self._listener = MarkerListener(self) + + def _configure_transport(transport): + sock = transport.get_extra_info("socket") + if sock is not None: + # Raise the UDP receive buffer from the default ~208 KB to 8 MB + # so that 1000+ uBridge processes can burst marker.match signals + # without kernel-side datagram loss. + sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 8 * 1024 * 1024) + + try: + self._transport, _ = await loop.create_datagram_endpoint( + lambda: self._listener, local_addr=(host, port) + ) + _configure_transport(self._transport) + except OSError: + if port != 0: + log.warning( + "Marker listener: port %s unavailable, falling back to OS-assigned port", port + ) + try: + self._transport, _ = await loop.create_datagram_endpoint( + lambda: self._listener, local_addr=(host, 0) + ) + _configure_transport(self._transport) + except OSError as e: + log.error( + "Marker listener startup failed: %s. Traffic insight signals are unavailable.", e + ) + self._listener = None + return + else: + log.error( + "Marker listener startup failed on OS-assigned port. Traffic insight signals are unavailable." + ) + self._listener = None + return + sock = self._transport.get_extra_info("socket") + self._host = host + self._port = sock.getsockname()[1] if sock else port + log.info("Marker signal sink listening on %s:%s", self._host, self._port) + self._stats_task = asyncio.create_task(self._log_stats()) + + async def _log_stats(self): + """Log marker.match throughput every 10 s so operators can tell whether + the single UDP sink keeps up with the aggregated uBridge traffic.""" + while self.running: + await asyncio.sleep(10) + listener = self._listener + if listener is None: + break + received, errors = listener._received, listener._errors + listener._received = 0 + listener._errors = 0 + if received: + log.info( + "marker sink: %d matches (%.0f/s), %d errors in last 10s", + received, received / 10.0, errors, + ) + + async def stop(self): + """Close the UDP sink and drop the whole registry.""" + + if hasattr(self, "_stats_task") and self._stats_task: + self._stats_task.cancel() + self._stats_task = None + if self._transport: + self._transport.close() + self._transport = None + self._listener = None + self._entries.clear() + self._by_project.clear() + self._host = None + self._port = None + + def register(self, project_id, node_id, filter_name, link_id, tag=None): + """ + Record that ``filter_name`` on ``node_id`` belongs to ``project_id`` / + ``link_id``. Called from the compute marker-start endpoint. + + Re-registering the same key updates the stored tag (e.g. on re-add). + """ + + key = (node_id, filter_name) + self._entries[key] = {"project_id": project_id, "link_id": link_id, "tag": tag} + self._by_project.setdefault(project_id, set()).add(key) + + def unregister(self, node_id, filter_name): + """Forget a single marker. Returns True if something was removed.""" + + key = (node_id, filter_name) + entry = self._entries.pop(key, None) + if entry is None: + return False + project_entries = self._by_project.get(entry["project_id"]) + if project_entries is not None: + project_entries.discard(key) + if not project_entries: + self._by_project.pop(entry["project_id"], None) + return True + + def unregister_project(self, project_id): + """Drop every marker belonging to ``project_id`` (project close).""" + + keys = self._by_project.pop(project_id, None) + if not keys: + return + for key in keys: + self._entries.pop(key, None) + + def lookup(self, node_id, filter_name): + """ + O(1) resolution of an incoming signal to its project/link/tag. + + :returns: (project_id, link_id, tag) or (None, None, None) on miss. + """ + + entry = self._entries.get((node_id, filter_name)) + if entry is None: + return None, None, None + return entry["project_id"], entry["link_id"], entry["tag"] + + def emit_match(self, project_id, event): + """ + Forward a parsed match as a project-scoped ``marker.match`` notification. + Flows compute -> controller dispatch -> project_emit -> web UI WS. + """ + + NotificationManager.instance().emit("marker.match", event, project_id=project_id) + + _instance = None + + @staticmethod + def instance(): + if MarkerManager._instance is None: + MarkerManager._instance = MarkerManager() + return MarkerManager._instance + + @staticmethod + def reset(): + MarkerManager._instance = None diff --git a/gns3server/compute/nios/nio.py b/gns3server/compute/nios/nio.py index 8ad5bd870..6fe57a130 100644 --- a/gns3server/compute/nios/nio.py +++ b/gns3server/compute/nios/nio.py @@ -30,6 +30,7 @@ class NIO: self._capturing = False self._suspended = False self._filters = {} + self._markers = {} self._pcap_output_file = "" self._pcap_data_link_type = "" @@ -118,3 +119,24 @@ class NIO: assert isinstance(new_filters, dict) self._filters = new_filters + + @property + def markers(self): + """ + Returns the traffic-insight markers for this NIO. + + :returns: markers (dictionary: name -> {bpf, tag, link_id}) + """ + + return self._markers + + @markers.setter + def markers(self, new_markers): + """ + Set the traffic-insight markers for this NIO. + + :param new_markers: markers (dictionary: name -> {bpf, tag, link_id}) + """ + + assert isinstance(new_markers, dict) + self._markers = new_markers diff --git a/gns3server/compute/nios/nio_udp.py b/gns3server/compute/nios/nio_udp.py index b7736a39e..e6f1bd8bc 100644 --- a/gns3server/compute/nios/nio_udp.py +++ b/gns3server/compute/nios/nio_udp.py @@ -80,5 +80,6 @@ class NIOUDP(NIO): "rport": self._rport, "rhost": self._rhost, "suspend": self._suspended, - "filters": self._filters + "filters": self._filters, + "markers": self._markers } diff --git a/gns3server/compute/port_manager.py b/gns3server/compute/port_manager.py index 6dd2549a8..dbb18fc0e 100644 --- a/gns3server/compute/port_manager.py +++ b/gns3server/compute/port_manager.py @@ -16,6 +16,7 @@ import socket import ipaddress +import threading from fastapi import HTTPException, status from gns3server.config import Config @@ -105,6 +106,13 @@ class PortManager: self._udp_host = "0.0.0.0" self._used_tcp_ports = set() self._used_udp_ports = set() + # Guards the find-then-add port allocation against concurrent threads: + # FastAPI runs sync route handlers (e.g. POST /ports/udp) in a thread + # pool, and a link allocates both of its ends concurrently — without + # the lock both threads can probe the same "free" port and hand the + # same number to both ends of a link (lport == rport self-loop). + # RLock because reserve_*_port falls back to get_free_*_port. + self._lock = threading.RLock() console_start_port_range = Config.instance().settings.Server.console_start_port_range console_end_port_range = Config.instance().settings.Server.console_end_port_range @@ -275,16 +283,17 @@ class PortManager: port_range_start = self._console_port_range[0] port_range_end = self._console_port_range[1] - port = self.find_unused_port( - port_range_start, - port_range_end, - host=self._console_host, - socket_type="TCP", - ignore_ports=self._used_tcp_ports, - ) + with self._lock: + port = self.find_unused_port( + port_range_start, + port_range_end, + host=self._console_host, + socket_type="TCP", + ignore_ports=self._used_tcp_ports, + ) - self._used_tcp_ports.add(port) - project.record_tcp_port(port) + self._used_tcp_ports.add(port) + project.record_tcp_port(port) log.debug(f"TCP port {port} has been allocated") return port @@ -305,32 +314,33 @@ class PortManager: port_range_start = self._console_port_range[0] port_range_end = self._console_port_range[1] - if port in self._used_tcp_ports: - old_port = port - port = self.get_free_tcp_port(project, port_range_start=port_range_start, port_range_end=port_range_end) - msg = f"TCP port {old_port} already in use on host {self._console_host}. Port has been replaced by {port}" - log.debug(msg) - return port - if port < port_range_start or port > port_range_end: - old_port = port - port = self.get_free_tcp_port(project, port_range_start=port_range_start, port_range_end=port_range_end) - msg = ( - f"TCP port {old_port} is outside the range {port_range_start}-{port_range_end} on host " - f"{self._console_host}. Port has been replaced by {port}" - ) - log.debug(msg) - return port - try: - PortManager._check_port(self._console_host, port, "TCP") - except OSError: - old_port = port - port = self.get_free_tcp_port(project, port_range_start=port_range_start, port_range_end=port_range_end) - msg = f"TCP port {old_port} already in use on host {self._console_host}. Port has been replaced by {port}" - log.debug(msg) - return port + with self._lock: + if port in self._used_tcp_ports: + old_port = port + port = self.get_free_tcp_port(project, port_range_start=port_range_start, port_range_end=port_range_end) + msg = f"TCP port {old_port} already in use on host {self._console_host}. Port has been replaced by {port}" + log.debug(msg) + return port + if port < port_range_start or port > port_range_end: + old_port = port + port = self.get_free_tcp_port(project, port_range_start=port_range_start, port_range_end=port_range_end) + msg = ( + f"TCP port {old_port} is outside the range {port_range_start}-{port_range_end} on host " + f"{self._console_host}. Port has been replaced by {port}" + ) + log.debug(msg) + return port + try: + PortManager._check_port(self._console_host, port, "TCP") + except OSError: + old_port = port + port = self.get_free_tcp_port(project, port_range_start=port_range_start, port_range_end=port_range_end) + msg = f"TCP port {old_port} already in use on host {self._console_host}. Port has been replaced by {port}" + log.debug(msg) + return port - self._used_tcp_ports.add(port) - project.record_tcp_port(port) + self._used_tcp_ports.add(port) + project.record_tcp_port(port) log.debug(f"TCP port {port} has been reserved") return port @@ -342,10 +352,11 @@ class PortManager: :param project: Project instance """ - if port in self._used_tcp_ports: - self._used_tcp_ports.remove(port) - project.remove_tcp_port(port) - log.debug(f"TCP port {port} has been released") + with self._lock: + if port in self._used_tcp_ports: + self._used_tcp_ports.remove(port) + project.remove_tcp_port(port) + log.debug(f"TCP port {port} has been released") def get_free_udp_port(self, project): """ @@ -353,16 +364,17 @@ class PortManager: :param project: Project instance """ - port = self.find_unused_port( - self._udp_port_range[0], - self._udp_port_range[1], - host=self._udp_host, - socket_type="UDP", - ignore_ports=self._used_udp_ports, - ) + with self._lock: + port = self.find_unused_port( + self._udp_port_range[0], + self._udp_port_range[1], + host=self._udp_host, + socket_type="UDP", + ignore_ports=self._used_udp_ports, + ) - self._used_udp_ports.add(port) - project.record_udp_port(port) + self._used_udp_ports.add(port) + project.record_udp_port(port) log.debug(f"UDP port {port} has been allocated") return port @@ -374,18 +386,20 @@ class PortManager: :param project: Project instance """ - if port in self._used_udp_ports: - raise HTTPException( - status_code=status.HTTP_409_CONFLICT, - detail=f"UDP port {port} already in use on host {self._console_host}", - ) - if port < self._udp_port_range[0] or port > self._udp_port_range[1]: - raise HTTPException( - status_code=status.HTTP_409_CONFLICT, - detail=f"UDP port {port} is outside the range " f"{self._udp_port_range[0]}-{self._udp_port_range[1]}", - ) - self._used_udp_ports.add(port) - project.record_udp_port(port) + with self._lock: + if port in self._used_udp_ports: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail=f"UDP port {port} already in use on host {self._console_host}", + ) + if port < self._udp_port_range[0] or port > self._udp_port_range[1]: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail=f"UDP port {port} is outside the range " + f"{self._udp_port_range[0]}-{self._udp_port_range[1]}", + ) + self._used_udp_ports.add(port) + project.record_udp_port(port) log.debug(f"UDP port {port} has been reserved") def release_udp_port(self, port, project): @@ -396,7 +410,8 @@ class PortManager: :param project: Project instance """ - if port in self._used_udp_ports: - self._used_udp_ports.remove(port) - project.remove_udp_port(port) - log.debug(f"UDP port {port} has been released") + with self._lock: + if port in self._used_udp_ports: + self._used_udp_ports.remove(port) + project.remove_udp_port(port) + log.debug(f"UDP port {port} has been released") diff --git a/gns3server/compute/project.py b/gns3server/compute/project.py index 175d9e916..ae3b5c8d1 100644 --- a/gns3server/compute/project.py +++ b/gns3server/compute/project.py @@ -16,6 +16,7 @@ import os import shutil +import magic import asyncio import hashlib import datetime @@ -245,6 +246,22 @@ class Project: raise ComputeError(f"Could not create the capture working directory: {e}") return workdir + def markers_working_directory(self): + """ + Returns the working directory where uBridge writes per-link marker pcaps + (matched packets, kept for later replay). + + :returns: path to the directory + """ + + workdir = os.path.join(self._path, "project-files", "markers") + if not self._deleted: + try: + os.makedirs(workdir, exist_ok=True) + except OSError as e: + raise ComputeError(f"Could not create the markers working directory: {e}") + return workdir + def add_node(self, node): """ Adds a node to the project. @@ -293,9 +310,14 @@ class Project: # we need to update docker nodes when variables changes if original_variables != variables: + # Parallelize node updates for better performance + tasks = [] for node in self.nodes: if hasattr(node, "update"): - await node.update() + tasks.append(node.update()) + + if tasks: + await asyncio.gather(*tasks, return_exceptions=True) async def close(self): """ @@ -419,65 +441,148 @@ class Project: return files - async def list_node_files(self, node_path: str): + async def list_node_files(self, node_path: str, subpath: str = "", recursive: bool = False): """ List files in a specific node directory with detailed metadata. :param node_path: Relative path to node directory (e.g., "project-files/qemu/node-id") + :param subpath: Optional subdirectory path. Defaults to root of node directory. + :param recursive: If True, recursively list all files (use with caution on large directories). :returns: Array of files in the node directory with metadata """ node_full_path = os.path.normpath(os.path.join(self.path, node_path)) + subpath = subpath.lstrip("/") - # Security check: ensure the path is within the project directory - if not os.path.commonpath([node_full_path, self.path]) == self.path: + if subpath: + target_path = os.path.normpath(os.path.join(node_full_path, subpath)) + else: + target_path = node_full_path + + # Security check: ensure the path is within the node directory + if not os.path.commonpath([target_path, node_full_path]) == node_full_path: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, - detail="Path is outside the project directory") - - if not os.path.exists(node_full_path): + detail="Path is outside the node directory") + if not os.path.exists(target_path): raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, - detail="Node directory not found") + detail="Path not found") + if recursive: + return await self._list_node_files_recursive(node_full_path, target_path, + node_full_path if subpath else None) + + # Non-recursive: list only the current directory level files = [] try: - filenames = os.listdir(node_full_path) - except OSError as e: - log.error(f"Error listing node directory: {e}") + scandir_iter = os.scandir(target_path) + except PermissionError: return files - - for filename in filenames: - file_path = os.path.join(node_full_path, filename) - if not os.path.isfile(file_path) or filename.endswith(".ghost"): - continue - + except OSError as e: + log.error(f"Error listing node directory '{target_path}': {e}") + return files + for entry in scandir_iter: + name = entry.name + rel_path = name if not subpath else os.path.join(subpath, name) try: - # Get file stat information - stat_info = await wait_run_in_executor(os.stat, file_path) - - # Get file extension - _, extension = os.path.splitext(filename) - extension = extension.lstrip('.') - - # Format timestamps as ISO 8601 - try: - created_at = datetime.datetime.fromtimestamp(stat_info.st_ctime).isoformat() - modified_at = datetime.datetime.fromtimestamp(stat_info.st_mtime).isoformat() - except (OSError, OverflowError, ValueError) as e: - log.warning(f"Invalid timestamp for '{filename}': {e}") - created_at = modified_at = "" - - file_info = { - "path": filename, - "size": stat_info.st_size, - "created_at": created_at, - "modified_at": modified_at, - "extension": extension - } - files.append(file_info) - except OSError as e: - log.warning(f"Error getting metadata for file '{filename}': {e}") + stat_info = await wait_run_in_executor(lambda e=entry: e.stat()) + is_dir = await wait_run_in_executor(lambda e=entry: e.is_dir()) + if is_dir: + try: + created_at = datetime.datetime.fromtimestamp(stat_info.st_ctime).isoformat() + modified_at = datetime.datetime.fromtimestamp(stat_info.st_mtime).isoformat() + except (OSError, OverflowError, ValueError): + created_at = modified_at = "" + files.append({ + "path": rel_path, + "size": stat_info.st_size, + "created_at": created_at, + "modified_at": modified_at, + "file_type": "directory" + }) + else: + if name.endswith(".ghost"): + continue + try: + file_type = await wait_run_in_executor( + lambda e=entry: magic.from_file(e.path, mime=False) + ) + except Exception as e: + log.warning(f"Error getting file type for '{rel_path}': {e}") + file_type = "" + try: + created_at = datetime.datetime.fromtimestamp(stat_info.st_ctime).isoformat() + modified_at = datetime.datetime.fromtimestamp(stat_info.st_mtime).isoformat() + except (OSError, OverflowError, ValueError): + created_at = modified_at = "" + files.append({ + "path": rel_path, + "size": stat_info.st_size, + "created_at": created_at, + "modified_at": modified_at, + "file_type": file_type + }) + except OSError: continue + return files + async def _list_node_files_recursive(self, node_full_path, start_path, base_path=None): + """ + Recursively list all files and directories under start_path. + """ + if base_path is None: + base_path = node_full_path + + files = [] + for dirpath, dirnames, filenames in os.walk(start_path, followlinks=False): + for dirname in dirnames: + dir_full_path = os.path.join(dirpath, dirname) + try: + stat_info = await wait_run_in_executor(os.stat, dir_full_path) + try: + created_at = datetime.datetime.fromtimestamp(stat_info.st_ctime).isoformat() + modified_at = datetime.datetime.fromtimestamp(stat_info.st_mtime).isoformat() + except (OSError, OverflowError, ValueError): + created_at = modified_at = "" + files.append({ + "path": os.path.relpath(dir_full_path, base_path), + "size": stat_info.st_size, + "created_at": created_at, + "modified_at": modified_at, + "file_type": "directory" + }) + except OSError: + continue + + for filename in filenames: + if filename.endswith(".ghost"): + continue + file_path = os.path.join(dirpath, filename) + rel_path = os.path.relpath(file_path, base_path) + try: + stat_info = await wait_run_in_executor(os.stat, file_path) + try: + file_type = await wait_run_in_executor( + lambda fp=file_path: magic.from_file(fp, mime=False) + ) + except Exception as e: + log.warning(f"Error getting file type for '{rel_path}': {e}") + file_type = "" + try: + created_at = datetime.datetime.fromtimestamp(stat_info.st_ctime).isoformat() + modified_at = datetime.datetime.fromtimestamp(stat_info.st_mtime).isoformat() + except (OSError, OverflowError, ValueError) as e: + log.warning(f"Invalid timestamp for '{rel_path}': {e}") + created_at = modified_at = "" + files.append({ + "path": rel_path, + "size": stat_info.st_size, + "created_at": created_at, + "modified_at": modified_at, + "file_type": file_type + }) + except OSError as e: + log.warning(f"Error getting metadata for file '{rel_path}': {e}") + continue return files def _hash_file(self, path): diff --git a/gns3server/compute/qemu/qemu_vm.py b/gns3server/compute/qemu/qemu_vm.py index f91177111..439523c65 100644 --- a/gns3server/compute/qemu/qemu_vm.py +++ b/gns3server/compute/qemu/qemu_vm.py @@ -34,6 +34,7 @@ import json import shlex import psutil +from pathlib import Path from gns3server.utils import parse_version from gns3server.utils.asyncio import subprocess_check_output, cancellable_wait_run_in_executor from .qemu_error import QemuError @@ -186,7 +187,7 @@ class QemuVM(BaseNode): log.warning(f"Config disk: image '{self.config_disk_name}' missing") self.config_disk_name = "" - log.info(f'QEMU VM "{self._name}" [{self._id}] has been created') + log.debug(f'QEMU VM "{self._name}" [{self._id}] has been created') @BaseNode.name.setter def name(self, new_name): @@ -269,7 +270,7 @@ class QemuVM(BaseNode): self._platform = re.sub(r'^qemu-system-(\w+).*$', r'\1', qemu_bin, flags=re.IGNORECASE) if self._platform.split(".")[0] not in list(QemuPlatform): raise QemuError(f"Platform {self._platform} is unknown") - log.info(f'QEMU VM "{self._name}" [{self._name}] has set the QEMU path to {qemu_path}') + log.debug(f'QEMU VM "{self._name}" [{self._name}] has set the QEMU path to {qemu_path}') def _check_qemu_path(self, qemu_path): @@ -291,7 +292,7 @@ class QemuVM(BaseNode): def platform(self, platform): self._platform = platform - log.info(f"QEMU VM '{self._name}' [{self._id}] has set the platform {platform}") + log.debug(f"QEMU VM '{self._name}' [{self._id}] has set the platform {platform}") self.qemu_path = f"qemu-system-{platform}" def _disk_setter(self, variable, value): @@ -310,7 +311,7 @@ class QemuVM(BaseNode): f"Sorry a node without the linked base setting enabled can only be used once on your server. {value} is already used by {node.name} in project {node.project.name}" ) setattr(self, "_" + variable, value) - log.info( + log.debug( 'QEMU VM "{name}" [{id}] has set the QEMU {variable} path to {disk_image}'.format( name=self._name, variable=variable, id=self._id, disk_image=value ) @@ -415,7 +416,7 @@ class QemuVM(BaseNode): """ self._hda_disk_interface = hda_disk_interface - log.info( + log.debug( 'QEMU VM "{name}" [{id}] has set the QEMU hda disk interface to {interface}'.format( name=self._name, id=self._id, interface=self._hda_disk_interface ) @@ -440,7 +441,7 @@ class QemuVM(BaseNode): """ self._hdb_disk_interface = hdb_disk_interface - log.info( + log.debug( 'QEMU VM "{name}" [{id}] has set the QEMU hdb disk interface to {interface}'.format( name=self._name, id=self._id, interface=self._hdb_disk_interface ) @@ -465,7 +466,7 @@ class QemuVM(BaseNode): """ self._hdc_disk_interface = hdc_disk_interface - log.info( + log.debug( 'QEMU VM "{name}" [{id}] has set the QEMU hdc disk interface to {interface}'.format( name=self._name, id=self._id, interface=self._hdc_disk_interface ) @@ -490,7 +491,7 @@ class QemuVM(BaseNode): """ self._hdd_disk_interface = hdd_disk_interface - log.info( + log.debug( 'QEMU VM "{name}" [{id}] has set the QEMU hdd disk interface to {interface}'.format( name=self._name, id=self._id, interface=self._hdd_disk_interface ) @@ -517,7 +518,7 @@ class QemuVM(BaseNode): if cdrom_image: self._cdrom_image = self.manager.get_abs_image_path(cdrom_image, self.working_dir) - log.info( + log.debug( 'QEMU VM "{name}" [{id}] has set the QEMU cdrom image path to {cdrom_image}'.format( name=self._name, id=self._id, cdrom_image=self._cdrom_image ) @@ -546,14 +547,14 @@ class QemuVM(BaseNode): self._cdrom_option() # this will check the cdrom image is accessible await self._control_vm("eject -f ide1-cd0") await self._control_vm(f"change ide1-cd0 {self._cdrom_image}") - log.info( + log.debug( 'QEMU VM "{name}" [{id}] has changed the cdrom image path to {cdrom_image}'.format( name=self._name, id=self._id, cdrom_image=self._cdrom_image ) ) else: await self._control_vm("eject -f ide1-cd0") - log.info(f'QEMU VM "{self._name}" [{self._id}] has ejected the cdrom image') + log.debug(f'QEMU VM "{self._name}" [{self._id}] has ejected the cdrom image') @property def bios_image(self): @@ -574,7 +575,7 @@ class QemuVM(BaseNode): """ self._bios_image = self.manager.get_abs_image_path(bios_image, self.working_dir) - log.info( + log.debug( 'QEMU VM "{name}" [{id}] has set the QEMU bios image path to {bios_image}'.format( name=self._name, id=self._id, bios_image=self._bios_image ) @@ -599,7 +600,7 @@ class QemuVM(BaseNode): """ self._boot_priority = boot_priority - log.info( + log.debug( 'QEMU VM "{name}" [{id}] has set the boot priority to {boot_priority}'.format( name=self._name, id=self._id, boot_priority=self._boot_priority ) @@ -634,7 +635,7 @@ class QemuVM(BaseNode): for adapter_number in range(0, adapters): self._ethernet_adapters.append(EthernetAdapter()) - log.info( + log.debug( 'QEMU VM "{name}" [{id}]: number of Ethernet adapters changed to {adapters}'.format( name=self._name, id=self._id, adapters=adapters ) @@ -660,7 +661,7 @@ class QemuVM(BaseNode): self._adapter_type = adapter_type - log.info( + log.debug( 'QEMU VM "{name}" [{id}]: adapter type changed to {adapter_type}'.format( name=self._name, id=self._id, adapter_type=adapter_type ) @@ -690,7 +691,7 @@ class QemuVM(BaseNode): else: self._mac_address = mac_address - log.info( + log.debug( 'QEMU VM "{name}" [{id}]: MAC address changed to {mac_addr}'.format( name=self._name, id=self._id, mac_addr=self._mac_address ) @@ -715,9 +716,9 @@ class QemuVM(BaseNode): """ if replicate_network_connection_state: - log.info(f'QEMU VM "{self._name}" [{self._id}] has enabled network connection state replication') + log.debug(f'QEMU VM "{self._name}" [{self._id}] has enabled network connection state replication') else: - log.info(f'QEMU VM "{self._name}" [{self._id}] has disabled network connection state replication') + log.debug(f'QEMU VM "{self._name}" [{self._id}] has disabled network connection state replication') self._replicate_network_connection_state = replicate_network_connection_state @property @@ -739,9 +740,9 @@ class QemuVM(BaseNode): """ if create_config_disk: - log.info(f'QEMU VM "{self._name}" [{self._id}] has enabled the config disk creation feature') + log.debug(f'QEMU VM "{self._name}" [{self._id}] has enabled the config disk creation feature') else: - log.info(f'QEMU VM "{self._name}" [{self._id}] has disabled the config disk creation feature') + log.debug(f'QEMU VM "{self._name}" [{self._id}] has disabled the config disk creation feature') self._create_config_disk = create_config_disk @property @@ -762,7 +763,7 @@ class QemuVM(BaseNode): :param on_close: string """ - log.info(f'QEMU VM "{self._name}" [{self._id}] set the close action to "{on_close}"') + log.debug(f'QEMU VM "{self._name}" [{self._id}] set the close action to "{on_close}"') self._on_close = on_close @property @@ -783,7 +784,7 @@ class QemuVM(BaseNode): :param cpu_throttling: integer """ - log.info( + log.debug( 'QEMU VM "{name}" [{id}] has set the percentage of CPU allowed to {cpu}'.format( name=self._name, id=self._id, cpu=cpu_throttling ) @@ -811,7 +812,7 @@ class QemuVM(BaseNode): :param process_priority: string """ - log.info( + log.debug( 'QEMU VM "{name}" [{id}] has set the process priority to {priority}'.format( name=self._name, id=self._id, priority=process_priority ) @@ -836,7 +837,7 @@ class QemuVM(BaseNode): :param ram: RAM amount in MB """ - log.info(f'QEMU VM "{self._name}" [{self._id}] has set the RAM to {ram}') + log.debug(f'QEMU VM "{self._name}" [{self._id}] has set the RAM to {ram}') self._ram = ram @property @@ -857,7 +858,7 @@ class QemuVM(BaseNode): :param cpus: number of vCPUs. """ - log.info(f'QEMU VM "{self._name}" [{self._id}] has set the number of vCPUs to {cpus}') + log.debug(f'QEMU VM "{self._name}" [{self._id}] has set the number of vCPUs to {cpus}') self._cpus = cpus @property @@ -878,7 +879,7 @@ class QemuVM(BaseNode): :param maxcpus: maximum number of hotpluggable vCPUs """ - log.info(f'QEMU VM "{self._name}" [{self._id}] has set maximum number of hotpluggable vCPUs to {maxcpus}') + log.debug(f'QEMU VM "{self._name}" [{self._id}] has set maximum number of hotpluggable vCPUs to {maxcpus}') self._maxcpus = maxcpus @property @@ -900,9 +901,9 @@ class QemuVM(BaseNode): """ if tpm: - log.info(f'QEMU VM "{self._name}" [{self._id}] has enabled the Trusted Platform Module (TPM)') + log.debug(f'QEMU VM "{self._name}" [{self._id}] has enabled the Trusted Platform Module (TPM)') else: - log.info(f'QEMU VM "{self._name}" [{self._id}] has disabled the Trusted Platform Module (TPM)') + log.debug(f'QEMU VM "{self._name}" [{self._id}] has disabled the Trusted Platform Module (TPM)') self._tpm = tpm @property @@ -924,9 +925,9 @@ class QemuVM(BaseNode): """ if uefi: - log.info(f'QEMU VM "{self._name}" [{self._id}] has enabled the UEFI boot mode') + log.debug(f'QEMU VM "{self._name}" [{self._id}] has enabled the UEFI boot mode') else: - log.info(f'QEMU VM "{self._name}" [{self._id}] has disabled the UEFI boot mode') + log.debug(f'QEMU VM "{self._name}" [{self._id}] has disabled the UEFI boot mode') self._uefi = uefi @property @@ -947,7 +948,7 @@ class QemuVM(BaseNode): :param options: QEMU options """ - log.info( + log.debug( 'QEMU VM "{name}" [{id}] has set the QEMU options to {options}'.format( name=self._name, id=self._id, options=options ) @@ -995,7 +996,7 @@ class QemuVM(BaseNode): initrd = self.manager.get_abs_image_path(initrd, self.working_dir) - log.info( + log.debug( 'QEMU VM "{name}" [{id}] has set the QEMU initrd path to {initrd}'.format( name=self._name, id=self._id, initrd=initrd ) @@ -1028,7 +1029,7 @@ class QemuVM(BaseNode): """ kernel_image = self.manager.get_abs_image_path(kernel_image, self.working_dir) - log.info( + log.debug( 'QEMU VM "{name}" [{id}] has set the QEMU kernel image path to {kernel_image}'.format( name=self._name, id=self._id, kernel_image=kernel_image ) @@ -1053,7 +1054,7 @@ class QemuVM(BaseNode): :param kernel_command_line: QEMU kernel command line """ - log.info( + log.debug( 'QEMU VM "{name}" [{id}] has set the QEMU kernel command line to {kernel_command_line}'.format( name=self._name, id=self._id, kernel_command_line=kernel_command_line ) @@ -1113,7 +1114,7 @@ class QemuVM(BaseNode): command = [cpulimit_exec, "--lazy", "--pid={}".format(self._process.pid), "--limit={}".format(self._cpu_throttling)] self._cpulimit_process = subprocess.Popen(command, cwd=self.working_dir) - log.info(f"CPU throttled to {self._cpu_throttling}%") + log.debug(f"CPU throttled to {self._cpu_throttling}%") except FileNotFoundError: raise QemuError("cpulimit could not be found, please install it or deactivate CPU throttling") except (OSError, subprocess.SubprocessError) as e: @@ -1171,16 +1172,16 @@ class QemuVM(BaseNode): command = await self._build_command() command_string = " ".join(shlex.quote(s) for s in command) try: - log.info(f"Starting QEMU with: {command_string}") + log.debug(f"Starting QEMU with: {command_string}") self._stdout_file = os.path.join(self.working_dir, "qemu.log") - log.info(f"logging to {self._stdout_file}") + log.debug(f"logging to {self._stdout_file}") with open(self._stdout_file, "w", encoding="utf-8") as fd: fd.write(f"Start QEMU with {command_string}\n\nExecution log:\n") self.command_line = " ".join(command) self._process = await asyncio.create_subprocess_exec( *command, stdout=fd, stderr=subprocess.STDOUT, cwd=self.working_dir ) - log.info(f'QEMU VM "{self._name}" started PID={self._process.pid}') + log.debug(f'QEMU VM "{self._name}" started PID={self._process.pid}') self._command_line_changed = False self.status = "started" monitor_process(self._process, self._termination_callback) @@ -1241,7 +1242,7 @@ class QemuVM(BaseNode): """ if self.started: - log.info("QEMU process has stopped, return code: %d", returncode) + log.debug("QEMU process has stopped, return code: %d", returncode) await self.stop() if returncode != 0: qemu_stdout = self.read_stdout() @@ -1269,7 +1270,7 @@ class QemuVM(BaseNode): # stop the QEMU process self._hw_virtualization = False if self.is_running(): - log.info(f'Stopping QEMU VM "{self._name}" PID={self._process.pid}') + log.debug(f'Stopping QEMU VM "{self._name}" PID={self._process.pid}') try: if self.on_close == "save_vm_state": @@ -1337,7 +1338,7 @@ class QemuVM(BaseNode): ) ) else: - log.info( + log.debug( f"Connected to QEMU monitor on {self._monitor_host}:{self._monitor} after {time.time() - begin:.4f} seconds" ) return reader, writer @@ -1354,7 +1355,7 @@ class QemuVM(BaseNode): result = None if self.is_running() and self._monitor: - log.info(f"Execute QEMU monitor command: {command}") + log.debug(f"Execute QEMU monitor command: {command}") reader, writer = await self._open_qemu_monitor_connection_vm() if reader is None and writer is None: return result @@ -1404,7 +1405,7 @@ class QemuVM(BaseNode): return for command in commands: - log.info(f"Execute QEMU monitor command: {command}") + log.debug(f"Execute QEMU monitor command: {command}") try: cmd_byte = command.encode("ascii") writer.write(cmd_byte + b"\n") @@ -1497,7 +1498,7 @@ class QemuVM(BaseNode): self.status = "suspended" log.debug("QEMU VM has been suspended") else: - log.info(f"QEMU VM is not running to be suspended, current status is {vm_status}") + log.debug(f"QEMU VM is not running to be suspended, current status is {vm_status}") async def reload(self): """ @@ -1524,7 +1525,7 @@ class QemuVM(BaseNode): self.status = "started" log.debug("QEMU VM has been resumed") else: - log.info(f"QEMU VM is not paused to be resumed, current status is {vm_status}") + log.debug(f"QEMU VM is not paused to be resumed, current status is {vm_status}") async def adapter_add_nio_binding(self, adapter_number, nio): """ @@ -1558,7 +1559,7 @@ class QemuVM(BaseNode): ) adapter.add_nio(0, nio) - log.info( + log.debug( 'QEMU VM "{name}" [{id}]: {nio} added to adapter {adapter_number}'.format( name=self._name, id=self._id, nio=nio, adapter_number=adapter_number ) @@ -1618,7 +1619,7 @@ class QemuVM(BaseNode): self.manager.port_manager.release_udp_port(nio.lport, self._project) adapter.remove_nio(0) - log.info( + log.debug( 'QEMU VM "{name}" [{id}]: {nio} removed from adapter {adapter_number}'.format( name=self._name, id=self._id, nio=nio, adapter_number=adapter_number ) @@ -1670,7 +1671,7 @@ class QemuVM(BaseNode): ) ) - log.info( + log.debug( "QEMU VM '{name}' [{id}]: starting packet capture on adapter {adapter_number}".format( name=self.name, id=self.id, adapter_number=adapter_number ) @@ -1691,7 +1692,7 @@ class QemuVM(BaseNode): if self.ubridge: await self._ubridge_send("bridge stop_capture {name}".format(name=f"QEMU-{self._id}-{adapter_number}")) - log.info( + log.debug( "QEMU VM '{name}' [{id}]: stopping packet capture on adapter {adapter_number}".format( name=self.name, id=self.id, adapter_number=adapter_number ) @@ -1730,7 +1731,7 @@ class QemuVM(BaseNode): stdout = self.read_qemu_img_stdout() raise QemuError(f"Could not create '{disk_name}' disk image: qemu-img returned with {retcode}\n{stdout}") else: - log.info(f"QEMU VM '{self.name}' [{self.id}]: Qemu disk image'{disk_name}' created") + log.debug(f"QEMU VM '{self.name}' [{self.id}]: Qemu disk image'{disk_name}' created") except (OSError, subprocess.SubprocessError) as e: stdout = self.read_qemu_img_stdout() raise QemuError(f"Could not create '{disk_name}' disk image: {e}\n{stdout}") @@ -1758,7 +1759,7 @@ class QemuVM(BaseNode): stdout = self.read_qemu_img_stdout() raise QemuError(f"Could not update '{disk_name}' disk image: qemu-img returned with {retcode}\n{stdout}") else: - log.info(f"QEMU VM '{self.name}' [{self.id}]: Qemu disk image '{disk_name}' extended by {extend} MB") + log.debug(f"QEMU VM '{self.name}' [{self.id}]: Qemu disk image '{disk_name}' extended by {extend} MB") except (OSError, subprocess.SubprocessError) as e: stdout = self.read_qemu_img_stdout() raise QemuError(f"Could not update '{disk_name}' disk image: {e}\n{stdout}") @@ -1974,16 +1975,16 @@ class QemuVM(BaseNode): async def _qemu_img_exec(self, command): self._qemu_img_stdout_file = os.path.join(self.working_dir, "qemu-img.log") - log.info(f"logging to {self._qemu_img_stdout_file}") + log.debug(f"logging to {self._qemu_img_stdout_file}") command_string = " ".join(shlex.quote(s) for s in command) - log.info(f"Executing qemu-img with: {command_string}") + log.debug(f"Executing qemu-img with: {command_string}") with open(self._qemu_img_stdout_file, "w", encoding="utf-8") as fd: process = await asyncio.create_subprocess_exec( *command, stdout=fd, stderr=subprocess.STDOUT, cwd=self.working_dir ) retcode = await process.wait() if retcode != 0: - log.info(f"{self._get_qemu_img()} returned with {retcode}") + log.debug(f"{self._get_qemu_img()} returned with {retcode}") return retcode async def _find_disk_file_format(self, disk): @@ -2292,26 +2293,37 @@ class QemuVM(BaseNode): options.extend(["-bios", self._bios_image.replace(",", ",,")]) elif self._uefi: - + system_ovmf_firmware_dir = Path(self.manager.config.settings.Qemu.ovmf_firmware_dir) + log.debug("Using OVMF firmware directory: {}".format(system_ovmf_firmware_dir)) old_ovmf_vars_path = os.path.join(self.working_dir, "OVMF_VARS.fd") if os.path.exists(old_ovmf_vars_path): # the node has its own UEFI variables store already, we must also use the old UEFI firmware ovmf_firmware_path = self.manager.get_abs_image_path("OVMF_CODE.fd") else: - system_ovmf_firmware_path = "/usr/share/OVMF/OVMF_CODE_4M.fd" - if os.path.exists(system_ovmf_firmware_path): - ovmf_firmware_path = system_ovmf_firmware_path + # Use a manual case-insensitive search instead + try: + system_ovmf_firmware_path = next((f for f in system_ovmf_firmware_dir.glob("*.fd") + if f.name.lower() == "ovmf_code_4m.fd"), None) + except (FileNotFoundError, StopIteration): + system_ovmf_firmware_path = None + + if system_ovmf_firmware_path: + ovmf_firmware_path = str(system_ovmf_firmware_path) else: # otherwise, get the UEFI firmware from the images directory ovmf_firmware_path = self.manager.get_abs_image_path("OVMF_CODE_4M.fd") - log.info("Configuring UEFI boot mode using OVMF file: '{}'".format(ovmf_firmware_path)) + log.debug("Configuring UEFI boot mode using OVMF file: '{}'".format(ovmf_firmware_path)) options.extend(["-drive", "if=pflash,format=raw,readonly,file={}".format(ovmf_firmware_path)]) # try to use the UEFI variables store from the system first - system_ovmf_vars_path = "/usr/share/OVMF/OVMF_VARS_4M.fd" - if os.path.exists(system_ovmf_vars_path): - ovmf_vars_path = system_ovmf_vars_path + try: + system_ovmf_vars_path = next((f for f in system_ovmf_firmware_dir.glob("*.fd") + if f.name.lower() == "ovmf_vars_4m.fd"), None) + except (FileNotFoundError, StopIteration): + system_ovmf_vars_path = None + if system_ovmf_vars_path: + ovmf_vars_path = str(system_ovmf_vars_path) else: # otherwise, get the UEFI variables store from the images directory ovmf_vars_path = self.manager.get_abs_image_path("OVMF_VARS_4M.fd") @@ -2327,6 +2339,10 @@ class QemuVM(BaseNode): except OSError as e: raise QemuError("Cannot copy OVMF_VARS_4M.fd file to the node working directory: {}".format(e)) options.extend(["-drive", "if=pflash,format=raw,file={}".format(ovmf_vars_node_path)]) + + # edk2 firmware requires a Random Number Generator (RNG) device in order to turn network adapters on + options.extend(["-object", "rng-random,filename=/dev/urandom,id=rng0"]) + options.extend(["-device", "virtio-rng-pci,rng=rng0"]) return options def _linux_boot_options(self): @@ -2381,9 +2397,9 @@ class QemuVM(BaseNode): "type=unixio,path={},terminate".format(tpm_sock) ] command_string = " ".join(shlex.quote(s) for s in command) - log.info("Starting swtpm (TPM emulator) with: {}".format(command_string)) + log.debug("Starting swtpm (TPM emulator) with: {}".format(command_string)) self._swtpm_process = subprocess.Popen(command, cwd=self.working_dir) - log.info("swtpm (TPM emulator) has started") + log.debug("swtpm (TPM emulator) has started") except (OSError, subprocess.SubprocessError) as e: raise QemuError("Could not start swtpm (TPM emulator): {}".format(e)) @@ -2571,7 +2587,7 @@ class QemuVM(BaseNode): stdout = self.read_qemu_img_stdout() log.warning(f"Could not delete saved VM state from disk {disk}: {stdout}") else: - log.info(f"Deleted saved VM state from disk {disk}") + log.debug(f"Deleted saved VM state from disk {disk}") except subprocess.SubprocessError as e: raise QemuError(f"Error while looking for the Qemu VM saved state snapshot: {e}") @@ -2601,7 +2617,7 @@ class QemuVM(BaseNode): if "snapshots" in json_data: for snapshot in json_data["snapshots"]: if snapshot["name"] == snapshot_name: - log.info( + log.debug( 'QEMU VM "{name}" [{id}] VM saved state detected (snapshot name: {snapshot})'.format( name=self._name, id=self.id, snapshot=snapshot_name ) @@ -2649,7 +2665,6 @@ class QemuVM(BaseNode): elif sys.platform.startswith("darwin"): command.extend(["-enable-hax"]) command.extend(["-boot", f"order={self._boot_priority}"]) - command.extend(self._bios_option()) command.extend(self._cdrom_option()) command.extend(await self._disk_options()) command.extend(self._linux_boot_options()) @@ -2659,6 +2674,8 @@ class QemuVM(BaseNode): command.extend(self._aux_options()) command.extend(self._monitor_options()) command.extend(await self._network_options()) + # bios options must be last to have predictable NIC numbering, see https://github.com/GNS3/gns3-server/issues/2838 + command.extend(self._bios_option()) if self.on_close != "save_vm_state": await self._clear_save_vm_stated() else: diff --git a/gns3server/compute/ubridge/hypervisor.py b/gns3server/compute/ubridge/hypervisor.py index a702adb34..661da580c 100644 --- a/gns3server/compute/ubridge/hypervisor.py +++ b/gns3server/compute/ubridge/hypervisor.py @@ -18,11 +18,11 @@ Represents a uBridge hypervisor and starts/stops the associated uBridge process. """ -import sys import os +import socket import subprocess import asyncio -import socket +import tempfile import re from gns3server.utils import parse_version @@ -44,17 +44,42 @@ class Hypervisor(UBridgeHypervisor): :param project: Project instance :param path: path to uBridge executable :param working_dir: working directory - :param host: host/address for this hypervisor - :param port: port for this hypervisor + :param transport: control channel transport — "unix" (-U) or "tcp" (-H) + :param host: host/address for the TCP transport (unused for "unix") + :param node_id: node id used to name the AF_UNIX socket (unix transport) """ - _instance_count = 1 + _instance_count = 0 - def __init__(self, project, path, working_dir, host, port=None): + def __init__(self, project, path, working_dir, transport, host=None, node_id=None): - if port is None: + self._project = project + self._path = path + self._working_dir = working_dir + + if transport == "unix": + # AF_UNIX control socket (-U). Name it after the node so the socket + # is self-describing (one ubridge per node => node_id is unique). + # sun_path is capped at 107 bytes; a single UUID fits comfortably + # (~69 bytes with this prefix), so no project_id is needed. + if node_id: + socket_name = f"ubridge-{node_id}.sock" + else: + Hypervisor._instance_count += 1 + socket_name = f"ubridge-{Hypervisor._instance_count}.sock" + runtime_dir = os.environ.get("XDG_RUNTIME_DIR") or tempfile.gettempdir() + socket_dir = os.path.join(runtime_dir, "gns3") + try: + os.makedirs(socket_dir, mode=0o700, exist_ok=True) + os.chmod(socket_dir, 0o700) + except OSError as e: + raise UbridgeError(f"Could not create uBridge socket directory {socket_dir}: {e}") + socket_path = os.path.join(socket_dir, socket_name) + super().__init__(socket_path=socket_path) + else: + # TCP control channel (-H): let the OS find an unused local port. + port = None try: - port = None info = socket.getaddrinfo(host, 0, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE) if not info: raise UbridgeError(f"getaddrinfo returns an empty list on {host}") @@ -68,11 +93,8 @@ class Hypervisor(UBridgeHypervisor): break except OSError as e: raise UbridgeError(f"Could not find free port for the uBridge hypervisor: {e}") + super().__init__(host=host, port=port) - super().__init__(host, port) - self._project = project - self._path = path - self._working_dir = working_dir self._command = [] self._process = None self._stdout_file = "" @@ -131,19 +153,17 @@ class Hypervisor(UBridgeHypervisor): async def _check_ubridge_version(self, env=None): """ - Checks if the ubridge executable version + Checks if the ubridge executable version meets the minimum required. """ try: output = await subprocess_check_output(self._path, "-v", cwd=self._working_dir, env=env) match = re.search(r"ubridge version ([0-9a-z\.]+)", output) if match: self._version = match.group(1) - if sys.platform.startswith("darwin"): - minimum_required_version = "0.9.12" - else: - # uBridge version 0.9.14 is required for packet filters - # to work for IOU nodes. - minimum_required_version = "0.9.14" + # uBridge >= 1.2.0 is required for features this server now + # relies on: the AF_UNIX control channel (-U), the marker + # (mark) filter, and the brctl-backed builtin Ethernet Switch. + minimum_required_version = "1.2.0" if parse_version(self._version) < parse_version(minimum_required_version): raise UbridgeError(f"uBridge executable version must be >= {minimum_required_version}") else: @@ -160,15 +180,26 @@ class Hypervisor(UBridgeHypervisor): await self._check_ubridge_version(env) try: command = self._build_command() - log.info(f"starting ubridge: {command}") + log.debug(f"starting ubridge: {command}") self._stdout_file = os.path.join(self._working_dir, "ubridge.log") - log.info(f"logging to {self._stdout_file}") + log.debug(f"logging to {self._stdout_file}") with open(self._stdout_file, "w", encoding="utf-8") as fd: self._process = await asyncio.create_subprocess_exec( *command, stdout=fd, stderr=subprocess.STDOUT, cwd=self._working_dir, env=env ) - log.info(f"ubridge started PID={self._process.pid}") + log.debug(f"ubridge started PID={self._process.pid}") + # An unsupported flag (e.g. -U on an old ubridge build) makes ubridge exit + # immediately with a non-zero code. Detect that here and surface the real + # reason from ubridge.log instead of waiting for connect() to time out with + # a confusing "couldn't connect" error. + await asyncio.sleep(0.3) + if self._process.returncode is not None: + raise UbridgeError( + f"uBridge exited immediately (code {self._process.returncode}); if " + f"ubridge_control_transport is 'unix', the installed ubridge may not " + f"support -U.\n{self.read_stdout()}" + ) # recv: Bad address is received by uBridge when a docker image stops by itself # see https://github.com/GNS3/gns3-gui/issues/2957 # monitor_process(self._process, self._termination_callback) @@ -189,7 +220,7 @@ class Hypervisor(UBridgeHypervisor): log.error(error_msg) self._project.emit("log.error", {"message": error_msg}) else: - log.info("uBridge process has stopped, return code: %d", returncode) + log.debug("uBridge process has stopped, return code: %d", returncode) async def stop(self): """ @@ -197,7 +228,7 @@ class Hypervisor(UBridgeHypervisor): """ if self.is_running(): - log.info(f"Stopping uBridge process PID={self._process.pid}") + log.debug(f"Stopping uBridge process PID={self._process.pid}") await UBridgeHypervisor.stop(self) try: await wait_for_process_termination(self._process, timeout=3) @@ -214,6 +245,16 @@ class Hypervisor(UBridgeHypervisor): os.remove(self._stdout_file) except OSError as e: log.warning(f"could not delete temporary uBridge log file: {e}") + + # ubridge unlinks its AF_UNIX control socket on a clean exit; for the + # unix transport remove it here too so a killed process leaves no stale + # socket behind. The TCP transport has no socket_path. + if self._socket_path: + try: + os.unlink(self._socket_path) + except OSError: + pass + self._process = None self._started = False @@ -250,7 +291,10 @@ class Hypervisor(UBridgeHypervisor): """ command = [self._path] - command.extend(["-H", f"{self._host}:{self._port}"]) + if self._socket_path: + command.extend(["-U", self._socket_path]) + else: + command.extend(["-H", f"{self._host}:{self._port}"]) if log.getEffectiveLevel() == logging.DEBUG: command.extend(["-d", "1"]) return command diff --git a/gns3server/compute/ubridge/ubridge_hypervisor.py b/gns3server/compute/ubridge/ubridge_hypervisor.py index a44bf3834..c010e1e2d 100644 --- a/gns3server/compute/ubridge/ubridge_hypervisor.py +++ b/gns3server/compute/ubridge/ubridge_hypervisor.py @@ -28,20 +28,29 @@ log = logging.getLogger(__name__) class UBridgeHypervisor: """ - Creates a new connection to uBridge hypervisor. + Creates a new connection to a uBridge hypervisor control channel. - :param host: the hostname or ip address string of the uBridge hypervisor - :param port: the tcp port integer + Two transports, selected by which argument is set: + * ``socket_path`` -> AF_UNIX (``-U``), authenticated in-kernel via + SO_PEERCRED (ubridge accepts only its own UID; the compute process that + spawned it shares that UID). Recommended on Linux. + * ``host``/``port`` -> TCP (``-H``), retained for backward compatibility. + + :param socket_path: path to the uBridge AF_UNIX control socket (None for TCP) + :param host: TCP hostname/IP (None for AF_UNIX) + :param port: TCP port :param timeout: timeout integer for how long to wait for a response to commands sent to the - hypervisor (defaults to 30 seconds) + hypervisor (defaults to 30 seconds) """ # Used to parse Ubridge response codes error_re = re.compile(r"""^2[0-9]{2}-""") success_re = re.compile(r"""^1[0-9]{2}\s{1}""") - def __init__(self, host, port, timeout=30.0): + def __init__(self, socket_path=None, host=None, port=None, timeout=30.0): + # Exactly one transport is active: socket_path (AF_UNIX) or host/port (TCP). + self._socket_path = socket_path self._host = host self._port = port self._version = "N/A" @@ -54,22 +63,23 @@ class UBridgeHypervisor: Connects to the hypervisor. """ - # connect to a local address by default - # if listening to all addresses (IPv4 or IPv6) - if self._host == "0.0.0.0": - host = "127.0.0.1" - elif self._host == "::": - host = "::1" - else: - host = self._host - begin = time.time() connection_success = False last_exception = None while time.time() - begin < timeout: await asyncio.sleep(0.1) try: - self._reader, self._writer = await asyncio.open_connection(host, self._port) + if self._socket_path: + self._reader, self._writer = await asyncio.open_unix_connection(self._socket_path) + else: + # connect to a local address by default if listening on all addresses + if self._host == "0.0.0.0": + host = "127.0.0.1" + elif self._host == "::": + host = "::1" + else: + host = self._host + self._reader, self._writer = await asyncio.open_connection(host, self._port) except OSError as e: last_exception = e continue @@ -77,9 +87,9 @@ class UBridgeHypervisor: break if not connection_success: - raise UbridgeError(f"Couldn't connect to hypervisor on {host}:{self._port} :{last_exception}") + raise UbridgeError(f"Couldn't connect to hypervisor on {self.endpoint} :{last_exception}") else: - log.info(f"Connected to uBridge hypervisor on {host}:{self._port} after {time.time() - begin:.4f} seconds") + log.debug(f"Connected to uBridge hypervisor on {self.endpoint} after {time.time() - begin:.4f} seconds") try: await asyncio.sleep(0.1) @@ -122,7 +132,7 @@ class UBridgeHypervisor: await self._writer.drain() self._writer.close() except OSError as e: - log.debug(f"Stopping hypervisor {self._host}:{self._port} {e}") + log.debug(f"Stopping hypervisor {self.endpoint} {e}") self._reader = self._writer = None async def reset(self): @@ -133,44 +143,17 @@ class UBridgeHypervisor: await self.send("hypervisor reset") @property - def port(self): + def endpoint(self): """ - Returns the port used to start the hypervisor. + Returns a human-readable control endpoint: the AF_UNIX socket path when + using -U, or host:port when using -H. Used for logging and errors. - :returns: port number (integer) + :returns: endpoint (string) """ - return self._port - - @port.setter - def port(self, port): - """ - Sets the port used to start the hypervisor. - - :param port: port number (integer) - """ - - self._port = port - - @property - def host(self): - """ - Returns the host (binding) used to start the hypervisor. - - :returns: host/address (string) - """ - - return self._host - - @host.setter - def host(self, host): - """ - Sets the host (binding) used to start the hypervisor. - - :param host: host/address (string) - """ - - self._host = host + if self._socket_path: + return self._socket_path + return f"{self._host}:{self._port}" @locking async def send(self, command): @@ -205,8 +188,8 @@ class UBridgeHypervisor: await self._writer.drain() except OSError as e: raise UbridgeError( - "Lost communication with {host}:{port} when sending command '{command}': {error}, uBridge process running: {run}".format( - host=self._host, port=self._port, command=command, error=e, run=self.is_running() + "Lost communication with {endpoint} when sending command '{command}': {error}, uBridge process running: {run}".format( + endpoint=self.endpoint, command=command, error=e, run=self.is_running() ) ) @@ -232,8 +215,8 @@ class UBridgeHypervisor: if not chunk: if retries > max_retries: raise UbridgeError( - "No data returned from {host}:{port} after sending command '{command}', uBridge process running: {run}".format( - host=self._host, port=self._port, command=command, run=self.is_running() + "No data returned from {endpoint} after sending command '{command}', uBridge process running: {run}".format( + endpoint=self.endpoint, command=command, run=self.is_running() ) ) else: @@ -244,8 +227,8 @@ class UBridgeHypervisor: buf += chunk.decode("utf-8") except OSError as e: raise UbridgeError( - "Lost communication with {host}:{port} after sending command '{command}': {error}, uBridge process running: {run}".format( - host=self._host, port=self._port, command=command, error=e, run=self.is_running() + "Lost communication with {endpoint} after sending command '{command}': {error}, uBridge process running: {run}".format( + endpoint=self.endpoint, command=command, error=e, run=self.is_running() ) ) @@ -255,8 +238,8 @@ class UBridgeHypervisor: continue except IndexError: raise UbridgeError( - "Could not communicate with {host}:{port} after sending command '{command}', uBridge process running: {run}".format( - host=self._host, port=self._port, command=command, run=self.is_running() + "Could not communicate with {endpoint} after sending command '{command}', uBridge process running: {run}".format( + endpoint=self.endpoint, command=command, run=self.is_running() ) ) diff --git a/gns3server/config.py b/gns3server/config.py index c579a5328..278432358 100644 --- a/gns3server/config.py +++ b/gns3server/config.py @@ -24,6 +24,7 @@ import shutil import secrets import configparser +from enum import Enum from pydantic import ValidationError from .schemas import ServerConfig from .version import __version_info__ @@ -34,6 +35,19 @@ import logging log = logging.getLogger(__name__) +class ConfigConflictError(Exception): + """ + Raised when a configuration option is set in a configuration file that + takes precedence over the main configuration file. + """ + + +# List options written back to the configuration file as semicolon-separated +# values; every other list option is comma-separated (must match the field +# validators splitting them in gns3server.schemas.config). +LIST_OPTION_SEPARATORS = {"additional_images_paths": ";"} + + class Config: """ Configuration file management using configparser. @@ -199,6 +213,13 @@ class Config: """ log.info(f"'{file_path}' has been updated, reloading the config...") + self.reload_and_notify() + + def reload_and_notify(self): + """ + Reload the configuration files and notify registered listeners. + """ + self.read_config() for callback in self._watch_callback: callback() @@ -210,6 +231,107 @@ class Config: self.read_config() + @staticmethod + def _stringify_option(option: str, value) -> str: + """ + Serialize a settings value to its INI string representation. + """ + + if isinstance(value, bool): + return str(value) + if isinstance(value, Enum): + return str(value.value) + if isinstance(value, list): + return LIST_OPTION_SEPARATORS.get(option, ",").join(value) + return str(value) + + def update_config(self, changes: dict) -> list: + """ + Apply setting changes to the main configuration file (read-modify-write). + + Only the submitted options are set or removed, preserving any unknown + options present in the file. The merged configuration is validated + before anything is written to disk, so an invalid change leaves the + file untouched (a file that fails validation would permanently kill + the FileWatcher polling loop when it gets reloaded). + + :param changes: mapping of section name to {option: value}; a value of + None removes the option from the file, restoring its default + :returns: sorted list of changed options as "Section.option" strings + + :raises pydantic.ValidationError: the merged settings are invalid + :raises ConfigConflictError: an option is set in a configuration file + that takes precedence over the main configuration file + :raises OSError: the configuration file could not be written + """ + + if not changes: + return [] + + main_config_file = self._main_config_file + existing_files = [file for file in self._files if os.path.isfile(file)] + + # per-file parsers to find which file wins for an option + # (later files take precedence, mirroring read_config) + per_file_parsers = [] + for file in existing_files: + parser = configparser.ConfigParser(interpolation=None) + parser.read(file, encoding="utf-8") + per_file_parsers.append(parser) + + # view of what gets written: the main configuration file only + write_parser = configparser.ConfigParser(interpolation=None) + if os.path.isfile(main_config_file): + write_parser.read(main_config_file, encoding="utf-8") + + # view of what the server will load: all configuration files merged + merged_parser = configparser.ConfigParser(interpolation=None) + merged_parser.read(existing_files, encoding="utf-8") + + changed = [] + for section, options in changes.items(): + for option, value in options.items(): + winner = None + for file, parser in zip(reversed(existing_files), reversed(per_file_parsers)): + if parser.has_option(section, option): + winner = file + break + if winner is not None and winner != main_config_file: + raise ConfigConflictError( + f"'{section}.{option}' is set in '{winner}' which takes precedence " + f"over the main configuration file '{main_config_file}'" + ) + if value is None: + # explicit null: remove the option to restore its default + if write_parser.has_option(section, option): + write_parser.remove_option(section, option) + if merged_parser.has_option(section, option): + merged_parser.remove_option(section, option) + else: + option_value = self._stringify_option(option, value) + if not write_parser.has_section(section): + write_parser.add_section(section) + write_parser.set(section, option, option_value) + if not merged_parser.has_section(section): + merged_parser.add_section(section) + merged_parser.set(section, option, option_value) + changed.append(f"{section}.{option}") + + # validate the merged settings before touching the file on disk + ServerConfig(**merged_parser._sections) + + directory_name = os.path.dirname(main_config_file) + if directory_name: + os.makedirs(directory_name, exist_ok=True) + tmp_file = main_config_file + ".tmp" + fd = os.open(tmp_file, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600) + with os.fdopen(fd, "w", encoding="utf-8") as f: + write_parser.write(f) + os.replace(tmp_file, main_config_file) + + self.reload_and_notify() + return sorted(changed) + def get_config_files(self): """ Return the config files in use. diff --git a/gns3server/config_samples/gns3_server.conf b/gns3server/config_samples/gns3_server.conf index c19a60077..c3eb76bc9 100644 --- a/gns3server/config_samples/gns3_server.conf +++ b/gns3server/config_samples/gns3_server.conf @@ -4,17 +4,23 @@ jwt_secret_key = efd08eccec3bd0a1be2e086670e5efa90969c68d07e072d7354a76cea5e33d4e jwt_algorithm = HS256 jwt_access_token_expire_minutes = 1440 +jwt_refresh_token_expire_minutes = 43200 -; Initial default super admin username -; It cannot be changed once the controller has started once +; Username of the super admin account seeded when the controller database is +; created. Changing it has no effect until the database is re-created (which +; resets the account back to these values). default_admin_username = admin -; Initial default super admin password -; It cannot be changed once the controller has started once +; Password of the super admin account seeded when the controller database is +; created. Changing it has no effect until the database is re-created (which +; resets the account back to these values). default_admin_password = admin [Server] +; Local server mode, set by the --local command line argument (not meant to be set by hand) +;local = False + ; Server name, default is what is returned by socket.gethostname() name = GNS3_Server @@ -76,6 +82,10 @@ console_start_port_range = 5000 ; Last console port of the range allocated to devices console_end_port_range = 10000 +; Allow console connections from remote machines +; (console ports only accept local connections by default) +;allow_remote_console = False + ; First VNC console port of the range allocated to devices. ; The value MUST BE >= 5900 and <= 65535 vnc_console_start_port_range = 5900 @@ -91,6 +101,19 @@ udp_end_port_range = 30000 ; uBridge executable location, default: search in PATH ;ubridge_path = ubridge +; uBridge control channel transport: "unix" (-U socket_path; AF_UNIX + +; SO_PEERCRED, default — recommended on Linux for kernel-level peer +; authentication) or "tcp" (-H host:port; retained for backward compatibility, +; binds loopback). +;ubridge_control_transport = unix + +; Marker (traffic-insight) UDP sink: one listener per compute process that +; receives uBridge MARK signals from every uBridge on this host. +; marker_listen_host defaults to 127.0.0.1 because uBridge runs locally. +; marker_listen_port defaults to 3070 (set to 0 for OS-chosen). +;marker_listen_host = 127.0.0.1 +;marker_listen_port = 3070 + ; Option to enable or disable compute HTTP authentication enable_http_auth = True @@ -121,9 +144,6 @@ install_builtin_appliances = True ; Automatically pull updates from the skills repository when reloading ; skills_auto_update = false -; check if hardware virtualization is used by other emulators (KVM, VMware or VirtualBox) -hardware_virtualization_check = True - [VPCS] ; VPCS executable location, default: search in PATH ;vpcs_path = vpcs @@ -169,6 +189,8 @@ enable_hardware_acceleration = True require_hardware_acceleration = False ; Allow unsafe additional command line options allow_unsafe_options = False +; Path to the OVMF firmware directory +ovmf_firmware_dir = "/usr/share/OVMF" [WebWireshark] ; Enable Web Wireshark feature (container-based Wireshark in browser) @@ -183,4 +205,12 @@ memory = 2g ; CPU cores per container (e.g., 1.0, 2.0) cpus = 1.0 ; Process limit per container -pids_limit = 1000 \ No newline at end of file +pids_limit = 1000 +; MCP (Model Context Protocol) transport security settings +; Disabled by default — allows connections from any host (matches GNS3 +; server's 0.0.0.0 binding). Enable and configure allowed hosts below +; for enhanced security against DNS rebinding attacks. +; Note: Only "host:*" port wildcards are supported (e.g., "127.0.0.1:*"). +;mcp_enable_dns_rebinding_protection = true +;mcp_allowed_hosts = 127.0.0.1:*,localhost:* +;mcp_allowed_origins = http://127.0.0.1:*,http://localhost:* diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py index 9cfe2bcd7..9593ee1cc 100644 --- a/gns3server/controller/__init__.py +++ b/gns3server/controller/__init__.py @@ -29,7 +29,7 @@ try: except ImportError: from importlib import resources as importlib_resources -from watchdog.events import FileSystemEventHandler +from watchdog.events import FileSystemEventHandler, DirDeletedEvent, FileDeletedEvent from watchdog.observers import Observer from ..config import Config @@ -73,6 +73,9 @@ class _ProjectsDirectoryEventHandler(FileSystemEventHandler): def on_moved(self, event): self._handle_event(event) + def on_deleted(self, event: DirDeletedEvent | FileDeletedEvent) -> None: + self._handle_event(event) + def _handle_event(self, event): if event.is_directory: # Only react to direct child directories of the projects path @@ -446,6 +449,12 @@ class Controller: return # Monitor was stopped, skip the scan try: await self.load_projects() + # Remove stale projects that no longer exist on disk + for project_id in list(self._projects): + project = self._projects[project_id] + if not os.path.exists(project.path): + log.info(f"Removing stale project '{project.name}' ('{project.path}' no longer exists)") + del self._projects[project.id] except Exception as e: log.warning(f"Projects directory rescan failed: {e}") @@ -736,11 +745,25 @@ class Controller: if not os.path.exists(path): raise ControllerError(f"'{path}' does not exist on the controller") + # A .gns3 file must live in its own directory: the project path is + # the file's parent directory. A file placed directly in the + # projects directory would register the shared projects root as the + # project directory, and deleting that project would wipe every + # project on the controller. + projects_path = os.path.realpath(self.projects_directory()) + if os.path.realpath(os.path.dirname(path)) == projects_path: + raise ControllerError( + f"'{path}' cannot be loaded: the .gns3 file must be in its own subdirectory of '{projects_path}'" + ) + topo_data = load_topology(path) topo_data.pop("topology") topo_data.pop("version") topo_data.pop("revision") topo_data.pop("type") + # marker_definitions is restored by Project.open() from the topology + # file; it must not be passed to Project.__init__. + topo_data.pop("marker_definitions", None) if topo_data["project_id"] in self._projects: project = self._projects[topo_data["project_id"]] diff --git a/gns3server/controller/appliance.py b/gns3server/controller/appliance.py index 629765b8b..fa964abfa 100644 --- a/gns3server/controller/appliance.py +++ b/gns3server/controller/appliance.py @@ -71,6 +71,17 @@ class Appliance: @property def type(self): + if self._data.get("registry_version", 0) >= 8: + # registry version 8: the node type comes from the settings template_type, + # the default settings take precedence over the other sets + settings_list = self._data.get("settings") or [] + for settings in settings_list: + if settings.get("default"): + return settings.get("template_type", "qemu") + if settings_list: + return settings_list[0].get("template_type", "qemu") + return "qemu" + if "iou" in self._data: return "iou" elif "dynamips" in self._data: diff --git a/gns3server/controller/appliance_manager.py b/gns3server/controller/appliance_manager.py index 0420e78cc..e411b9852 100644 --- a/gns3server/controller/appliance_manager.py +++ b/gns3server/controller/appliance_manager.py @@ -174,7 +174,7 @@ class ApplianceManager: version_images = version.get("images") if version_images: for appliance_key, appliance_file in version_images.items(): - for image in appliance.images: + for image in appliance.images or []: if appliance_file == image.get("filename"): image_checksum = image.get("md5sum") image_in_db = await images_repo.get_image_by_checksum(image_checksum) @@ -204,9 +204,9 @@ class ApplianceManager: else: raise ControllerError(f"Could not find '{appliance_file}'") - async def _create_template(self, template_data, templates_repo, rbac_repo, current_user): + async def _create_template(self, template_data, templates_repo, rbac_repo, current_user) -> dict: """ - Create a new template + Create a new template and return it as a dict. """ try: @@ -217,6 +217,7 @@ class ApplianceManager: #template_id = template.get("template_id") #await rbac_repo.add_permission_to_user_with_path(current_user.user_id, f"/templates/{template_id}/*") log.info(f"Template '{template.get('name')}' has been created") + return template async def _appliance_to_template(self, appliance: Appliance, version: str = None) -> dict: """ @@ -225,12 +226,15 @@ class ApplianceManager: from . import Controller - # downloading missing custom symbol for this appliance - if appliance.symbol and not appliance.symbol.startswith(":/symbols/"): - destination_path = os.path.join(Controller.instance().symbols.symbols_path(), appliance.symbol) + template_data = ApplianceToTemplate().new_template(appliance.asdict(), version, "local") # FIXME: "local" + # download the custom symbol used by the template if it is missing; + # the symbol can be defined at the appliance, version or settings level + symbol = template_data.get("symbol") + if symbol and not symbol.startswith(":/symbols/"): + destination_path = os.path.join(Controller.instance().symbols.symbols_path(), symbol) if not os.path.exists(destination_path): - await self._download_symbol(appliance.symbol, destination_path) - return ApplianceToTemplate().new_template(appliance.asdict(), version, "local") # FIXME: "local" + await self._download_symbol(symbol, destination_path) + return template_data async def install_appliances_from_image( self, @@ -241,11 +245,16 @@ class ApplianceManager: rbac_repo: RbacRepository, current_user: schemas.User, image_dir: str - ) -> None: + ) -> List[dict]: """ - Install appliances using an image checksum + Install appliances using an image checksum. + + Returns a manifest of what happened: one entry per attempted template, + either {"status": "created", ...template fields} or + {"status": "skipped", "name", "reason"}. """ + results: List[dict] = [] appliances_info = self._find_appliances_from_image_checksum(image_checksum) for appliance, image_version in appliances_info: try: @@ -253,15 +262,48 @@ class ApplianceManager: ApplianceModel.model_validate(appliance.asdict()) except ValidationError as e: log.warning(f"Could not validate appliance '{appliance.id}': {e}") + results.append({ + "status": "skipped", + "name": appliance.name, + "reason": f"could not validate appliance '{appliance.id}': {e}", + }) + continue if appliance.versions: for version in appliance.versions: if version.get("name") == image_version: try: await self._find_appliance_version_images(appliance, version, images_repo, image_dir) template_data = await self._appliance_to_template(appliance, version) - await self._create_template(template_data, templates_repo, rbac_repo, current_user) + name = template_data.get("name") + existing = await templates_repo.get_template_by_name(name) if name else None + if existing is not None: + # never automatically create a second template with the same + # name: the name+version check in TemplatesService would allow + # duplicates when the appliance version differs, but two + # templates sharing a name is never what the user asked for here + log.warning(f"Template '{name}' already exists, skipping automatic template creation") + results.append({ + "status": "skipped", + "name": name, + "reason": f"a template named '{name}' already exists", + }) + continue + template = await self._create_template(template_data, templates_repo, rbac_repo, current_user) + results.append({ + "status": "created", + "template_id": str(template.get("template_id")), + "name": template.get("name"), + "version": template.get("version"), + "template_type": template.get("template_type"), + }) except (ControllerError, InvalidImageError) as e: log.warning(f"Could not automatically create template using image '{image_path}': {e}") + results.append({ + "status": "skipped", + "name": appliance.name, + "reason": str(e), + }) + return results async def install_appliance( self, @@ -290,11 +332,14 @@ class ApplianceManager: if not appliance.versions: raise ControllerBadRequestError(message=f"Appliance '{appliance_id}' do not have versions") - image_dir = default_images_directory(appliance.type) for appliance_version_info in appliance.versions: if appliance_version_info.get("name") == version: try: - await self._find_appliance_version_images(appliance, appliance_version_info, images_repo, image_dir) + template_type = ApplianceToTemplate().get_template_type(appliance.asdict(), appliance_version_info) + if template_type != "docker": + # docker appliances have no image files to find or download + image_dir = default_images_directory(template_type) + await self._find_appliance_version_images(appliance, appliance_version_info, images_repo, image_dir) except InvalidImageError as e: raise ControllerError(message=f"Image error: {e}") template_data = await self._appliance_to_template(appliance, appliance_version_info) @@ -362,7 +407,16 @@ class ApplianceManager: symbol_theme = controller.symbols.theme category = appliance["category"] if category == "guest": - if "docker" in appliance: + if appliance.get("registry_version", 0) >= 8: + # registry version 8: the emulator type comes from the default + # settings set (or the only one present), not a top-level block + settings = appliance.get("settings") or [] + selected = next((s for s in settings if s.get("default")), settings[0] if settings else None) + if selected and selected.get("template_type") == "docker": + return controller.symbols.get_default_symbol("docker_guest", symbol_theme) + if selected: + return controller.symbols.get_default_symbol("qemu_guest", symbol_theme) + elif "docker" in appliance: return controller.symbols.get_default_symbol("docker_guest", symbol_theme) elif "qemu" in appliance: return controller.symbols.get_default_symbol("qemu_guest", symbol_theme) diff --git a/gns3server/controller/appliance_to_template.py b/gns3server/controller/appliance_to_template.py index 658dc42c7..3602a7a7e 100644 --- a/gns3server/controller/appliance_to_template.py +++ b/gns3server/controller/appliance_to_template.py @@ -18,9 +18,32 @@ import logging +from .controller_error import ControllerError + log = logging.getLogger(__name__) +# appliance fields that describe the appliance (vendor information, default +# credentials...) and are kept on the template as metadata instead of being +# dropped at installation time +_APPLIANCE_METADATA_FIELDS = ( + "description", + "vendor_name", + "vendor_url", + "vendor_logo_url", + "documentation_url", + "product_name", + "product_url", + "status", + "availability", + "maintainer", + "maintainer_email", + "installation_instructions", + "default_username", + "default_password", +) + + class ApplianceToTemplate: """ Appliance installation. @@ -31,6 +54,9 @@ class ApplianceToTemplate: Creates a new template from an appliance. """ + if appliance_config.get("registry_version", 0) >= 8: + return self._new_template_v8(appliance_config, version, server) + new_template = { "compute_id": server, "name": appliance_config["name"], @@ -53,6 +79,13 @@ class ApplianceToTemplate: if "tags" in appliance_config: new_template["tags"] = appliance_config.get("tags") + if appliance_config.get("netmiko_device_type"): + new_template["netmiko_device_type"] = appliance_config["netmiko_device_type"] + + appliance_metadata = self._build_appliance_metadata(appliance_config, version) + if appliance_metadata: + new_template["appliance_metadata"] = appliance_metadata + if new_template.get("symbol") is None: if appliance_config["category"] == "guest": if "docker" in appliance_config: @@ -133,3 +166,172 @@ class ApplianceToTemplate: new_config.update(appliance_config["iou"]) new_config["path"] = version.get("images").get("image") + + def _new_template_v8(self, appliance_config, version, server): + """ + Creates a new template from an appliance using the registry version 8 format. + """ + + settings = self._select_v8_settings(appliance_config, version) + properties = self._merge_v8_properties(settings, appliance_config) + + new_template = { + "compute_id": server, + "template_type": settings["template_type"], + "name": appliance_config["name"], + } + + if version: + new_template["version"] = version.get("name") + + # category/usage/symbol can be defined in template_properties (already merged above), + # otherwise at the version level, otherwise at the appliance level + for prop in ("category", "usage", "symbol"): + if prop not in properties: + if version and version.get(prop) is not None: + properties[prop] = version[prop] + elif appliance_config.get(prop) is not None: + properties[prop] = appliance_config[prop] + + category_before_remap = properties.get("category") + if category_before_remap == "multilayer_switch": + properties["category"] = "switch" + + if settings["template_type"] == "qemu": + # kvm is not a valid template property: convert it to the + # equivalent qemu options like for registry versions 1-6 + kvm = properties.pop("kvm", None) or "allow" + options = properties.get("options") or "" + if kvm == "disable" and "-machine accel=tcg" not in options: + options += " -machine accel=tcg" + properties["options"] = options.strip() + + # template_properties must not override the structural fields + for reserved in ("template_type", "compute_id", "version"): + properties.pop(reserved, None) + + new_template.update(properties) + if "tags" in appliance_config: + new_template["tags"] = appliance_config.get("tags") + + if appliance_config.get("netmiko_device_type"): + new_template["netmiko_device_type"] = appliance_config["netmiko_device_type"] + + appliance_metadata = self._build_appliance_metadata(appliance_config, version) + if appliance_metadata: + new_template["appliance_metadata"] = appliance_metadata + + if not new_template.get("symbol"): + # apply a default symbol based on the effective category and template type + if category_before_remap == "guest": + if settings["template_type"] == "docker": + new_template["symbol"] = ":/symbols/docker_guest.svg" + else: + new_template["symbol"] = ":/symbols/qemu_guest.svg" + else: + symbols = { + "router": ":/symbols/router.svg", + "switch": ":/symbols/ethernet_switch.svg", + "multilayer_switch": ":/symbols/multilayer_switch.svg", + "firewall": ":/symbols/firewall.svg", + } + new_template["symbol"] = symbols.get(category_before_remap) + + if version and version.get("images"): + if settings["template_type"] == "iou": + # IOU templates take the image path, not an image name + new_template["path"] = version["images"].get("image") + else: + new_template.update(version["images"]) + + if version and settings["template_type"] == "dynamips" and version.get("idlepc"): + # settings level idlepc takes precedence over the version level + new_template.setdefault("idlepc", version["idlepc"]) + + return new_template + + def _build_appliance_metadata(self, appliance_config, version): + """ + Builds the appliance metadata kept on the template: the fields that + describe the appliance, with version level values (e.g. credentials + specific to the installed version) overriding the appliance level ones. + """ + + version = version or {} + metadata = {} + for field in _APPLIANCE_METADATA_FIELDS: + value = version.get(field) + if value is None: + value = appliance_config.get(field) + if value is not None: + metadata[field] = value + appliance_id = appliance_config.get("appliance_id") + if appliance_id: + metadata["appliance_id"] = str(appliance_id) + return metadata or None + + def get_template_type(self, appliance_config, version): + """ + Returns the template type of the settings set used to install the given + version: for registry versions 1-6 it comes from the emulator block, for + version 8 from the settings set selected for the version. + """ + + if appliance_config.get("registry_version", 0) >= 8: + return self._select_v8_settings(appliance_config, version)["template_type"] + if "iou" in appliance_config: + return "iou" + if "dynamips" in appliance_config: + return "dynamips" + if "docker" in appliance_config: + return "docker" + return "qemu" + + def _select_v8_settings(self, appliance_config, version): + """ + Selects the settings set to use: the one referenced by the version, + otherwise the default set, otherwise the only set present. + """ + + settings_list = appliance_config.get("settings") or [] + if not settings_list: + raise ControllerError(f"Appliance '{appliance_config['name']}' has no settings") + + if version and version.get("settings"): + settings_name = version["settings"] + for settings in settings_list: + if settings.get("name") == settings_name: + return settings + raise ControllerError( + f"Could not find settings '{settings_name}' referenced by " + f"version '{version.get('name')}' in appliance '{appliance_config['name']}'" + ) + + for settings in settings_list: + if settings.get("default"): + return settings + + if len(settings_list) == 1: + return settings_list[0] + + raise ControllerError( + f"Appliance '{appliance_config['name']}' has multiple settings " + f"but none is marked as default" + ) + + def _merge_v8_properties(self, settings, appliance_config): + """ + Merges the template properties of the selected settings with the default + settings properties, unless inheritance is disabled or the default set + is selected. Only a default set of the same emulator type is inherited + from, so properties of a different type never pollute the template. + """ + + properties = {} + if not settings.get("default") and settings.get("inherit_default_properties", True): + for other_settings in appliance_config.get("settings") or []: + if other_settings.get("default") and other_settings.get("template_type") == settings["template_type"]: + properties.update(other_settings.get("template_properties") or {}) + break + properties.update(settings.get("template_properties") or {}) + return properties diff --git a/gns3server/controller/compute.py b/gns3server/controller/compute.py index c33cf5315..a418f92a6 100644 --- a/gns3server/controller/compute.py +++ b/gns3server/controller/compute.py @@ -24,7 +24,6 @@ import sys import io from fastapi import HTTPException -from aiohttp import web if sys.version_info >= (3, 11): from asyncio import timeout as asynctimeout @@ -32,7 +31,7 @@ else: from async_timeout import timeout as asynctimeout from ..utils import parse_version -from ..utils.asyncio import locking +from ..utils.asyncio import locking, async_iterable_to_stream from ..controller.controller_error import ( ControllerError, ControllerBadRequestError, @@ -98,6 +97,10 @@ class Compute: self.name = name # Cache of interfaces on remote host self._interfaces_cache = None + # Cached resolution of self._host — socket.gethostbyname is a blocking + # call; resolving it on every host_ip access (several times per link + # via get_ip_on_same_subnet) freezes the event loop for all coroutines. + self._host_ip_cache = None self._connection_failure = 0 def _session(self): @@ -218,14 +221,17 @@ class Compute: """ Return the IP associated to the host """ - try: - return socket.gethostbyname(self._host) - except socket.gaierror: - return "0.0.0.0" + if self._host_ip_cache is None: + try: + self._host_ip_cache = socket.gethostbyname(self._host) + except socket.gaierror: + self._host_ip_cache = "0.0.0.0" + return self._host_ip_cache @host.setter def host(self, host): self._host = host + self._host_ip_cache = None # invalidate; re-resolve on next access if self._console_host is None: self._console_host = host @@ -341,9 +347,11 @@ class Compute: raise ControllerNotFoundError(f"{image} not found on compute") return response - async def http_query(self, method, path, data=None, dont_connect=False, **kwargs): + async def http_query(self, method, path, data=None, dont_connect=False, stream=False, params=None, **kwargs): """ :param dont_connect: If true do not reconnect if not connected + :param stream: If True, return raw aiohttp response for streaming + :param params: Optional dict of query parameters to append to the URL """ if not self._connected and not dont_connect: @@ -352,7 +360,7 @@ class Compute: await self.connect() if not self._connected and not dont_connect: raise ComputeError(f"Cannot connect to compute '{self._name}' with request {method} {path}") - response = await self._run_http_query(method, path, data=data, **kwargs) + response = await self._run_http_query(method, path, data=data, stream=stream, params=params, **kwargs) return response async def _try_reconnect(self): @@ -364,6 +372,27 @@ class Compute: except ControllerError: pass + async def _report_connection_failure(self, error): + """ + Update the connection state after a failure, notify clients and + schedule a reconnection attempt with exponential backoff. + """ + + self._connected = False + self._last_error = str(error) + self._controller.notification.controller_emit("compute.updated", self.asdict()) + # Try to reconnect if server unavailable only if not during tests (otherwise we create a ressource usage bomb) + if hasattr(sys, "_called_from_test") and sys._called_from_test: + return + self._connection_failure += 1 + # After 10 failures we close the project using the compute to avoid sync issues + if self._connection_failure == 10: + log.error(f"Could not connect to compute '{self._id}' after multiple attempts: {error}") + await self._controller.close_compute_projects(self) + # Exponential backoff: 5s, 10s, 20s, 40s, 80s, then cap at 300s + delay = min(5 * (2 ** (self._connection_failure - 1)), 300) + asyncio.get_event_loop().call_later(delay, lambda: asyncio.ensure_future(self._try_reconnect())) + @locking async def connect(self, report_failed_connection=False): """ @@ -376,32 +405,20 @@ class Compute: response = await self._run_http_query("GET", "/capabilities") except ComputeError as e: # Update connection status and notify UI - self._connected = False - self._last_error = str(e) - self._controller.notification.controller_emit("compute.updated", self.asdict()) - + await self._report_connection_failure(e) if report_failed_connection: raise log.warning(f"Cannot connect to compute '{self._id}': {e}") - # Try to reconnect if server unavailable only if not during tests (otherwise we create a ressource usage bomb) - if not hasattr(sys, "_called_from_test") or not sys._called_from_test: - self._connection_failure += 1 - # After 10 failures we close the project using the compute to avoid sync issues - if self._connection_failure == 10: - log.error(f"Could not connect to compute '{self._id}' after multiple attempts: {e}") - await self._controller.close_compute_projects(self) - # Exponential backoff: 5s, 10s, 20s, 40s, 80s, then cap at 300s - delay = min(5 * (2 ** (self._connection_failure - 1)), 300) - asyncio.get_event_loop().call_later(delay, lambda: asyncio.ensure_future(self._try_reconnect())) return - except web.HTTPNotFound: - raise ControllerNotFoundError(f"The server {self._id} is not a GNS3 server or it's a 1.X server") - except web.HTTPUnauthorized: - raise ControllerUnauthorizedError(f"Invalid auth for server {self._id}") - except web.HTTPServiceUnavailable: - raise ControllerNotFoundError(f"The server {self._id} is unavailable") - except ValueError: - raise ComputeError(f"Invalid server url for server {self._id}") + except (ControllerError, HTTPException) as e: + # _run_http_query translates HTTP status errors into ControllerError + # subclasses (or a raw HTTPException for unexpected status codes). + # They used to escape this method and silently kill the fire-and-forget + # connect() task started at controller startup: no notification, no retry. + # Schedule the retry, then re-raise so explicit callers still get the error. + await self._report_connection_failure(e) + log.warning(f"Cannot connect to compute '{self._id}': {e}") + raise if "version" not in response.json: msg = f"The server {self._id} is not a GNS3 server" @@ -479,22 +496,27 @@ class Compute: elif response.type == aiohttp.WSMsgType.CLOSED: pass break - except aiohttp.ClientError as e: - log.error(f"Client response error received on compute '{self._id}' WebSocket '{ws_url}': {e}") + except asyncio.CancelledError: + raise + except Exception as e: + # A malformed frame or an error raised while dispatching a compute event + # used to escape this task (only aiohttp.ClientError was caught) and + # permanently killed the notification stream: no more compute.updated + # events and no reconnection until the server was restarted. Log the + # error with its traceback and reconnect below. + log.error(f"Error on compute '{self._id}' notification stream '{ws_url}': {e!r}", exc_info=True) finally: self._connected = False + self._cpu_usage_percent = None + self._memory_usage_percent = None + self._disk_usage_percent = None log.info(f"Connection closed to compute '{self._id}' WebSocket '{ws_url}'") - - # Try to reconnect after 1 second if server unavailable only if not during tests (otherwise we create a resources usage bomb) - from gns3server.api.server import app - if not app.state.exiting and not hasattr(sys, "_called_from_test"): - log.info(f"Reconnecting to compute '{self._id}' WebSocket '{ws_url}'") - asyncio.get_event_loop().call_later(1, lambda: asyncio.ensure_future(self.connect())) - - self._cpu_usage_percent = None - self._memory_usage_percent = None - self._disk_usage_percent = None - self._controller.notification.controller_emit("compute.updated", self.asdict()) + self._controller.notification.controller_emit("compute.updated", self.asdict()) + # Try to reconnect after 1 second if server unavailable only if not during tests (otherwise we create a resources usage bomb) + from gns3server.api.server import app + if not app.state.exiting and not hasattr(sys, "_called_from_test"): + log.info(f"Reconnecting to compute '{self._id}' WebSocket '{ws_url}'") + asyncio.get_event_loop().call_later(1, lambda: asyncio.ensure_future(self.connect())) def _getUrl(self, path): host = self._host @@ -515,7 +537,7 @@ class Compute: """ Returns URL for specific path at Compute""" return self._getUrl(path) - async def _run_http_query(self, method, path, data=None, timeout=120, raw=False): + async def _run_http_query(self, method, path, data=None, timeout=120, raw=False, stream=False, params=None): async with asynctimeout(delay=timeout): url = self._getUrl(path) headers = {"content-type": "application/json"} @@ -531,6 +553,11 @@ class Compute: elif isinstance(data, aiohttp.streams.StreamReader) or isinstance(data, bytes): chunked = True headers["content-type"] = "application/octet-stream" + # Stream from an async iterable (e.g. Starlette request.stream()) + elif hasattr(data, "__aiter__"): + chunked = True + headers["content-type"] = "application/octet-stream" + data = await async_iterable_to_stream(data) # If the data is an open file we will iterate on it elif isinstance(data, io.BufferedIOBase): chunked = True @@ -540,7 +567,7 @@ class Compute: try: log.debug(f"Attempting request to compute: {method} {url} {headers}") response = await self._session().request( - method, url, headers=headers, data=data, auth=self._auth, chunked=chunked, timeout=timeout + method, url, headers=headers, data=data, auth=self._auth, params=params, chunked=chunked, timeout=timeout ) except asyncio.TimeoutError: raise ComputeError(f"Timeout error for {method} call to {url} after {timeout}s") @@ -554,6 +581,18 @@ class Compute: ) as e: # aiohttp 2.3.1 raises socket.gaierror when cannot find host raise ComputeError(str(e)) + + if stream: + if response.status >= 300: + body = await response.read() + msg = body.decode() if body else "" + if response.status == 404: + raise ControllerNotFoundError(f"{method} {path} not found") + elif response.status == 403: + raise ControllerForbiddenError(msg) + raise ControllerError(f"HTTP {response.status}: {msg}") + return response + body = await response.read() if body and not raw: body = body.decode() diff --git a/gns3server/controller/import_project.py b/gns3server/controller/import_project.py index 18ff6cd3a..e5da5460c 100644 --- a/gns3server/controller/import_project.py +++ b/gns3server/controller/import_project.py @@ -182,24 +182,46 @@ async def import_project( project = await controller.load_project(dot_gns3_path, load=False) return project + def _create_symbolic_links(zip_file, path): """ Manually create symbolic links (if any) because ZipFile does not support it. + Refuse any target that escapes `path`. :param zip_file: ZipFile instance :param path: project location """ + path_root = os.path.realpath(path) + os.sep for zip_info in zip_file.infolist(): - if stat.S_ISLNK(zip_info.external_attr >> 16): - symlink_target = zip_file.read(zip_info.filename).decode() - symlink_path = os.path.join(path, zip_info.filename) - try: - # remove the regular file and replace it by a symbolic link - os.remove(symlink_path) - os.symlink(symlink_target, symlink_path) - except OSError as e: - raise ControllerError(f"Cannot create symbolic link: {e}") + if not stat.S_ISLNK(zip_info.external_attr >> 16): + continue + symlink_target = zip_file.read(zip_info.filename).decode() + symlink_path = os.path.join(path, zip_info.filename) + + # 1. Reject absolute targets outright. + if os.path.isabs(symlink_target): + raise ControllerError(f"Symlink {zip_info.filename!r} has absolute target {symlink_target!r}, refusing") + + # 2. Reject paths where the entry name itself escapes (defence in depth; + # extractall normally would already have caught this). + member_abs = os.path.realpath(symlink_path) + if not (member_abs + os.sep).startswith(path_root) and member_abs + os.sep != path_root: + raise ControllerError(f"Symlink entry {zip_info.filename!r} escapes project dir, refusing") + + # 3. Resolve the symlink target relative to the entry's own parent + # directory and verify the resolved real path stays inside `path`. + link_dir = os.path.realpath(os.path.dirname(symlink_path)) + resolved_target = os.path.realpath(os.path.join(link_dir, symlink_target)) + if not (resolved_target + os.sep).startswith(path_root) and resolved_target + os.sep != path_root: + raise ControllerError("Symlink {zip_info.filename!r} -> {symlink_target!r} escapes project dir, refusing") + + try: + os.remove(symlink_path) + os.symlink(symlink_target, symlink_path) + except OSError as e: + raise ControllerError(f"Cannot create symbolic link: {e}") + def regenerate_topology_ids(topology, new_project_path, reset_mac_addresses=False): """ @@ -295,14 +317,19 @@ async def _import_images(controller, images_path): for (dirpath, dirnames, filenames) in os.walk(root, followlinks=False): for filename in filenames: path = os.path.join(dirpath, filename) - if os.path.islink(path): - continue dst = os.path.join(image_dir, os.path.relpath(path, root)) os.makedirs(os.path.dirname(dst), exist_ok=True) if not os.path.exists(dst): await wait_run_in_executor(shutil.move, path, dst) - os.chmod(dst, stat.S_IWRITE | stat.S_IREAD | stat.S_IEXEC) - + try: + with open(dst, "rb") as f: + # read the first 7 bytes of the file. + elf_header_start = f.read(7) + # IOU images must start with the ELF magic number, be 32-bit or 64-bit, little endian and have an ELF version of 1 + if elf_header_start == b'\x7fELF\x01\x01\x01' or elf_header_start == b'\x7fELF\x02\x01\x01': + os.chmod(dst, stat.S_IWRITE | stat.S_IREAD | stat.S_IEXEC) + except OSError as e: + continue async def update_snapshots(snapshots_dir, project_path, project_name, project_id, reset_mac_addresses=True): """ diff --git a/gns3server/controller/link.py b/gns3server/controller/link.py index ecd74b045..5f68e484b 100644 --- a/gns3server/controller/link.py +++ b/gns3server/controller/link.py @@ -23,12 +23,20 @@ import html from .controller_error import ControllerError, ControllerNotFoundError from gns3server.agent.web_wireshark.manager import WebWiresharkManager from gns3server.config import Config +from gns3server.utils.packet_filter_validation import validate_all_filters, filter_inactive_filters, FilterValidationError import logging log = logging.getLogger(__name__) +# Sentinel for "argument not passed". Distinct from None so marker/definition +# updaters can tell "caller omitted direction" (keep current value) from +# "caller passed direction=None" (clear it back to both directions). See +# UDPLink.update_marker and Project.update_marker_definition. +_UNSET = object() + + FILTERS = [ { "type": "frequency_drop", @@ -47,7 +55,7 @@ FILTERS = [ "name": "Delay", "description": "Delay packets in milliseconds. You can add jitter in milliseconds (+/-) of the delay", "parameters": [ - {"name": "Latency", "minimum": 0, "maximum": 32767, "unit": "ms", "type": "int"}, + {"name": "Latency", "minimum": 1, "maximum": 32767, "unit": "ms", "type": "int"}, {"name": "Jitter (-/+)", "minimum": 0, "maximum": 32767, "unit": "ms", "type": "int"}, ], }, @@ -87,8 +95,10 @@ class Link: self._link_type = "ethernet" self._suspended = False self._filters = {} + self._markers = {} self._link_style = {} self._wireshark = False + self._show_filters_icon = True @property def filters(self): @@ -97,6 +107,65 @@ class Link: """ return self._filters + @property + def markers(self): + """ + Get the traffic insight markers dict: name → {bpf, tag, enabled} + """ + return self._markers + + async def inherit_marker(self, def_name, marker_def, dump=True, memory_only=False): + """ + Apply a project-level marker definition to this link. + + The marker is stored under ``global-{def_name}`` so it cannot collide + with a per-link private marker of the same name. It carries an + ``inherited_from`` back-reference that (a) guards against per-link + edits and (b) lets the project sync changes to every copy at once. + + The pcap link-layer follows the link type: Ethernet is always EN10MB. + A serial link needs the definition's WAN encapsulation (HDLC / PPP / + Frame Relay); if none was chosen the serial link is skipped — an EN10MB + pcap on a serial link is undecodable. + """ + + def_data_link_type = marker_def.get("data_link_type", "DLT_EN10MB") + if self._link_type == "serial": + if def_data_link_type.upper() == "DLT_EN10MB": + return # definition is Ethernet-only; skip this serial link + data_link_type = def_data_link_type + else: + data_link_type = "DLT_EN10MB" + + await self.start_marker( + name=f"global-{def_name}", + bpf=marker_def["bpf"], + tag=marker_def.get("tag"), + direction=marker_def.get("direction"), + data_link_type=data_link_type, + color=marker_def.get("color"), + highlight_duration=marker_def.get("highlight_duration"), + enabled=not marker_def.get("paused", False), + inherited_from=def_name, + dump=dump, + memory_only=memory_only, + ) + + def _persist_markers(self): + """ + Return only the per-link (non-inherited) markers suitable for + persistence in a topology dump. Inherited markers are re-created from + ``project._marker_definitions`` on load so they do not need to be saved. + """ + return {k: v for k, v in self._markers.items() if not v.get("inherited_from")} + + @property + def show_filters_icon(self): + """ + Get whether to show filters icon in Web UI + """ + return getattr(self, '_show_filters_icon', True) + @property def project(self): """ @@ -139,19 +208,17 @@ class Link: """ Modify the filters list. - Filter with value 0 will be dropped because not active + Filters with value 0 will be filtered out as inactive, with special + handling for delay filter to distinguish between "disabled" and "invalid config". """ - new_filters = {} - for (filter, values) in filters.items(): - new_values = [] - for value in values: - if isinstance(value, str): - new_values.append(value.strip("\n ")) - else: - new_values.append(int(value)) - values = new_values - if len(values) != 0 and values[0] != 0 and values[0] != "": - new_filters[filter] = values + # Filter out inactive filters using the utility function + new_filters = filter_inactive_filters(filters) + + # Validate filter parameters before applying + try: + validate_all_filters(new_filters) + except FilterValidationError as e: + raise ControllerError(f"Invalid packet filter parameters: {str(e)}") if new_filters != self.filters: self._filters = new_filters @@ -167,6 +234,17 @@ class Link: self._project.emit_notification("link.updated", self.asdict()) self._project.dump() + async def update_show_filters_icon(self, value): + """ + Update the show_filters_icon property. + + :param value: Boolean indicating whether to show filters icon in Web UI + """ + if value != self._show_filters_icon: + self._show_filters_icon = value + self._project.emit_notification("link.updated", self.asdict()) + self._project.dump() + async def update_link_style(self, link_style): if link_style != self._link_style: self._link_style = link_style @@ -180,11 +258,14 @@ class Link: """ return self._created - async def add_node(self, node, adapter_number, port_number, label=None, dump=True): + async def add_node(self, node, adapter_number, port_number, label=None, dump=True, batch=False): """ Add a node to the link :param dump: Dump project on disk + :param batch: When True, do not create the link on the computes once + both nodes are attached — the caller drives creation via the + project-open bulk path. Used to avoid one HTTP round-trip per link. """ port = node.get_port(adapter_number, port_number) @@ -228,7 +309,7 @@ class Link: {"node": node, "adapter_number": adapter_number, "port_number": port_number, "port": port, "label": label} ) - if len(self._nodes) == 2: + if len(self._nodes) == 2 and not batch: await self.create() for n in self._nodes: n["node"].add_link(self) @@ -280,6 +361,27 @@ class Link: raise NotImplementedError + async def start_marker(self, name, bpf, tag=None, direction=None, capture_node_id=None, enabled=True): + """ + Attach a traffic-insight marker to this link (base — UDPLink overrides). + """ + raise NotImplementedError + + async def stop_marker(self, name): + """ + Remove a traffic-insight marker from this link (base — UDPLink overrides). + """ + raise NotImplementedError + + async def update_marker(self, name, bpf=None, tag=None, enabled=None, direction=_UNSET): + """ + Update an existing marker's BPF, tag, or enabled flag. + + A BPF change is a delete+re-add on the ubridge side so the pcap is + flushed and the new filter takes effect. + """ + raise NotImplementedError + async def start_capture(self, data_link_type="DLT_EN10MB", capture_file_name=None, wireshark=False, jwt_token=None): """ Start capture on the link @@ -522,6 +624,8 @@ class Link: "nat", "virtualbox", "docker", + # the brctl Ethernet switch applies filters on its per-port uBridge relays + "ethernet_switch", ): return node["node"] return None @@ -553,10 +657,12 @@ class Link: "nodes": res, "link_id": self._id, "filters": self._filters, + "markers": self._persist_markers(), "link_style": self._link_style, "suspend": self._suspended, + "show_filters_icon": getattr(self, '_show_filters_icon', True), } - return { + result = { "nodes": res, "link_id": self._id, "project_id": self._project.id, @@ -566,7 +672,10 @@ class Link: "capture_compute_id": self.capture_compute_id, "link_type": self._link_type, "filters": self._filters, + "markers": self._markers, "suspend": self._suspended, "link_style": self._link_style, "wireshark": self._wireshark, + "show_filters_icon": getattr(self, '_show_filters_icon', True), } + return result diff --git a/gns3server/controller/node.py b/gns3server/controller/node.py index 3378f055f..cd474912f 100644 --- a/gns3server/controller/node.py +++ b/gns3server/controller/node.py @@ -57,6 +57,9 @@ class Node: "ports", "category", "console_auto_start", + "netmiko_device_type", + "default_username", + "default_password", ] def __init__(self, project, compute, name, node_id=None, node_type=None, template_id=None, **kwargs): @@ -112,6 +115,9 @@ class Node: self._port_segment_size = 0 self._first_port_name = None self._console_auto_start = False + self._netmiko_device_type = None + self._default_username = None + self._default_password = None # This properties will be recomputed ignore_properties = ("width", "height", "hover_symbol") @@ -212,6 +218,30 @@ class Node: def console_auto_start(self, val): self._console_auto_start = val + @property + def netmiko_device_type(self): + return self._netmiko_device_type + + @netmiko_device_type.setter + def netmiko_device_type(self, val): + self._netmiko_device_type = val + + @property + def default_username(self): + return self._default_username + + @default_username.setter + def default_username(self, val): + self._default_username = val + + @property + def default_password(self): + return self._default_password + + @default_password.setter + def default_password(self, val): + self._default_password = val + @property def properties(self): return self._properties @@ -639,6 +669,14 @@ class Node: except asyncio.TimeoutError: raise ControllerTimeoutError(f"Timeout when reset console {self._name}") + async def get(self, path="", **kwargs): + """ + HTTP get on the node + """ + return await self._compute.get( + f"/projects/{self._project.id}/{self._node_type}/nodes/{self._id}{path}", **kwargs + ) + async def post(self, path, data=None, **kwargs): """ HTTP post on the node @@ -825,6 +863,9 @@ class Node: "console": self._console, "console_type": self._console_type, "console_auto_start": self._console_auto_start, + "netmiko_device_type": self._netmiko_device_type, + "default_username": self._default_username, + "default_password": self._default_password, "aux": self._aux, "aux_type": self._aux_type, "properties": self._properties, diff --git a/gns3server/controller/notification.py b/gns3server/controller/notification.py index 3a63d9b87..d3540a746 100644 --- a/gns3server/controller/notification.py +++ b/gns3server/controller/notification.py @@ -31,6 +31,7 @@ class Notification: self._controller = controller self._project_listeners = {} + self._project_marker_listeners = {} self._controller_listeners = set() @contextmanager @@ -49,6 +50,26 @@ class Notification: finally: self._project_listeners[project_id].remove(queue) + @contextmanager + def project_marker_queue(self, project_id): + """ + Get a queue of marker notifications (marker.match etc.) for a project. + + Marker events are delivered on this dedicated channel instead of the + main project queue, so high-frequency marker.matches do not cause + head-of-line blocking for topology events (node.*/link.*). + + Use it with Python with + """ + + queue = NotificationQueue() + self._project_marker_listeners.setdefault(project_id, set()) + self._project_marker_listeners[project_id].add(queue) + try: + yield queue + finally: + self._project_marker_listeners[project_id].remove(queue) + @contextmanager def controller_queue(self): """ @@ -104,6 +125,8 @@ class Notification: elif action == "ping": event["compute_id"] = compute_id self.project_emit(action, event) + elif action.startswith("marker."): + self.marker_emit(action, event, project_id) else: self.project_emit(action, event, project_id) @@ -120,6 +143,25 @@ class Notification: else: self._send_event_to_all_projects(action, event) + def marker_emit(self, action, event, project_id): + """ + Send a marker notification (e.g. marker.match) to clients listening on + the dedicated marker channel for this project. Marker events are kept + off the main project queue on purpose, to avoid head-of-line blocking + from high-frequency matches. + + :param action: Action name + :param event: Event to send + :param project_id: Project id the marker belongs to + """ + + try: + marker_listeners = self._project_marker_listeners[project_id] + except KeyError: + return + for listener in marker_listeners: + asyncio.get_running_loop().call_soon_threadsafe(listener.put_nowait, (action, event, {})) + def _send_event_to_project(self, project_id, action, event): """ Send an event to all the client listening for notifications for diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index e0a38838c..01506a734 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -37,10 +37,12 @@ from .snapshot import Snapshot from .drawing import Drawing from .topology import project_to_topology, load_topology from .udp_link import UDPLink +from .link import _UNSET from ..config import Config from ..utils.path import check_path_allowed, get_default_project_directory from ..utils.application_id import get_next_application_id from ..utils.asyncio.pool import Pool +from ..utils.packet_filter_validation import validate_bpf_syntax from ..utils.asyncio import locking from ..utils.asyncio import aiozipstream from ..utils.asyncio import wait_run_in_executor @@ -95,7 +97,7 @@ class Project: show_grid=False, grid_size=75, drawing_grid_size=25, - show_interface_labels=False, + show_interface_labels=True, variables=None, supplier=None, created_by=None, @@ -158,6 +160,12 @@ class Project: self.dump() self._iou_id_lock = asyncio.Lock() + # Serialise the "ensure project exists on this compute" check in + # _create_node: without it, concurrent node creations all pass the + # `compute not in _project_created_on_compute` check before any has + # registered, and each fires a redundant POST /projects at the compute. + self._create_node_lock = asyncio.Lock() + self._preallocated_udp_ports = {} # compute_id -> list of pre-allocated UDP ports log.debug(f'Project "{self.name}" [{self._id}] loaded') self.emit_controller_notification("project.created", self.asdict()) @@ -197,9 +205,11 @@ class Project: self.emit_controller_notification("project.updated", self.asdict()) self.dump() - # update on computes - for compute in list(self._project_created_on_compute): - await compute.put(f"/projects/{self._id}", {"variables": self.variables}) + # Only notify computes if variables actually changed and have content + # None and empty list are semantically equivalent (no variables) and don't affect running nodes + if "variables" in kwargs and kwargs["variables"]: + for compute in list(self._project_created_on_compute): + await compute.put(f"/projects/{self._id}", {"variables": self.variables}) def reset(self): """ @@ -208,12 +218,14 @@ class Project: self._allocated_node_names = set() self._nodes = {} self._links = {} + self._marker_definitions = {} # name → {bpf, tag, color, highlight_duration} self._drawings = {} self._snapshots = {} self._computes = [] self._load_snapshot_config() # Create the project on demand on the compute node self._project_created_on_compute = set() + self._preallocated_udp_ports = {} @property def scene_height(self): @@ -428,7 +440,21 @@ class Project: @name.setter def name(self, val): + old_filename = self._filename self._name = val + self._filename = val + ".gns3" + + # Rename the .gns3 file on disk when the project name changes + if old_filename != self._filename: + old_path = os.path.join(self._path, old_filename) + new_path = os.path.join(self._path, self._filename) + if os.path.exists(old_path): + try: + shutil.move(old_path, new_path) + log.info(f"Project file renamed from '{old_filename}' to '{self._filename}'") + except OSError as e: + log.warning(f"Could not rename project file from '{old_filename}' to '{self._filename}': {e}") + self._filename = old_filename @property def id(self): @@ -445,6 +471,18 @@ class Project: @path.setter def path(self, path): check_path_allowed(path) + + # The projects directory itself (or one of its ancestors) must + # never become a project directory: deleting such a "project" + # would wipe every project on the controller. + real_path = os.path.realpath(path) + real_projects_path = os.path.realpath(get_default_project_directory()) + if os.path.commonpath([real_path, real_projects_path]) == real_path: + raise ControllerForbiddenError( + f"The project directory cannot be '{path}': it must be a subdirectory " + f"of '{real_projects_path}', not the projects directory itself or one of its parents" + ) + try: os.makedirs(path, exist_ok=True) except OSError as e: @@ -545,13 +583,11 @@ class Project: """ Create a node from a template. """ - template["x"] = x template["y"] = y node_type = template.pop("template_type") if compute_id: - # use a custom compute_id compute = self.controller.get_compute(compute_id) else: compute = self.controller.get_compute(template.pop("compute_id")) @@ -560,6 +596,12 @@ class Project: default_name_format = template.pop("default_name_format", "{name}-{0}") if name is None: name = default_name_format.replace("{name}", template_name) + # the appliance metadata stays template level: only the default + # credentials are seeded on the node (where they can be overridden) + appliance_metadata = template.pop("appliance_metadata", None) or {} + for field in ("default_username", "default_password"): + if appliance_metadata.get(field): + template[field] = appliance_metadata[field] node_id = str(uuid.uuid4()) node = await self.add_node(compute, name, node_id, node_type=node_type, **template) return node @@ -567,18 +609,21 @@ class Project: async def _create_node(self, compute, name, node_id, node_type=None, **kwargs): node = Node(self, compute, name, node_id=node_id, node_type=node_type, **kwargs) - if compute not in self._project_created_on_compute: - # For a local server we send the project path - if compute.id == "local": - data = {"name": self._name, "project_id": self._id, "path": self._path} - else: - data = {"name": self._name, "project_id": self._id} - - if self._variables: - data["variables"] = self._variables - - await compute.post("/projects", data=data) - self._project_created_on_compute.add(compute) + # Hold the lock across the check + POST + register so that concurrent + # node creations on the same compute don't all race past the check and + # each POST /projects (the compute-side sync handler then instantiated + # the Project N times). Once one creation registers the compute, the + # rest see it in the set and return immediately. + async with self._create_node_lock: + if compute not in self._project_created_on_compute: + if compute.id == "local": + data = {"name": self._name, "project_id": self._id, "path": self._path} + else: + data = {"name": self._name, "project_id": self._id} + if self._variables: + data["variables"] = self._variables + await compute.post("/projects", data=data) + self._project_created_on_compute.add(compute) await node.create() self._nodes[node.id] = node @@ -602,16 +647,15 @@ class Project: if node_type == "iou": async with self._iou_id_lock: - # wait for an IOU node to be completely created before adding a new one - # this is important otherwise we allocate the same application ID (used - # to generate MAC addresses) when creating multiple IOU node at the same time + # IOU application IDs must be allocated serially to avoid duplicates. + # The lock must also cover _create_node() because get_next_application_id() + # checks in-memory nodes (self._nodes), which are only registered + # after _create_node() completes. if "properties" in kwargs.keys(): - # allocate a new application id for nodes loaded from the project kwargs.get("properties")["application_id"] = get_next_application_id( self._controller.projects, self._computes ) elif "application_id" not in kwargs.keys() and not kwargs.get("properties"): - # allocate a new application id for nodes added to the project kwargs["application_id"] = get_next_application_id(self._controller.projects, self._computes) node = await self._create_node(compute, name, node_id, node_type, **kwargs) else: @@ -735,6 +779,171 @@ class Project: self.dump() self.emit_notification("drawing.deleted", drawing.asdict()) + async def _create_link_from_topology_data(self, link_data): + """ + Create a link from topology data (used during project loading). + + Extracted into a separate method so links can be created in parallel + via Pool() during project.open(). + + :param link_data: Link data from the topology file + """ + link = await self.add_link(link_id=link_data["link_id"]) + if "filters" in link_data: + try: + await link.update_filters(link_data["filters"]) + except ControllerError as e: + log.warning( + "Dropping invalid filters on link %s: %s", + link_data.get("link_id"), e + ) + # Restore traffic-insight markers directly into link state (mirrors how + # filters are restored via update_filters). The capture_node_id persisted + # last time is reused for NIO routing; no side resolution is possible here + # because the link's nodes are added later. The marker is applied to + # uBridge by _ubridge_apply_markers when create() runs. Invalid BPF is + # dropped (like invalid filters). + for name, marker in (link_data.get("markers") or {}).items(): + bpf = marker.get("bpf") + if not bpf: + log.warning("Dropping marker %s on link %s: missing bpf", name, link_data.get("link_id")) + continue + result = validate_bpf_syntax(bpf) + if not result.get("valid"): + log.warning( + "Dropping marker %s on link %s: invalid BPF (%s)", + name, link_data.get("link_id"), result.get("error") + ) + continue + link._markers[name] = { + "bpf": bpf, + "tag": marker.get("tag"), + "enabled": marker.get("enabled", True), + "color": marker.get("color"), + "highlight_duration": marker.get("highlight_duration"), + "capture_node_id": marker.get("capture_node_id"), + "direction": marker.get("direction"), + } + if "link_style" in link_data: + await link.update_link_style(link_data["link_style"]) + if "show_filters_icon" in link_data: + await link.update_show_filters_icon(link_data["show_filters_icon"]) + for node_link in link_data.get("nodes", []): + node = self.get_node(node_link["node_id"]) + port = node.get_port(node_link["adapter_number"], node_link["port_number"]) + if port is None: + log.warning( + "Port {}/{} for {} not found".format( + node_link["adapter_number"], node_link["port_number"], node.name + ) + ) + continue + if port.link is not None: + log.warning( + "Port {}/{} is already connected to link ID {}".format( + node_link["adapter_number"], node_link["port_number"], port.link.id + ) + ) + continue + await link.add_node( + node, + node_link["adapter_number"], + node_link["port_number"], + label=node_link.get("label"), + dump=False, + ) + if len(link.nodes) != 2: + # a link should have 2 attached nodes, this can happen with corrupted projects + await self.delete_link(link.id, force_delete=True) + + async def _prepare_link_from_topology(self, link_data): + """ + Build a link locally from topology data WITHOUT dispatching NIOs to the + computes. Returns ``(link, entries)`` where ``entries`` is the list of + ``(node, adapter_number, port_number, nio_data)`` tuples produced by + ``UDPLink._prepare()``, or ``None`` if the link is invalid/incomplete. + + Used by the project-open bulk path so all NIOs can be sent in a single + batch HTTP call per compute instead of one round-trip per link. + """ + + link = await self.add_link(link_id=link_data["link_id"], dump=False) + if "filters" in link_data: + try: + await link.update_filters(link_data["filters"]) + except ControllerError as e: + log.warning("Dropping invalid filters on link %s: %s", link_data.get("link_id"), e) + for name, marker in (link_data.get("markers") or {}).items(): + bpf = marker.get("bpf") + if not bpf: + log.warning("Dropping marker %s on link %s: missing bpf", name, link_data.get("link_id")) + continue + result = validate_bpf_syntax(bpf) + if not result.get("valid"): + log.warning( + "Dropping marker %s on link %s: invalid BPF (%s)", + name, link_data.get("link_id"), result.get("error") + ) + continue + link._markers[name] = { + "bpf": bpf, + "tag": marker.get("tag"), + "enabled": marker.get("enabled", True), + "color": marker.get("color"), + "highlight_duration": marker.get("highlight_duration"), + "capture_node_id": marker.get("capture_node_id"), + "direction": marker.get("direction"), + } + # Set style/icon directly: the update_* helpers unconditionally dump + # the whole topology and emit "link.updated", neither of which is + # appropriate mid-prepare (the link is finalised, notified and the + # project dumped once at the end of open). + if "link_style" in link_data: + link._link_style = link_data["link_style"] + if "show_filters_icon" in link_data: + link._show_filters_icon = link_data["show_filters_icon"] + for node_link in link_data.get("nodes", []): + node = self.get_node(node_link["node_id"]) + port = node.get_port(node_link["adapter_number"], node_link["port_number"]) + if port is None: + log.warning( + "Port {}/{} for {} not found".format( + node_link["adapter_number"], node_link["port_number"], node.name + ) + ) + continue + if port.link is not None: + log.warning( + "Port {}/{} is already connected to link ID {}".format( + node_link["adapter_number"], node_link["port_number"], port.link.id + ) + ) + continue + # batch=True: attach the node without triggering per-link NIO HTTP + await link.add_node( + node, + node_link["adapter_number"], + node_link["port_number"], + label=node_link.get("label"), + dump=False, + batch=True, + ) + if len(link.nodes) != 2: + # a link should have 2 attached nodes, this can happen with corrupted projects + await self.delete_link(link.id, force_delete=True) + return None + # Apply project-level marker definitions onto the link's memory + # (memory_only) before _prepare() so the inherited markers ride the + # batch NIO dispatch — zero extra HTTP round-trips. The final + # apply_defs_to_new_link in finalize is removed. + for def_name, d in self._marker_definitions.items(): + try: + await link.inherit_marker(def_name, d, dump=False, memory_only=True) + except ControllerError as e: + log.warning("Marker definition '%s' could not be applied to link %s: %s", def_name, link.id, e) + entries = await link._prepare() + return (link, entries) + @open_required async def add_link(self, link_id=None, dump=True): """ @@ -750,6 +959,35 @@ class Project: self.dump() return link + async def preallocate_udp_ports_for_compute(self, compute, count): + """ + Pre-allocate UDP ports from a compute in a single batch call. + + Used during project loading to reduce HTTP round-trips when + creating many links. + + :param compute: Compute instance + :param count: Number of UDP ports to pre-allocate + """ + if count <= 0: + return + response = await compute.post(f"/projects/{self._id}/ports/udp/batch", data={"count": count}) + ports = response.json["udp_ports"] + self._preallocated_udp_ports.setdefault(compute.id, []) + self._preallocated_udp_ports[compute.id].extend(ports) + + def pop_preallocated_udp_port(self, compute_id): + """ + Pop a pre-allocated UDP port for a compute. + + :param compute_id: Compute ID + :returns: UDP port number or None if no pre-allocated port is available + """ + ports = self._preallocated_udp_ports.get(compute_id, []) + if ports: + return ports.pop() + return None + @open_required async def delete_link(self, link_id, force_delete=False): link = self.get_link(link_id) @@ -781,6 +1019,340 @@ class Project: return self._get_closed_data("links", "link_id") return self._links + @property + def markers(self): + """ + Project-level read-only aggregation of all markers across every link. + + Each entry is keyed ``"{link_id}/{marker_name}"`` so the flat dict is + globally unique within the project. The value is a clone of the link's + per-marker dict plus ``link_id`` and ``node_id`` (the capture-side node) + for convenience — the frontend can filter/group by link or node without + extra round-trips. + + :returns: dict[str, dict] — keyed by "{link_id}/{marker_name}" + """ + result = {} + for link_id, link in self._links.items(): + for name, info in link.markers.items(): + key = f"{link_id}/{name}" + result[key] = { + **info, + "link_id": link_id, + "node_id": info.get("capture_node_id"), + } + return result + + async def pause_marker_definition(self, name): + """ + Pause every inherited copy of a definition (``global-{name}``) on every + link: toggle each filter off in place via ``update_marker(enabled=False)`` + — uBridge ``enable_packet_filter off``, no NIO rebuild, pcap/emitted + preserved. The definition's ``paused`` flag is persisted, so links + created later inherit the marker already paused. + """ + + if name not in self._marker_definitions: + raise ControllerError(f"Marker definition '{name}' not found") + self._marker_definitions[name]["paused"] = True + marker_name = f"global-{name}" + affected = [ + link for link in self._links.values() + if marker_name in link.markers and link.markers[marker_name].get("inherited_from") == name + ] + await self._marker_apply_concurrently( + affected, + lambda link: link.update_marker(marker_name, enabled=False, inherited=True, dump=False), + lambda link, e: f"Failed to pause marker {marker_name} on link {link.id}: {e}", + ) + self.dump() + self.emit_notification("project.updated", self.asdict()) + + async def resume_marker_definition(self, name): + """Resume every inherited copy of a definition (toggle on).""" + + if name not in self._marker_definitions: + raise ControllerError(f"Marker definition '{name}' not found") + self._marker_definitions[name]["paused"] = False + marker_name = f"global-{name}" + affected = [ + link for link in self._links.values() + if marker_name in link.markers and link.markers[marker_name].get("inherited_from") == name + ] + await self._marker_apply_concurrently( + affected, + lambda link: link.update_marker(marker_name, enabled=True, inherited=True, dump=False), + lambda link, e: f"Failed to resume marker {marker_name} on link {link.id}: {e}", + ) + self.dump() + self.emit_notification("project.updated", self.asdict()) + + @property + def marker_definitions(self): + """ + :returns: dict of project-level marker definitions (name → {bpf, tag, color, highlight_duration}) + """ + return self._marker_definitions + + def _validate_marker_definition_bpf(self, name, bpf): + """ + Validate a marker definition's BPF once, here, so the fan-out to every + link (``_apply_def_to_all_links`` → ``inherit_marker`` → ``start_marker``) + and the per-link sync (``update_marker_definition`` → ``update_marker``) + can skip re-validation for the inherited copies — otherwise one + ``tcpdump -d`` subprocess runs per link for the same expression. A + private per-link marker still validates in ``start_marker``/``update_marker``. + """ + result = validate_bpf_syntax(bpf) + if not result.get("valid"): + raise ControllerError( + f"Marker definition '{name}': invalid BPF — {result.get('error', 'unknown error')}" + ) + + def _validate_marker_definition_direction(self, name, direction): + """ + Reject tx/rx on a marker definition: a definition fans out to every link + and auto-selects its capture node on each (``_choose_marker_side``), + while tx/rx is relative to that node, so a fixed direction has no + consistent meaning across links. Only 'both' (the default, = ``None``) + is allowed — encode the direction in the BPF instead (e.g. + ``icmp[icmptype]==8`` for echo requests), or use a per-link marker whose + capture node is pinned. + """ + if direction in ("tx", "rx"): + raise ControllerError( + f"Marker definition '{name}': direction '{direction}' is not allowed. " + "A definition fans out to every link and auto-selects its capture node on each, " + "but tx/rx is relative to that node, so a fixed direction has no consistent " + "meaning across links. Keep 'both' (the default) and encode the direction in " + "the BPF instead, e.g. 'icmp and icmp[icmptype]==8' for echo requests only. " + "For a capture-node-relative direction on a single link, use a per-link marker." + ) + + async def create_marker_definition(self, name, bpf, tag=None, direction=None, color=None, highlight_duration=None, data_link_type="DLT_EN10MB"): + """ + Create a project-level marker definition and fan out to every existing + link that has a capable node. Links without a capable node are silently + skipped. + """ + + if name in self._marker_definitions: + raise ControllerError( + f"Marker definition '{name}' already exists in this project" + ) + + self._validate_marker_definition_bpf(name, bpf) + self._validate_marker_definition_direction(name, direction) + self._marker_definitions[name] = {"bpf": bpf, "tag": tag, "direction": direction, "color": color, "highlight_duration": highlight_duration, "data_link_type": data_link_type, "paused": False} + await self._apply_def_to_all_links(name) + self.dump() + self.emit_notification("project.updated", self.asdict()) + + async def update_marker_definition(self, name, bpf=None, tag=None, direction=_UNSET, color=None, highlight_duration=None, data_link_type=_UNSET): + """ + Update a marker definition and sync every inherited copy on every link. + """ + + if name not in self._marker_definitions: + raise ControllerNotFoundError( + f"Marker definition '{name}' not found in this project" + ) + + d = self._marker_definitions[name] + if bpf is not None: + self._validate_marker_definition_bpf(name, bpf) + d["bpf"] = bpf + if tag is not None: + d["tag"] = tag + if color is not None: + d["color"] = color + if highlight_duration is not None: + d["highlight_duration"] = highlight_duration + if direction is not _UNSET: + self._validate_marker_definition_direction(name, direction) + d["direction"] = direction # None = clear back to both directions + if data_link_type is not _UNSET: + d["data_link_type"] = data_link_type + + # Links that currently carry an inherited copy of this definition. + affected = [ + link for link in self._links.values() + if f"global-{name}" in link.markers + and link.markers[f"global-{name}"].get("inherited_from") == name + ] + + if data_link_type is not _UNSET: + # data_link_type decides which links host an inherited copy (serial + # links are skipped unless a WAN encapsulation is chosen), so a change + # needs a full re-fan-out: drop every copy, then re-apply. + for link in affected: + try: + await link.stop_marker(f"global-{name}", inherited=True, dump=False, memory_only=True) + except ControllerError as e: + log.warning("Failed to remove inherited marker global-%s from link %s: %s", name, link.id, e) + await self._apply_def_to_all_links(name) + else: + # Sync: update every inherited copy across all links in memory, then + # batch-push to computes (one PUT /nios/batch per compute). + for link in affected: + try: + await link.update_marker( + f"global-{name}", bpf=d["bpf"], tag=d.get("tag"), direction=d.get("direction"), + color=d.get("color"), highlight_duration=d.get("highlight_duration"), inherited=True, + dump=False, memory_only=True + ) + except ControllerError as e: + log.warning("Failed to sync marker global-%s on link %s: %s", name, link.id, e) + await self._batch_update_link_nios(affected) + self.dump() + self.emit_notification("project.updated", self.asdict()) + + async def delete_marker_definition(self, name): + """ + Delete a marker definition and remove every inherited copy from every link. + """ + + if name not in self._marker_definitions: + raise ControllerNotFoundError( + f"Marker definition '{name}' not found in this project" + ) + + del self._marker_definitions[name] + + affected = [ + link for link in self._links.values() + if f"global-{name}" in link.markers + and link.markers[f"global-{name}"].get("inherited_from") == name + ] + for link in affected: + try: + await link.stop_marker(f"global-{name}", inherited=True, memory_only=True) + except ControllerError as e: + # A missing compute or broken link shouldn't block the delete. + log.warning("Failed to remove inherited marker global-%s from link %s: %s", name, link.id, e) + await self._batch_update_link_nios(affected) + + self.dump() + self.emit_notification("project.updated", self.asdict()) + + async def _apply_def_to_all_links(self, def_name): + """ + Fan out a single marker definition to every existing link in the project. + Links that have no capable node (``_MARKER_CAPABLE_TYPES``) are silently + skipped — the marker can only live on a uBridge bridge. + + Two-phase to avoid one HTTP round-trip per link end: (1) write the + inherited marker into each link's memory (``memory_only`` refreshes + ``_link_data`` without pushing), then (2) batch-update every affected + NIO via a single ``PUT /projects/{id}/nios/batch`` per compute. + """ + + d = self._marker_definitions[def_name] + affected = [] + for link in self._links.values(): + try: + await link.inherit_marker(def_name, d, dump=False, memory_only=True) + affected.append(link) + except ControllerError as e: + log.warning("Marker definition '%s' could not be applied to link %s: %s", def_name, link.id, e) + await self._batch_update_link_nios(affected) + + async def _batch_update_link_nios(self, links): + """ + Push the current ``_link_data`` (markers/filters) of *links* to their + computes in one ``PUT /projects/{id}/nios/batch`` per compute — replacing + one PUT /nio round-trip per link end. Started nodes re-apply uBridge; + stopped nodes update in memory. + """ + + per_compute = {} + for link in links: + if len(link._link_data) < 2: + continue + for i, side in enumerate(link._nodes): + node = side["node"] + per_compute.setdefault(node.compute, []).append( + { + "node_id": node.id, + "adapter_number": side["adapter_number"], + "port_number": side["port_number"], + "nio": link._link_data[i], + } + ) + + async def _dispatch(compute, entries): + await compute.put( + f"/projects/{self._id}/nios/batch", + data={"nios": entries}, + timeout=300, + ) + + if per_compute: + await asyncio.gather( + *[_dispatch(c, n) for c, n in per_compute.items()] + ) + + async def apply_defs_to_new_link(self, link): + """ + Apply every active marker definition to a newly created link so it + inherits project-level rules automatically. + + Deliberately serial: all definitions share the same link, and each + ``inherit_marker`` pushes the link's full marker set — concurrent + pushes would race (a later push overwriting an earlier one's spec and + losing markers). + """ + + for def_name, d in self._marker_definitions.items(): + try: + # dump=False: the caller (link create / project open) dumps once + # after; per-def dumps here would be N full topology writes. + await link.inherit_marker(def_name, d, dump=False) + except ControllerError as e: + log.warning( + "Marker definition '%s' could not be applied to new link %s: %s", + def_name, link.id, e + ) + + async def _marker_apply_concurrently(self, links, operation, fail_msg): + """ + Run an async per-link marker operation across *links* with bounded + concurrency. A serial loop takes N sequential compute round-trips — a + definition over 1000 links would take minutes on remote computes — so + fan out in parallel batches. Links are independent (own ``_markers`` / + ``_link_data``), so this is race-free; per-link ``ControllerError`` is + logged and skipped, preserving the serial loop's isolation semantics. + ``Project.dump`` is synchronous and writes atomically (tmp + rename), + so concurrent dumps from the fan-out cannot corrupt the topology file. + + :param links: iterable of links to operate on + :param operation: async callable ``(link) -> coroutine`` + :param fail_msg: callable ``(link, error) -> log message`` + """ + + links = list(links) + if not links: + return + _t0 = time.time() + log.info( + "Project '%s' [%s]: fanning out marker operation to %d links...", + self._name, self._id, len(links) + ) + sem = asyncio.Semaphore(32) + + async def guarded(link): + async with sem: + try: + await operation(link) + except ControllerError as e: + log.warning(fail_msg(link, e)) + + await asyncio.gather(*(guarded(link) for link in links)) + log.info( + "Project '%s' [%s]: marker fan-out done in %.2fs", + self._name, self._id, time.time() - _t0 + ) + @property def snapshots(self): """ @@ -882,6 +1454,7 @@ class Project: log.warning(f"Closing project '{self.name}' ignored because it is being loaded") return self._closing = True + log.info("Project '%s' [%s]: closing...", self._name, self._id) try: await self.stop_all() except HTTPException as e: @@ -905,6 +1478,7 @@ class Project: self.reset() self._closing = False + log.info("Project '%s' [%s]: closed", self._name, self._id) def _clean_pictures(self): """ @@ -1034,7 +1608,7 @@ class Project: if self._status != "opened": try: - await self.open() + await self.open(auto_start=False) except ControllerError as e: # ignore missing images or other conflicts when deleting a project log.warning(f"Conflict while deleting project: {e}") @@ -1046,11 +1620,19 @@ class Project: await self._cleanup_web_wireshark_container() try: - project_directory = get_default_project_directory() - if not os.path.commonprefix([project_directory, self.path]) == project_directory: + project_directory = os.path.realpath(get_default_project_directory()) + path = os.path.realpath(self.path) + if os.path.commonpath([path, project_directory]) != project_directory: raise ControllerError( f"Project '{self._name}' cannot be deleted because it is not in the default project directory: '{project_directory}'" ) + if path == project_directory: + # A poisoned or hand-crafted entry whose path is the + # projects root itself must never be deletable: rmtree + # would wipe every project on the controller. + raise ControllerError( + f"Project '{self._name}' cannot be deleted because its directory is the projects directory itself: '{path}'" + ) shutil.rmtree(self.path) except OSError as e: raise ControllerError(f"Cannot delete project directory {self.path}: {str(e)}") @@ -1123,10 +1705,21 @@ class Project: def _topology_file(self): return os.path.join(self.path, self._filename) + @property + def topology_file(self): + """ + Absolute path of the .gns3 topology file + """ + + return self._topology_file() + @locking - async def open(self): + async def open(self, auto_start=True): """ Load topology elements + + :param auto_start: whether the nodes may be started when the project + has auto start enabled """ if self._closing is True: @@ -1171,6 +1764,29 @@ class Project: if val is not None: setattr(self, key, val) + # marker_definitions is loaded separately (it is not a __init__ kwarg + # nor a simple attribute — it backs a read-only property). Each BPF + # is validated once here so the inherited fan-out (start_marker) can + # skip re-validation; an invalid definition is dropped with a warning + # rather than failing the open — it could not fan out anyway. + defs = project_data.get("marker_definitions") + if isinstance(defs, dict): + clean_defs = {} + for def_name, d in defs.items(): + bpf = d.get("bpf") + if not bpf: + log.warning("Dropping marker definition '%s' on load: missing bpf", def_name) + continue + result = validate_bpf_syntax(bpf) + if not result.get("valid"): + log.warning( + "Dropping marker definition '%s' on load: invalid BPF (%s)", + def_name, result.get("error") + ) + continue + clean_defs[def_name] = d + self._marker_definitions = clean_defs + topology = project_data["topology"] for compute in topology.get("computes", []): compute_id = compute.get("compute_id") @@ -1194,50 +1810,102 @@ class Project: f"Please check the connection and try again." ) + # Parallel node creation for improved performance + # especially for projects with multiple Docker containers + nodes_to_create = [] for node in topology.get("nodes", []): compute = self.controller.get_compute(node.pop("compute_id")) name = node.pop("name") node_id = node.pop("node_id", str(uuid.uuid4())) - await self.add_node(compute, name, node_id, dump=False, **node) + nodes_to_create.append((compute, name, node_id, node)) + + # Create nodes in parallel with limited concurrency + # to avoid overwhelming the system with too many simultaneous operations + log.info("Project '%s' [%s]: loading %d nodes...", self._name, self._id, len(nodes_to_create)) + pool = Pool(concurrency=100) + for compute, name, node_id, node_data in nodes_to_create: + pool.append(self.add_node, compute, name, node_id, dump=False, **node_data) + await pool.join() + log.info("Project '%s' [%s]: loaded %d nodes", self._name, self._id, len(nodes_to_create)) + # Pre-allocate UDP ports for all links in batch to reduce HTTP round-trips + ports_per_compute = {} for link_data in topology.get("links", []): if "link_id" not in link_data.keys(): - # skip the link continue - link = await self.add_link(link_id=link_data["link_id"]) - if "filters" in link_data: - await link.update_filters(link_data["filters"]) - if "link_style" in link_data: - await link.update_link_style(link_data["link_style"]) for node_link in link_data.get("nodes", []): - node = self.get_node(node_link["node_id"]) - port = node.get_port(node_link["adapter_number"], node_link["port_number"]) - if port is None: - log.warning( - "Port {}/{} for {} not found".format( - node_link["adapter_number"], node_link["port_number"], node.name - ) - ) - continue - if port.link is not None: - log.warning( - "Port {}/{} is already connected to link ID {}".format( - node_link["adapter_number"], node_link["port_number"], port.link.id - ) - ) - continue - await link.add_node( - node, - node_link["adapter_number"], - node_link["port_number"], - label=node_link.get("label"), - dump=False, + node = self._nodes.get(node_link["node_id"]) + if node: + ports_per_compute[node.compute.id] = ports_per_compute.get(node.compute.id, 0) + 1 + for compute in self.computes: + count = ports_per_compute.get(compute.id, 0) + if count > 0: + await self.preallocate_udp_ports_for_compute(compute, count) + # Create links via the bulk path: build every link locally (no NIO + # HTTP), then dispatch all NIOs to each compute in a single batch + # request. This replaces one HTTP round-trip per link (~5000 for a + # 2500-link topology) with one round-trip per compute. + link_data_list = [d for d in topology.get("links", []) if "link_id" in d.keys()] + log.info("Project '%s' [%s]: creating %d links...", self._name, self._id, len(link_data_list)) + sem = asyncio.Semaphore(100) + + async def _prepare_one(data): + async with sem: + try: + return await self._prepare_link_from_topology(data) + except Exception as e: + log.warning("Could not load link %s: %s", data.get("link_id"), e) + return None + + prepared = await asyncio.gather(*[_prepare_one(d) for d in link_data_list]) + valid = [p for p in prepared if p is not None] + + # Group the prepared NIO entries by destination compute and send + # each compute a single /nios/batch request. + per_compute = {} # compute -> list of {node_id, adapter_number, port_number, nio} + for link, entries in valid: + for node, adapter_number, port_number, nio_data in entries: + per_compute.setdefault(node.compute, []).append( + { + "node_id": node.id, + "adapter_number": adapter_number, + "port_number": port_number, + "nio": nio_data, + } ) - if len(link.nodes) != 2: - # a link should have 2 attached nodes, this can happen with corrupted projects - await self.delete_link(link.id, force_delete=True) + + async def _dispatch_batch(compute, nio_entries): + await compute.post( + f"/projects/{self._id}/nios/batch", + data={"nios": nio_entries}, + timeout=300, + ) + + if per_compute: + await asyncio.gather( + *[_dispatch_batch(c, n) for c, n in per_compute.items()] + ) + + # Finalise every link: wire node/port back-references, mark created, + # notify clients, and apply project-level marker definitions. + for link, _entries in valid: + for n in link._nodes: + n["node"].add_link(link) + n["port"].link = link + link._created = True + self.emit_notification("link.created", link.asdict()) + log.info("Project '%s' [%s]: created %d links", self._name, self._id, len(valid)) + # Release any pre-allocated UDP ports that were not consumed by links + for compute_id, ports in self._preallocated_udp_ports.items(): + if ports: + log.warning(f"Releasing {len(ports)} unconsumed pre-allocated UDP ports on compute {compute_id}") + self._preallocated_udp_ports.clear() for drawing_data in topology.get("drawings", []): await self.add_drawing(dump=False, **drawing_data) + # Note: project-level marker definitions are applied to each link + # inside UDPLink.create() (the inheritance hook), so they are + # already present once links are loaded — no separate fan-out here. + self.dump() # We catch all error to be able to roll back the .gns3 to the previous state except Exception as e: @@ -1266,7 +1934,7 @@ class Project: self._loading = False self.emit_controller_notification("project.opened", self.asdict()) # Should we start the nodes when project is open - if self._auto_start: + if self._auto_start and auto_start: # Start all in the background without waiting for completion # we ignore errors because we want to let the user open # their project and fix it @@ -1370,6 +2038,10 @@ class Project: :param reset_mac_addresses: Reset MAC addresses for the duplicated project """ + # We don't duplicate a running project + if self.is_running(): + raise ControllerError("Project must be stopped in order to duplicate it") + # remote replication is not supported with remote computes for compute in self.computes: if compute.id != "local": @@ -1386,7 +2058,11 @@ class Project: # copy dir await wait_run_in_executor(shutil.copytree, self.path, new_project_path.as_posix(), symlinks=True, ignore_dangling_symlinks=True) log.info("Project content copied from '{}' to '{}' in {}s".format(self.path, new_project_path, time.time() - t0)) - topology = json.loads(new_project_path.joinpath('{}.gns3'.format(self.name)).read_bytes()) + + # Read the topology file using the actual filename (self._filename), not self.name + # This handles the case where a project has been renamed but we need to read the actual file + old_gns3_file = new_project_path.joinpath(self._filename) + topology = json.loads(old_gns3_file.read_bytes()) project_name = name or topology["name"] # If the project name is already used we generate a new one project_name = self.controller.get_free_project_name(project_name) @@ -1410,7 +2086,8 @@ class Project: if os.path.isdir(snapshots_dir): await update_snapshots(snapshots_dir, new_project_path, project_name, new_project_id) - os.remove(new_project_path.joinpath('{}.gns3'.format(self.name))) + # Remove the old .gns3 file (which has the original project name) + os.remove(old_gns3_file) project = await self.controller.load_project(dot_gns3_path, load=False) log.info("Project '{}': fast duplicated in {:.4f} seconds".format(project.name, time.time() - t0)) return project @@ -1464,6 +2141,10 @@ class Project: Check if all items in a project are locked and not """ + if not self._drawings and not self._nodes: + # a project without drawings or nodes has nothing to lock and would + # otherwise always report as locked, even after unlocking it + return False for drawing in self._drawings.values(): if not drawing.locked: return False @@ -1489,29 +2170,39 @@ class Project: @open_required async def start_all(self): """ - Start all nodes + Start all nodes (except always-running types like Ethernet switch, Cloud, NAT, etc.) """ - pool = Pool(concurrency=3) - for node in self.nodes.values(): + nodes_to_start = [n for n in self.nodes.values() if not n.is_always_running()] + if not nodes_to_start: + return + log.info("Project '%s' [%s]: starting %d nodes...", self._name, self._id, len(nodes_to_start)) + pool = Pool(concurrency=10) + for node in nodes_to_start: pool.append(node.start) await pool.join() + log.info("Project '%s' [%s]: started %d nodes", self._name, self._id, len(nodes_to_start)) @open_required async def stop_all(self): """ - Stop all nodes + Stop all nodes (except always-running types like Ethernet switch, Cloud, NAT, etc.) """ - pool = Pool(concurrency=3) - for node in self.nodes.values(): + nodes_to_stop = [n for n in self.nodes.values() if not n.is_always_running()] + if not nodes_to_stop: + return + log.info("Project '%s' [%s]: stopping %d nodes...", self._name, self._id, len(nodes_to_stop)) + pool = Pool(concurrency=100) + for node in nodes_to_stop: pool.append(node.stop) await pool.join() + log.info("Project '%s' [%s]: stopped %d nodes", self._name, self._id, len(nodes_to_stop)) @open_required async def suspend_all(self): """ Suspend all nodes """ - pool = Pool(concurrency=3) + pool = Pool(concurrency=50) for node in self.nodes.values(): pool.append(node.suspend) await pool.join() @@ -1583,6 +2274,7 @@ class Project: "links": len(self._links), "drawings": len(self._drawings), "snapshots": len(self._snapshots), + "markers": sum(len(link.markers) for link in self._links.values()), } def asdict(self): @@ -1607,6 +2299,7 @@ class Project: "supplier": self._supplier, "variables": self._variables, "created_by": self._created_by, + "marker_definitions": self._marker_definitions, } def __repr__(self): diff --git a/gns3server/controller/topology.py b/gns3server/controller/topology.py index 150283c39..a71b88dd6 100644 --- a/gns3server/controller/topology.py +++ b/gns3server/controller/topology.py @@ -88,6 +88,7 @@ def project_to_topology(project): "variables": project.variables, "supplier": project.supplier, "created_by": project.created_by, + "marker_definitions": project.marker_definitions, "topology": {"nodes": [], "links": [], "computes": [], "drawings": []}, "type": "topology", "revision": GNS3_FILE_FORMAT_REVISION, diff --git a/gns3server/controller/udp_link.py b/gns3server/controller/udp_link.py index 330e6a4f7..bc6cf2d52 100644 --- a/gns3server/controller/udp_link.py +++ b/gns3server/controller/udp_link.py @@ -16,9 +16,25 @@ # along with this program. If not, see . +import asyncio +import logging + from .controller_error import ControllerError, ControllerNotFoundError -from .link import Link +from .link import Link, _UNSET from .node_types import BUILTIN_NODE_TYPES +from gns3server.utils.packet_filter_validation import validate_bpf_syntax, FilterValidationError + +# Node types that can host a marker (have a uBridge bridge to attach the +# `mark` filter to). Mirrors _get_filter_node in link.py, minus "nat" +# (which has no uBridge) and "ethernet_hub" (still Dynamips-hosted, no +# uBridge of its own). "ethernet_switch" hosts markers on the per-port +# uBridge relays of its brctl kernel-bridge backend. +_MARKER_CAPABLE_TYPES = frozenset({ + "vpcs", "qemu", "docker", "iou", "dynamips", "cloud", "ethernet_switch", +}) + + +log = logging.getLogger(__name__) class UDPLink(Link): @@ -37,7 +53,7 @@ class UDPLink(Link): def _get_node_filters(self, node1, node2): """ Determine which node gets the active filters applied. - + :returns: Tuple of (node1_filters, node2_filters) """ filter_node = self._get_filter_node() @@ -46,10 +62,49 @@ class UDPLink(Link): self.get_active_filters() if filter_node == node2 else {}, ) - async def create(self): + def _markers_for_node(self, node): """ - Create the link on the nodes + Marker specs (name -> {bpf, tag, link_id, direction, data_link_type, + enabled}) for the markers whose capture side is ``node``. Routed by + capture_node_id so a marker only rides the NIO of the node whose uBridge + will host it. A disabled marker is included (installed then turned + ``off`` at uBridge, not dropped) so the UI can toggle it instantly + without an NIO rebuild. """ + return { + name: {"bpf": m["bpf"], "tag": m.get("tag"), "link_id": self._id, + "direction": m.get("direction"), + "data_link_type": m.get("data_link_type", "DLT_EN10MB"), + "enabled": m.get("enabled", True)} + for name, m in self._markers.items() + if m.get("capture_node_id") == node.id + } + + def _get_node_markers(self, node1, node2): + """ + Determine which node gets which markers applied. + + :returns: Tuple of (node1_markers, node2_markers) + """ + return self._markers_for_node(node1), self._markers_for_node(node2) + + async def _prepare(self): + """ + Local-only link setup: resolve peer addresses, reserve UDP ports and + build the two NIO tunnel specs (``self._link_data``). No NIO is sent to + the computes — the caller decides how to dispatch them (one-by-one via + :meth:`create`, or batched via the project-open bulk path). + + :returns: list of two ``(node, adapter_number, port_number, nio_data)`` + tuples, ready to be POSTed to each node's compute. + """ + + # Start from a clean slate: reset() re-creates the link on the same + # object (delete() + create()), and _commit_nios()/update() always + # address indices 0/1 — appending onto the previous run's entries + # would re-commit the stale (already released) port pair and orphan + # the freshly allocated ports. + self._link_data = [] node1 = self._nodes[0]["node"] adapter_number1 = self._nodes[0]["adapter_number"] @@ -64,15 +119,25 @@ class UDPLink(Link): except ValueError as e: raise ControllerError(f"Cannot get an IP address on same subnet: {e}") - # Reserve a UDP port on both side - response = await node1.compute.post(f"/projects/{self._project.id}/ports/udp") - self._node1_port = response.json["udp_port"] - response = await node2.compute.post(f"/projects/{self._project.id}/ports/udp") - self._node2_port = response.json["udp_port"] + # Reserve a UDP port on both sides in parallel. Pre-allocated ports + # (used during batch project loading) are popped from memory; otherwise + # each side falls back to a single HTTP round-trip to its compute. + async def _allocate_port(compute): + port = self._project.pop_preallocated_udp_port(compute.id) + if port is not None: + return port + response = await compute.post(f"/projects/{self._project.id}/ports/udp") + return response.json["udp_port"] + + self._node1_port, self._node2_port = await asyncio.gather( + _allocate_port(node1.compute), _allocate_port(node2.compute) + ) node1_filters, node2_filters = self._get_node_filters(node1, node2) + node1_markers, node2_markers = self._get_node_markers(node1, node2) - # Create the tunnel on both side + # Build the tunnel specs for both sides. Index 0 is always node1 so + # that update()/delete() keep addressing self._link_data[0]/[1]. self._link_data.append( { "lport": self._node1_port, @@ -80,11 +145,10 @@ class UDPLink(Link): "rport": self._node2_port, "type": "nio_udp", "filters": node1_filters, + "markers": node1_markers, "suspend": self._suspended, } ) - await node1.post(f"/adapters/{adapter_number1}/ports/{port_number1}/nio", data=self._link_data[0], timeout=120) - self._link_data.append( { "lport": self._node2_port, @@ -92,19 +156,67 @@ class UDPLink(Link): "rport": self._node1_port, "type": "nio_udp", "filters": node2_filters, + "markers": node2_markers, "suspend": self._suspended, } ) - try: - await node2.post( - f"/adapters/{adapter_number2}/ports/{port_number2}/nio", data=self._link_data[1], timeout=120 - ) - except Exception as e: - # We clean the first NIO - await node1.delete(f"/adapters/{adapter_number1}/ports/{port_number1}/nio", timeout=120) - raise e + + return [ + (node1, adapter_number1, port_number1, self._link_data[0]), + (node2, adapter_number2, port_number2, self._link_data[1]), + ] + + async def _commit_nios(self, entries): + """ + Send the two NIO tunnel POSTs in parallel and roll back on failure. + + :param entries: the two ``(node, adapter_number, port_number, nio_data)`` + tuples returned by :meth:`_prepare`. + """ + + (node1, adapter_number1, port_number1, nio_data1), \ + (node2, adapter_number2, port_number2, nio_data2) = entries + + # The two ends are independent once the ports and peer addresses are + # known — each node talks to its own compute/uBridge with no shared + # lock between them — so the two POSTs overlap. If either fails, roll + # back whichever side succeeded before re-raising the first error. + results = await asyncio.gather( + node1.post( + f"/adapters/{adapter_number1}/ports/{port_number1}/nio", data=nio_data1, timeout=120 + ), + node2.post( + f"/adapters/{adapter_number2}/ports/{port_number2}/nio", data=nio_data2, timeout=120 + ), + return_exceptions=True, + ) + errors = [result for result in results if isinstance(result, Exception)] + if errors: + cleanup = [] + if not isinstance(results[0], Exception): + cleanup.append( + node1.delete(f"/adapters/{adapter_number1}/ports/{port_number1}/nio", timeout=120) + ) + if not isinstance(results[1], Exception): + cleanup.append( + node2.delete(f"/adapters/{adapter_number2}/ports/{port_number2}/nio", timeout=120) + ) + if cleanup: + await asyncio.gather(*cleanup, return_exceptions=True) + raise errors[0] self._created = True + async def create(self): + """ + Create the link on the nodes (interactive path: prepare + commit). + """ + + entries = await self._prepare() + await self._commit_nios(entries) + # New links automatically inherit every active project-level marker + # definition so the user doesn't have to reconfigure. + await self._project.apply_defs_to_new_link(self) + async def update(self): """ Update the link on the nodes @@ -116,12 +228,17 @@ class UDPLink(Link): node2 = self._nodes[1]["node"] node1_filters, node2_filters = self._get_node_filters(node1, node2) + node1_markers, node2_markers = self._get_node_markers(node1, node2) adapter_number1 = self._nodes[0]["adapter_number"] port_number1 = self._nodes[0]["port_number"] self._link_data[0]["filters"] = node1_filters + self._link_data[0]["markers"] = node1_markers self._link_data[0]["suspend"] = self._suspended - if node1.node_type not in ("ethernet_switch", "ethernet_hub"): + # The Ethernet hub is still Dynamips-hosted (no uBridge of its own and + # no PUT NIO route) — keep skipping its side. Every other node type, + # including the brctl Ethernet switch, re-applies via the NIO update. + if node1.node_type != "ethernet_hub": await node1.put( f"/adapters/{adapter_number1}/ports/{port_number1}/nio", data=self._link_data[0], timeout=120 ) @@ -129,8 +246,9 @@ class UDPLink(Link): adapter_number2 = self._nodes[1]["adapter_number"] port_number2 = self._nodes[1]["port_number"] self._link_data[1]["filters"] = node2_filters + self._link_data[1]["markers"] = node2_markers self._link_data[1]["suspend"] = self._suspended - if node2.node_type not in ("ethernet_switch", "ethernet_hub"): + if node2.node_type != "ethernet_hub": await node2.put( f"/adapters/{adapter_number2}/ports/{port_number2}/nio", data=self._link_data[1], timeout=221 ) @@ -236,9 +354,295 @@ class UDPLink(Link): raise ControllerError("Cannot capture because there is no running device on this link") + def _choose_marker_side(self): + """ + Pick the node that will host the marker, mirroring ``_get_filter_node`` + in link.py. Only types with a uBridge bridge (``_MARKER_CAPABLE_TYPES``) + are eligible. A running node is preferred, but a stopped one is + accepted — like packet filters, the marker is stored on the NIO and + applied when the node starts. + """ + + # Prefer started. + for node in self._nodes: + if ( + node["node"].node_type in _MARKER_CAPABLE_TYPES + and node["node"].status == "started" + ): + return node + + # Accept stopped but capable (marker rides NIO, applied at start). + for node in self._nodes: + if node["node"].node_type in _MARKER_CAPABLE_TYPES: + return node + + raise ControllerError( + "Cannot add marker because no device on this link supports " + "traffic insight" + ) + + def _node_by_id(self, node_id): + """ + Resolve a caller-chosen capture node by id, validating it is an + endpoint of this link and marker-capable. Used when the caller + (REST/MCP) explicitly pins the observer side instead of letting + ``_choose_marker_side`` auto-pick. + + :param node_id: node id (UUID or str) the caller requested + :returns: a ``self._nodes`` entry (node/adapter_number/port_number) + """ + + target = str(node_id) + for node in self._nodes: + if str(node["node"].id) != target: + continue + if node["node"].node_type not in _MARKER_CAPABLE_TYPES: + raise ControllerError( + f"Node {node_id} ({node['node'].node_type}) cannot host a " + f"marker — no uBridge bridge to attach the filter to" + ) + return node + raise ControllerNotFoundError( + f"Node {node_id} is not an endpoint of link {self._id}" + ) + async def node_updated(self, node): """ Called when a node member of the link is updated """ if self._capture_node and node == self._capture_node["node"] and node.status != "started": await self.stop_capture() + # Marker clean-up is *not* done on node stop — markers are a persistent + # link-scoped feature that recovers via NIO on restart (see + # _ubridge_apply_markers in add_ubridge_udp_connection). The user + # explicitly deletes a marker via the REST API, and a marker is torn + # down automatically only when its link is deleted. + + async def start_marker(self, name, bpf, tag=None, direction=None, data_link_type="DLT_EN10MB", capture_node_id=None, color=None, highlight_duration=None, enabled=True, inherited_from=None, dump=True, memory_only=False): + """ + Attach a traffic-insight marker to this link. + + State-only model (mirrors ``update_filters``): record the marker in + ``_markers`` (with its capture-side node id for NIO routing), then push + via ``self.update()`` so it rides the NIO and is applied by + ``_ubridge_apply_markers``. No dedicated uBridge round-trip — exactly + how packet filters are applied. + + :param name: stable filter name — echoed in MARK signals + pcap identity + :param bpf: libpcap BPF expression + :param tag: optional correlation id + :param capture_node_id: optional explicit observer node. When set the + marker is pinned to that endpoint's uBridge (and ``direction`` is + interpreted from its perspective); validated by ``_node_by_id``. + Omitted = auto-pick via ``_choose_marker_side``. Ignored for + inherited markers (project defs are link-agnostic → always auto). + :param color: optional hex color for the Web UI (e.g. '#ff5722'); stored + with the link and persisted in the topology, never sent to uBridge + :param highlight_duration: optional UI-only hint (milliseconds) for how + long a match keeps the marker highlighted; stored, never sent to uBridge + :param inherited_from: def name when this marker is a project-level + inheritance copy; set automatically, never exposed to REST callers + """ + + if name in self._markers: + raise ControllerError(f"Marker '{name}' already exists on link {self._id}") + + # Validate the BPF only for private per-link markers. An inherited copy + # (``inherited_from`` set) fans out from a definition whose BPF was + # already validated once at create/update (and on project load), so + # re-validating per link would spawn one ``tcpdump -d`` per link for the + # same expression. + if not inherited_from: + result = validate_bpf_syntax(bpf) + if not result.get("valid"): + raise ControllerError(f"Invalid BPF expression: {result.get('error', 'unknown error')}") + + if capture_node_id and not inherited_from: + marker_side = self._node_by_id(capture_node_id) + else: + marker_side = self._choose_marker_side() + marker_entry = { + "bpf": bpf, + "tag": tag, + "enabled": enabled, + "color": color, + "highlight_duration": highlight_duration, + "capture_node_id": marker_side["node"].id, + "direction": direction, + "data_link_type": data_link_type, + } + if inherited_from: + marker_entry["inherited_from"] = inherited_from + self._markers[name] = marker_entry + if memory_only: + # Project-open prepare / marker-def fan-out: only refresh the + # in-memory NIO specs so a later batch dispatch carries the new + # markers — no per-link update HTTP, notification or dump. + self._refresh_link_data() + return + if self._created: + await self.update() + self._project.emit_notification("link.updated", self.asdict()) + # Bulk fan-out passes dump=False: N per-link topology writes on a + # 500-link project are the dominant cost — the caller dumps once after. + if dump: + self._project.dump() + + def _refresh_link_data(self): + """ + Recompute the filters / markers / suspend fields of ``_link_data`` from + the current link state without pushing to computes. Used by the + memory-only marker path so a batch dispatch picks up the new markers. + """ + + if len(self._link_data) < 2: + return + node1 = self._nodes[0]["node"] + node2 = self._nodes[1]["node"] + node1_filters, node2_filters = self._get_node_filters(node1, node2) + node1_markers, node2_markers = self._get_node_markers(node1, node2) + self._link_data[0]["filters"] = node1_filters + self._link_data[0]["markers"] = node1_markers + self._link_data[0]["suspend"] = self._suspended + self._link_data[1]["filters"] = node2_filters + self._link_data[1]["markers"] = node2_markers + self._link_data[1]["suspend"] = self._suspended + + async def stop_marker(self, name, inherited=False, dump=True, memory_only=False): + """ + Remove a traffic-insight marker from this link. + + Drop it from ``_markers`` and push via ``self.update()``: the NIO + reset+reapply in ``_ubridge_apply_filters``/``_ubridge_apply_markers`` + drops it from uBridge. Mirrors how deleting a packet filter works. + + :param name: filter name to remove + :param inherited: set by project-level def-delete to bypass the + inheritance guard (the project layer is the legitimate remover) + """ + + if name not in self._markers: + raise ControllerNotFoundError(f"Marker '{name}' not found on link {self._id}") + + if self._markers[name].get("inherited_from") and not inherited: + raise ControllerError( + f"Marker '{name}' is inherited from the project-level " + f"definition '{self._markers[name]['inherited_from']}'. " + "Delete or update it via the marker-definitions API instead." + ) + + capture_node_id = self._markers[name].get("capture_node_id") + del self._markers[name] + if memory_only: + # Project-level def-delete fan-out: marker is gone from _markers; + # refresh _link_data so the batch dispatch drops it from uBridge + # via full reapply. No per-link delete round-trip, notification or dump. + self._refresh_link_data() + return + # Remove the marker filter + its pcap on the capture node directly — NOT a + # full NIO reapply (which would reset_packet_filters and close/reopen every + # sibling marker's pcap). delete_packet_filter removes just this filter; + # the marker is already gone from _markers, so any later reapply (filter + # change, node restart) won't re-add it either. + if capture_node_id is not None: + side = next((s for s in self._nodes if str(s["node"].id) == str(capture_node_id)), None) + if side is not None: + try: + await side["node"].delete( + f"/adapters/{side['adapter_number']}/ports/{side['port_number']}/markers/{name}", + params={"link_id": self._id}, + ) + except Exception: + pass # best-effort: old compute without the route leaves the file + self._project.emit_notification("link.updated", self.asdict()) + if dump: + self._project.dump() + + async def update_marker(self, name, bpf=None, tag=None, enabled=None, direction=_UNSET, color=None, highlight_duration=None, inherited=False, dump=True, memory_only=False): + """ + Update an existing marker's fields and push to uBridge fine-grained — no + full NIO reapply, so sibling markers' pcaps stay open. bpf/tag/direction + rebuild just this filter (delete + add); enabled is an instant toggle; + color/highlight_duration are UI-only (stored, never pushed). + + :param name: filter name to update + :param bpf: new BPF expression (None = keep existing) + :param tag: new tag id (None = keep existing) + :param enabled: toggle (None = keep existing) + :param color: new hex color (None = keep existing) + :param highlight_duration: new UI highlight duration in ms (None = keep existing) + :param inherited: set by project-level sync to bypass the inheritance + guard (the project layer is the legitimate editor) + """ + + marker_info = self._markers.get(name) + if not marker_info: + raise ControllerNotFoundError(f"Marker '{name}' not found on link {self._id}") + + if marker_info.get("inherited_from") and not inherited: + raise ControllerError( + f"Marker '{name}' is inherited from the project-level " + f"definition '{marker_info['inherited_from']}'. " + "Update it via the marker-definitions API instead." + ) + + # Merge every changed field into the marker state first. + if bpf is not None and bpf != marker_info["bpf"]: + # An inherited marker is synced from a definition whose BPF was + # already validated at create/update (or load); re-validating per + # link is redundant. Private markers validate here as before. + if not inherited: + result = validate_bpf_syntax(bpf) + if not result.get("valid"): + raise ControllerError(f"Invalid BPF expression: {result.get('error', 'unknown error')}") + marker_info["bpf"] = bpf + if tag is not None: + marker_info["tag"] = tag + if enabled is not None: + marker_info["enabled"] = enabled + if color is not None: + marker_info["color"] = color + if highlight_duration is not None: + marker_info["highlight_duration"] = highlight_duration + if direction is not _UNSET: + marker_info["direction"] = direction # None = clear back to both directions + + if memory_only: + # Project-level def sync fan-out: state is already merged into + # _markers; just refresh _link_data so the batch dispatch carries + # it. No per-link uBridge rebuild, notification or dump. + self._refresh_link_data() + return + + # Push to uBridge fine-grained — NO full NIO reapply (which would + # reset_packet_filters and close/reopen every sibling marker's pcap): + # * bpf/tag/direction changed → rebuild just this filter (delete + add), + # reopening only this marker's pcap (expected, new BPF) + # * only enabled changed → instant toggle (enable_packet_filter) + # * only UI fields changed → nothing to push to uBridge + if self._created: + ubridge_rebuild = (bpf is not None) or (tag is not None) or (direction is not _UNSET) + capture_node_id = marker_info.get("capture_node_id") + side = next((s for s in self._nodes if str(s["node"].id) == str(capture_node_id)), None) + if side is not None: + try: + if ubridge_rebuild: + await side["node"].put( + f"/markers/{name}/rebuild", + data={ + "bpf": marker_info["bpf"], + "tag": marker_info.get("tag"), + "direction": marker_info.get("direction"), + "enabled": marker_info.get("enabled", True), + "link_id": self._id, + }, + ) + elif enabled is not None: + await side["node"].put(f"/markers/{name}", data={"enabled": enabled}) + except Exception: + # Old compute without the route / node down: state is already + # correct in _markers; the next NIO reapply converges uBridge. + pass + self._project.emit_notification("link.updated", self.asdict()) + if dump: + self._project.dump() diff --git a/gns3server/core/tasks.py b/gns3server/core/tasks.py index 99ab6c122..f8e8dadc9 100644 --- a/gns3server/core/tasks.py +++ b/gns3server/core/tasks.py @@ -24,6 +24,7 @@ from gns3server.controller import Controller from gns3server.config import Config from gns3server.compute import MODULES from gns3server.compute.port_manager import PortManager +from gns3server.compute.marker.marker_manager import MarkerManager from gns3server.utils.http_client import HTTPClient from gns3server.db.tasks import connect_to_db, get_computes, disconnect_from_db, discover_images_on_filesystem @@ -84,6 +85,22 @@ async def startup(app: FastAPI) -> None: m = module.instance() m.port_manager = PortManager.instance() + # Start the marker (traffic-insight) UDP sink. One listener per compute + # process receives ubridge MARK signals; ubridges are told its host/port at + # startup (see BaseNode._start_ubridge). + server_settings = Config.instance().settings.Server + await MarkerManager.instance().start( + host=server_settings.marker_listen_host, port=server_settings.marker_listen_port + ) + + # Mark MCP server as ready to accept connections (if MCP is available) + from gns3server.agent import MCP_AVAILABLE + + if MCP_AVAILABLE: + from gns3server.agent.mcp import set_mcp_server_ready + set_mcp_server_ready(True) + log.info("GNS3 server startup completed") + async def shutdown(app: FastAPI) -> None: """ @@ -93,6 +110,7 @@ async def shutdown(app: FastAPI) -> None: if auto_discover_images_task_handle is not None and not auto_discover_images_task_handle.cancelled(): auto_discover_images_task_handle.cancel() await HTTPClient.close_session() + await MarkerManager.instance().stop() await Controller.instance().stop() for module in MODULES: diff --git a/gns3server/crash_report.py b/gns3server/crash_report.py index 6fa2f8374..66ceea1f8 100644 --- a/gns3server/crash_report.py +++ b/gns3server/crash_report.py @@ -58,7 +58,7 @@ class CrashReport: Report crash to a third party service """ - DSN = "https://2fe45e33c13e7f8cc1f5483e323765f8@o19455.ingest.us.sentry.io/38482" + DSN = "https://b8b31d4b05b8a353824c5e45f7792885@o19455.ingest.us.sentry.io/38482" _instance = None def __init__(self): diff --git a/gns3server/custom_symbols/AlpiNet.svg b/gns3server/custom_symbols/AlpiNet.svg index 985d9af01..f0c1067a6 100644 --- a/gns3server/custom_symbols/AlpiNet.svg +++ b/gns3server/custom_symbols/AlpiNet.svg @@ -4,20 +4,43 @@ . + +from sqlalchemy import Column, String, Boolean, DateTime, ForeignKey, func + +from .base import BaseTable, GUID + + +class ApiKey(BaseTable): + + __tablename__ = "api_keys" + + api_key_id = Column(GUID, primary_key=True) + user_id = Column(GUID, ForeignKey("users.user_id", ondelete="CASCADE"), nullable=False, index=True) + name = Column(String(128), nullable=False) + key_hash = Column(String(128), nullable=False) + key_prefix = Column(String(8), nullable=False) + last_used_at = Column(DateTime, nullable=True) + created_at = Column(DateTime, server_default=func.current_timestamp(), nullable=False) + revoked = Column(Boolean, default=False, nullable=False) diff --git a/gns3server/db/models/privileges.py b/gns3server/db/models/privileges.py index ad63503c9..c446b17ee 100644 --- a/gns3server/db/models/privileges.py +++ b/gns3server/db/models/privileges.py @@ -226,6 +226,26 @@ def create_default_roles(target, connection, **kw): { "description": "View an appliance", "name": "Appliance.Audit" + }, + { + "description": "Create or delete an LLM model configuration", + "name": "LLMConfig.Allocate" + }, + { + "description": "View an LLM model configuration", + "name": "LLMConfig.Audit" + }, + { + "description": "Update an LLM model configuration", + "name": "LLMConfig.Modify" + }, + { + "description": "View server settings", + "name": "Server.Audit" + }, + { + "description": "Update server settings", + "name": "Server.Modify" } ] @@ -295,7 +315,9 @@ def add_privileges_to_default_roles(target, connection, **kw): "Image.Audit", "Compute.Audit", "Appliance.Allocate", - "Appliance.Audit" + "Appliance.Audit", + "LLMConfig.Audit", + "LLMConfig.Modify" ) add_privileges_to_role(target, connection, "User", user_privileges) diff --git a/gns3server/db/models/templates.py b/gns3server/db/models/templates.py index f0100352c..759cc0fc8 100644 --- a/gns3server/db/models/templates.py +++ b/gns3server/db/models/templates.py @@ -35,6 +35,8 @@ class Template(BaseTable): symbol = Column(String) builtin = Column(Boolean, default=False) usage = Column(String) + netmiko_device_type = Column(String) + appliance_metadata = Column(JSON) template_type = Column(String) tags = Column(JSON) compute_id = Column(String) @@ -78,6 +80,7 @@ class DockerTemplate(Template): console_resolution = Column(String) extra_hosts = Column(String) extra_volumes = Column(JSON) + extra_configs = Column(JSON) memory = Column(Integer) cpus = Column(Float) custom_adapters = Column(JSON) diff --git a/gns3server/db/repositories/api_keys.py b/gns3server/db/repositories/api_keys.py new file mode 100644 index 000000000..ae56dd37e --- /dev/null +++ b/gns3server/db/repositories/api_keys.py @@ -0,0 +1,104 @@ +# +# Copyright (C) 2026 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from uuid import UUID +from typing import Optional, List +from datetime import datetime, timezone +from sqlalchemy import select, update, delete, func +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.orm import selectinload + +from .base import BaseRepository +import gns3server.db.models as models + +import logging + +log = logging.getLogger(__name__) + + +class ApiKeysRepository(BaseRepository): + + def __init__(self, db_session: AsyncSession) -> None: + super().__init__(db_session) + + async def create_api_key( + self, api_key_id: UUID, user_id: UUID, name: str, key_hash: str, key_prefix: str + ) -> models.ApiKey: + db_api_key = models.ApiKey( + api_key_id=api_key_id, + user_id=user_id, + name=name, + key_hash=key_hash, + key_prefix=key_prefix, + ) + self._db_session.add(db_api_key) + await self._db_session.commit() + await self._db_session.refresh(db_api_key) + return db_api_key + + async def get_api_key(self, api_key_id: UUID) -> Optional[models.ApiKey]: + query = select(models.ApiKey).where(models.ApiKey.api_key_id == api_key_id) + result = await self._db_session.execute(query) + return result.scalars().first() + + async def get_api_keys_by_user(self, user_id: UUID) -> List[models.ApiKey]: + query = ( + select(models.ApiKey) + .where(models.ApiKey.user_id == user_id) + .order_by(models.ApiKey.created_at.desc()) + ) + result = await self._db_session.execute(query) + return list(result.scalars().all()) + + async def get_api_key_by_hash(self, key_hash: str) -> Optional[models.ApiKey]: + query = select(models.ApiKey).where(models.ApiKey.key_hash == key_hash) + result = await self._db_session.execute(query) + return result.scalars().first() + + async def revoke_api_key(self, api_key_id: UUID) -> bool: + query = ( + update(models.ApiKey) + .where(models.ApiKey.api_key_id == api_key_id) + .values(revoked=True) + ) + result = await self._db_session.execute(query) + await self._db_session.commit() + return result.rowcount > 0 + + async def restore_api_key(self, api_key_id: UUID) -> bool: + query = ( + update(models.ApiKey) + .where(models.ApiKey.api_key_id == api_key_id) + .values(revoked=False) + ) + result = await self._db_session.execute(query) + await self._db_session.commit() + return result.rowcount > 0 + + async def update_last_used(self, api_key_id: UUID) -> None: + query = ( + update(models.ApiKey) + .where(models.ApiKey.api_key_id == api_key_id) + .values(last_used_at=func.now()) + ) + await self._db_session.execute(query) + await self._db_session.commit() + + async def delete_api_key(self, api_key_id: UUID) -> bool: + query = delete(models.ApiKey).where(models.ApiKey.api_key_id == api_key_id) + result = await self._db_session.execute(query) + await self._db_session.commit() + return result.rowcount > 0 diff --git a/gns3server/db/repositories/pools.py b/gns3server/db/repositories/pools.py index 84ce6e47a..336c29c8d 100644 --- a/gns3server/db/repositories/pools.py +++ b/gns3server/db/repositories/pools.py @@ -156,6 +156,14 @@ class ResourcePoolsRepository(BaseRepository): Delete a resource pool. """ + # Get all resources in the pool first + resources = await self.get_pool_resources(resource_pool_id) + + # Delete all resource records + for resource in resources: + await self.delete_resource(resource.resource_id) + + # Now delete the resource pool query = delete(models.ResourcePool).where(models.ResourcePool.resource_pool_id == resource_pool_id) result = await self._db_session.execute(query) await self._db_session.commit() @@ -203,6 +211,7 @@ class ResourcePoolsRepository(BaseRepository): resource_pool_db.resources.remove(resource) await self._db_session.commit() await self._db_session.refresh(resource_pool_db) + return resource_pool_db async def get_pool_resources(self, resource_pool_id: UUID) -> List[models.Resource]: diff --git a/gns3server/db/repositories/rbac.py b/gns3server/db/repositories/rbac.py index 6d7514654..bd8f1ad9a 100644 --- a/gns3server/db/repositories/rbac.py +++ b/gns3server/db/repositories/rbac.py @@ -230,6 +230,25 @@ class RbacRepository(BaseRepository): result = await self._db_session.execute(query) return result.scalars().all() + async def get_aces_for_path(self, path: str) -> List[models.ACE]: + """ + Get all ACEs for a specific path (exact match or starting with path). + + This method includes related user, group, and role information. + """ + + query = select(models.ACE).\ + where( + (models.ACE.path == path) | (models.ACE.path.startswith(path + "/")) + ).\ + options( + selectinload(models.ACE.user), + selectinload(models.ACE.group), + selectinload(models.ACE.role) + ) + result = await self._db_session.execute(query) + return result.scalars().all() + async def check_ace_exists(self, path: str) -> bool: """ Check if an ACE exists. @@ -385,6 +404,83 @@ class RbacRepository(BaseRepository): pool_resources.extend(await self._get_resources_in_pools(group_aces)) return list(set(pool_resources)) + async def get_accessible_project_ids( + self, + user_id: UUID, + privilege_name: str, + all_project_ids: List[str] + ): + """ + Batch check which projects a user can access via direct ACE or resource pools. + Performs 3 fixed DB queries regardless of project count. + + Returns: + (direct_ace_ids, pool_accessible_ids) where: + - direct_ace_ids: projects the user has a direct ACE on (needs created_by filter) + - pool_accessible_ids: projects in resource pools the user can access (no created_by filter) + """ + + user_aces = await self._get_user_aces(user_id, privilege_name) + group_aces = await self._get_group_aces(user_id, privilege_name) + + # Single query for all resources and their pool memberships + query = select(models.Resource).options(selectinload(models.Resource.resource_pools)) + result = await self._db_session.execute(query) + all_resources = result.scalars().all() + + # Precompute pool_id -> set of project_ids + pool_to_projects = {} + for r in all_resources: + if r.resource_type == "project": + for pool in r.resource_pools: + pool_to_projects.setdefault(str(pool.resource_pool_id), set()).add(str(r.resource_id)) + + # --- User ACE: direct path check --- + direct_ace_ids = set() + user_denied = set() + + for pid in all_project_ids: + path = f"/projects/{pid}" + try: + if self._check_path_with_aces(path, user_aces): + direct_ace_ids.add(pid) + except PermissionError: + user_denied.add(pid) + + # --- User ACE: pool check --- + user_pool_ids = {ace_path.split("/")[2] for ace_path, _, ace_allowed, _ in user_aces + if ace_path.startswith("/pools/") and ace_allowed} + + pool_accessible_ids = set() + for pool_id, project_ids in pool_to_projects.items(): + if pool_id in user_pool_ids: + for pid in project_ids: + if pid not in user_denied: + pool_accessible_ids.add(pid) + + # --- Group ACE: direct path check (skip already accessible or denied) --- + for pid in all_project_ids: + if pid in direct_ace_ids or pid in user_denied: + continue + path = f"/projects/{pid}" + try: + if self._check_path_with_aces(path, group_aces): + direct_ace_ids.add(pid) + except PermissionError: + pass + + # --- Group ACE: pool check --- + group_pool_ids = {ace_path.split("/")[2] for ace_path, _, ace_allowed, _ in group_aces + if ace_path.startswith("/pools/") and ace_allowed} + + for pool_id, project_ids in pool_to_projects.items(): + if pool_id in group_pool_ids: + for pid in project_ids: + if pid not in user_denied and pid not in pool_accessible_ids: + pool_accessible_ids.add(pid) + + return direct_ace_ids, pool_accessible_ids + async def check_user_has_privilege(self, user_id: UUID, path: str, privilege_name: str) -> bool: """ Resource paths form a file system like tree and privileges can be inherited by paths down that tree @@ -409,22 +505,26 @@ class RbacRepository(BaseRepository): aces = await self._get_user_aces(user_id, privilege_name) try: + # Check regular ACEs first + if self._check_path_with_aces(path, aces): + # the user has an ACE matching the path and privilege, there is no need to check group ACEs + return True + # Then check resource pool ACEs if path_is_in_pool: if await self._get_resources_in_pools(aces, path): return True - elif self._check_path_with_aces(path, aces): - # the user has an ACE matching the path and privilege, there is no need to check group ACEs - return True except PermissionError: return False aces = await self._get_group_aces(user_id, privilege_name) try: + # Check regular ACEs first + if self._check_path_with_aces(path, aces): + return True + # Then check resource pool ACEs if path_is_in_pool: if await self._get_resources_in_pools(aces, path): return True - elif self._check_path_with_aces(path, aces): - return True except PermissionError: return False return False diff --git a/gns3server/db/repositories/templates.py b/gns3server/db/repositories/templates.py index ec8215af8..a08b58053 100644 --- a/gns3server/db/repositories/templates.py +++ b/gns3server/db/repositories/templates.py @@ -16,6 +16,7 @@ # along with this program. If not, see . import os +import logging from uuid import UUID from typing import List, Union, Optional @@ -29,6 +30,8 @@ from .base import BaseRepository import gns3server.db.models as models from gns3server import schemas +log = logging.getLogger(__name__) + TEMPLATE_TYPE_TO_MODEL = { "cloud": models.CloudTemplate, "docker": models.DockerTemplate, @@ -49,6 +52,9 @@ class TemplatesRepository(BaseRepository): super().__init__(db_session) + def configs_path(self) -> str: + return os.path.join(os.getcwd(), "configs") + async def get_template(self, template_id: UUID) -> Union[None, models.Template]: query = select(models.Template).\ @@ -65,6 +71,17 @@ class TemplatesRepository(BaseRepository): result = await self._db_session.execute(query) return result.scalars().first() + async def get_template_by_name(self, name: str) -> Union[None, models.Template]: + """ + Return the first template with this name, regardless of version. + """ + + query = select(models.Template).\ + options(selectinload(models.Template.images)).\ + where(models.Template.name == name) + result = await self._db_session.execute(query) + return result.scalars().first() + async def get_templates(self) -> List[models.Template]: query = select(models.Template).options(selectinload(models.Template.images)) @@ -123,8 +140,16 @@ class TemplatesRepository(BaseRepository): where(models.Image.filename == image_name, models.Image.path.endswith(image_path)) else: query = select(models.Image).where(models.Image.filename == image_name) + query = query.order_by(models.Image.image_id) result = await self._db_session.execute(query) - return result.scalars().one_or_none() + images = result.scalars().all() + if len(images) > 1: + log.warning( + f"Multiple DB entries found for image '{image_path}' " + f"({len(images)} rows). This indicates a data integrity issue. " + f"Using the entry with the lowest image_id ({images[0].image_id})." + ) + return images[0] if images else None async def add_image_to_template( self, diff --git a/gns3server/db/tasks.py b/gns3server/db/tasks.py index 887ff9bf1..285d95b54 100644 --- a/gns3server/db/tasks.py +++ b/gns3server/db/tasks.py @@ -80,6 +80,27 @@ async def connect_to_db(app: FastAPI) -> None: db_path = os.path.join(Config.instance().config_dir, "gns3_controller.db") db_url = os.environ.get("GNS3_DATABASE_URI", f"sqlite+aiosqlite:///{db_path}") engine = create_async_engine(db_url, connect_args={"check_same_thread": False, "timeout": 20}, future=True, pool_size=512, max_overflow=1024) + + # Register PRAGMA on the sync engine to ensure it fires for async connections + @event.listens_for(engine.sync_engine, "connect") + def set_sqlite_pragma(dbapi_connection, connection_record): + cursor = dbapi_connection.cursor() + cursor.execute("PRAGMA journal_mode=WAL") + cursor.execute("PRAGMA foreign_keys=ON") + cursor.close() + + # Verify WAL mode is active + async with engine.connect() as _verify_conn: + def _check_wal(conn): + cursor = conn.connection.cursor() + cursor.execute("PRAGMA journal_mode") + row = cursor.fetchone() + cursor.close() + return row[0] if row else "unknown" + wal_mode = await _verify_conn.run_sync(_check_wal) + log.info(f"SQLite journal mode: {wal_mode}") + if wal_mode and wal_mode.upper() != "WAL": + log.warning("WAL mode not active - concurrent writes may cause 'database is locked' errors") alembic_cfg = config.Config() alembic_cfg.set_main_option("script_location", "gns3server:db_migrations") #alembic_cfg.set_main_option('sqlalchemy.url', db_url) @@ -146,16 +167,6 @@ async def disconnect_from_db(app: FastAPI) -> None: log.info(f"Disconnected from database") -@event.listens_for(Engine, "connect") -def set_sqlite_pragma(dbapi_connection, connection_record): - - # Enable SQL foreign key support for SQLite - # https://docs.sqlalchemy.org/en/14/dialects/sqlite.html#foreign-key-support - cursor = dbapi_connection.cursor() - cursor.execute("PRAGMA foreign_keys=ON") - cursor.close() - - async def get_computes(app: FastAPI) -> List[dict]: computes = [] diff --git a/gns3server/db_migrations/versions/8f2a1c4e9d3b_add_extra_configs_to_docker_templates.py b/gns3server/db_migrations/versions/8f2a1c4e9d3b_add_extra_configs_to_docker_templates.py new file mode 100644 index 000000000..2be50f287 --- /dev/null +++ b/gns3server/db_migrations/versions/8f2a1c4e9d3b_add_extra_configs_to_docker_templates.py @@ -0,0 +1,26 @@ +"""add extra_configs to docker templates table + +Revision ID: 8f2a1c4e9d3b +Revises: f0b0de2a9 +Create Date: 2026-08-14 10:00:00.000000 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '8f2a1c4e9d3b' +down_revision = 'f0b0de2a9' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + + op.add_column('docker_templates', sa.Column('extra_configs', sa.JSON())) + + +def downgrade() -> None: + + op.drop_column('docker_templates', 'extra_configs') diff --git a/gns3server/db_migrations/versions/a8829e6c069b_add_llm_config_privileges.py b/gns3server/db_migrations/versions/a8829e6c069b_add_llm_config_privileges.py new file mode 100644 index 000000000..cb87fd15b --- /dev/null +++ b/gns3server/db_migrations/versions/a8829e6c069b_add_llm_config_privileges.py @@ -0,0 +1,113 @@ +"""add llm config privileges to existing database + +Revision ID: a8829e6c069b +Revises: aff810fc119a +Create Date: 2026-05-26 + +""" +from alembic import op +import sqlalchemy as sa +from uuid import uuid4 + +# revision identifiers, used by Alembic. +revision = 'a8829e6c069b' +down_revision = 'aff810fc119a' +branch_labels = None +depends_on = None + +privileges_table = sa.table( + 'privileges', + sa.column('privilege_id', sa.String), + sa.column('name', sa.String), + sa.column('description', sa.String), +) + +roles_table = sa.table( + 'roles', + sa.column('role_id', sa.String), + sa.column('name', sa.String), +) + +privilege_role_map = sa.table( + 'privilege_role_map', + sa.column('privilege_id', sa.String), + sa.column('role_id', sa.String), +) + + +def upgrade() -> None: + conn = op.get_bind() + + # Insert new LLMConfig privileges if they don't already exist + new_privileges = [ + {"name": "LLMConfig.Allocate", "description": "Create or delete an LLM model configuration"}, + {"name": "LLMConfig.Audit", "description": "View an LLM model configuration"}, + {"name": "LLMConfig.Modify", "description": "Update an LLM model configuration"}, + ] + + privilege_ids = {} + for priv in new_privileges: + result = conn.execute( + sa.select(privileges_table.c.privilege_id).where( + privileges_table.c.name == priv["name"] + ) + ).fetchone() + + if result: + privilege_ids[priv["name"]] = result[0] + else: + priv_id = str(uuid4()) + conn.execute( + privileges_table.insert().values( + privilege_id=priv_id, + name=priv["name"], + description=priv["description"], + ) + ) + privilege_ids[priv["name"]] = priv_id + + # Add LLMConfig.Audit and LLMConfig.Modify to the User role + user_role = conn.execute( + sa.select(roles_table.c.role_id).where(roles_table.c.name == "User") + ).fetchone() + + if user_role: + user_role_id = user_role[0] + for priv_name in ("LLMConfig.Audit", "LLMConfig.Modify"): + conn.execute( + privilege_role_map.insert().values( + privilege_id=privilege_ids[priv_name], + role_id=user_role_id, + ) + ) + + +def downgrade() -> None: + conn = op.get_bind() + + user_role = conn.execute( + sa.select(roles_table.c.role_id).where(roles_table.c.name == "User") + ).fetchone() + + if user_role: + user_role_id = user_role[0] + for priv_name in ("LLMConfig.Audit", "LLMConfig.Modify"): + priv = conn.execute( + sa.select(privileges_table.c.privilege_id).where( + privileges_table.c.name == priv_name + ) + ).fetchone() + if priv: + conn.execute( + privilege_role_map.delete().where( + privilege_role_map.c.privilege_id == priv[0], + privilege_role_map.c.role_id == user_role_id, + ) + ) + + for priv_name in ("LLMConfig.Allocate", "LLMConfig.Audit", "LLMConfig.Modify"): + conn.execute( + privileges_table.delete().where( + privileges_table.c.name == priv_name + ) + ) diff --git a/gns3server/db_migrations/versions/b3c7e2a91d4f_add_netmiko_device_type_to_templates.py b/gns3server/db_migrations/versions/b3c7e2a91d4f_add_netmiko_device_type_to_templates.py new file mode 100644 index 000000000..bc0241c12 --- /dev/null +++ b/gns3server/db_migrations/versions/b3c7e2a91d4f_add_netmiko_device_type_to_templates.py @@ -0,0 +1,26 @@ +"""add netmiko_device_type to templates table + +Revision ID: b3c7e2a91d4f +Revises: 8f2a1c4e9d3b +Create Date: 2026-08-16 10:00:00.000000 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'b3c7e2a91d4f' +down_revision = '8f2a1c4e9d3b' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + + op.add_column('templates', sa.Column('netmiko_device_type', sa.String())) + + +def downgrade() -> None: + + op.drop_column('templates', 'netmiko_device_type') diff --git a/gns3server/db_migrations/versions/c7e4a9f1d2b6_add_appliance_metadata_to_templates.py b/gns3server/db_migrations/versions/c7e4a9f1d2b6_add_appliance_metadata_to_templates.py new file mode 100644 index 000000000..7c7b18fbb --- /dev/null +++ b/gns3server/db_migrations/versions/c7e4a9f1d2b6_add_appliance_metadata_to_templates.py @@ -0,0 +1,26 @@ +"""add appliance_metadata to templates table + +Revision ID: c7e4a9f1d2b6 +Revises: b3c7e2a91d4f +Create Date: 2026-08-16 12:00:00.000000 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'c7e4a9f1d2b6' +down_revision = 'b3c7e2a91d4f' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + + op.add_column('templates', sa.Column('appliance_metadata', sa.JSON())) + + +def downgrade() -> None: + + op.drop_column('templates', 'appliance_metadata') diff --git a/gns3server/db_migrations/versions/f0b0de2a9_add_api_keys_table.py b/gns3server/db_migrations/versions/f0b0de2a9_add_api_keys_table.py new file mode 100644 index 000000000..9c8b4fdb6 --- /dev/null +++ b/gns3server/db_migrations/versions/f0b0de2a9_add_api_keys_table.py @@ -0,0 +1,43 @@ +"""add api_keys table + +Revision ID: f0b0de2a9 +Revises: a8829e6c069b +Create Date: 2026-06-11 10:00:00.000000 + +""" +from alembic import op +import sqlalchemy as sa +import gns3server.db.models.base as models + +# revision identifiers, used by Alembic. +revision = 'f0b0de2a9' +down_revision = 'a8829e6c069b' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + + op.create_table( + 'api_keys', + sa.Column('api_key_id', models.GUID(), nullable=False), + sa.Column('user_id', models.GUID(), nullable=False), + sa.Column('name', sa.String(128), nullable=False), + sa.Column('key_hash', sa.String(128), nullable=False), + sa.Column('key_prefix', sa.String(8), nullable=False), + sa.Column('last_used_at', sa.DateTime(), nullable=True), + sa.Column('created_at', sa.DateTime(), server_default=sa.func.current_timestamp(), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.func.current_timestamp(), nullable=False), + sa.Column('revoked', sa.Boolean(), default=False, nullable=False), + sa.ForeignKeyConstraint(['user_id'], ['users.user_id'], ondelete='CASCADE'), + sa.PrimaryKeyConstraint('api_key_id'), + ) + op.create_index('ix_api_keys_user_id', 'api_keys', ['user_id']) + op.create_index('ix_api_keys_key_hash', 'api_keys', ['key_hash']) + + +def downgrade() -> None: + + op.drop_index('ix_api_keys_key_hash', table_name='api_keys') + op.drop_index('ix_api_keys_user_id', table_name='api_keys') + op.drop_table('api_keys') diff --git a/gns3server/main.py b/gns3server/main.py index bb30a2d81..4069f5e3a 100644 --- a/gns3server/main.py +++ b/gns3server/main.py @@ -31,6 +31,8 @@ import os import sys import asyncio import argparse +import logging +import resource def daemonize(): @@ -97,6 +99,34 @@ def parse_arguments(argv): return parser, args +log = logging.getLogger(__name__) + + +def _raise_open_files_limit(target=65535): + """ + Raise RLIMIT_NOFILE at startup so large topologies don't hit EMFILE. + Every started node holds ~3 file descriptors in the server's table + (pidfd + stdout/stderr pipes per child process), so a few hundred nodes + exhaust the default 1024 limit. Best-effort: the hard limit caps what we + can request; failures are logged but never fatal. Runs before daemonize() + so the daemon inherits the raised limit. + """ + try: + soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE) + if soft >= target: + return + new_soft = min(target, hard) + resource.setrlimit(resource.RLIMIT_NOFILE, (new_soft, hard)) + if new_soft < target: + log.warning( + f"Open-files limit raised to {new_soft} (hard limit), below the requested {target}" + ) + else: + log.info(f"Open-files limit raised from {soft} to {new_soft}") + except (OSError, ValueError) as e: + log.warning(f"Could not raise the open-files limit: {e}") + + def main(): """ Entry point for GNS3 server @@ -104,6 +134,7 @@ def main(): if sys.platform.startswith("win"): raise SystemExit("Windows is not a supported platform to run the GNS3 server") + _raise_open_files_limit() if "--daemon" in sys.argv: daemonize() diff --git a/gns3server/schemas/__init__.py b/gns3server/schemas/__init__.py index 0c58eee28..bc8cca5b9 100644 --- a/gns3server/schemas/__init__.py +++ b/gns3server/schemas/__init__.py @@ -20,7 +20,7 @@ from .common import ErrorMessage from .version import Version # Controller schemas -from .controller.links import LinkCreate, LinkUpdate, Link, UDPPortInfo, EthernetPortInfo, LinkCapture +from .controller.links import LinkCreate, LinkUpdate, Link, UDPPortInfo, EthernetPortInfo, LinkCapture, MarkerCreate, MarkerUpdate, MarkerDefinitionCreate from .controller.computes import ComputeCreate, ComputeUpdate, ComputeVirtualBoxVM, ComputeVMwareVM, ComputeDockerImage, AutoIdlePC, Compute from .controller.templates import TemplateCreate, TemplateUpdate, TemplateUsage, Template from .controller.images import Image, ImageType @@ -56,11 +56,13 @@ except ImportError: pass from .controller.rbac import RoleCreate, RoleUpdate, Role, Privilege, ACECreate, ACEUpdate, ACE +from .controller.settings import SettingsResponse, SettingsUpdate, SettingsUpdateResponse from .controller.pools import Resource, ResourceCreate, ResourcePoolCreate, ResourcePoolUpdate, ResourcePool -from .controller.tokens import Token +from .controller.tokens import Token, ApiKeyCreate, RefreshTokenRequest from .controller.snapshots import SnapshotCreate, Snapshot from .controller.iou_license import IOULicense from .controller.capabilities import Capabilities +from .controller.netmiko import NetmikoDeviceType, NetmikoDeviceTypeList # Controller template schemas from .controller.templates.vpcs_templates import VPCSTemplate, VPCSTemplateUpdate @@ -91,7 +93,7 @@ from .controller.templates.dynamips_templates import ( ) # Compute schemas -from .compute.nios import UDPNIO, TAPNIO, EthernetNIO +from .compute.nios import UDPNIO, TAPNIO, EthernetNIO, MarkerToggle, MarkerRebuild, BatchNIOEntry, BatchNIOCreate from .compute.atm_switch_nodes import ATMSwitchCreate, ATMSwitchUpdate, ATMSwitch from .compute.cloud_nodes import CloudCreate, CloudUpdate, Cloud from .compute.docker_nodes import DockerCreate, DockerUpdate, Docker diff --git a/gns3server/schemas/common.py b/gns3server/schemas/common.py index ce37c337f..68ca0b861 100644 --- a/gns3server/schemas/common.py +++ b/gns3server/schemas/common.py @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, field_validator from typing import Optional from enum import Enum @@ -48,6 +48,35 @@ class CustomAdapter(BaseModel): mac_address: Optional[str] = Field(None, pattern="^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$") +class ExtraConfig(BaseModel): + """ + A configuration file injected into a Docker container. + + GNS3 writes ``content`` to a host file and bind-mounts it read-only at + ``target`` inside the container. Used to seed NOS startup configs (e.g. + XRd first-boot config, FRR frr.conf) without rebuilding the image. + """ + + target: str = Field(..., description="Absolute path inside the container where the file is mounted") + content: str = Field("", description="File content written by GNS3 and bind-mounted read-only into the container") + + @field_validator("target") + @classmethod + def target_is_an_absolute_file_path(cls, v): + """ + Reject at save time (template/appliance/node PUT) what would only + blow up at node-create time — after a potentially multi-GB image + pull: relative paths, '..' components and directory forms ('/', + '/etc/'). + """ + if not v.startswith("/") or v.endswith("/") or ".." in v.split("/"): + raise ValueError( + "target must be an absolute file path inside the container " + "(start with '/', name a file, no '..' components)" + ) + return v + + class ConsoleType(str, Enum): """ Supported console types. @@ -61,6 +90,7 @@ class ConsoleType(str, Enum): spice = "spice" spice_agent = "spice+agent" none = "none" + docker_exec = "docker_exec" class AuxType(str, Enum): diff --git a/gns3server/schemas/compute/cloud_nodes.py b/gns3server/schemas/compute/cloud_nodes.py index a4dbfae40..28e01c72d 100644 --- a/gns3server/schemas/compute/cloud_nodes.py +++ b/gns3server/schemas/compute/cloud_nodes.py @@ -28,6 +28,28 @@ class HostInterfaceType(str, Enum): tap = "tap" +class IPAddressFamily(str, Enum): + + ipv4 = "ipv4" + ipv6 = "ipv6" + + +class InterfaceStatus(str, Enum): + + up = "up" + down = "down" + + +class HostInterfaceIPAddress(BaseModel): + """ + An IP address (with optional netmask) bound to a host interface. + """ + + family: IPAddressFamily = Field(..., description="Address family (ipv4 or ipv6)") + address: str = Field(..., description="IP address") + netmask: Optional[str] = Field(None, description="Network mask, if available") + + class HostInterface(BaseModel): """ Interface on this host. @@ -36,6 +58,13 @@ class HostInterface(BaseModel): name: str = Field(..., description="Interface name") type: HostInterfaceType = Field(..., description="Interface type") special: bool = Field(..., description="Whether the interface is non standard") + ip_addresses: List[HostInterfaceIPAddress] = Field( + default_factory=list, description="All IPv4 and IPv6 addresses on this interface" + ) + status: InterfaceStatus = Field(InterfaceStatus.down, description="Interface status (up or down)") + speed: int = Field(0, description="Interface speed in Mbit/s (0 if unknown)") + mtu: int = Field(0, description="Interface MTU") + flags: List[str] = Field(default_factory=list, description="Interface flags") class EthernetType(str, Enum): diff --git a/gns3server/schemas/compute/docker_nodes.py b/gns3server/schemas/compute/docker_nodes.py index 2328baabc..7b39921f6 100644 --- a/gns3server/schemas/compute/docker_nodes.py +++ b/gns3server/schemas/compute/docker_nodes.py @@ -14,11 +14,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, field_validator from typing import Optional, List from uuid import UUID -from ..common import NodeStatus, CustomAdapter, ConsoleType, AuxType +from ..common import NodeStatus, CustomAdapter, ConsoleType, AuxType, ExtraConfig class DockerBase(BaseModel): @@ -26,6 +26,21 @@ class DockerBase(BaseModel): Common Docker node properties. """ + @field_validator("start_command", "environment", "extra_hosts", mode="before") + @classmethod + def _empty_string_to_none(cls, value): + # Web clients serialize empty form fields as "" while unset values are + # stored as None on the node: normalize before the update diff runs, + # otherwise every full PUT would see a phantom change and recreate + # the container for nothing. + return value or None + + @field_validator("console_http_path", mode="before") + @classmethod + def _empty_string_to_root_path(cls, value): + # the canonical "no path" value is "/" (the creation default) + return value or "/" + name: str image: str = Field(..., description="Docker image name") node_id: Optional[UUID] = None @@ -43,6 +58,7 @@ class DockerBase(BaseModel): environment: Optional[str] = Field(None, description="Docker environment variables") extra_hosts: Optional[str] = Field(None, description="Docker extra hosts (added to /etc/hosts)") extra_volumes: Optional[List[str]] = Field(None, description="Additional directories to make persistent") + extra_configs: Optional[List[ExtraConfig]] = Field(None, description="Configuration files injected into the container (bind-mounted read-only)") memory: Optional[int] = Field(None, ge=0, description="Maximum amount of memory the container can use in MB") cpus: Optional[float] = Field(None, ge=0, description="Maximum amount of CPU resources the container can use") custom_adapters: Optional[List[CustomAdapter]] = Field(None, description="Custom adapters") diff --git a/gns3server/schemas/compute/iou_nodes.py b/gns3server/schemas/compute/iou_nodes.py index fe3fe8570..c04b6f0ef 100644 --- a/gns3server/schemas/compute/iou_nodes.py +++ b/gns3server/schemas/compute/iou_nodes.py @@ -38,7 +38,10 @@ class IOUBase(BaseModel): ethernet_adapters: Optional[int] = Field(None, description="How many Ethernet adapters are connected to IOU") ram: Optional[int] = Field(None, gt=0, description="Amount of RAM in MB") nvram: Optional[int] = Field(None, gt=0, description="Amount of NVRAM in KB") - l1_keepalives: Optional[bool] = Field(None, description="Use default IOU values") + l1_keepalives: Optional[bool] = Field( + None, + description="Enable Layer 1 keepalives so IOU interfaces report accurate link state", + ) use_default_iou_values: Optional[bool] = Field(None, description="Use default IOU values") startup_config_content: Optional[str] = Field(None, description="Content of IOU startup configuration file") private_config_content: Optional[str] = Field(None, description="Content of IOU private configuration file") diff --git a/gns3server/schemas/compute/nios.py b/gns3server/schemas/compute/nios.py index cf69f9e30..a1d8641b3 100644 --- a/gns3server/schemas/compute/nios.py +++ b/gns3server/schemas/compute/nios.py @@ -16,7 +16,7 @@ from pydantic import BaseModel, Field -from typing import Optional +from typing import Optional, List from enum import Enum @@ -36,6 +36,7 @@ class UDPNIO(BaseModel): rport: int = Field(..., gt=0, le=65535, description="Remote port") suspend: Optional[bool] = Field(None, description="Suspend the NIO") filters: Optional[dict] = Field(None, description="Packet filters") + markers: Optional[dict] = Field(None, description="Traffic-insight markers") class EthernetNIOType(str, Enum): @@ -64,3 +65,50 @@ class TAPNIO(BaseModel): type: TAPNIOType tap_device: str = Field(..., description="TAP device name e.g. tap0") + + +class MarkerToggle(BaseModel): + """ + Body for the per-marker enable/disable toggle endpoint: flips a running + uBridge marker filter with ``enable_packet_filter on|off`` (no NIO rebuild, + so the pcap identity and emitted counter are preserved). + """ + + enabled: bool + + +class MarkerRebuild(BaseModel): + """ + Body for the per-marker rebuild endpoint: re-install a single uBridge marker + filter with new BPF/tag/direction via ``delete_packet_filter`` + add (NOT a + bridge-wide reset), so sibling markers keep their pcaps open. The marker's + own pcap is reopened by uBridge on re-add (new capture session for the new + BPF), which is expected. + """ + + bpf: str + tag: Optional[int] = None + direction: Optional[str] = None + enabled: bool = True + link_id: str = "" + + +class BatchNIOEntry(BaseModel): + """ + A single NIO binding to create as part of a project-wide batch. + """ + + node_id: str = Field(..., description="Node the NIO is attached to") + adapter_number: int = Field(0, ge=0, description="Adapter number") + port_number: int = Field(0, ge=0, description="Port number") + nio: UDPNIO = Field(..., description="NIO settings") + + +class BatchNIOCreate(BaseModel): + """ + Body for the project-wide batch NIO endpoint: create many NIO bindings in a + single request (used during project open) to avoid one HTTP round-trip per + NIO between controller and compute. + """ + + nios: List[BatchNIOEntry] = Field(..., description="NIO bindings to create") diff --git a/gns3server/schemas/config.py b/gns3server/schemas/config.py index e1a3ab5a9..227129b28 100644 --- a/gns3server/schemas/config.py +++ b/gns3server/schemas/config.py @@ -27,61 +27,87 @@ from pydantic import ( field_validator, model_validator ) -from typing import List +from typing import List, Optional class ControllerSettings(BaseModel): - jwt_secret_key: str = None - jwt_algorithm: str = "HS256" - jwt_access_token_expire_minutes: int = 1440 # 24 hours - default_admin_username: str = "admin" - default_admin_password: SecretStr = SecretStr("admin") + jwt_secret_key: Optional[str] = Field( + None, + description="Secret key used to sign the JWT authentication tokens " + "(normally managed via the secrets directory, not the configuration file)") + jwt_algorithm: str = Field("HS256", description="Algorithm used to sign the JWT tokens") + jwt_access_token_expire_minutes: int = Field( + 1440, description="Lifetime of the JWT access tokens in minutes (24 hours by default)") + jwt_refresh_token_expire_minutes: int = Field( + 43200, description="Lifetime of the JWT refresh tokens in minutes (30 days by default)") + default_admin_username: str = Field( + "admin", + description="Username of the super admin account seeded when the controller database is created; " + "changing it has no effect until the database is re-created (which resets the account)") + default_admin_password: SecretStr = Field( + SecretStr("admin"), + description="Password of the super admin account seeded when the controller database is created; " + "changing it has no effect until the database is re-created (which resets the account)") model_config = ConfigDict(validate_assignment=True, str_strip_whitespace=True) class VPCSSettings(BaseModel): - vpcs_path: str = "vpcs" + vpcs_path: str = Field("vpcs", description="VPCS executable location, default: search in PATH") model_config = ConfigDict(validate_assignment=True, str_strip_whitespace=True) class DynamipsSettings(BaseModel): - allocate_aux_console_ports: bool = False - mmap_support: bool = True - dynamips_path: str = "dynamips" - sparse_memory_support: bool = True - ghost_ios_support: bool = True + allocate_aux_console_ports: bool = Field( + False, description="Allocate auxiliary console ports on IOS routers") + mmap_support: bool = Field( + True, description="Use memory-mapped flash files (mmap) to lower the memory usage of routers") + dynamips_path: str = Field("dynamips", description="Dynamips executable location, default: search in PATH") + sparse_memory_support: bool = Field( + True, description="Use sparse memory allocation to lower the memory usage of routers") + ghost_ios_support: bool = Field( + True, description="Enable Ghost IOS support to share memory between identical IOS images") model_config = ConfigDict(validate_assignment=True, str_strip_whitespace=True) class IOUSettings(BaseModel): - iourc_path: str = None - license_check: bool = True + iourc_path: Optional[str] = Field( + None, description="Path of your .iourc file, the file is searched in $HOME/.iourc if not provided") + license_check: bool = Field( + True, + description="Validate the iourc license file (if disabled, IOU will not start and no errors " + "will be shown when the license is invalid)") model_config = ConfigDict(validate_assignment=True, str_strip_whitespace=True) class QemuSettings(BaseModel): - enable_monitor: bool = True - monitor_host: str = "127.0.0.1" - enable_hardware_acceleration: bool = True - require_hardware_acceleration: bool = False - allow_unsafe_options: bool = False + enable_monitor: bool = Field( + True, description="Use the Qemu monitor feature to communicate with Qemu VMs") + monitor_host: str = Field("127.0.0.1", description="IP used to listen for the monitor") + enable_hardware_acceleration: bool = Field( + True, description="Enable hardware acceleration (KVM)") + require_hardware_acceleration: bool = Field( + False, description="Require hardware acceleration in order to start VMs") + allow_unsafe_options: bool = Field( + False, description="Allow unsafe additional command line options") + ovmf_firmware_dir: str = Field( + "/usr/share/OVMF", description="Path to the OVMF firmware directory") model_config = ConfigDict(validate_assignment=True, str_strip_whitespace=True) class VirtualBoxSettings(BaseModel): - vboxmanage_path: str = None + vboxmanage_path: Optional[str] = None model_config = ConfigDict(validate_assignment=True, str_strip_whitespace=True) class VMwareSettings(BaseModel): - vmrun_path: str = None + vmrun_path: Optional[str] = None vmnet_start_range: int = Field(2, ge=1, le=255) vmnet_end_range: int = Field(255, ge=1, le=255) # should be limited to 19 on Windows block_host_traffic: bool = False @@ -96,12 +122,17 @@ class VMwareSettings(BaseModel): class WebWiresharkSettings(BaseModel): - enabled: bool = True - image: str = "gns3/web-wireshark:latest" - network_subnet: str = "172.31.0.0/22" - memory: str = "2g" - cpus: float = 1.0 - pids_limit: int = 1000 + enabled: bool = Field( + True, description="Enable the Web Wireshark feature (container-based Wireshark in the browser)") + image: str = Field( + "gns3/web-wireshark:latest", description="Docker image for the Web Wireshark containers") + network_subnet: str = Field( + "172.31.0.0/22", + description="Docker network subnet for the Web Wireshark containers (change it if it conflicts " + "with your existing network)") + memory: str = Field("2g", description='Memory limit per container (e.g. "512m", "2g")') + cpus: float = Field(1.0, description="CPU cores per container (e.g. 1.0, 2.0)") + pids_limit: int = Field(1000, description="Process limit per container") model_config = ConfigDict(validate_assignment=True, str_strip_whitespace=True) @@ -111,6 +142,16 @@ class ServerProtocol(str, Enum): https = "https" +class UbridgeControlTransport(str, Enum): + + # TCP control channel: -H host:port. ubridge now binds loopback by default, + # so this is reachable only locally. Retained for backward compatibility. + tcp = "tcp" + # AF_UNIX control channel: -U socket_path, authenticated in-kernel via + # SO_PEERCRED (ubridge accepts only its own UID). Recommended on Linux. + unix = "unix" + + class BuiltinSymbolTheme(str, Enum): classic = "Classic" @@ -124,59 +165,142 @@ class BuiltinSymbolTheme(str, Enum): class ServerSettings(BaseModel): - local: bool = False - enable_http_auth: bool = True - name: str = f"{socket.gethostname()} (controller)" - protocol: ServerProtocol = ServerProtocol.http - host: str = "0.0.0.0" - port: int = Field(3080, gt=0, le=65535) - secrets_dir: DirectoryPath = None - certfile: FilePath = None - certkey: FilePath = None - enable_ssl: bool = False - images_path: str = "~/GNS3/images" - projects_path: str = "~/GNS3/projects" - appliances_path: str = "~/GNS3/appliances" - symbols_path: str = "~/GNS3/symbols" - configs_path: str = "~/GNS3/configs" - resources_path: str = None - default_symbol_theme: BuiltinSymbolTheme = BuiltinSymbolTheme.affinity_square_blue - allow_raw_images: bool = True - auto_discover_images: bool = True - report_errors: bool = True - additional_images_paths: List[str] = Field(default_factory=list) - console_start_port_range: int = Field(5000, gt=0, le=65535) - console_end_port_range: int = Field(10000, gt=0, le=65535) - vnc_console_start_port_range: int = Field(5900, ge=5900, le=65535) - vnc_console_end_port_range: int = Field(10000, ge=5900, le=65535) - udp_start_port_range: int = Field(10000, gt=0, le=65535) - udp_end_port_range: int = Field(30000, gt=0, le=65535) - ubridge_path: str = "ubridge" - compute_username: str = "gns3" - compute_password: SecretStr = SecretStr("") - allowed_interfaces: List[str] = Field(default_factory=list) - default_nat_interface: str = None - allow_remote_console: bool = False - enable_builtin_templates: bool = True - install_builtin_appliances: bool = True - skills_repo_url: str = "https://github.com/yueguobin/GNS3-Skills.git" - skills_repo_branch: str = "main" - skills_auto_update: bool = True + local: bool = Field( + False, + description="Local server mode, set by the --local command line argument (not meant to be set by hand)") + enable_http_auth: bool = Field(True, description="Enable compute HTTP authentication") + name: str = Field( + f"{socket.gethostname()} (controller)", + description="Server name, default is what is returned by socket.gethostname()") + protocol: ServerProtocol = Field( + ServerProtocol.http, description="Protocol used by the server: http or https") + host: str = Field("0.0.0.0", description="IP address where the server listens for connections") + port: int = Field(3080, gt=0, le=65535, description="HTTP port used to control the server") + secrets_dir: Optional[DirectoryPath] = Field( + None, description="Directory where secrets are stored (e.g. the JWT secret key)") + certfile: Optional[FilePath] = Field(None, description="SSL certificate file, requires enable_ssl") + certkey: Optional[FilePath] = Field(None, description="SSL key file, requires enable_ssl") + enable_ssl: bool = Field(False, description="Enable SSL encryption") + images_path: str = Field("~/GNS3/images", description="Path where binary images are stored") + projects_path: str = Field("~/GNS3/projects", description="Path where user projects are stored") + appliances_path: str = Field("~/GNS3/appliances", description="Path where custom user appliances are stored") + symbols_path: str = Field("~/GNS3/symbols", description="Path where custom user symbols are stored") + configs_path: str = Field("~/GNS3/configs", description="Path where custom user configs are stored") + resources_path: Optional[str] = Field( + None, + description="Path where files like built-in appliances and Docker resources are stored " + "(defaults to the local user data directory)") + default_symbol_theme: BuiltinSymbolTheme = Field( + BuiltinSymbolTheme.affinity_square_blue, + description='Default symbol theme, e.g. "Classic" or "Affinity-square-blue"') + allow_raw_images: bool = Field( + True, description="Allow raw images to be uploaded to the server") + auto_discover_images: bool = Field( + True, description="Automatically discover images in the images directory") + report_errors: bool = Field( + True, description="Automatically send crash reports to the GNS3 team") + additional_images_paths: List[str] = Field( + default_factory=list, + description="Additional paths to look for images (semicolon-separated in the configuration file)") + console_start_port_range: int = Field( + 5000, gt=0, le=65535, description="First console port of the range allocated to devices") + console_end_port_range: int = Field( + 10000, gt=0, le=65535, description="Last console port of the range allocated to devices") + vnc_console_start_port_range: int = Field( + 5900, ge=5900, le=65535, description="First VNC console port of the range allocated to devices") + vnc_console_end_port_range: int = Field( + 10000, ge=5900, le=65535, description="Last VNC console port of the range allocated to devices") + udp_start_port_range: int = Field( + 10000, gt=0, le=65535, + description="First UDP port of the range allocated for inter-device communication (two ports per link)") + udp_end_port_range: int = Field( + 30000, gt=0, le=65535, + description="Last UDP port of the range allocated for inter-device communication (two ports per link)") + ubridge_path: str = Field("ubridge", description="uBridge executable location, default: search in PATH") + ubridge_control_transport: UbridgeControlTransport = Field( + UbridgeControlTransport.unix, + description='uBridge control channel transport: "unix" (AF_UNIX + SO_PEERCRED, recommended ' + 'on Linux) or "tcp" (loopback, kept for backward compatibility)') + marker_listen_host: str = Field( + "127.0.0.1", + description="Marker (traffic-insight) UDP sink listen host: one listener per compute process " + "receives uBridge MARK signals from every uBridge on this host") + marker_listen_port: int = Field( + 3070, ge=0, le=65535, + description="Marker UDP sink listen port (0 lets the operating system choose a free port)") + compute_username: str = Field( + "gns3", description='Username for compute HTTP authentication, "gns3" is the default') + compute_password: SecretStr = Field( + SecretStr(""), + description="Password for compute HTTP authentication, a randomly generated password is used if not set") + allowed_interfaces: List[str] = Field( + default_factory=list, + description="Only allow these interfaces to be used by GNS3, for the Cloud node for example " + "(comma-separated; do not forget virbr0 for the NAT node to work)") + default_nat_interface: Optional[str] = Field( + None, description="Interface used by the NAT node, default is virbr0 on Linux (requires libvirt)") + allow_remote_console: bool = Field( + False, + description="Allow console connections from remote machines " + "(console ports only accept local connections by default)") + enable_builtin_templates: bool = Field(True, description="Enable the built-in templates") + install_builtin_appliances: bool = Field(True, description="Install the built-in appliances") + skills_repo_url: str = Field( + "https://github.com/gns3/gns3-skills.git", + description="Git repository URL for the external GNS3 Copilot skills " + "(injection skills, prompts and device skills)") + skills_repo_branch: str = Field("main", description="Git branch of the skills repository") + skills_auto_update: bool = Field( + True, description="Automatically pull updates from the skills repository when reloading") + mcp_enable_dns_rebinding_protection: bool = Field( + False, + description="Enable MCP transport DNS rebinding protection " + "(allowed hosts and origins must be configured)") + mcp_allowed_hosts: list[str] = Field( + default_factory=list, + description='Allowed hosts for MCP connections, only "host:*" port wildcards are supported ' + '(e.g. "127.0.0.1:*")') + mcp_allowed_origins: list[str] = Field( + default_factory=list, + description='Allowed origins for MCP connections (e.g. "http://localhost:*")') + model_config = ConfigDict(validate_assignment=True, str_strip_whitespace=True) + @field_validator("mcp_allowed_hosts", mode="before") + @classmethod + def split_mcp_allowed_hosts(cls, v): + if v and isinstance(v, str): + return v.split(",") + if not v: + return list() + return v + + @field_validator("mcp_allowed_origins", mode="before") + @classmethod + def split_mcp_allowed_origins(cls, v): + if v and isinstance(v, str): + return v.split(",") + if not v: + return list() + return v + @field_validator("additional_images_paths", mode="before") @classmethod def split_additional_images_paths(cls, v): - if v: + if v and isinstance(v, str): return v.split(";") - return list() + if not v: + return list() + return v @field_validator("allowed_interfaces", mode="before") @classmethod def split_allowed_interfaces(cls, v): - if v: + if v and isinstance(v, str): return v.split(",") - return list() + if not v: + return list() + return v @model_validator(mode="after") def check_console_port_range(self) -> "ServerSettings": diff --git a/gns3server/schemas/controller/appliances.py b/gns3server/schemas/controller/appliances.py index 6a0ef5720..9fb818b8a 100644 --- a/gns3server/schemas/controller/appliances.py +++ b/gns3server/schemas/controller/appliances.py @@ -19,7 +19,8 @@ from enum import Enum from typing import Annotated, List, Literal, Optional, Union from uuid import UUID -from pydantic import AnyUrl, BaseModel, Discriminator, EmailStr, Field, Tag +from pydantic import AnyUrl, BaseModel, Discriminator, EmailStr, Field, Tag, model_validator +from ..common import ExtraConfig # ============================================================================ @@ -285,6 +286,7 @@ class DockerConsoleType(str, Enum): http = 'http' https = 'https' none = 'none' + docker_exec = 'docker_exec' class ChecksumType(str, Enum): @@ -328,6 +330,7 @@ class Docker(BaseModel): console_http_path: Optional[str] = Field(None, description='Path of the web interface') extra_hosts: Optional[str] = Field(None, description='Hosts which will be written to /etc/hosts into container') extra_volumes: Optional[List[str]] = Field(None, description='Additional directories to make persistent that are not included in the images VOLUME directive') + extra_configs: Optional[List[ExtraConfig]] = Field(None, description='Configuration files injected into the container (bind-mounted read-only)') class Iou(BaseModel): @@ -478,6 +481,10 @@ class DockerPropertiesV8(BaseModel): extra_volumes: Optional[List[str]] = Field( None, title='Additional directories to make persistent' ) + custom_adapters: Optional[List[CustomAdapterItem]] = Field(None, title='Custom adapters') + extra_configs: Optional[List[ExtraConfig]] = Field( + None, title='Configuration files injected into the container (bind-mounted read-only)' + ) class IouPropertiesV8(BaseModel): @@ -560,14 +567,23 @@ class QemuPropertiesV8(BaseModel): title='Optional define the disk boot priory. Refer to -boot option in qemu manual for more details.', ) kernel_command_line: Optional[str] = Field(None, title='Command line parameters send to the kernel') + kvm: Optional[Kvm] = Field(None, title='KVM requirements') options: Optional[str] = Field(None, title='Optional additional qemu command line options') - cpu_throttling: Optional[Annotated[float, Field(ge=0.0, le=100.0)]] = Field(None, title='Throttle the CPU') + cpu_throttling: Optional[Annotated[int, Field(ge=0, le=800)]] = Field(None, title='Throttle the CPU') tpm: Optional[bool] = Field(None, title='Enable the Trusted Platform Module (TPM)') uefi: Optional[bool] = Field(None, title='Enable the UEFI boot mode') on_close: Optional[QemuOnClose] = Field(None, title='Action to execute on the VM is closed') process_priority: Optional[QemuProcessPriority] = Field(None, title='Process priority for QEMU') +_V8_PROPERTIES_MODELS = { + TemplateType.qemu: QemuPropertiesV8, + TemplateType.dynamips: DynamipsPropertiesV8, + TemplateType.iou: IouPropertiesV8, + TemplateType.docker: DockerPropertiesV8, +} + + class TemplateSetting(BaseModel): """Emulator settings configuration (v8)""" @@ -580,12 +596,34 @@ class TemplateSetting(BaseModel): title='Properties for the template' ) + @model_validator(mode='before') + @classmethod + def _validate_template_properties(cls, data): + """ + Validate template_properties against the model matching template_type. + The template_type discriminator lives at the settings level (not inside + template_properties), so the union cannot be discriminated by pydantic + alone and would misroute properties between the per-type models. + """ + + if isinstance(data, dict): + # work on a copy: replacing template_properties with the validated + # model must not mutate the caller's data + data = data.copy() + template_type = data.get("template_type") + template_properties = data.get("template_properties") + model = _V8_PROPERTIES_MODELS.get(template_type) + if model is not None and isinstance(template_properties, dict): + data["template_properties"] = model.model_validate(template_properties) + return data + class ApplianceVersionV8(BaseModel): """Appliance version definition (v8)""" name: str = Field(..., title='Name of the version') settings: Optional[str] = Field(None, title='Template settings to use to run the version') + idlepc: Optional[str] = Field(None, pattern=r'^0x[0-9a-f]{8}') category: Optional[Category] = Field(None, title='Category of the version') installation_instructions: Optional[str] = Field(None, title='Optional installation instructions for the version') usage: Optional[str] = Field(None, title='Optional instructions about using the version') @@ -625,12 +663,18 @@ class ApplianceV1_6(BaseModel): maintainer_email: Optional[Union[EmailStr, Annotated[str, Field(max_length=0)]]] = Field(None, title='Maintainer email') usage: Optional[str] = Field(None, title='How to use the appliance') symbol: Optional[str] = Field(None, title='An optional symbol for the appliance') + netmiko_device_type: Optional[str] = Field( + None, title='Device type for Netmiko-based automation tools', pattern=r'^[a-z0-9_]+$|^$' + ) first_port_name: Optional[str] = Field(None, title='Optional name of the first networking port example: eth0') port_name_format: Optional[str] = Field(None, title='Optional formating of the networking port example: eth{0}') port_segment_size: Optional[int] = Field( None, title='Optional port segment size. A port segment is a block of port. For example Ethernet0/0 Ethernet0/1 is the module 0 with a port segment size of 2', ) + custom_adapters: Optional[List[CustomAdapterItem]] = Field( + None, title='Optional per-adapter overrides (port name, adapter type, MAC address)' + ) linked_clone: Optional[bool] = Field(None, title="False if you don't want to use a single image for all nodes") docker: Optional[Docker] = Field(None, title='Docker specific options') iou: Optional[Iou] = Field(None, title='IOU specific options') @@ -675,6 +719,9 @@ class ApplianceV8(BaseModel): default_username: Optional[str] = Field(None, title='Default username for the appliance') default_password: Optional[str] = Field(None, title='Default password for the appliance') symbol: Optional[str] = Field(None, title='An optional symbol for the appliance') + netmiko_device_type: Optional[str] = Field( + None, title='Device type for Netmiko-based automation tools', pattern=r'^[a-z0-9_]+$|^$' + ) tags: Optional[List[str]] = Field(None, title='User-defined metadata tags for the appliance') settings: List[TemplateSetting] = Field(..., title='Settings for running the appliance') images: Optional[List[ApplianceImage]] = Field(None, title='Images for this appliance') diff --git a/gns3server/schemas/controller/links.py b/gns3server/schemas/controller/links.py index 258696ea3..fef63f8b1 100644 --- a/gns3server/schemas/controller/links.py +++ b/gns3server/schemas/controller/links.py @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, field_validator from typing import List, Optional, Tuple from enum import Enum from uuid import UUID, uuid4 @@ -62,6 +62,14 @@ class LinkBase(BaseModel): suspend: Optional[bool] = None link_style: Optional[LinkStyle] = None filters: Optional[dict] = None + markers: Optional[dict] = Field( + None, + description="Traffic-insight markers on this link: name → {bpf, tag, enabled}" + ) + show_filters_icon: Optional[bool] = Field( + True, + description="Show filters icon in Web UI" + ) class LinkCreate(LinkBase): @@ -131,3 +139,154 @@ class LinkCapture(BaseModel): data_link_type: str = "DLT_EN10MB" capture_file_name: Optional[str] = None wireshark: bool = False + + +class MarkerCreate(BaseModel): + """ + Body for attaching a traffic-insight marker to a link. + + ``name`` is optional at the controller REST layer (auto-generated when + absent) but always set when the controller forwards to the compute. + """ + + name: Optional[str] = Field( + None, + pattern=r"^[A-Za-z0-9][A-Za-z0-9_.-]*$", + max_length=32, + description='Unique marker name on the link. Auto-generated when absent.', + ) + bpf: str + tag: Optional[int] = None + link_id: Optional[str] = None + color: Optional[str] = Field( + None, + description="User-chosen hex color for this marker in the Web UI, e.g. '#ff5722'", + ) + highlight_duration: Optional[int] = Field( + None, + ge=1, + description=( + "How long (milliseconds) the Web UI keeps this marker highlighted " + "after a match. Omitted = use the UI default. Pure render hint — " + "stored on the link, never sent to uBridge." + ), + ) + enabled: Optional[bool] = Field( + None, + description="Whether the marker is active. Defaults to true on creation.", + ) + direction: Optional[str] = Field( + None, + pattern=r"^(tx|rx|both)$", + description="Direction filter: 'tx' = capture node sending only, 'rx' = capture node receiving only, 'both' or null = both directions.", + ) + capture_node_id: Optional[UUID] = Field( + None, + description=( + "Which endpoint's uBridge hosts this marker (the 'observer'). " + "tx/rx in `direction` are interpreted from this node's perspective. " + "Must be one of the link's two endpoints and a marker-capable type. " + "Omitted = server auto-picks (first started marker-capable endpoint)." + ), + ) + data_link_type: str = Field( + "DLT_EN10MB", + description=( + "pcap link-layer type the marker's BPF compiles against and its " + "capture file is written with (a uBridge `linktype` token). Defaults " + "to DLT_EN10MB (Ethernet), which is omitted from the uBridge command. " + "Only meaningful for serial links: set it to the matching serial DLT " + "from the port's data_link_types — DLT_C_HDLC / DLT_PPP_SERIAL / " + "DLT_FRELAY / DLT_ATM_RFC1483 — so the BPF offsets and pcap decode " + "match the encapsulation configured in IOS. Create-only (changing it " + "would invalidate the pcap)." + ), + ) + + @field_validator("direction", mode="before") + @classmethod + def _both_to_none(cls, v): + return None if v == "both" else v + + +class MarkerUpdate(BaseModel): + """ + Body for updating a marker — partial update, every field optional. + + ``bpf`` is optional here (it is required on create). ``capture_node_id`` and + ``name`` are create-only / path-driven and intentionally absent; an explicit + ``direction: null`` clears the direction back to both (omitting keeps it). + """ + + bpf: Optional[str] = None + tag: Optional[int] = None + direction: Optional[str] = Field( + None, + pattern=r"^(tx|rx|both)$", + description="Direction filter; 'both' or an explicit null clears it to both. Omit to keep.", + ) + color: Optional[str] = Field(None, description="Hex color render hint, e.g. '#ff5722'") + highlight_duration: Optional[int] = Field( + None, ge=1, description="UI highlight duration in ms; null = UI default" + ) + enabled: Optional[bool] = Field(None, description="Toggle the marker on/off (instant).") + + @field_validator("direction", mode="before") + @classmethod + def _both_to_none(cls, v): + return None if v == "both" else v + + +class MarkerDefinitionCreate(BaseModel): + """ + Body for creating / updating a project-level marker definition. + + The definition is a template — when applied to a link the marker name is + prefixed with ``global-`` (e.g. ``arp`` → ``global-arp``) so it can never + collide with a per-link private marker. + """ + + name: Optional[str] = Field( + None, + pattern=r"^[A-Za-z0-9][A-Za-z0-9_.-]*$", + max_length=32, + description="Unique definition name. Auto-generated when absent.", + ) + bpf: str + tag: Optional[int] = None + color: Optional[str] = Field( + None, + description="User-chosen hex color for the marker in the Web UI, e.g. '#ff5722'", + ) + highlight_duration: Optional[int] = Field( + None, + ge=1, + description=( + "How long (milliseconds) the Web UI keeps this marker highlighted " + "after a match. Omitted = use the UI default. Pure render hint — " + "stored with the definition, never sent to uBridge." + ), + ) + direction: Optional[str] = Field( + None, + pattern=r"^(tx|rx|both)$", + description="Direction filter: 'tx' = capture node sending only, 'rx' = capture node receiving only, 'both' or null = both directions.", + ) + data_link_type: str = Field( + "DLT_EN10MB", + description=( + "pcap link-layer type for inherited markers on serial links (uBridge " + "`linktype`). Defaults to DLT_EN10MB (Ethernet): the definition then " + "applies only to Ethernet links and serial links are skipped. Set a " + "serial DLT — DLT_C_HDLC / DLT_PPP_SERIAL / DLT_FRELAY / " + "DLT_ATM_RFC1483 — to also cover serial links with that encapsulation; " + "Ethernet links stay EN10MB regardless. Changing it re-fans-out." + ), + ) + + @field_validator("direction", mode="before") + @classmethod + def _both_to_none(cls, v): + return None if v == "both" else v + + diff --git a/gns3server/schemas/controller/netmiko.py b/gns3server/schemas/controller/netmiko.py new file mode 100644 index 000000000..00eb6e5a4 --- /dev/null +++ b/gns3server/schemas/controller/netmiko.py @@ -0,0 +1,38 @@ +# +# Copyright (C) 2020 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +from pydantic import BaseModel, Field +from typing import List + + +class NetmikoDeviceType(BaseModel): + """ + A Netmiko device type supported by the installed Netmiko library. + """ + + name: str = Field(..., description="Device type name to store in the netmiko_device_type field") + telnet: bool = Field(False, description="Whether the device type connects over Telnet") + custom: bool = Field(False, description="Whether the device type is a GNS3-copilot custom driver (gns3_ prefix)") + + +class NetmikoDeviceTypeList(BaseModel): + """ + List of Netmiko device types supported by the installed Netmiko library. + """ + + netmiko_version: str = Field(..., description="Version of the installed Netmiko library") + device_types: List[NetmikoDeviceType] = Field(..., description="Supported device types, sorted by name") diff --git a/gns3server/schemas/controller/nodes.py b/gns3server/schemas/controller/nodes.py index 840bfdce6..85672ffb8 100644 --- a/gns3server/schemas/controller/nodes.py +++ b/gns3server/schemas/controller/nodes.py @@ -116,6 +116,19 @@ class NodeBase(BaseModel): console_auto_start: Optional[bool] = Field( False, description="Automatically start the console when the node has started" ) + netmiko_device_type: Optional[str] = Field( + None, + description="Device type for Netmiko-based automation tools, overrides the template value", + pattern=r"^[a-z0-9_]+$|^$", + ) + default_username: Optional[str] = Field( + None, + description="Default username to log into the node, seeded from the template appliance metadata", + ) + default_password: Optional[str] = Field( + None, + description="Default password to log into the node, seeded from the template appliance metadata", + ) aux: Optional[int] = Field(None, gt=0, le=65535, description="Auxiliary console TCP port") aux_type: Optional[ConsoleType] = None properties: Optional[dict] = Field(default_factory=dict, description="Properties specific to an emulator") diff --git a/gns3server/schemas/controller/projects.py b/gns3server/schemas/controller/projects.py index f537b122f..ab79a344a 100644 --- a/gns3server/schemas/controller/projects.py +++ b/gns3server/schemas/controller/projects.py @@ -114,7 +114,7 @@ class NodeFile(BaseModel): size: int = Field(..., description="File size in bytes") created_at: str = Field(..., description="File creation time (ISO 8601)") modified_at: str = Field(..., description="File modification time (ISO 8601)") - extension: str = Field(..., description="File extension") + file_type: str = Field(..., description="File type determined by the file command") class ProjectCompression(str, Enum): diff --git a/gns3server/schemas/controller/settings.py b/gns3server/schemas/controller/settings.py new file mode 100644 index 000000000..73bace5bf --- /dev/null +++ b/gns3server/schemas/controller/settings.py @@ -0,0 +1,230 @@ +# +# Copyright (C) 2026 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +""" +Schemas for the server settings endpoints (GET/PUT /v3/settings). + +The VirtualBox and VMware sections are deprecated and intentionally not +exposed. Controller.jwt_secret_key is excluded everywhere: it is loaded +from the secrets directory and overrides whatever the configuration file +says, so exposing or writing it via the API would be useless at best and +a secret leak at worst. +""" + +from typing import List, Optional + +from pydantic import ConfigDict, BaseModel, Field + +from ..config import ( + BuiltinSymbolTheme, + ControllerSettings, + DynamipsSettings, + IOUSettings, + QemuSettings, + ServerProtocol, + ServerSettings, + UbridgeControlTransport, + VPCSSettings, + WebWiresharkSettings, +) + +# matches the pydantic v2 SecretStr serialization mask +SECRET_MASK = "**********" + + +class ServerSettingsResponse(ServerSettings): + + # plain strings instead of FilePath/DirectoryPath: paths are validated when + # the settings are loaded or updated, not when echoed back to the client + secrets_dir: Optional[str] = Field( + None, description="Directory where secrets are stored (e.g. the JWT secret key)") + certfile: Optional[str] = Field(None, description="SSL certificate file, requires enable_ssl") + certkey: Optional[str] = Field(None, description="SSL key file, requires enable_ssl") + # Optional overrides: typed as plain "str = None" in the config schema, + # which fails re-validation when the value actually is None + resources_path: Optional[str] = Field( + None, + description="Path where files like built-in appliances and Docker resources are stored " + "(defaults to the local user data directory)") + default_nat_interface: Optional[str] = Field( + None, description="Interface used by the NAT node, default is virbr0 on Linux (requires libvirt)") + + +class ControllerSettingsResponse(ControllerSettings): + + # never serialized: managed via the secrets directory, not the configuration file + jwt_secret_key: Optional[str] = Field( + default=None, exclude=True, + description="Secret key used to sign the JWT authentication tokens " + "(normally managed via the secrets directory, not the configuration file)") + + +class IOUSettingsResponse(IOUSettings): + + iourc_path: Optional[str] = Field( + None, description="Path of your .iourc file, the file is searched in $HOME/.iourc if not provided") + + +class SettingsResponse(BaseModel): + + Server: ServerSettingsResponse + Controller: ControllerSettingsResponse + VPCS: VPCSSettings + Dynamips: DynamipsSettings + IOU: IOUSettingsResponse + Qemu: QemuSettings + WebWireshark: WebWiresharkSettings + + +class ServerSettingsUpdate(BaseModel): + """ + Every field optional: JSON null removes the option from the configuration + file (restoring its default), missing fields are left untouched. + """ + + model_config = ConfigDict(extra="forbid", str_strip_whitespace=True) + + local: Optional[bool] = None + enable_http_auth: Optional[bool] = None + name: Optional[str] = None + protocol: Optional[ServerProtocol] = None + host: Optional[str] = None + port: Optional[int] = Field(None, gt=0, le=65535) + secrets_dir: Optional[str] = None + certfile: Optional[str] = None + certkey: Optional[str] = None + enable_ssl: Optional[bool] = None + images_path: Optional[str] = None + projects_path: Optional[str] = None + appliances_path: Optional[str] = None + symbols_path: Optional[str] = None + configs_path: Optional[str] = None + resources_path: Optional[str] = None + default_symbol_theme: Optional[BuiltinSymbolTheme] = None + allow_raw_images: Optional[bool] = None + auto_discover_images: Optional[bool] = None + report_errors: Optional[bool] = None + additional_images_paths: Optional[List[str]] = None + console_start_port_range: Optional[int] = Field(None, gt=0, le=65535) + console_end_port_range: Optional[int] = Field(None, gt=0, le=65535) + vnc_console_start_port_range: Optional[int] = Field(None, ge=5900, le=65535) + vnc_console_end_port_range: Optional[int] = Field(None, ge=5900, le=65535) + udp_start_port_range: Optional[int] = Field(None, gt=0, le=65535) + udp_end_port_range: Optional[int] = Field(None, gt=0, le=65535) + ubridge_path: Optional[str] = None + ubridge_control_transport: Optional[UbridgeControlTransport] = None + marker_listen_host: Optional[str] = None + marker_listen_port: Optional[int] = Field(None, ge=0, le=65535) + compute_username: Optional[str] = None + # plain str so the route can compare against SECRET_MASK / empty string + compute_password: Optional[str] = None + allowed_interfaces: Optional[List[str]] = None + default_nat_interface: Optional[str] = None + allow_remote_console: Optional[bool] = None + enable_builtin_templates: Optional[bool] = None + install_builtin_appliances: Optional[bool] = None + skills_repo_url: Optional[str] = None + skills_repo_branch: Optional[str] = None + skills_auto_update: Optional[bool] = None + mcp_enable_dns_rebinding_protection: Optional[bool] = None + mcp_allowed_hosts: Optional[List[str]] = None + mcp_allowed_origins: Optional[List[str]] = None + + +class ControllerSettingsUpdate(BaseModel): + """ + No jwt_secret_key field on purpose (see module docstring). + """ + + model_config = ConfigDict(extra="forbid", str_strip_whitespace=True) + + jwt_algorithm: Optional[str] = None + jwt_access_token_expire_minutes: Optional[int] = None + jwt_refresh_token_expire_minutes: Optional[int] = None + default_admin_username: Optional[str] = None + default_admin_password: Optional[str] = None + + +class VPCSSettingsUpdate(BaseModel): + + model_config = ConfigDict(extra="forbid", str_strip_whitespace=True) + + vpcs_path: Optional[str] = None + + +class DynamipsSettingsUpdate(BaseModel): + + model_config = ConfigDict(extra="forbid", str_strip_whitespace=True) + + allocate_aux_console_ports: Optional[bool] = None + mmap_support: Optional[bool] = None + dynamips_path: Optional[str] = None + sparse_memory_support: Optional[bool] = None + ghost_ios_support: Optional[bool] = None + + +class IOUSettingsUpdate(BaseModel): + + model_config = ConfigDict(extra="forbid", str_strip_whitespace=True) + + iourc_path: Optional[str] = None + license_check: Optional[bool] = None + + +class QemuSettingsUpdate(BaseModel): + + model_config = ConfigDict(extra="forbid", str_strip_whitespace=True) + + enable_monitor: Optional[bool] = None + monitor_host: Optional[str] = None + enable_hardware_acceleration: Optional[bool] = None + require_hardware_acceleration: Optional[bool] = None + allow_unsafe_options: Optional[bool] = None + ovmf_firmware_dir: Optional[str] = None + + +class WebWiresharkSettingsUpdate(BaseModel): + + model_config = ConfigDict(extra="forbid", str_strip_whitespace=True) + + enabled: Optional[bool] = None + image: Optional[str] = None + network_subnet: Optional[str] = None + memory: Optional[str] = None + cpus: Optional[float] = None + pids_limit: Optional[int] = None + + +class SettingsUpdate(BaseModel): + + model_config = ConfigDict(extra="forbid") + + Server: Optional[ServerSettingsUpdate] = None + Controller: Optional[ControllerSettingsUpdate] = None + VPCS: Optional[VPCSSettingsUpdate] = None + Dynamips: Optional[DynamipsSettingsUpdate] = None + IOU: Optional[IOUSettingsUpdate] = None + Qemu: Optional[QemuSettingsUpdate] = None + WebWireshark: Optional[WebWiresharkSettingsUpdate] = None + + +class SettingsUpdateResponse(SettingsResponse): + + restart_required: List[str] = Field( + default_factory=list, + description="Changed 'Section.option' settings that require a server restart to take effect" + ) diff --git a/gns3server/schemas/controller/templates/__init__.py b/gns3server/schemas/controller/templates/__init__.py index dee74b7b9..3c75c6b1f 100644 --- a/gns3server/schemas/controller/templates/__init__.py +++ b/gns3server/schemas/controller/templates/__init__.py @@ -34,6 +34,34 @@ class Category(str, Enum): firewall = "firewall" +class ApplianceMetadata(BaseModel): + """ + Metadata kept on a template installed from an appliance: vendor + information, default credentials and other fields that describe + the appliance but are not node properties. + """ + + model_config = ConfigDict(extra="allow") + + appliance_id: Optional[str] = Field( + None, description="ID of the appliance the template was installed from" + ) + description: Optional[str] = None + vendor_name: Optional[str] = None + vendor_url: Optional[str] = None + vendor_logo_url: Optional[str] = None + documentation_url: Optional[str] = None + product_name: Optional[str] = None + product_url: Optional[str] = None + status: Optional[str] = None + availability: Optional[str] = None + maintainer: Optional[str] = None + maintainer_email: Optional[str] = None + installation_instructions: Optional[str] = None + default_username: Optional[str] = None + default_password: Optional[str] = None + + class TemplateBase(BaseModel): """ Common template properties. @@ -48,10 +76,19 @@ class TemplateBase(BaseModel): template_type: Optional[NodeType] = None compute_id: Optional[str] = None usage: Optional[str] = "" + netmiko_device_type: Optional[str] = Field( + None, + description="Device type for Netmiko-based automation tools (e.g. 'cisco_xr' or 'nokia_srl')", + pattern=r"^[a-z0-9_]+$|^$", + ) tags: Optional[List[str]] = Field( default_factory=list, description="User-defined metadata tags (e.g. 'vendor:cisco' or 'model:7200')" ) + appliance_metadata: Optional[ApplianceMetadata] = Field( + None, + description="Metadata inherited from the appliance the template was installed from" + ) class TemplateCreate(TemplateBase): diff --git a/gns3server/schemas/controller/templates/docker_templates.py b/gns3server/schemas/controller/templates/docker_templates.py index 5eb10d7f5..3d7688cbb 100644 --- a/gns3server/schemas/controller/templates/docker_templates.py +++ b/gns3server/schemas/controller/templates/docker_templates.py @@ -16,7 +16,7 @@ from . import Category, TemplateBase -from ...common import ConsoleType, AuxType, CustomAdapter +from ...common import ConsoleType, AuxType, CustomAdapter, ExtraConfig from pydantic import Field from typing import Optional, List @@ -49,6 +49,7 @@ class DockerTemplate(TemplateBase): ) extra_hosts: Optional[str] = Field("", description="Docker extra hosts (added to /etc/hosts)") extra_volumes: Optional[List] = Field([], description="Additional directories to make persistent") + extra_configs: Optional[List[ExtraConfig]] = Field(default_factory=list, description="Configuration files injected into the container (bind-mounted read-only)") memory: Optional[int] = Field(0, ge=0, description="Maximum amount of memory the container can use in MB") cpus: Optional[float] = Field(0, ge=0, description="Maximum amount of CPU resources the container can use") custom_adapters: Optional[List[CustomAdapter]] = Field(default_factory=list, description="Custom adapters") diff --git a/gns3server/schemas/controller/templates/iou_templates.py b/gns3server/schemas/controller/templates/iou_templates.py index 6dd83d14b..c0a8d398b 100644 --- a/gns3server/schemas/controller/templates/iou_templates.py +++ b/gns3server/schemas/controller/templates/iou_templates.py @@ -35,7 +35,10 @@ class IOUTemplate(TemplateBase): use_default_iou_values: Optional[bool] = Field(False, description="Use default IOU values") startup_config: Optional[str] = Field("iou_l3_base_startup-config.txt", description="Startup-config of IOU") private_config: Optional[str] = Field("", description="Private-config of IOU") - l1_keepalives: Optional[bool] = Field(False, description="Always keep up Ethernet interface (does not always work)") + l1_keepalives: Optional[bool] = Field( + False, + description="Enable Layer 1 keepalives so IOU interfaces report accurate link state", + ) console_type: Optional[ConsoleType] = Field(ConsoleType.telnet, description="Console type") console_auto_start: Optional[bool] = Field( False, description="Automatically start the console when the node has started" diff --git a/gns3server/schemas/controller/tokens.py b/gns3server/schemas/controller/tokens.py index 86c1a9377..61945e257 100644 --- a/gns3server/schemas/controller/tokens.py +++ b/gns3server/schemas/controller/tokens.py @@ -22,9 +22,23 @@ class Token(BaseModel): access_token: str token_type: str + refresh_token: Optional[str] = None class TokenData(BaseModel): username: Optional[str] = None token_version: int = 0 + token_use: str = "access" + + +class RefreshTokenRequest(BaseModel): + """Schema for requesting a token refresh.""" + + refresh_token: str + + +class ApiKeyCreate(BaseModel): + """Schema for creating a new API key.""" + + name: str diff --git a/gns3server/services/__init__.py b/gns3server/services/__init__.py index f9be542e2..572a235a2 100644 --- a/gns3server/services/__init__.py +++ b/gns3server/services/__init__.py @@ -15,5 +15,7 @@ # along with this program. If not, see . from .authentication import AuthService +from .access_tickets import AccessTicketService auth_service = AuthService() +access_ticket_service = AccessTicketService() diff --git a/gns3server/services/access_tickets.py b/gns3server/services/access_tickets.py new file mode 100644 index 000000000..287e4df5c --- /dev/null +++ b/gns3server/services/access_tickets.py @@ -0,0 +1,149 @@ +# +# Copyright (C) 2026 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Short-lived access tickets. + +An access ticket is a short random string ("gns3t_" + 16 urlsafe chars) that +replaces the long JWTs previously embedded in URLs and curl commands returned +to LLM clients, which reliably corrupted the ~200-char JWT when retyping it +into a shell command. The ticket carries no information itself — the server +remembers what it maps to, which is what makes a 96-bit random string a +sufficient credential. + +A ticket is bound to exactly one target, in one of two modes: + +- node binding (project_id + node_id): redeemable on that node's console + WebSocket endpoints (console/ws and console/vnc share the binding), + validated against the route's path parameters. +- path binding (exact resource path): redeemable on one REST resource path + (e.g. a capture file download or a symbol image), validated against + request.url.path. + +Tickets are multi-use within their TTL, so clients that reconnect keep +working. Redemption re-checks the minting user's token_version, so logging +out invalidates outstanding tickets just like it invalidates JWTs. +""" + +import logging +import secrets +import time +from dataclasses import dataclass +from typing import Optional + +log = logging.getLogger(__name__) + +# Distinguishes tickets from JWTs (which contain dots) and API keys ("gns3_") +TICKET_PREFIX = "gns3t_" +DEFAULT_TICKET_TTL = 600 # seconds + + +@dataclass +class AccessTicket: + username: str + token_version: int + project_id: Optional[str] = None # node binding: console WebSocket endpoints + node_id: Optional[str] = None + path: Optional[str] = None # path binding: one exact REST resource path + expires_at: float = 0.0 # time.monotonic() based + + +class AccessTicketService: + """ + In-memory store for access tickets. + + Single-process asyncio app: minting (MCP tool handlers, via worker + threads) and redemption (auth dependencies, event loop) share one dict. + dict get/set/del are atomic under the GIL and keys are random, so no + locking is needed. Tickets vanish on restart — clients just request a + new one. + """ + + def __init__(self) -> None: + self._tickets: dict[str, AccessTicket] = {} + + def mint( + self, + username: str, + token_version: int, + project_id: Optional[str] = None, + node_id: Optional[str] = None, + path: Optional[str] = None, + ttl: int = DEFAULT_TICKET_TTL, + ) -> str: + # 12 random bytes → 16 urlsafe chars (~96 bits); comfortably + # unguessable within the 10-minute window and short enough that + # LLM clients copy it without corruption. + ticket = TICKET_PREFIX + secrets.token_urlsafe(12) + self._sweep_expired() + self._tickets[ticket] = AccessTicket( + username=username, + token_version=token_version, + project_id=project_id, + node_id=node_id, + path=path, + expires_at=time.monotonic() + ttl, + ) + return ticket + + def redeem(self, ticket: str, path_params: dict) -> Optional[AccessTicket]: + """ + Validate a node-bound ticket against a WebSocket route. + + Returns the ticket record if valid, None otherwise. The route's path + parameters must match the ticket's binding, which confines a ticket + to the node's console endpoints — routes without a node_id path + parameter (notifications, web wireshark, …) always fail the binding, + and so do path-bound (REST) tickets. + """ + + record = self._get_valid(ticket) + if record is None: + return None + if record.node_id is None or record.path is not None: + return None + if path_params.get("project_id") != record.project_id or path_params.get("node_id") != record.node_id: + return None + return record + + def redeem_for_path(self, ticket: str, path: str) -> Optional[AccessTicket]: + """ + Validate a path-bound ticket against a REST resource path. + + The path must match exactly — a ticket minted for one capture file + download cannot be replayed against any other resource. + """ + + record = self._get_valid(ticket) + if record is None: + return None + if record.path is None or record.path != path: + return None + return record + + def _get_valid(self, ticket: str) -> Optional[AccessTicket]: + record = self._tickets.get(ticket) + if record is None: + return None + if time.monotonic() >= record.expires_at: + self._tickets.pop(ticket, None) + return None + return record + + def _sweep_expired(self) -> None: + now = time.monotonic() + for ticket in [t for t, record in self._tickets.items() if now >= record.expires_at]: + del self._tickets[ticket] diff --git a/gns3server/services/authentication.py b/gns3server/services/authentication.py index 9b9c6ffa7..90f943b99 100644 --- a/gns3server/services/authentication.py +++ b/gns3server/services/authentication.py @@ -16,7 +16,10 @@ from joserfc import jwt from joserfc.jwk import OctKey -from joserfc.errors import JoseError +from joserfc.errors import JoseError, BadSignatureError +import base64 +import json +import time from datetime import datetime, timedelta, timezone import bcrypt @@ -33,6 +36,17 @@ log = logging.getLogger(__name__) DEFAULT_JWT_SECRET_KEY = "efd08eccec3bd0a1be2e086670e5efa90969c68d07e072d7354a76cea5e33d4e" +def _extract_alg(token: str) -> str: + """Best-effort extraction of the unverified JWT header "alg" value — for logging only.""" + + try: + header_segment = token.split(".", 1)[0] + header = json.loads(base64.urlsafe_b64decode(header_segment + "=" * (-len(header_segment) % 4))) + return str(header.get("alg", "")) + except Exception: + return "" + + class AuthService: def hash_password(self, password: str) -> str: @@ -45,12 +59,11 @@ class AuthService: return bcrypt.checkpw(password=password.encode('utf-8'), hashed_password=hashed_password.encode('utf-8')) - def create_access_token(self, username, token_version: int = 0, secret_key: str = None, expires_in: int = 0) -> str: + def _create_token(self, username, token_version, token_type, expires_in, secret_key=None) -> str: + """Shared helper to create any kind of signed JWT token.""" - if not expires_in: - expires_in = Config.instance().settings.Controller.jwt_access_token_expire_minutes expire = datetime.now(timezone.utc) + timedelta(minutes=expires_in) - to_encode = {"sub": username, "exp": expire, "ver": token_version} + to_encode = {"sub": username, "exp": expire, "ver": token_version, "type": token_type} if secret_key is None: secret_key = Config.instance().settings.Controller.jwt_secret_key if secret_key is None: @@ -61,29 +74,52 @@ class AuthService: encoded_jwt = jwt.encode({"alg": algorithm}, to_encode, key) return encoded_jwt + def create_access_token(self, username, token_version: int = 0, secret_key: str = None, expires_in: int = 0) -> str: + + if not expires_in: + expires_in = Config.instance().settings.Controller.jwt_access_token_expire_minutes + return self._create_token(username, token_version, "access", expires_in, secret_key) + + def create_refresh_token(self, username, token_version: int = 0, secret_key: str = None, expires_in: int = 0) -> str: + + if not expires_in: + expires_in = Config.instance().settings.Controller.jwt_refresh_token_expire_minutes + return self._create_token(username, token_version, "refresh", expires_in, secret_key) + def get_token_data(self, token: str, secret_key: str = None) -> TokenData: - credentials_exception = HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Could not validate credentials", - headers={"WWW-Authenticate": "Bearer"}, - ) + def auth_error(detail: str) -> HTTPException: + return HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail=detail, + headers={"WWW-Authenticate": "Bearer"}, + ) + + if secret_key is None: + secret_key = Config.instance().settings.Controller.jwt_secret_key + if secret_key is None: + secret_key = DEFAULT_JWT_SECRET_KEY + log.error("A JWT secret key must be configured to secure the server, using an unsecured default key!") + algorithm = Config.instance().settings.Controller.jwt_algorithm + key = OctKey.import_key(secret_key) try: - if secret_key is None: - secret_key = Config.instance().settings.Controller.jwt_secret_key - if secret_key is None: - secret_key = DEFAULT_JWT_SECRET_KEY - log.error("A JWT secret key must be configured to secure the server, using an unsecured default key!") - algorithm = Config.instance().settings.Controller.jwt_algorithm - key = OctKey.import_key(secret_key) payload = jwt.decode(token, key, algorithms=[algorithm]) username: str = payload.claims.get("sub") if username is None: - raise credentials_exception + raise auth_error("Invalid token: missing subject claim") + # Validate the exp claim — joserfc does not validate time-based claims by default + token_exp: int = payload.claims.get("exp", 0) + if token_exp and time.time() > token_exp: + raise auth_error("Token has expired") token_version: int = payload.claims.get("ver", 0) - token_data = TokenData(username=username, token_version=token_version) - except (JoseError, ValidationError, ValueError): - raise credentials_exception + token_use: str = payload.claims.get("type", "access") + token_data = TokenData(username=username, token_version=token_version, token_use=token_use) + except BadSignatureError as e: + log.warning("JWT rejected: bad signature (header alg: '%s', error: %s)", _extract_alg(token), e) + raise auth_error("Invalid token signature") + except (JoseError, ValidationError, ValueError) as e: + log.warning("JWT rejected: %s: %s (header alg: '%s')", type(e).__name__, e, _extract_alg(token)) + raise auth_error(f"Invalid token ({type(e).__name__})") return token_data def get_username_from_token(self, token: str, secret_key: str = None) -> Optional[str]: diff --git a/gns3server/services/computes.py b/gns3server/services/computes.py index 995cb7698..a97c8fcf4 100644 --- a/gns3server/services/computes.py +++ b/gns3server/services/computes.py @@ -54,8 +54,12 @@ class ComputesService: self._controller.notification.controller_emit("compute.created", compute.asdict()) return db_compute - async def get_compute(self, compute_id: Union[str, UUID]) -> models.Compute: + async def get_compute(self, compute_id: Union[str, UUID]) -> Union[models.Compute, dict]: + if str(compute_id) == "local": + # the built-in local compute only lives in the controller, not in the database; + # drop unset fields (e.g. user) as the response schema types them as str + return {k: v for k, v in self._controller.get_compute("local").asdict().items() if v is not None} db_compute = await self._computes_repo.get_compute(compute_id) if not db_compute: raise ControllerNotFoundError(f"Compute '{compute_id}' not found") diff --git a/gns3server/services/templates.py b/gns3server/services/templates.py index 92b78633e..c079608dd 100644 --- a/gns3server/services/templates.py +++ b/gns3server/services/templates.py @@ -33,6 +33,7 @@ from gns3server.controller.controller_error import ( ControllerForbiddenError, ) + TEMPLATE_TYPE_TO_SCHEMA = { "cloud": schemas.CloudTemplate, "ethernet_hub": schemas.EthernetHubTemplate, @@ -174,6 +175,9 @@ class TemplatesService: if builtin_template["template_id"] == template_id: return jsonable_encoder(builtin_template) + def _base_path(self): + return self._templates_repo.configs_path() + async def get_templates(self) -> List[dict]: templates = [] @@ -259,6 +263,7 @@ class TemplatesService: async def get_template(self, template_id: UUID) -> dict: db_template = await self._templates_repo.get_template(template_id) + if db_template: template = db_template.asjson() else: @@ -267,9 +272,13 @@ class TemplatesService: raise ControllerNotFoundError(f"Template '{template_id}' not found") return template - async def _remove_image(self, template_id: UUID, image_path:str) -> None: + async def _remove_image(self, template_id: UUID, image_path: str) -> None: + if not image_path: + return image = await self._templates_repo.get_image(image_path) + if image is None: + return await self._templates_repo.remove_image_from_template(template_id, image) async def update_template(self, template_id: UUID, template_update: schemas.TemplateUpdate) -> dict: @@ -336,3 +345,45 @@ class TemplatesService: self._controller.notification.controller_emit("template.deleted", {"template_id": str(template_id)}) else: raise ControllerNotFoundError(f"Template '{template_id}' not found") + + def _template_path(self, template_id: str) -> str: + return os.path.join(self._base_path(), str(template_id)) + + def list_files(self, template_id: str): + path = self._template_path(template_id) + + if not os.path.exists(path): + return [] + + return [ + {"filename": f} + for f in sorted(os.listdir(path)) + if os.path.isfile(os.path.join(path, f)) + ] + + def get_file(self, template_id: str, filename: str): + safe_filename = os.path.basename(filename) + path = os.path.join(self._template_path(template_id), safe_filename) + + if not os.path.isfile(path): + raise ControllerNotFoundError(f"File '{safe_filename}' not found") + + try: + with open(path, encoding="utf-8", errors="ignore") as f: + return f.read() + except OSError as e: + raise ControllerError(str(e)) + + def update_file(self, template_id: str, filename: str, content: str): + safe_filename = os.path.basename(filename) + + dir_path = self._template_path(template_id) + path = os.path.join(dir_path, safe_filename) + + os.makedirs(dir_path, exist_ok=True) + + try: + with open(path, "w", encoding="utf-8") as f: + f.write(content) + except OSError as e: + raise ControllerError(str(e)) \ No newline at end of file diff --git a/gns3server/static/web-ui/chunk-OEFNHQTH.js b/gns3server/static/web-ui/chunk-3PDLDM3Z.js similarity index 97% rename from gns3server/static/web-ui/chunk-OEFNHQTH.js rename to gns3server/static/web-ui/chunk-3PDLDM3Z.js index c79b7341c..2c11c1d96 100644 --- a/gns3server/static/web-ui/chunk-OEFNHQTH.js +++ b/gns3server/static/web-ui/chunk-3PDLDM3Z.js @@ -1 +1 @@ -import{b as V,d as A,e as z,l as $,m as R,p as L,q as G,r as W,s as Y,t as X,u as U,v as q,x as H}from"./chunk-KS2DZGNZ.js";import{Cc as O,Gb as _,Ib as f,Kb as s,Pa as l,Wb as v,Wc as F,Yb as a,Zb as x,_ as w,_a as M,a as b,bc as k,cc as T,dc as D,hf as B,ia as C,if as N,j as S,ma as p,na as u,ob as j,pb as m,rb as d,ub as E,vb as I,wb as h,xb as e,ya as g,yb as n,zb as P}from"./chunk-LG2N72QL.js";function J(o,c){if(o&1){let t=_();e(0,"div",3)(1,"mat-icon",6),a(2,"warning"),n(),e(3,"p",7),a(4,"Ready to inject a network fault?"),n(),e(5,"div",8)(6,"p",9),a(7,"Number of faults to inject"),n(),e(8,"mat-button-toggle-group",10),D("valueChange",function(i){p(t);let y=s();return T(y.faultType,i)||(y.faultType=i),u(i)}),e(9,"mat-button-toggle",11),a(10,"1"),n(),e(11,"mat-button-toggle",11),a(12,"2"),n(),e(13,"mat-button-toggle",11),a(14,"3"),n(),e(15,"mat-button-toggle",11),a(16,"Random"),n()()(),e(17,"p",12),a(18," This will inject a simulated network fault into your topology for troubleshooting practice. Make sure you have saved your current work. "),n(),e(19,"p",12),a(20," The fault injection process will analyze your topology, select an appropriate fault, and apply it automatically. You'll be able to see the details in AI Chat. "),n()()}if(o&2){let t=s();l(8),k("value",t.faultType),l(),h("value",1),l(2),h("value",2),l(2),h("value",3),l(2),h("value","random")}}function K(o,c){if(o&1&&(e(0,"p",17),a(1),n(),e(2,"p",18),a(3,"Agent is working"),n()),o&2){let t=s(2);l(),x(t.currentStep()||"Injecting fault...")}}function Q(o,c){if(o&1&&(e(0,"div",19)(1,"mat-icon",20),a(2),n(),e(3,"div",21)(4,"p",22),a(5),n(),e(6,"p",23),a(7,"Check the AI Chat panel for detailed execution history and tool results."),n()()()),o&2){let t=s(2);v("success",t.completionStatus()==="success")("error",t.completionStatus()==="error")("aborted",t.completionStatus()==="aborted"),l(2),x(t.completionStatus()==="success"?"check_circle":t.completionStatus()==="aborted"?"cancel":"error"),l(3),x(t.completionTitle())}}function Z(o,c){if(o&1&&(e(0,"div",26)(1,"mat-icon"),a(2),n(),e(3,"div",27)(4,"p",28),a(5),n()()()),o&2){let t=c.$implicit,r=s(3);v("success",t.type==="success")("error",t.type==="error"),l(2),x(r.getEventIcon(t.type)),l(3),x(t.message)}}function tt(o,c){if(o&1&&(e(0,"div",16)(1,"h3"),a(2,"Progress"),n(),e(3,"div",24),E(4,Z,6,6,"div",25,j().trackByEventId,!0),n()()),o&2){let t=s(2);l(4),I(t.displayedEvents())}}function et(o,c){if(o&1&&(e(0,"div",13),P(1,"img",14),n(),m(2,K,4,1),m(3,Q,8,8,"div",15),m(4,tt,6,0,"div",16)),o&2){let t=s();v("injecting",t.isInjecting()),l(2),d(t.isInjecting()?2:-1),l(),d(t.completed()&&!t.isInjecting()?3:-1),l(),d(t.isInjecting()&&t.displayedEvents().length>0?4:-1)}}function nt(o,c){if(o&1){let t=_();e(0,"button",29),f("click",function(){p(t);let i=s();return u(i.onCancelConfirm())}),a(1,"Cancel"),n(),e(2,"button",30),f("click",function(){p(t);let i=s();return u(i.onConfirmInject())}),e(3,"mat-icon"),a(4,"bug_report"),n(),e(5,"span"),a(6,"Confirm & Inject"),n()()}}function it(o,c){if(o&1){let t=_();e(0,"button",29),f("click",function(){p(t);let i=s();return u(i.onAbort())}),a(1,"Abort"),n()}}function ot(o,c){if(o&1){let t=_();e(0,"button",29),f("click",function(){p(t);let i=s();return u(i.onCancel())}),a(1,"Cancel"),n(),e(2,"button",30),f("click",function(){p(t);let i=s();return u(i.onInject())}),e(3,"mat-icon"),a(4,"bug_report"),n(),e(5,"span"),a(6,"Inject Fault"),n()()}}function at(o,c){if(o&1){let t=_();e(0,"button",29),f("click",function(){p(t);let i=s();return u(i.onViewDetails())}),e(1,"mat-icon"),a(2,"chat"),n(),e(3,"span"),a(4,"View in AI Chat"),n()(),e(5,"button",31),f("click",function(){p(t);let i=s();return u(i.onClose())}),e(6,"mat-icon"),a(7,"check"),n(),e(8,"span"),a(9,"Done"),n()()}}var bt=(()=>{class o{dialogRef=C($);data=C(R);aiChatService=C(H);controller=this.data.controller;project=this.data.project;isInjecting=g(!1);completed=g(!1);showConfirm=g(!1);faultType=O(1);faceState=g("idle");completionStatus=g("success");completionTitle=g("");currentStep=g("");eventsBuffer=[];displayedEvents=g([]);firstToolCallProcessed=!1;destroy$=new S;static MAX_DISPLAYED_EVENTS=3;ngOnInit(){this.faceState.set("idle")}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}onInject(){this.showConfirm.set(!0)}onCancelConfirm(){this.showConfirm.set(!1)}onConfirmInject(){if(this.isInjecting())return;this.showConfirm.set(!1),this.isInjecting.set(!0),this.completed.set(!1),this.faceState.set("injecting"),this.eventsBuffer=[],this.displayedEvents.set([]),this.firstToolCallProcessed=!1;let r=`Inject ${this.faultType()==="random"?"random":String(this.faultType())} network fault(s) for troubleshooting practice`;this.aiChatService.injectFault(this.controller,this.project.project_id,r).pipe(w(this.destroy$)).subscribe({next:i=>{this.handleFaultEvent(i)},error:i=>{this.handleError(i)},complete:()=>{}})}handleFaultEvent(t){if(console.log("Fault injection event:",t),!(t.type==="heartbeat"||t.type==="content"))switch(t.type){case"tool_call":if(t.tool_call&&!this.firstToolCallProcessed){let r=t.tool_call.function.name;this.currentStep.set(`Preparing: ${r}`),this.addEvent({type:"tool_call",message:`Preparing: ${r}`}),this.firstToolCallProcessed=!0}break;case"tool_start":t.tool_name&&(this.currentStep.set(`Executing: ${t.tool_name}`),this.addEvent({type:"info",message:`Executing: ${t.tool_name}`}));break;case"tool_end":t.tool_name&&this.addEvent({type:"success",message:`Completed: ${t.tool_name}`});break;case"error":this.handleError(t);break;case"done":this.isInjecting.set(!1),this.completed.set(!0),this.faceState.set("success"),this.completionStatus.set("success"),this.completionTitle.set("Fault injected successfully!"),this.currentStep.set("");break}}handleError(t){console.error("Fault injection error:",t);let r=t?.error?.message||t?.message||t?.error||"Failed to inject fault";this.addEvent({type:"error",message:"Error injecting fault",details:r}),this.isInjecting.set(!1),this.completed.set(!0),this.faceState.set("error"),this.completionStatus.set("error"),this.completionTitle.set("Failed to inject fault"),this.currentStep.set("")}addEvent(t){let r=b({id:`event_${Date.now()}_${Math.random().toString(36).substring(2,11)}`,timestamp:new Date().toISOString()},t);this.eventsBuffer.push(r),this.eventsBuffer.length>o.MAX_DISPLAYED_EVENTS&&this.eventsBuffer.shift(),this.displayedEvents.set([...this.eventsBuffer])}getEventIcon(t){switch(t){case"info":return"info";case"tool_call":return"build";case"success":return"check_circle";case"error":return"error";default:return"info"}}onAbort(){this.isInjecting.set(!1),this.completed.set(!0),this.faceState.set("aborted"),this.completionStatus.set("aborted"),this.completionTitle.set("Fault injection aborted"),this.currentStep.set(""),this.destroy$.next(),this.addEvent({type:"info",message:"Fault injection aborted by user"})}onViewDetails(){this.dialogRef.close({success:this.completionStatus()==="success",openAIChat:!0})}onCancel(){this.isInjecting()||this.dialogRef.close(null)}onClose(){this.dialogRef.close({success:this.completionStatus()==="success",events:this.displayedEvents()})}trackByEventId(t,r){return r.id}static \u0275fac=function(r){return new(r||o)};static \u0275cmp=M({type:o,selectors:[["app-fault-injection-dialog"]],inputs:{faultType:[1,"faultType"]},outputs:{faultType:"faultTypeChange"},decls:13,vars:6,consts:[["mat-dialog-title","",1,"fault-injection-title"],[1,"fault-injection-icon"],["mat-dialog-content","",1,"fault-injection-content"],[1,"confirm-panel"],["mat-dialog-actions","","align","end"],["mat-button",""],[1,"confirm-panel__icon"],[1,"confirm-panel__title"],[1,"fault-type-selector"],[1,"fault-type-selector__label"],["hideSingleSelectionIndicator","true",1,"fault-type-selector__group",3,"valueChange","value"],[1,"fault-type-btn",3,"value"],[1,"confirm-panel__desc"],[1,"animation-area"],["src","assets/gns3_icon.svg","alt","GNS3",1,"gns3-logo"],[1,"completion-message",3,"success","error","aborted"],[1,"events-section"],[1,"status-main"],[1,"status-sub"],[1,"completion-message"],[1,"completion-icon"],[1,"completion-content"],[1,"completion-title"],[1,"completion-text"],[1,"events-list"],[1,"event",3,"success","error"],[1,"event"],[1,"event-content"],[1,"event-msg"],["mat-button","",3,"click"],["mat-raised-button","","color","warn",3,"click"],["mat-raised-button","","color","primary",3,"click"]],template:function(r,i){r&1&&(e(0,"h2",0)(1,"mat-icon",1),a(2,"bug_report"),n(),e(3,"span"),a(4,"Fault Injection"),n()(),e(5,"div",2),m(6,J,21,5,"div",3),m(7,et,5,5),n(),e(8,"div",4),m(9,nt,7,0),m(10,it,2,0,"button",5),m(11,ot,7,0),m(12,at,10,0),n()),r&2&&(l(6),d(i.showConfirm()?6:-1),l(),d(!i.showConfirm()||i.isInjecting()||i.completed()?7:-1),l(2),d(i.showConfirm()?9:-1),l(),d(i.isInjecting()?10:-1),l(),d(!i.showConfirm()&&!i.isInjecting()&&!i.completed()?11:-1),l(),d(i.completed()&&!i.isInjecting()?12:-1))},dependencies:[F,Y,L,W,G,N,B,z,A,V,q,X,U],styles:[".fault-injection-title[_ngcontent-%COMP%]{display:flex;align-items:center;gap:12px}.fault-injection-icon[_ngcontent-%COMP%]{font-size:24px;width:24px;height:24px;color:var(--mat-sys-error)}.fault-injection-content[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:24px}.confirm-panel[_ngcontent-%COMP%]{display:flex;flex-direction:column;align-items:center;gap:16px;padding:32px 24px;text-align:center}.confirm-panel__icon[_ngcontent-%COMP%]{font-size:48px;width:48px;height:48px;color:var(--mat-sys-error)}.confirm-panel__title[_ngcontent-%COMP%]{font-size:20px;font-weight:500;margin:0;color:var(--mat-sys-on-surface)}.confirm-panel__desc[_ngcontent-%COMP%]{font-size:14px;margin:0;color:var(--mat-sys-on-surface-variant);line-height:1.6;text-align:left;width:100%}.fault-type-selector[_ngcontent-%COMP%]{width:100%;max-width:360px}.fault-type-selector__label[_ngcontent-%COMP%]{font-size:12px;font-weight:500;margin:0 0 8px;text-align:left;color:var(--mat-sys-on-surface-variant)}.fault-type-selector__group[_ngcontent-%COMP%]{display:flex;gap:6px;width:100%;border:none;border-radius:0}.fault-type-btn[_ngcontent-%COMP%]{flex:1;min-width:0;height:36px;font-size:13px;font-weight:500;border:1px solid var(--mat-sys-outline-variant);border-radius:8px;color:var(--mat-sys-on-surface);display:flex;align-items:center;justify-content:center}.fault-type-btn.mat-button-toggle-checked[_ngcontent-%COMP%]{border-color:var(--mat-sys-primary);background:var(--mat-sys-primary-container);color:var(--mat-sys-on-primary-container);font-weight:600}.animation-area[_ngcontent-%COMP%]{position:relative;display:flex;align-items:center;justify-content:center;padding:32px;background:var(--mat-sys-surface-container-low);border-radius:12px;min-height:200px}.animation-area.injecting[_ngcontent-%COMP%]{background:color-mix(in srgb,var(--mat-sys-error-container) 30%,var(--mat-sys-surface-container-low))}.gns3-logo[_ngcontent-%COMP%]{width:120px;height:120px}.status-main[_ngcontent-%COMP%]{font-size:18px;font-weight:500;margin:0;text-align:center;color:var(--mat-sys-on-surface)}.status-sub[_ngcontent-%COMP%]{font-size:14px;margin:0;text-align:center;color:var(--mat-sys-on-surface-variant)}.completion-message[_ngcontent-%COMP%]{display:flex;gap:16px;padding:20px;background:var(--mat-sys-surface-container-low);border-radius:12px;border-left:4px solid var(--mat-sys-primary)}.completion-message.success[_ngcontent-%COMP%]{border-left-color:var(--mat-sys-primary);background:var(--mat-sys-primary-container-low)}.completion-message.error[_ngcontent-%COMP%]{border-left-color:var(--mat-sys-error);background:var(--mat-sys-error-container-low)}.completion-message.aborted[_ngcontent-%COMP%]{border-left-color:var(--mat-sys-outline);background:var(--mat-sys-surface-container-high)}.completion-icon[_ngcontent-%COMP%]{font-size:32px;width:32px;height:32px;flex-shrink:0;color:var(--mat-sys-primary)}.completion-message.error[_ngcontent-%COMP%] .completion-icon[_ngcontent-%COMP%]{color:var(--mat-sys-error)}.completion-message.aborted[_ngcontent-%COMP%] .completion-icon[_ngcontent-%COMP%]{color:var(--mat-sys-on-surface-variant)}.completion-content[_ngcontent-%COMP%]{flex:1}.completion-title[_ngcontent-%COMP%]{font-size:18px;font-weight:500;margin:0 0 8px;color:var(--mat-sys-on-surface)}.completion-text[_ngcontent-%COMP%]{font-size:14px;margin:0;color:var(--mat-sys-on-surface-variant);line-height:1.5}.events-section[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:12px}.events-section[_ngcontent-%COMP%] h3[_ngcontent-%COMP%]{font-size:16px;font-weight:500;margin:0;color:var(--mat-sys-on-surface)}.events-list[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:8px}.event[_ngcontent-%COMP%]{display:flex;gap:12px;padding:12px 16px;background:var(--mat-sys-surface-container-low);border-radius:8px;border-left:3px solid var(--mat-sys-outline)}.event.success[_ngcontent-%COMP%]{border-left-color:var(--mat-sys-primary);background:var(--mat-sys-primary-container-low)}.event.error[_ngcontent-%COMP%]{border-left-color:var(--mat-sys-error);background:var(--mat-sys-error-container-low)}.event[_ngcontent-%COMP%] mat-icon[_ngcontent-%COMP%]{font-size:20px;width:20px;height:20px;flex-shrink:0;color:var(--mat-sys-on-surface-variant)}.event-content[_ngcontent-%COMP%]{flex:1;min-width:0}.event-msg[_ngcontent-%COMP%]{font-size:14px;font-weight:500;margin:0 0 4px;color:var(--mat-sys-on-surface);word-wrap:break-word}.event-details[_ngcontent-%COMP%]{font-size:12px;margin:0;color:var(--mat-sys-on-surface-variant);word-wrap:break-word}"],changeDetection:0})}return o})();export{bt as FaultInjectionDialogComponent}; +import{b as V,d as A,e as z,l as $,m as R,p as L,q as G,r as W,s as Y,t as X,u as U,v as q,x as H}from"./chunk-ERB75MEN.js";import{$a as d,Ea as l,Eb as v,Gb as a,Hb as x,Lb as k,Ma as M,Mb as T,Nb as D,X as w,Ya as j,Za as m,Ze as B,_e as N,a as b,cb as E,da as C,db as I,eb as h,fb as e,ga as p,gb as n,ha as u,hb as P,i as S,jc as O,ob as _,qa as g,qb as f,sb as s,yc as F}from"./chunk-TYGV4UPE.js";function J(o,c){if(o&1){let t=_();e(0,"div",3)(1,"mat-icon",6),a(2,"warning"),n(),e(3,"p",7),a(4,"Ready to inject a network fault?"),n(),e(5,"div",8)(6,"p",9),a(7,"Number of faults to inject"),n(),e(8,"mat-button-toggle-group",10),D("valueChange",function(i){p(t);let y=s();return T(y.faultType,i)||(y.faultType=i),u(i)}),e(9,"mat-button-toggle",11),a(10,"1"),n(),e(11,"mat-button-toggle",11),a(12,"2"),n(),e(13,"mat-button-toggle",11),a(14,"3"),n(),e(15,"mat-button-toggle",11),a(16,"Random"),n()()(),e(17,"p",12),a(18," This will inject a simulated network fault into your topology for troubleshooting practice. Make sure you have saved your current work. "),n(),e(19,"p",12),a(20," The fault injection process will analyze your topology, select an appropriate fault, and apply it automatically. You'll be able to see the details in AI Chat. "),n()()}if(o&2){let t=s();l(8),k("value",t.faultType),l(),h("value",1),l(2),h("value",2),l(2),h("value",3),l(2),h("value","random")}}function K(o,c){if(o&1&&(e(0,"p",17),a(1),n(),e(2,"p",18),a(3,"Agent is working"),n()),o&2){let t=s(2);l(),x(t.currentStep()||"Injecting fault...")}}function Q(o,c){if(o&1&&(e(0,"div",19)(1,"mat-icon",20),a(2),n(),e(3,"div",21)(4,"p",22),a(5),n(),e(6,"p",23),a(7,"Check the AI Chat panel for detailed execution history and tool results."),n()()()),o&2){let t=s(2);v("success",t.completionStatus()==="success")("error",t.completionStatus()==="error")("aborted",t.completionStatus()==="aborted"),l(2),x(t.completionStatus()==="success"?"check_circle":t.completionStatus()==="aborted"?"cancel":"error"),l(3),x(t.completionTitle())}}function Z(o,c){if(o&1&&(e(0,"div",26)(1,"mat-icon"),a(2),n(),e(3,"div",27)(4,"p",28),a(5),n()()()),o&2){let t=c.$implicit,r=s(3);v("success",t.type==="success")("error",t.type==="error"),l(2),x(r.getEventIcon(t.type)),l(3),x(t.message)}}function tt(o,c){if(o&1&&(e(0,"div",16)(1,"h3"),a(2,"Progress"),n(),e(3,"div",24),E(4,Z,6,6,"div",25,j().trackByEventId,!0),n()()),o&2){let t=s(2);l(4),I(t.displayedEvents())}}function et(o,c){if(o&1&&(e(0,"div",13),P(1,"img",14),n(),m(2,K,4,1),m(3,Q,8,8,"div",15),m(4,tt,6,0,"div",16)),o&2){let t=s();v("injecting",t.isInjecting()),l(2),d(t.isInjecting()?2:-1),l(),d(t.completed()&&!t.isInjecting()?3:-1),l(),d(t.isInjecting()&&t.displayedEvents().length>0?4:-1)}}function nt(o,c){if(o&1){let t=_();e(0,"button",29),f("click",function(){p(t);let i=s();return u(i.onCancelConfirm())}),a(1,"Cancel"),n(),e(2,"button",30),f("click",function(){p(t);let i=s();return u(i.onConfirmInject())}),e(3,"mat-icon"),a(4,"bug_report"),n(),e(5,"span"),a(6,"Confirm & Inject"),n()()}}function it(o,c){if(o&1){let t=_();e(0,"button",29),f("click",function(){p(t);let i=s();return u(i.onAbort())}),a(1,"Abort"),n()}}function ot(o,c){if(o&1){let t=_();e(0,"button",29),f("click",function(){p(t);let i=s();return u(i.onCancel())}),a(1,"Cancel"),n(),e(2,"button",30),f("click",function(){p(t);let i=s();return u(i.onInject())}),e(3,"mat-icon"),a(4,"bug_report"),n(),e(5,"span"),a(6,"Inject Fault"),n()()}}function at(o,c){if(o&1){let t=_();e(0,"button",29),f("click",function(){p(t);let i=s();return u(i.onViewDetails())}),e(1,"mat-icon"),a(2,"chat"),n(),e(3,"span"),a(4,"View in AI Chat"),n()(),e(5,"button",31),f("click",function(){p(t);let i=s();return u(i.onClose())}),e(6,"mat-icon"),a(7,"check"),n(),e(8,"span"),a(9,"Done"),n()()}}var bt=(()=>{class o{dialogRef=C($);data=C(R);aiChatService=C(H);controller=this.data.controller;project=this.data.project;isInjecting=g(!1);completed=g(!1);showConfirm=g(!1);faultType=O(1);faceState=g("idle");completionStatus=g("success");completionTitle=g("");currentStep=g("");eventsBuffer=[];displayedEvents=g([]);firstToolCallProcessed=!1;destroy$=new S;static MAX_DISPLAYED_EVENTS=3;ngOnInit(){this.faceState.set("idle")}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}onInject(){this.showConfirm.set(!0)}onCancelConfirm(){this.showConfirm.set(!1)}onConfirmInject(){if(this.isInjecting())return;this.showConfirm.set(!1),this.isInjecting.set(!0),this.completed.set(!1),this.faceState.set("injecting"),this.eventsBuffer=[],this.displayedEvents.set([]),this.firstToolCallProcessed=!1;let r=`Inject ${this.faultType()==="random"?"random":String(this.faultType())} network fault(s) for troubleshooting practice`;this.aiChatService.injectFault(this.controller,this.project.project_id,r).pipe(w(this.destroy$)).subscribe({next:i=>{this.handleFaultEvent(i)},error:i=>{this.handleError(i)},complete:()=>{}})}handleFaultEvent(t){if(console.log("Fault injection event:",t),!(t.type==="heartbeat"||t.type==="content"))switch(t.type){case"tool_call":if(t.tool_call&&!this.firstToolCallProcessed){let r=t.tool_call.function.name;this.currentStep.set(`Preparing: ${r}`),this.addEvent({type:"tool_call",message:`Preparing: ${r}`}),this.firstToolCallProcessed=!0}break;case"tool_start":t.tool_name&&(this.currentStep.set(`Executing: ${t.tool_name}`),this.addEvent({type:"info",message:`Executing: ${t.tool_name}`}));break;case"tool_end":t.tool_name&&this.addEvent({type:"success",message:`Completed: ${t.tool_name}`});break;case"error":this.handleError(t);break;case"done":this.isInjecting.set(!1),this.completed.set(!0),this.faceState.set("success"),this.completionStatus.set("success"),this.completionTitle.set("Fault injected successfully!"),this.currentStep.set("");break}}handleError(t){console.error("Fault injection error:",t);let r=t?.error?.message||t?.message||t?.error||"Failed to inject fault";this.addEvent({type:"error",message:"Error injecting fault",details:r}),this.isInjecting.set(!1),this.completed.set(!0),this.faceState.set("error"),this.completionStatus.set("error"),this.completionTitle.set("Failed to inject fault"),this.currentStep.set("")}addEvent(t){let r=b({id:`event_${Date.now()}_${Math.random().toString(36).substring(2,11)}`,timestamp:new Date().toISOString()},t);this.eventsBuffer.push(r),this.eventsBuffer.length>o.MAX_DISPLAYED_EVENTS&&this.eventsBuffer.shift(),this.displayedEvents.set([...this.eventsBuffer])}getEventIcon(t){switch(t){case"info":return"info";case"tool_call":return"build";case"success":return"check_circle";case"error":return"error";default:return"info"}}onAbort(){this.isInjecting.set(!1),this.completed.set(!0),this.faceState.set("aborted"),this.completionStatus.set("aborted"),this.completionTitle.set("Fault injection aborted"),this.currentStep.set(""),this.destroy$.next(),this.addEvent({type:"info",message:"Fault injection aborted by user"})}onViewDetails(){this.dialogRef.close({success:this.completionStatus()==="success",openAIChat:!0})}onCancel(){this.isInjecting()||this.dialogRef.close(null)}onClose(){this.dialogRef.close({success:this.completionStatus()==="success",events:this.displayedEvents()})}trackByEventId(t,r){return r.id}static \u0275fac=function(r){return new(r||o)};static \u0275cmp=M({type:o,selectors:[["app-fault-injection-dialog"]],inputs:{faultType:[1,"faultType"]},outputs:{faultType:"faultTypeChange"},decls:13,vars:6,consts:[["mat-dialog-title","",1,"fault-injection-title"],[1,"fault-injection-icon"],["mat-dialog-content","",1,"fault-injection-content"],[1,"confirm-panel"],["mat-dialog-actions","","align","end"],["mat-button",""],[1,"confirm-panel__icon"],[1,"confirm-panel__title"],[1,"fault-type-selector"],[1,"fault-type-selector__label"],["hideSingleSelectionIndicator","true",1,"fault-type-selector__group",3,"valueChange","value"],[1,"fault-type-btn",3,"value"],[1,"confirm-panel__desc"],[1,"animation-area"],["src","assets/gns3_icon.svg","alt","GNS3",1,"gns3-logo"],[1,"completion-message",3,"success","error","aborted"],[1,"events-section"],[1,"status-main"],[1,"status-sub"],[1,"completion-message"],[1,"completion-icon"],[1,"completion-content"],[1,"completion-title"],[1,"completion-text"],[1,"events-list"],[1,"event",3,"success","error"],[1,"event"],[1,"event-content"],[1,"event-msg"],["mat-button","",3,"click"],["mat-raised-button","","color","warn",3,"click"],["mat-raised-button","","color","primary",3,"click"]],template:function(r,i){r&1&&(e(0,"h2",0)(1,"mat-icon",1),a(2,"bug_report"),n(),e(3,"span"),a(4,"Fault Injection"),n()(),e(5,"div",2),m(6,J,21,5,"div",3),m(7,et,5,5),n(),e(8,"div",4),m(9,nt,7,0),m(10,it,2,0,"button",5),m(11,ot,7,0),m(12,at,10,0),n()),r&2&&(l(6),d(i.showConfirm()?6:-1),l(),d(!i.showConfirm()||i.isInjecting()||i.completed()?7:-1),l(2),d(i.showConfirm()?9:-1),l(),d(i.isInjecting()?10:-1),l(),d(!i.showConfirm()&&!i.isInjecting()&&!i.completed()?11:-1),l(),d(i.completed()&&!i.isInjecting()?12:-1))},dependencies:[F,Y,L,W,G,N,B,z,A,V,q,X,U],styles:[".fault-injection-title[_ngcontent-%COMP%]{display:flex;align-items:center;gap:12px}.fault-injection-icon[_ngcontent-%COMP%]{font-size:24px;width:24px;height:24px;color:var(--mat-sys-error)}.fault-injection-content[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:24px}.confirm-panel[_ngcontent-%COMP%]{display:flex;flex-direction:column;align-items:center;gap:16px;padding:32px 24px;text-align:center}.confirm-panel__icon[_ngcontent-%COMP%]{font-size:48px;width:48px;height:48px;color:var(--mat-sys-error)}.confirm-panel__title[_ngcontent-%COMP%]{font-size:20px;font-weight:500;margin:0;color:var(--mat-sys-on-surface)}.confirm-panel__desc[_ngcontent-%COMP%]{font-size:14px;margin:0;color:var(--mat-sys-on-surface-variant);line-height:1.6;text-align:left;width:100%}.fault-type-selector[_ngcontent-%COMP%]{width:100%;max-width:360px}.fault-type-selector__label[_ngcontent-%COMP%]{font-size:12px;font-weight:500;margin:0 0 8px;text-align:left;color:var(--mat-sys-on-surface-variant)}.fault-type-selector__group[_ngcontent-%COMP%]{display:flex;gap:6px;width:100%;border:none;border-radius:0}.fault-type-btn[_ngcontent-%COMP%]{flex:1;min-width:0;height:36px;font-size:13px;font-weight:500;border:1px solid var(--mat-sys-outline-variant);border-radius:8px;color:var(--mat-sys-on-surface);display:flex;align-items:center;justify-content:center}.fault-type-btn.mat-button-toggle-checked[_ngcontent-%COMP%]{border-color:var(--mat-sys-primary);background:var(--mat-sys-primary-container);color:var(--mat-sys-on-primary-container);font-weight:600}.animation-area[_ngcontent-%COMP%]{position:relative;display:flex;align-items:center;justify-content:center;padding:32px;background:var(--mat-sys-surface-container-low);border-radius:12px;min-height:200px}.animation-area.injecting[_ngcontent-%COMP%]{background:color-mix(in srgb,var(--mat-sys-error-container) 30%,var(--mat-sys-surface-container-low))}.gns3-logo[_ngcontent-%COMP%]{width:120px;height:120px}.status-main[_ngcontent-%COMP%]{font-size:18px;font-weight:500;margin:0;text-align:center;color:var(--mat-sys-on-surface)}.status-sub[_ngcontent-%COMP%]{font-size:14px;margin:0;text-align:center;color:var(--mat-sys-on-surface-variant)}.completion-message[_ngcontent-%COMP%]{display:flex;gap:16px;padding:20px;background:var(--mat-sys-surface-container-low);border-radius:12px;border-left:4px solid var(--mat-sys-primary)}.completion-message.success[_ngcontent-%COMP%]{border-left-color:var(--mat-sys-primary);background:var(--mat-sys-primary-container-low)}.completion-message.error[_ngcontent-%COMP%]{border-left-color:var(--mat-sys-error);background:var(--mat-sys-error-container-low)}.completion-message.aborted[_ngcontent-%COMP%]{border-left-color:var(--mat-sys-outline);background:var(--mat-sys-surface-container-high)}.completion-icon[_ngcontent-%COMP%]{font-size:32px;width:32px;height:32px;flex-shrink:0;color:var(--mat-sys-primary)}.completion-message.error[_ngcontent-%COMP%] .completion-icon[_ngcontent-%COMP%]{color:var(--mat-sys-error)}.completion-message.aborted[_ngcontent-%COMP%] .completion-icon[_ngcontent-%COMP%]{color:var(--mat-sys-on-surface-variant)}.completion-content[_ngcontent-%COMP%]{flex:1}.completion-title[_ngcontent-%COMP%]{font-size:18px;font-weight:500;margin:0 0 8px;color:var(--mat-sys-on-surface)}.completion-text[_ngcontent-%COMP%]{font-size:14px;margin:0;color:var(--mat-sys-on-surface-variant);line-height:1.5}.events-section[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:12px}.events-section[_ngcontent-%COMP%] h3[_ngcontent-%COMP%]{font-size:16px;font-weight:500;margin:0;color:var(--mat-sys-on-surface)}.events-list[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:8px}.event[_ngcontent-%COMP%]{display:flex;gap:12px;padding:12px 16px;background:var(--mat-sys-surface-container-low);border-radius:8px;border-left:3px solid var(--mat-sys-outline)}.event.success[_ngcontent-%COMP%]{border-left-color:var(--mat-sys-primary);background:var(--mat-sys-primary-container-low)}.event.error[_ngcontent-%COMP%]{border-left-color:var(--mat-sys-error);background:var(--mat-sys-error-container-low)}.event[_ngcontent-%COMP%] mat-icon[_ngcontent-%COMP%]{font-size:20px;width:20px;height:20px;flex-shrink:0;color:var(--mat-sys-on-surface-variant)}.event-content[_ngcontent-%COMP%]{flex:1;min-width:0}.event-msg[_ngcontent-%COMP%]{font-size:14px;font-weight:500;margin:0 0 4px;color:var(--mat-sys-on-surface);word-wrap:break-word}.event-details[_ngcontent-%COMP%]{font-size:12px;margin:0;color:var(--mat-sys-on-surface-variant);word-wrap:break-word}"],changeDetection:0})}return o})();export{bt as FaultInjectionDialogComponent}; diff --git a/gns3server/static/web-ui/chunk-4LQ6HNI2.js b/gns3server/static/web-ui/chunk-4LQ6HNI2.js deleted file mode 100644 index 022b0107c..000000000 --- a/gns3server/static/web-ui/chunk-4LQ6HNI2.js +++ /dev/null @@ -1 +0,0 @@ -import{$ as a}from"./chunk-6EPHFCHO.js";import"./chunk-LG2N72QL.js";export{a as TopologySummaryComponent}; diff --git a/gns3server/static/web-ui/chunk-6EPHFCHO.js b/gns3server/static/web-ui/chunk-6EPHFCHO.js deleted file mode 100644 index 870e7da36..000000000 --- a/gns3server/static/web-ui/chunk-6EPHFCHO.js +++ /dev/null @@ -1,18 +0,0 @@ -import{$a as N,$b as di,$d as Se,$e as Q,A as Ye,Aa as Wt,Ab as kt,Ba as Y,Bb as Ot,Bc as bi,Cb as ke,Cd as Ci,D as Ke,Da as O,Dc as W,Dd as Ti,Ea as ai,Ed as ae,Ee as le,F as J,Fc as S,Fe as Bi,G as dt,Gb as lt,Gc as bt,Gd as ne,Ge as zi,Hb as Rt,Hc as gi,Ib as u,Id as U,Ie as Ni,Jb as si,Je as ji,Kb as p,Kd as qt,Ke as Ut,Lb as B,Ld as oe,Le as ce,M as Ze,Mb as y,Md as Mi,N as Xe,Nb as et,Nd as Si,Oa as ye,Ob as V,Oc as vi,Od as Lt,Oe as de,Pa as c,Pb as m,Pd as Ii,Pe as Vi,Qb as h,Qd as ct,Qe as Hi,Ra as rt,Rb as li,Rc as yi,Re as $i,Sa as Dt,Sb as ci,Sc as xi,Sd as re,Tb as we,Td as Di,U as Je,Ua as st,Ub as ht,Ue as Wi,Vb as Ce,Ve as Ie,W as ti,Wb as _,Wc as ki,Wd as Ei,We as Qi,X as ei,Xa as ee,Xb as _t,Xd as Fi,Xe as K,Y as at,Yb as E,Yd as Oi,Z as $t,Zb as pt,Zd as se,Ze as Gi,_ as I,_a as C,_b as ft,_d as Me,a as X,ab as x,ac as mi,ae as Pt,af as De,bc as hi,bf as Yt,cc as pi,cf as gt,da as P,db as q,dc as fi,ea as z,eb as tt,ef as Ct,fb as ni,fe as Ri,g as it,ga as w,gc as $,gf as Kt,ha as nt,hb as oi,hc as ui,he as Ai,hf as qi,i as Jt,ia as r,ic as _i,if as Ui,j as k,jf as vt,k as St,lf as me,ma as R,mb as ri,me as Li,na as A,nb as T,nd as wi,nf as Yi,o as Ue,oa as mt,od as wt,of as Ki,pb as g,q as te,qa as G,qc as Te,ra as It,rb as v,rd as Gt,tb as xe,ua as F,ub as Et,uc as At,ud as ot,v as Ht,va as H,vb as Ft,vd as ut,wb as D,wc as ie,wd as j,we as Pi,xb as l,ya as Z,yb as d,za as ii,zb as M,zc as Qt}from"./chunk-LG2N72QL.js";var Ee=class{_box;_destroyed=new k;_resizeSubject=new k;_resizeObserver;_elementObservables=new Map;constructor(s){this._box=s,typeof ResizeObserver<"u"&&(this._resizeObserver=new ResizeObserver(t=>this._resizeSubject.next(t)))}observe(s){return this._elementObservables.has(s)||this._elementObservables.set(s,new Jt(t=>{let e=this._resizeSubject.subscribe(t);return this._resizeObserver?.observe(s,{box:this._box}),()=>{this._resizeObserver?.unobserve(s),e.unsubscribe(),this._elementObservables.delete(s)}}).pipe(dt(t=>t.some(e=>e.target===s)),ti({bufferSize:1,refCount:!0}),I(this._destroyed))),this._elementObservables.get(s)}destroy(){this._destroyed.next(),this._destroyed.complete(),this._resizeSubject.complete(),this._elementObservables.clear()}},he=(()=>{class a{_cleanupErrorListener;_observers=new Map;_ngZone=r(H);constructor(){typeof ResizeObserver<"u"}ngOnDestroy(){for(let[,t]of this._observers)t.destroy();this._observers.clear(),this._cleanupErrorListener?.()}observe(t,e){let i=e?.box||"content-box";return this._observers.has(i)||this._observers.set(i,new Ee(i)),this._observers.get(i).observe(t)}static \u0275fac=function(e){return new(e||a)};static \u0275prov=P({token:a,factory:a.\u0275fac,providedIn:"root"})}return a})();var Bt=["*"];function on(a,s){a&1&&y(0)}var ta=["tabListContainer"],ea=["tabList"],ia=["tabListInner"],aa=["nextPaginator"],na=["previousPaginator"],rn=["content"];function sn(a,s){}var ln=["tabBodyWrapper"],cn=["tabHeader"];function dn(a,s){}function mn(a,s){if(a&1&&tt(0,dn,0,0,"ng-template",12),a&2){let t=p().$implicit;D("cdkPortalOutlet",t.templateLabel)}}function hn(a,s){if(a&1&&E(0),a&2){let t=p().$implicit;pt(t.textLabel)}}function pn(a,s){if(a&1){let t=lt();l(0,"div",7,2),u("click",function(){let i=R(t),n=i.$implicit,o=i.$index,f=p(),b=ht(1);return A(f._handleClick(n,b,o))})("cdkFocusChange",function(i){let n=R(t).$index,o=p();return A(o._tabFocusChanged(i,n))}),M(2,"span",8)(3,"div",9),l(4,"span",10)(5,"span",11),g(6,mn,1,1,null,12)(7,hn,1,1),d()()()}if(a&2){let t=s.$implicit,e=s.$index,i=ht(1),n=p();_t(t.labelClass),_("mdc-tab--active",n.selectedIndex===e),D("id",n._getTabLabelId(t,e))("disabled",t.disabled)("fitInkBarToContent",n.fitInkBarToContent),T("tabIndex",n._getTabIndex(e))("aria-posinset",e+1)("aria-setsize",n._tabs.length)("aria-controls",n._getTabContentId(e))("aria-selected",n.selectedIndex===e)("aria-label",t.ariaLabel||null)("aria-labelledby",!t.ariaLabel&&t.ariaLabelledby?t.ariaLabelledby:null),c(3),D("matRippleTrigger",i)("matRippleDisabled",t.disabled||n.disableRipple),c(3),v(t.templateLabel?6:7)}}function fn(a,s){a&1&&y(0)}function un(a,s){if(a&1){let t=lt();l(0,"mat-tab-body",13),u("_onCentered",function(){R(t);let i=p();return A(i._removeTabBodyWrapperHeight())})("_onCentering",function(i){R(t);let n=p();return A(n._setTabBodyWrapperHeight(i))})("_beforeCentering",function(i){R(t);let n=p();return A(n._bodyCentered(i))}),d()}if(a&2){let t=s.$implicit,e=s.$index,i=p();_t(t.bodyClass),D("id",i._getTabContentId(e))("content",t.content)("position",t.position)("animationDuration",i.animationDuration)("preserveContent",i.preserveContent),T("tabindex",i.contentTabIndex!=null&&i.selectedIndex===e?i.contentTabIndex:null)("aria-labelledby",i._getTabLabelId(t,e))("aria-hidden",i.selectedIndex!==e)}}var _n=["mat-tab-nav-bar",""],bn=["mat-tab-link",""],gn=new w("MatTabContent"),vn=(()=>{class a{template=r(Dt);constructor(){}static \u0275fac=function(e){return new(e||a)};static \u0275dir=x({type:a,selectors:[["","matTabContent",""]],features:[$([{provide:gn,useExisting:a}])]})}return a})(),yn=new w("MatTabLabel"),oa=new w("MAT_TAB"),xn=(()=>{class a extends Si{_closestTab=r(oa,{optional:!0});static \u0275fac=(()=>{let t;return function(i){return(t||(t=Y(a)))(i||a)}})();static \u0275dir=x({type:a,selectors:[["","mat-tab-label",""],["","matTabLabel",""]],features:[$([{provide:yn,useExisting:a}]),q]})}return a})(),ra=new w("MAT_TAB_GROUP"),Ae=(()=>{class a{_viewContainerRef=r(ee);_closestTabGroup=r(ra,{optional:!0});disabled=!1;get templateLabel(){return this._templateLabel}set templateLabel(t){this._setTemplateLabelInput(t)}_templateLabel;_explicitContent=void 0;_implicitContent;textLabel="";ariaLabel;ariaLabelledby;labelClass;bodyClass;id=null;_contentPortal=null;get content(){return this._contentPortal}_stateChanges=new k;position=null;origin=null;isActive=!1;constructor(){r(wt).load(Ct)}ngOnChanges(t){(t.hasOwnProperty("textLabel")||t.hasOwnProperty("disabled"))&&this._stateChanges.next()}ngOnDestroy(){this._stateChanges.complete()}ngOnInit(){this._contentPortal=new oe(this._explicitContent||this._implicitContent,this._viewContainerRef)}_setTemplateLabelInput(t){t&&t._closestTab===this&&(this._templateLabel=t)}static \u0275fac=function(e){return new(e||a)};static \u0275cmp=C({type:a,selectors:[["mat-tab"]],contentQueries:function(e,i,n){if(e&1&&et(n,xn,5)(n,vn,7,Dt),e&2){let o;m(o=h())&&(i.templateLabel=o.first),m(o=h())&&(i._explicitContent=o.first)}},viewQuery:function(e,i){if(e&1&&V(Dt,7),e&2){let n;m(n=h())&&(i._implicitContent=n.first)}},hostAttrs:["hidden",""],hostVars:1,hostBindings:function(e,i){e&2&&T("id",null)},inputs:{disabled:[2,"disabled","disabled",S],textLabel:[0,"label","textLabel"],ariaLabel:[0,"aria-label","ariaLabel"],ariaLabelledby:[0,"aria-labelledby","ariaLabelledby"],labelClass:"labelClass",bodyClass:"bodyClass",id:"id"},exportAs:["matTab"],features:[$([{provide:oa,useExisting:a}]),Wt],ngContentSelectors:Bt,decls:1,vars:0,template:function(e,i){e&1&&(B(),ni(0,on,1,0,"ng-template"))},encapsulation:2})}return a})(),Fe="mdc-tab-indicator--active",Zi="mdc-tab-indicator--no-transition",pe=class{_items;_currentItem;constructor(s){this._items=s}hide(){this._items.forEach(s=>s.deactivateInkBar()),this._currentItem=void 0}alignToElement(s){let t=this._items.find(i=>i.elementRef.nativeElement===s),e=this._currentItem;if(t!==e&&(e?.deactivateInkBar(),t)){let i=e?.elementRef.nativeElement.getBoundingClientRect?.();t.activateInkBar(i),this._currentItem=t}}},sa=(()=>{class a{_elementRef=r(O);_inkBarElement=null;_inkBarContentElement=null;_fitToContent=!1;get fitInkBarToContent(){return this._fitToContent}set fitInkBarToContent(t){this._fitToContent!==t&&(this._fitToContent=t,this._inkBarElement&&this._appendInkBarElement())}activateInkBar(t){let e=this._elementRef.nativeElement;if(!t||!e.getBoundingClientRect||!this._inkBarContentElement){e.classList.add(Fe);return}let i=e.getBoundingClientRect(),n=t.width/i.width,o=t.left-i.left;e.classList.add(Zi),this._inkBarContentElement.style.setProperty("transform",`translateX(${o}px) scaleX(${n})`),e.getBoundingClientRect(),e.classList.remove(Zi),e.classList.add(Fe),this._inkBarContentElement.style.setProperty("transform","")}deactivateInkBar(){this._elementRef.nativeElement.classList.remove(Fe)}ngOnInit(){this._createInkBarElement()}ngOnDestroy(){this._inkBarElement?.remove(),this._inkBarElement=this._inkBarContentElement=null}_createInkBarElement(){let t=this._elementRef.nativeElement.ownerDocument||document,e=this._inkBarElement=t.createElement("span"),i=this._inkBarContentElement=t.createElement("span");e.className="mdc-tab-indicator",i.className="mdc-tab-indicator__content mdc-tab-indicator__content--underline",e.appendChild(this._inkBarContentElement),this._appendInkBarElement()}_appendInkBarElement(){this._inkBarElement;let t=this._fitToContent?this._elementRef.nativeElement.querySelector(".mdc-tab__content"):this._elementRef.nativeElement;t.appendChild(this._inkBarElement)}static \u0275fac=function(e){return new(e||a)};static \u0275dir=x({type:a,inputs:{fitInkBarToContent:[2,"fitInkBarToContent","fitInkBarToContent",S]}})}return a})();var la=(()=>{class a extends sa{elementRef=r(O);disabled=!1;focus(){this.elementRef.nativeElement.focus()}getOffsetLeft(){return this.elementRef.nativeElement.offsetLeft}getOffsetWidth(){return this.elementRef.nativeElement.offsetWidth}static \u0275fac=(()=>{let t;return function(i){return(t||(t=Y(a)))(i||a)}})();static \u0275dir=x({type:a,selectors:[["","matTabLabelWrapper",""]],hostVars:3,hostBindings:function(e,i){e&2&&(T("aria-disabled",!!i.disabled),_("mat-mdc-tab-disabled",i.disabled))},inputs:{disabled:[2,"disabled","disabled",S]},features:[q]})}return a})(),Xi={passive:!0},kn=650,wn=100,ca=(()=>{class a{_elementRef=r(O);_changeDetectorRef=r(W);_viewportRuler=r(ae);_dir=r(ut,{optional:!0});_ngZone=r(H);_platform=r(ot);_sharedResizeObserver=r(he);_injector=r(G);_renderer=r(st);_animationsDisabled=Q();_eventCleanups;_scrollDistance=0;_selectedIndexChanged=!1;_destroyed=new k;_showPaginationControls=!1;_disableScrollAfter=!0;_disableScrollBefore=!0;_tabLabelCount;_scrollDistanceChanged=!1;_keyManager;_currentTextContent;_stopScrolling=new k;disablePagination=!1;get selectedIndex(){return this._selectedIndex}set selectedIndex(t){let e=isNaN(t)?0:t;this._selectedIndex!=e&&(this._selectedIndexChanged=!0,this._selectedIndex=e,this._keyManager&&this._keyManager.updateActiveItem(e))}_selectedIndex=0;selectFocusedIndex=new F;indexFocused=new F;constructor(){this._eventCleanups=this._ngZone.runOutsideAngular(()=>[this._renderer.listen(this._elementRef.nativeElement,"mouseleave",()=>this._stopInterval())])}ngAfterViewInit(){this._eventCleanups.push(this._renderer.listen(this._previousPaginator.nativeElement,"touchstart",()=>this._handlePaginatorPress("before"),Xi),this._renderer.listen(this._nextPaginator.nativeElement,"touchstart",()=>this._handlePaginatorPress("after"),Xi))}ngAfterContentInit(){let t=this._dir?this._dir.change:te("ltr"),e=this._sharedResizeObserver.observe(this._elementRef.nativeElement).pipe(Ze(32),I(this._destroyed)),i=this._viewportRuler.change(150).pipe(I(this._destroyed)),n=()=>{this.updatePagination(),this._alignInkBarToSelectedTab()};this._keyManager=new $i(this._items).withHorizontalOrientation(this._getLayoutDirection()).withHomeAndEnd().withWrap().skipPredicate(()=>!1),this._keyManager.updateActiveItem(Math.max(this._selectedIndex,0)),rt(n,{injector:this._injector}),J(t,i,e,this._items.changes,this._itemsResized()).pipe(I(this._destroyed)).subscribe(()=>{this._ngZone.run(()=>{Promise.resolve().then(()=>{this._scrollDistance=Math.max(0,Math.min(this._getMaxScrollDistance(),this._scrollDistance)),n()})}),this._keyManager?.withHorizontalOrientation(this._getLayoutDirection())}),this._keyManager.change.subscribe(o=>{this.indexFocused.emit(o),this._setTabFocus(o)})}_itemsResized(){return typeof ResizeObserver!="function"?Ue:this._items.changes.pipe(at(this._items),$t(t=>new Jt(e=>this._ngZone.runOutsideAngular(()=>{let i=new ResizeObserver(n=>e.next(n));return t.forEach(n=>i.observe(n.elementRef.nativeElement)),()=>{i.disconnect()}}))),ei(1),dt(t=>t.some(e=>e.contentRect.width>0&&e.contentRect.height>0)))}ngAfterContentChecked(){this._tabLabelCount!=this._items.length&&(this.updatePagination(),this._tabLabelCount=this._items.length,this._changeDetectorRef.markForCheck()),this._selectedIndexChanged&&(this._scrollToLabel(this._selectedIndex),this._checkScrollingControls(),this._alignInkBarToSelectedTab(),this._selectedIndexChanged=!1,this._changeDetectorRef.markForCheck()),this._scrollDistanceChanged&&(this._updateTabScrollPosition(),this._scrollDistanceChanged=!1,this._changeDetectorRef.markForCheck())}ngOnDestroy(){this._eventCleanups.forEach(t=>t()),this._keyManager?.destroy(),this._destroyed.next(),this._destroyed.complete(),this._stopScrolling.complete()}_handleKeydown(t){if(!ct(t))switch(t.keyCode){case 13:case 32:if(this.focusIndex!==this.selectedIndex){let e=this._items.get(this.focusIndex);e&&!e.disabled&&(this.selectFocusedIndex.emit(this.focusIndex),this._itemSelected(t))}break;default:this._keyManager?.onKeydown(t)}}_onContentChanges(){let t=this._elementRef.nativeElement.textContent;t!==this._currentTextContent&&(this._currentTextContent=t||"",this._ngZone.run(()=>{this.updatePagination(),this._alignInkBarToSelectedTab(),this._changeDetectorRef.markForCheck()}))}updatePagination(){this._checkPaginationEnabled(),this._checkScrollingControls(),this._updateTabScrollPosition()}get focusIndex(){return this._keyManager?this._keyManager.activeItemIndex:0}set focusIndex(t){!this._isValidIndex(t)||this.focusIndex===t||!this._keyManager||this._keyManager.setActiveItem(t)}_isValidIndex(t){return this._items?!!this._items.toArray()[t]:!0}_setTabFocus(t){if(this._showPaginationControls&&this._scrollToLabel(t),this._items&&this._items.length){this._items.toArray()[t].focus();let e=this._tabListContainer.nativeElement;this._getLayoutDirection()=="ltr"?e.scrollLeft=0:e.scrollLeft=e.scrollWidth-e.offsetWidth}}_getLayoutDirection(){return this._dir&&this._dir.value==="rtl"?"rtl":"ltr"}_updateTabScrollPosition(){if(this.disablePagination)return;let t=this.scrollDistance,e=this._getLayoutDirection()==="ltr"?-t:t;this._tabList.nativeElement.style.transform=`translateX(${Math.round(e)}px)`,(this._platform.TRIDENT||this._platform.EDGE)&&(this._tabListContainer.nativeElement.scrollLeft=0)}get scrollDistance(){return this._scrollDistance}set scrollDistance(t){this._scrollTo(t)}_scrollHeader(t){let e=this._tabListContainer.nativeElement.offsetWidth,i=(t=="before"?-1:1)*e/3;return this._scrollTo(this._scrollDistance+i)}_handlePaginatorClick(t){this._stopInterval(),this._scrollHeader(t)}_scrollToLabel(t){if(this.disablePagination)return;let e=this._items?this._items.toArray()[t]:null;if(!e)return;let i=this._tabListContainer.nativeElement.offsetWidth,{offsetLeft:n,offsetWidth:o}=e.elementRef.nativeElement,f,b;this._getLayoutDirection()=="ltr"?(f=n,b=f+o):(b=this._tabListInner.nativeElement.offsetWidth-n,f=b-o);let xt=this.scrollDistance,Mt=this.scrollDistance+i;fMt&&(this.scrollDistance+=Math.min(b-Mt,f-xt))}_checkPaginationEnabled(){if(this.disablePagination)this._showPaginationControls=!1;else{let t=this._tabListInner.nativeElement.scrollWidth,e=this._elementRef.nativeElement.offsetWidth,i=t-e>=5;i||(this.scrollDistance=0),i!==this._showPaginationControls&&(this._showPaginationControls=i,this._changeDetectorRef.markForCheck())}}_checkScrollingControls(){this.disablePagination?this._disableScrollAfter=this._disableScrollBefore=!0:(this._disableScrollBefore=this.scrollDistance==0,this._disableScrollAfter=this.scrollDistance==this._getMaxScrollDistance(),this._changeDetectorRef.markForCheck())}_getMaxScrollDistance(){let t=this._tabListInner.nativeElement.scrollWidth,e=this._tabListContainer.nativeElement.offsetWidth;return t-e||0}_alignInkBarToSelectedTab(){let t=this._items&&this._items.length?this._items.toArray()[this.selectedIndex]:null,e=t?t.elementRef.nativeElement:null;e?this._inkBar.alignToElement(e):this._inkBar.hide()}_stopInterval(){this._stopScrolling.next()}_handlePaginatorPress(t,e){e&&e.button!=null&&e.button!==0||(this._stopInterval(),Ke(kn,wn).pipe(I(J(this._stopScrolling,this._destroyed))).subscribe(()=>{let{maxScrollDistance:i,distance:n}=this._scrollHeader(t);(n===0||n>=i)&&this._stopInterval()}))}_scrollTo(t){if(this.disablePagination)return{maxScrollDistance:0,distance:0};let e=this._getMaxScrollDistance();return this._scrollDistance=Math.max(0,Math.min(e,t)),this._scrollDistanceChanged=!0,this._checkScrollingControls(),{maxScrollDistance:e,distance:this._scrollDistance}}static \u0275fac=function(e){return new(e||a)};static \u0275dir=x({type:a,inputs:{disablePagination:[2,"disablePagination","disablePagination",S],selectedIndex:[2,"selectedIndex","selectedIndex",bt]},outputs:{selectFocusedIndex:"selectFocusedIndex",indexFocused:"indexFocused"}})}return a})(),Cn=(()=>{class a extends ca{_items;_tabListContainer;_tabList;_tabListInner;_nextPaginator;_previousPaginator;_inkBar;ariaLabel;ariaLabelledby;disableRipple=!1;ngAfterContentInit(){this._inkBar=new pe(this._items),super.ngAfterContentInit()}_itemSelected(t){t.preventDefault()}static \u0275fac=(()=>{let t;return function(i){return(t||(t=Y(a)))(i||a)}})();static \u0275cmp=C({type:a,selectors:[["mat-tab-header"]],contentQueries:function(e,i,n){if(e&1&&et(n,la,4),e&2){let o;m(o=h())&&(i._items=o)}},viewQuery:function(e,i){if(e&1&&V(ta,7)(ea,7)(ia,7)(aa,5)(na,5),e&2){let n;m(n=h())&&(i._tabListContainer=n.first),m(n=h())&&(i._tabList=n.first),m(n=h())&&(i._tabListInner=n.first),m(n=h())&&(i._nextPaginator=n.first),m(n=h())&&(i._previousPaginator=n.first)}},hostAttrs:[1,"mat-mdc-tab-header"],hostVars:4,hostBindings:function(e,i){e&2&&_("mat-mdc-tab-header-pagination-controls-enabled",i._showPaginationControls)("mat-mdc-tab-header-rtl",i._getLayoutDirection()=="rtl")},inputs:{ariaLabel:[0,"aria-label","ariaLabel"],ariaLabelledby:[0,"aria-labelledby","ariaLabelledby"],disableRipple:[2,"disableRipple","disableRipple",S]},features:[q],ngContentSelectors:Bt,decls:13,vars:10,consts:[["previousPaginator",""],["tabListContainer",""],["tabList",""],["tabListInner",""],["nextPaginator",""],["mat-ripple","",1,"mat-mdc-tab-header-pagination","mat-mdc-tab-header-pagination-before",3,"click","mousedown","touchend","matRippleDisabled"],[1,"mat-mdc-tab-header-pagination-chevron"],[1,"mat-mdc-tab-label-container",3,"keydown"],["role","tablist",1,"mat-mdc-tab-list",3,"cdkObserveContent"],[1,"mat-mdc-tab-labels"],["mat-ripple","",1,"mat-mdc-tab-header-pagination","mat-mdc-tab-header-pagination-after",3,"mousedown","click","touchend","matRippleDisabled"]],template:function(e,i){e&1&&(B(),l(0,"div",5,0),u("click",function(){return i._handlePaginatorClick("before")})("mousedown",function(o){return i._handlePaginatorPress("before",o)})("touchend",function(){return i._stopInterval()}),M(2,"div",6),d(),l(3,"div",7,1),u("keydown",function(o){return i._handleKeydown(o)}),l(5,"div",8,2),u("cdkObserveContent",function(){return i._onContentChanges()}),l(7,"div",9,3),y(9),d()()(),l(10,"div",10,4),u("mousedown",function(o){return i._handlePaginatorPress("after",o)})("click",function(){return i._handlePaginatorClick("after")})("touchend",function(){return i._stopInterval()}),M(12,"div",6),d()),e&2&&(_("mat-mdc-tab-header-pagination-disabled",i._disableScrollBefore),D("matRippleDisabled",i._disableScrollBefore||i.disableRipple),c(3),_("_mat-animation-noopable",i._animationsDisabled),c(2),T("aria-label",i.ariaLabel||null)("aria-labelledby",i.ariaLabelledby||null),c(5),_("mat-mdc-tab-header-pagination-disabled",i._disableScrollAfter),D("matRippleDisabled",i._disableScrollAfter||i.disableRipple))},dependencies:[gt,Ut],styles:[`.mat-mdc-tab-header{display:flex;overflow:hidden;position:relative;flex-shrink:0}.mdc-tab-indicator .mdc-tab-indicator__content{transition-duration:var(--mat-tab-animation-duration, 250ms)}.mat-mdc-tab-header-pagination{-webkit-user-select:none;user-select:none;position:relative;display:none;justify-content:center;align-items:center;min-width:32px;cursor:pointer;z-index:2;-webkit-tap-highlight-color:rgba(0,0,0,0);touch-action:none;box-sizing:content-box;outline:0}.mat-mdc-tab-header-pagination::-moz-focus-inner{border:0}.mat-mdc-tab-header-pagination .mat-ripple-element{opacity:.12;background-color:var(--mat-tab-inactive-ripple-color, var(--mat-sys-on-surface))}.mat-mdc-tab-header-pagination-controls-enabled .mat-mdc-tab-header-pagination{display:flex}.mat-mdc-tab-header-pagination-before,.mat-mdc-tab-header-rtl .mat-mdc-tab-header-pagination-after{padding-left:4px}.mat-mdc-tab-header-pagination-before .mat-mdc-tab-header-pagination-chevron,.mat-mdc-tab-header-rtl .mat-mdc-tab-header-pagination-after .mat-mdc-tab-header-pagination-chevron{transform:rotate(-135deg)}.mat-mdc-tab-header-rtl .mat-mdc-tab-header-pagination-before,.mat-mdc-tab-header-pagination-after{padding-right:4px}.mat-mdc-tab-header-rtl .mat-mdc-tab-header-pagination-before .mat-mdc-tab-header-pagination-chevron,.mat-mdc-tab-header-pagination-after .mat-mdc-tab-header-pagination-chevron{transform:rotate(45deg)}.mat-mdc-tab-header-pagination-chevron{border-style:solid;border-width:2px 2px 0 0;height:8px;width:8px;border-color:var(--mat-tab-pagination-icon-color, var(--mat-sys-on-surface))}.mat-mdc-tab-header-pagination-disabled{box-shadow:none;cursor:default;pointer-events:none}.mat-mdc-tab-header-pagination-disabled .mat-mdc-tab-header-pagination-chevron{opacity:.4}.mat-mdc-tab-list{flex-grow:1;position:relative;transition:transform 500ms cubic-bezier(0.35, 0, 0.25, 1)}._mat-animation-noopable .mat-mdc-tab-list{transition:none}.mat-mdc-tab-label-container{display:flex;flex-grow:1;overflow:hidden;z-index:1;border-bottom-style:solid;border-bottom-width:var(--mat-tab-divider-height, 1px);border-bottom-color:var(--mat-tab-divider-color, var(--mat-sys-surface-variant))}.mat-mdc-tab-group-inverted-header .mat-mdc-tab-label-container{border-bottom:none;border-top-style:solid;border-top-width:var(--mat-tab-divider-height, 1px);border-top-color:var(--mat-tab-divider-color, var(--mat-sys-surface-variant))}.mat-mdc-tab-labels{display:flex;flex:1 0 auto}[mat-align-tabs=center]>.mat-mdc-tab-header .mat-mdc-tab-labels{justify-content:center}[mat-align-tabs=end]>.mat-mdc-tab-header .mat-mdc-tab-labels{justify-content:flex-end}.cdk-drop-list .mat-mdc-tab-labels,.mat-mdc-tab-labels.cdk-drop-list{min-height:var(--mat-tab-container-height, 48px)}.mat-mdc-tab::before{margin:5px}@media(forced-colors: active){.mat-mdc-tab[aria-disabled=true]{color:GrayText}} -`],encapsulation:2})}return a})(),da=new w("MAT_TABS_CONFIG"),Ji=(()=>{class a extends Lt{_host=r(Oe);_ngZone=r(H);_centeringSub=it.EMPTY;_leavingSub=it.EMPTY;constructor(){super()}ngOnInit(){super.ngOnInit(),this._centeringSub=this._host._beforeCentering.pipe(at(this._host._isCenterPosition())).subscribe(t=>{this._host._content&&t&&!this.hasAttached()&&this._ngZone.run(()=>{Promise.resolve().then(),this.attach(this._host._content)})}),this._leavingSub=this._host._afterLeavingCenter.subscribe(()=>{this._host.preserveContent||this._ngZone.run(()=>this.detach())})}ngOnDestroy(){super.ngOnDestroy(),this._centeringSub.unsubscribe(),this._leavingSub.unsubscribe()}static \u0275fac=function(e){return new(e||a)};static \u0275dir=x({type:a,selectors:[["","matTabBodyHost",""]],features:[q]})}return a})(),Oe=(()=>{class a{_elementRef=r(O);_dir=r(ut,{optional:!0});_ngZone=r(H);_injector=r(G);_renderer=r(st);_diAnimationsDisabled=Q();_eventCleanups;_initialized=!1;_fallbackTimer;_positionIndex;_dirChangeSubscription=it.EMPTY;_position;_previousPosition;_onCentering=new F;_beforeCentering=new F;_afterLeavingCenter=new F;_onCentered=new F(!0);_portalHost;_contentElement;_content;animationDuration="500ms";preserveContent=!1;set position(t){this._positionIndex=t,this._computePositionAnimationState()}constructor(){if(this._dir){let t=r(W);this._dirChangeSubscription=this._dir.change.subscribe(e=>{this._computePositionAnimationState(e),t.markForCheck()})}}ngOnInit(){this._bindTransitionEvents(),this._position==="center"&&(this._setActiveClass(!0),rt(()=>this._onCentering.emit(this._elementRef.nativeElement.clientHeight),{injector:this._injector})),this._initialized=!0}ngOnDestroy(){clearTimeout(this._fallbackTimer),this._eventCleanups?.forEach(t=>t()),this._dirChangeSubscription.unsubscribe()}_bindTransitionEvents(){this._ngZone.runOutsideAngular(()=>{let t=this._elementRef.nativeElement,e=i=>{i.target===this._contentElement?.nativeElement&&(this._elementRef.nativeElement.classList.remove("mat-tab-body-animating"),i.type==="transitionend"&&this._transitionDone())};this._eventCleanups=[this._renderer.listen(t,"transitionstart",i=>{i.target===this._contentElement?.nativeElement&&(this._elementRef.nativeElement.classList.add("mat-tab-body-animating"),this._transitionStarted())}),this._renderer.listen(t,"transitionend",e),this._renderer.listen(t,"transitioncancel",e)]})}_transitionStarted(){clearTimeout(this._fallbackTimer);let t=this._position==="center";this._beforeCentering.emit(t),t&&this._onCentering.emit(this._elementRef.nativeElement.clientHeight)}_transitionDone(){this._position==="center"?this._onCentered.emit():this._previousPosition==="center"&&this._afterLeavingCenter.emit()}_setActiveClass(t){this._elementRef.nativeElement.classList.toggle("mat-mdc-tab-body-active",t)}_getLayoutDirection(){return this._dir&&this._dir.value==="rtl"?"rtl":"ltr"}_isCenterPosition(){return this._positionIndex===0}_computePositionAnimationState(t=this._getLayoutDirection()){this._previousPosition=this._position,this._positionIndex<0?this._position=t=="ltr"?"left":"right":this._positionIndex>0?this._position=t=="ltr"?"right":"left":this._position="center",this._animationsDisabled()?this._simulateTransitionEvents():this._initialized&&(this._position==="center"||this._previousPosition==="center")&&(clearTimeout(this._fallbackTimer),this._fallbackTimer=this._ngZone.runOutsideAngular(()=>setTimeout(()=>this._simulateTransitionEvents(),100)))}_simulateTransitionEvents(){this._transitionStarted(),rt(()=>this._transitionDone(),{injector:this._injector})}_animationsDisabled(){return this._diAnimationsDisabled||this.animationDuration==="0ms"||this.animationDuration==="0s"}static \u0275fac=function(e){return new(e||a)};static \u0275cmp=C({type:a,selectors:[["mat-tab-body"]],viewQuery:function(e,i){if(e&1&&V(Ji,5)(rn,5),e&2){let n;m(n=h())&&(i._portalHost=n.first),m(n=h())&&(i._contentElement=n.first)}},hostAttrs:[1,"mat-mdc-tab-body"],hostVars:1,hostBindings:function(e,i){e&2&&T("inert",i._position==="center"?null:"")},inputs:{_content:[0,"content","_content"],animationDuration:"animationDuration",preserveContent:"preserveContent",position:"position"},outputs:{_onCentering:"_onCentering",_beforeCentering:"_beforeCentering",_onCentered:"_onCentered"},decls:3,vars:6,consts:[["content",""],["cdkScrollable","",1,"mat-mdc-tab-body-content"],["matTabBodyHost",""]],template:function(e,i){e&1&&(l(0,"div",1,0),tt(2,sn,0,0,"ng-template",2),d()),e&2&&_("mat-tab-body-content-left",i._position==="left")("mat-tab-body-content-right",i._position==="right")("mat-tab-body-content-can-animate",i._position==="center"||i._previousPosition==="center")},dependencies:[Ji,Ti],styles:[`.mat-mdc-tab-body{top:0;left:0;right:0;bottom:0;position:absolute;display:block;overflow:hidden;outline:0;flex-basis:100%}.mat-mdc-tab-body.mat-mdc-tab-body-active{position:relative;overflow-x:hidden;overflow-y:auto;z-index:1;flex-grow:1}.mat-mdc-tab-group.mat-mdc-tab-group-dynamic-height .mat-mdc-tab-body.mat-mdc-tab-body-active{overflow-y:hidden}.mat-mdc-tab-body-content{height:100%;overflow:auto;transform:none;visibility:hidden}.mat-tab-body-animating>.mat-mdc-tab-body-content,.mat-mdc-tab-body-active>.mat-mdc-tab-body-content{visibility:visible}.mat-tab-body-animating>.mat-mdc-tab-body-content{min-height:1px}.mat-mdc-tab-group-dynamic-height .mat-mdc-tab-body-content{overflow:hidden}.mat-tab-body-content-can-animate{transition:transform var(--mat-tab-animation-duration) 1ms cubic-bezier(0.35, 0, 0.25, 1)}.mat-mdc-tab-body-wrapper._mat-animation-noopable .mat-tab-body-content-can-animate{transition:none}.mat-tab-body-content-left{transform:translate3d(-100%, 0, 0)}.mat-tab-body-content-right{transform:translate3d(100%, 0, 0)} -`],encapsulation:2})}return a})(),ma=(()=>{class a{_elementRef=r(O);_changeDetectorRef=r(W);_ngZone=r(H);_tabsSubscription=it.EMPTY;_tabLabelSubscription=it.EMPTY;_tabBodySubscription=it.EMPTY;_diAnimationsDisabled=Q();_allTabs;_tabBodies;_tabBodyWrapper;_tabHeader;_tabs=new ai;_indexToSelect=0;_lastFocusedTabIndex=null;_tabBodyWrapperHeight=0;color;get fitInkBarToContent(){return this._fitInkBarToContent}set fitInkBarToContent(t){this._fitInkBarToContent=t,this._changeDetectorRef.markForCheck()}_fitInkBarToContent=!1;stretchTabs=!0;alignTabs=null;dynamicHeight=!1;get selectedIndex(){return this._selectedIndex}set selectedIndex(t){this._indexToSelect=isNaN(t)?null:t}_selectedIndex=null;headerPosition="above";get animationDuration(){return this._animationDuration}set animationDuration(t){let e=t+"";this._animationDuration=/^\d+$/.test(e)?t+"ms":e}_animationDuration;get contentTabIndex(){return this._contentTabIndex}set contentTabIndex(t){this._contentTabIndex=isNaN(t)?null:t}_contentTabIndex=null;disablePagination=!1;disableRipple=!1;preserveContent=!1;get backgroundColor(){return this._backgroundColor}set backgroundColor(t){let e=this._elementRef.nativeElement.classList;e.remove("mat-tabs-with-background",`mat-background-${this.backgroundColor}`),t&&e.add("mat-tabs-with-background",`mat-background-${t}`),this._backgroundColor=t}_backgroundColor;ariaLabel;ariaLabelledby;selectedIndexChange=new F;focusChange=new F;animationDone=new F;selectedTabChange=new F(!0);_groupId;_isServer=!r(ot).isBrowser;constructor(){let t=r(da,{optional:!0});this._groupId=r(U).getId("mat-tab-group-"),this.animationDuration=t&&t.animationDuration?t.animationDuration:"500ms",this.disablePagination=t&&t.disablePagination!=null?t.disablePagination:!1,this.dynamicHeight=t&&t.dynamicHeight!=null?t.dynamicHeight:!1,t?.contentTabIndex!=null&&(this.contentTabIndex=t.contentTabIndex),this.preserveContent=!!t?.preserveContent,this.fitInkBarToContent=t&&t.fitInkBarToContent!=null?t.fitInkBarToContent:!1,this.stretchTabs=t&&t.stretchTabs!=null?t.stretchTabs:!0,this.alignTabs=t&&t.alignTabs!=null?t.alignTabs:null}ngAfterContentChecked(){let t=this._indexToSelect=this._clampTabIndex(this._indexToSelect);if(this._selectedIndex!=t){let e=this._selectedIndex==null;if(!e){this.selectedTabChange.emit(this._createChangeEvent(t));let i=this._tabBodyWrapper.nativeElement;i.style.minHeight=i.clientHeight+"px"}Promise.resolve().then(()=>{this._tabs.forEach((i,n)=>i.isActive=n===t),e||(this.selectedIndexChange.emit(t),this._tabBodyWrapper.nativeElement.style.minHeight="")})}this._tabs.forEach((e,i)=>{e.position=i-t,this._selectedIndex!=null&&e.position==0&&!e.origin&&(e.origin=t-this._selectedIndex)}),this._selectedIndex!==t&&(this._selectedIndex=t,this._lastFocusedTabIndex=null,this._changeDetectorRef.markForCheck())}ngAfterContentInit(){this._subscribeToAllTabChanges(),this._subscribeToTabLabels(),this._tabsSubscription=this._tabs.changes.subscribe(()=>{let t=this._clampTabIndex(this._indexToSelect);if(t===this._selectedIndex){let e=this._tabs.toArray(),i;for(let n=0;n{e[t].isActive=!0,this.selectedTabChange.emit(this._createChangeEvent(t))})}this._changeDetectorRef.markForCheck()})}ngAfterViewInit(){this._tabBodySubscription=this._tabBodies.changes.subscribe(()=>this._bodyCentered(!0))}_subscribeToAllTabChanges(){this._allTabs.changes.pipe(at(this._allTabs)).subscribe(t=>{this._tabs.reset(t.filter(e=>e._closestTabGroup===this||!e._closestTabGroup)),this._tabs.notifyOnChanges()})}ngOnDestroy(){this._tabs.destroy(),this._tabsSubscription.unsubscribe(),this._tabLabelSubscription.unsubscribe(),this._tabBodySubscription.unsubscribe()}realignInkBar(){this._tabHeader&&this._tabHeader._alignInkBarToSelectedTab()}updatePagination(){this._tabHeader&&this._tabHeader.updatePagination()}focusTab(t){let e=this._tabHeader;e&&(e.focusIndex=t)}_focusChanged(t){this._lastFocusedTabIndex=t,this.focusChange.emit(this._createChangeEvent(t))}_createChangeEvent(t){let e=new Re;return e.index=t,this._tabs&&this._tabs.length&&(e.tab=this._tabs.toArray()[t]),e}_subscribeToTabLabels(){this._tabLabelSubscription&&this._tabLabelSubscription.unsubscribe(),this._tabLabelSubscription=J(...this._tabs.map(t=>t._stateChanges)).subscribe(()=>this._changeDetectorRef.markForCheck())}_clampTabIndex(t){return Math.min(this._tabs.length-1,Math.max(t||0,0))}_getTabLabelId(t,e){return t.id||`${this._groupId}-label-${e}`}_getTabContentId(t){return`${this._groupId}-content-${t}`}_setTabBodyWrapperHeight(t){if(!this.dynamicHeight||!this._tabBodyWrapperHeight){this._tabBodyWrapperHeight=t;return}let e=this._tabBodyWrapper.nativeElement;e.style.height=this._tabBodyWrapperHeight+"px",this._tabBodyWrapper.nativeElement.offsetHeight&&(e.style.height=t+"px")}_removeTabBodyWrapperHeight(){let t=this._tabBodyWrapper.nativeElement;this._tabBodyWrapperHeight=t.clientHeight,t.style.height="",this._ngZone.run(()=>this.animationDone.emit())}_handleClick(t,e,i){e.focusIndex=i,t.disabled||(this.selectedIndex=i)}_getTabIndex(t){let e=this._lastFocusedTabIndex??this.selectedIndex;return t===e?0:-1}_tabFocusChanged(t,e){t&&t!=="mouse"&&t!=="touch"&&(this._tabHeader.focusIndex=e)}_bodyCentered(t){t&&this._tabBodies?.forEach((e,i)=>e._setActiveClass(i===this._selectedIndex))}_animationsDisabled(){return this._diAnimationsDisabled||this.animationDuration==="0"||this.animationDuration==="0ms"}static \u0275fac=function(e){return new(e||a)};static \u0275cmp=C({type:a,selectors:[["mat-tab-group"]],contentQueries:function(e,i,n){if(e&1&&et(n,Ae,5),e&2){let o;m(o=h())&&(i._allTabs=o)}},viewQuery:function(e,i){if(e&1&&V(ln,5)(cn,5)(Oe,5),e&2){let n;m(n=h())&&(i._tabBodyWrapper=n.first),m(n=h())&&(i._tabHeader=n.first),m(n=h())&&(i._tabBodies=n)}},hostAttrs:[1,"mat-mdc-tab-group"],hostVars:11,hostBindings:function(e,i){e&2&&(T("mat-align-tabs",i.alignTabs),_t("mat-"+(i.color||"primary")),Ce("--mat-tab-animation-duration",i.animationDuration),_("mat-mdc-tab-group-dynamic-height",i.dynamicHeight)("mat-mdc-tab-group-inverted-header",i.headerPosition==="below")("mat-mdc-tab-group-stretch-tabs",i.stretchTabs))},inputs:{color:"color",fitInkBarToContent:[2,"fitInkBarToContent","fitInkBarToContent",S],stretchTabs:[2,"mat-stretch-tabs","stretchTabs",S],alignTabs:[0,"mat-align-tabs","alignTabs"],dynamicHeight:[2,"dynamicHeight","dynamicHeight",S],selectedIndex:[2,"selectedIndex","selectedIndex",bt],headerPosition:"headerPosition",animationDuration:"animationDuration",contentTabIndex:[2,"contentTabIndex","contentTabIndex",bt],disablePagination:[2,"disablePagination","disablePagination",S],disableRipple:[2,"disableRipple","disableRipple",S],preserveContent:[2,"preserveContent","preserveContent",S],backgroundColor:"backgroundColor",ariaLabel:[0,"aria-label","ariaLabel"],ariaLabelledby:[0,"aria-labelledby","ariaLabelledby"]},outputs:{selectedIndexChange:"selectedIndexChange",focusChange:"focusChange",animationDone:"animationDone",selectedTabChange:"selectedTabChange"},exportAs:["matTabGroup"],features:[$([{provide:ra,useExisting:a}])],ngContentSelectors:Bt,decls:9,vars:8,consts:[["tabHeader",""],["tabBodyWrapper",""],["tabNode",""],[3,"indexFocused","selectFocusedIndex","selectedIndex","disableRipple","disablePagination","aria-label","aria-labelledby"],["role","tab","matTabLabelWrapper","","cdkMonitorElementFocus","",1,"mdc-tab","mat-mdc-tab","mat-focus-indicator",3,"id","mdc-tab--active","class","disabled","fitInkBarToContent"],[1,"mat-mdc-tab-body-wrapper"],["role","tabpanel",3,"id","class","content","position","animationDuration","preserveContent"],["role","tab","matTabLabelWrapper","","cdkMonitorElementFocus","",1,"mdc-tab","mat-mdc-tab","mat-focus-indicator",3,"click","cdkFocusChange","id","disabled","fitInkBarToContent"],[1,"mdc-tab__ripple"],["mat-ripple","",1,"mat-mdc-tab-ripple",3,"matRippleTrigger","matRippleDisabled"],[1,"mdc-tab__content"],[1,"mdc-tab__text-label"],[3,"cdkPortalOutlet"],["role","tabpanel",3,"_onCentered","_onCentering","_beforeCentering","id","content","position","animationDuration","preserveContent"]],template:function(e,i){e&1&&(B(),l(0,"mat-tab-header",3,0),u("indexFocused",function(o){return i._focusChanged(o)})("selectFocusedIndex",function(o){return i.selectedIndex=o}),Et(2,pn,8,17,"div",4,xe),d(),g(4,fn,1,0),l(5,"div",5,1),Et(7,un,1,10,"mat-tab-body",6,xe),d()),e&2&&(D("selectedIndex",i.selectedIndex||0)("disableRipple",i.disableRipple)("disablePagination",i.disablePagination),ri("aria-label",i.ariaLabel)("aria-labelledby",i.ariaLabelledby),c(2),Ft(i._tabs),c(2),v(i._isServer?4:-1),c(),_("_mat-animation-noopable",i._animationsDisabled()),c(2),Ft(i._tabs))},dependencies:[Cn,la,Bi,gt,Lt,Oe],styles:[`.mdc-tab{min-width:90px;padding:0 24px;display:flex;flex:1 0 auto;justify-content:center;box-sizing:border-box;border:none;outline:none;text-align:center;white-space:nowrap;cursor:pointer;z-index:1;touch-action:manipulation}.mdc-tab__content{display:flex;align-items:center;justify-content:center;height:inherit;pointer-events:none}.mdc-tab__text-label{transition:150ms color linear;display:inline-block;line-height:1;z-index:2}.mdc-tab--active .mdc-tab__text-label{transition-delay:100ms}._mat-animation-noopable .mdc-tab__text-label{transition:none}.mdc-tab-indicator{display:flex;position:absolute;top:0;left:0;justify-content:center;width:100%;height:100%;pointer-events:none;z-index:1}.mdc-tab-indicator__content{transition:var(--mat-tab-animation-duration, 250ms) transform cubic-bezier(0.4, 0, 0.2, 1);transform-origin:left;opacity:0}.mdc-tab-indicator__content--underline{align-self:flex-end;box-sizing:border-box;width:100%;border-top-style:solid}.mdc-tab-indicator--active .mdc-tab-indicator__content{opacity:1}._mat-animation-noopable .mdc-tab-indicator__content,.mdc-tab-indicator--no-transition .mdc-tab-indicator__content{transition:none}.mat-mdc-tab-ripple.mat-mdc-tab-ripple{position:absolute;top:0;left:0;bottom:0;right:0;pointer-events:none}.mat-mdc-tab{-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-decoration:none;background:none;height:var(--mat-tab-container-height, 48px);font-family:var(--mat-tab-label-text-font, var(--mat-sys-title-small-font));font-size:var(--mat-tab-label-text-size, var(--mat-sys-title-small-size));letter-spacing:var(--mat-tab-label-text-tracking, var(--mat-sys-title-small-tracking));line-height:var(--mat-tab-label-text-line-height, var(--mat-sys-title-small-line-height));font-weight:var(--mat-tab-label-text-weight, var(--mat-sys-title-small-weight))}.mat-mdc-tab.mdc-tab{flex-grow:0}.mat-mdc-tab .mdc-tab-indicator__content--underline{border-color:var(--mat-tab-active-indicator-color, var(--mat-sys-primary));border-top-width:var(--mat-tab-active-indicator-height, 2px);border-radius:var(--mat-tab-active-indicator-shape, 0)}.mat-mdc-tab:hover .mdc-tab__text-label{color:var(--mat-tab-inactive-hover-label-text-color, var(--mat-sys-on-surface))}.mat-mdc-tab:focus .mdc-tab__text-label{color:var(--mat-tab-inactive-focus-label-text-color, var(--mat-sys-on-surface))}.mat-mdc-tab.mdc-tab--active .mdc-tab__text-label{color:var(--mat-tab-active-label-text-color, var(--mat-sys-on-surface))}.mat-mdc-tab.mdc-tab--active .mdc-tab__ripple::before,.mat-mdc-tab.mdc-tab--active .mat-ripple-element{background-color:var(--mat-tab-active-ripple-color, var(--mat-sys-on-surface))}.mat-mdc-tab.mdc-tab--active:hover .mdc-tab__text-label{color:var(--mat-tab-active-hover-label-text-color, var(--mat-sys-on-surface))}.mat-mdc-tab.mdc-tab--active:hover .mdc-tab-indicator__content--underline{border-color:var(--mat-tab-active-hover-indicator-color, var(--mat-sys-primary))}.mat-mdc-tab.mdc-tab--active:focus .mdc-tab__text-label{color:var(--mat-tab-active-focus-label-text-color, var(--mat-sys-on-surface))}.mat-mdc-tab.mdc-tab--active:focus .mdc-tab-indicator__content--underline{border-color:var(--mat-tab-active-focus-indicator-color, var(--mat-sys-primary))}.mat-mdc-tab.mat-mdc-tab-disabled{opacity:.4;pointer-events:none}.mat-mdc-tab.mat-mdc-tab-disabled .mdc-tab__content{pointer-events:none}.mat-mdc-tab.mat-mdc-tab-disabled .mdc-tab__ripple::before,.mat-mdc-tab.mat-mdc-tab-disabled .mat-ripple-element{background-color:var(--mat-tab-disabled-ripple-color, var(--mat-sys-on-surface-variant))}.mat-mdc-tab .mdc-tab__ripple::before{content:"";display:block;position:absolute;top:0;left:0;right:0;bottom:0;opacity:0;pointer-events:none;background-color:var(--mat-tab-inactive-ripple-color, var(--mat-sys-on-surface))}.mat-mdc-tab .mdc-tab__text-label{color:var(--mat-tab-inactive-label-text-color, var(--mat-sys-on-surface));display:inline-flex;align-items:center}.mat-mdc-tab .mdc-tab__content{position:relative;pointer-events:auto}.mat-mdc-tab:hover .mdc-tab__ripple::before{opacity:.04}.mat-mdc-tab.cdk-program-focused .mdc-tab__ripple::before,.mat-mdc-tab.cdk-keyboard-focused .mdc-tab__ripple::before{opacity:.12}.mat-mdc-tab .mat-ripple-element{opacity:.12;background-color:var(--mat-tab-inactive-ripple-color, var(--mat-sys-on-surface))}.mat-mdc-tab-group.mat-mdc-tab-group-stretch-tabs>.mat-mdc-tab-header .mat-mdc-tab{flex-grow:1}.mat-mdc-tab-group{display:flex;flex-direction:column;max-width:100%}.mat-mdc-tab-group.mat-tabs-with-background>.mat-mdc-tab-header,.mat-mdc-tab-group.mat-tabs-with-background>.mat-mdc-tab-header-pagination{background-color:var(--mat-tab-background-color)}.mat-mdc-tab-group.mat-tabs-with-background.mat-primary>.mat-mdc-tab-header .mat-mdc-tab .mdc-tab__text-label{color:var(--mat-tab-foreground-color)}.mat-mdc-tab-group.mat-tabs-with-background.mat-primary>.mat-mdc-tab-header .mdc-tab-indicator__content--underline{border-color:var(--mat-tab-foreground-color)}.mat-mdc-tab-group.mat-tabs-with-background:not(.mat-primary)>.mat-mdc-tab-header .mat-mdc-tab:not(.mdc-tab--active) .mdc-tab__text-label{color:var(--mat-tab-foreground-color)}.mat-mdc-tab-group.mat-tabs-with-background:not(.mat-primary)>.mat-mdc-tab-header .mat-mdc-tab:not(.mdc-tab--active) .mdc-tab-indicator__content--underline{border-color:var(--mat-tab-foreground-color)}.mat-mdc-tab-group.mat-tabs-with-background>.mat-mdc-tab-header .mat-mdc-tab-header-pagination-chevron,.mat-mdc-tab-group.mat-tabs-with-background>.mat-mdc-tab-header .mat-focus-indicator::before,.mat-mdc-tab-group.mat-tabs-with-background>.mat-mdc-tab-header-pagination .mat-mdc-tab-header-pagination-chevron,.mat-mdc-tab-group.mat-tabs-with-background>.mat-mdc-tab-header-pagination .mat-focus-indicator::before{border-color:var(--mat-tab-foreground-color)}.mat-mdc-tab-group.mat-tabs-with-background>.mat-mdc-tab-header .mat-ripple-element,.mat-mdc-tab-group.mat-tabs-with-background>.mat-mdc-tab-header .mdc-tab__ripple::before,.mat-mdc-tab-group.mat-tabs-with-background>.mat-mdc-tab-header-pagination .mat-ripple-element,.mat-mdc-tab-group.mat-tabs-with-background>.mat-mdc-tab-header-pagination .mdc-tab__ripple::before{background-color:var(--mat-tab-foreground-color)}.mat-mdc-tab-group.mat-tabs-with-background>.mat-mdc-tab-header .mat-mdc-tab-header-pagination-chevron,.mat-mdc-tab-group.mat-tabs-with-background>.mat-mdc-tab-header-pagination .mat-mdc-tab-header-pagination-chevron{color:var(--mat-tab-foreground-color)}.mat-mdc-tab-group.mat-mdc-tab-group-inverted-header{flex-direction:column-reverse}.mat-mdc-tab-group.mat-mdc-tab-group-inverted-header .mdc-tab-indicator__content--underline{align-self:flex-start}.mat-mdc-tab-body-wrapper{position:relative;overflow:hidden;display:flex;transition:height 500ms cubic-bezier(0.35, 0, 0.25, 1)}.mat-mdc-tab-body-wrapper._mat-animation-noopable{transition:none !important;animation:none !important} -`],encapsulation:2})}return a})(),Re=class{index;tab},Tn=(()=>{class a extends ca{_focusedItem=Z(null);get fitInkBarToContent(){return this._fitInkBarToContent.value}set fitInkBarToContent(t){this._fitInkBarToContent.next(t),this._changeDetectorRef.markForCheck()}_fitInkBarToContent=new St(!1);stretchTabs=!0;get animationDuration(){return this._animationDuration}set animationDuration(t){let e=t+"";this._animationDuration=/^\d+$/.test(e)?t+"ms":e}_animationDuration;_items;get backgroundColor(){return this._backgroundColor}set backgroundColor(t){let e=this._elementRef.nativeElement.classList;e.remove("mat-tabs-with-background",`mat-background-${this.backgroundColor}`),t&&e.add("mat-tabs-with-background",`mat-background-${t}`),this._backgroundColor=t}_backgroundColor;get disableRipple(){return this._disableRipple()}set disableRipple(t){this._disableRipple.set(t)}_disableRipple=Z(!1);color="primary";tabPanel;_tabListContainer;_tabList;_tabListInner;_nextPaginator;_previousPaginator;_inkBar;constructor(){let t=r(da,{optional:!0});super(),this.disablePagination=t&&t.disablePagination!=null?t.disablePagination:!1,this.fitInkBarToContent=t&&t.fitInkBarToContent!=null?t.fitInkBarToContent:!1,this.stretchTabs=t&&t.stretchTabs!=null?t.stretchTabs:!0}_itemSelected(){}ngAfterContentInit(){this._inkBar=new pe(this._items),this._items.changes.pipe(at(null),I(this._destroyed)).subscribe(()=>this.updateActiveLink()),super.ngAfterContentInit(),this._keyManager.change.pipe(at(null),I(this._destroyed)).subscribe(()=>this._focusedItem.set(this._keyManager?.activeItem||null))}ngAfterViewInit(){this.tabPanel,super.ngAfterViewInit()}updateActiveLink(){if(!this._items)return;let t=this._items.toArray();for(let e=0;e.mat-mdc-tab-link-container .mat-mdc-tab-links{justify-content:center}[mat-align-tabs=end]>.mat-mdc-tab-link-container .mat-mdc-tab-links{justify-content:flex-end}.cdk-drop-list .mat-mdc-tab-links,.mat-mdc-tab-links.cdk-drop-list{min-height:var(--mat-tab-container-height, 48px)}.mat-mdc-tab-link-container{display:flex;flex-grow:1;overflow:hidden;z-index:1;border-bottom-style:solid;border-bottom-width:var(--mat-tab-divider-height, 1px);border-bottom-color:var(--mat-tab-divider-color, var(--mat-sys-surface-variant))}.mat-mdc-tab-nav-bar.mat-tabs-with-background>.mat-mdc-tab-link-container,.mat-mdc-tab-nav-bar.mat-tabs-with-background>.mat-mdc-tab-header-pagination{background-color:var(--mat-tab-background-color)}.mat-mdc-tab-nav-bar.mat-tabs-with-background.mat-primary>.mat-mdc-tab-link-container .mat-mdc-tab-link .mdc-tab__text-label{color:var(--mat-tab-foreground-color)}.mat-mdc-tab-nav-bar.mat-tabs-with-background.mat-primary>.mat-mdc-tab-link-container .mdc-tab-indicator__content--underline{border-color:var(--mat-tab-foreground-color)}.mat-mdc-tab-nav-bar.mat-tabs-with-background:not(.mat-primary)>.mat-mdc-tab-link-container .mat-mdc-tab-link:not(.mdc-tab--active) .mdc-tab__text-label{color:var(--mat-tab-foreground-color)}.mat-mdc-tab-nav-bar.mat-tabs-with-background:not(.mat-primary)>.mat-mdc-tab-link-container .mat-mdc-tab-link:not(.mdc-tab--active) .mdc-tab-indicator__content--underline{border-color:var(--mat-tab-foreground-color)}.mat-mdc-tab-nav-bar.mat-tabs-with-background>.mat-mdc-tab-link-container .mat-mdc-tab-header-pagination-chevron,.mat-mdc-tab-nav-bar.mat-tabs-with-background>.mat-mdc-tab-link-container .mat-focus-indicator::before,.mat-mdc-tab-nav-bar.mat-tabs-with-background>.mat-mdc-tab-header-pagination .mat-mdc-tab-header-pagination-chevron,.mat-mdc-tab-nav-bar.mat-tabs-with-background>.mat-mdc-tab-header-pagination .mat-focus-indicator::before{border-color:var(--mat-tab-foreground-color)}.mat-mdc-tab-nav-bar.mat-tabs-with-background>.mat-mdc-tab-link-container .mat-ripple-element,.mat-mdc-tab-nav-bar.mat-tabs-with-background>.mat-mdc-tab-link-container .mdc-tab__ripple::before,.mat-mdc-tab-nav-bar.mat-tabs-with-background>.mat-mdc-tab-header-pagination .mat-ripple-element,.mat-mdc-tab-nav-bar.mat-tabs-with-background>.mat-mdc-tab-header-pagination .mdc-tab__ripple::before{background-color:var(--mat-tab-foreground-color)}.mat-mdc-tab-nav-bar.mat-tabs-with-background>.mat-mdc-tab-link-container .mat-mdc-tab-header-pagination-chevron,.mat-mdc-tab-nav-bar.mat-tabs-with-background>.mat-mdc-tab-header-pagination .mat-mdc-tab-header-pagination-chevron{color:var(--mat-tab-foreground-color)} -`],encapsulation:2})}return a})(),Mn=(()=>{class a extends sa{_tabNavBar=r(Tn);elementRef=r(O);_focusMonitor=r(le);_destroyed=new k;_isActive=!1;_tabIndex=At(()=>this._tabNavBar._focusedItem()===this?this.tabIndex:-1);get active(){return this._isActive}set active(t){t!==this._isActive&&(this._isActive=t,this._tabNavBar.updateActiveLink())}disabled=!1;get disableRipple(){return this._disableRipple()}set disableRipple(t){this._disableRipple.set(t)}_disableRipple=Z(!1);tabIndex=0;rippleConfig;get rippleDisabled(){return this.disabled||this.disableRipple||this._tabNavBar.disableRipple||!!this.rippleConfig.disabled}id=r(U).getId("mat-tab-link-");constructor(){super(),r(wt).load(Ct);let t=r(Yt,{optional:!0}),e=r(new ie("tabindex"),{optional:!0});this.rippleConfig=t||{},this.tabIndex=e==null?0:parseInt(e)||0,Q()&&(this.rippleConfig.animation={enterDuration:0,exitDuration:0}),this._tabNavBar._fitInkBarToContent.pipe(I(this._destroyed)).subscribe(i=>{this.fitInkBarToContent=i})}focus(){this.elementRef.nativeElement.focus()}ngAfterViewInit(){this._focusMonitor.monitor(this.elementRef)}ngOnDestroy(){this._destroyed.next(),this._destroyed.complete(),super.ngOnDestroy(),this._focusMonitor.stopMonitoring(this.elementRef)}_handleFocus(){this._tabNavBar.focusIndex=this._tabNavBar._items.toArray().indexOf(this)}_handleKeydown(t){(t.keyCode===32||t.keyCode===13)&&(this.disabled?t.preventDefault():this._tabNavBar.tabPanel&&(t.keyCode===32&&t.preventDefault(),this.elementRef.nativeElement.click()))}_getAriaControls(){return this._tabNavBar.tabPanel?this._tabNavBar.tabPanel?.id:this.elementRef.nativeElement.getAttribute("aria-controls")}_getAriaSelected(){return this._tabNavBar.tabPanel?this.active?"true":"false":this.elementRef.nativeElement.getAttribute("aria-selected")}_getAriaCurrent(){return this.active&&!this._tabNavBar.tabPanel?"page":null}_getRole(){return this._tabNavBar.tabPanel?"tab":this.elementRef.nativeElement.getAttribute("role")}static \u0275fac=function(e){return new(e||a)};static \u0275cmp=C({type:a,selectors:[["","mat-tab-link",""],["","matTabLink",""]],hostAttrs:[1,"mdc-tab","mat-mdc-tab-link","mat-focus-indicator"],hostVars:11,hostBindings:function(e,i){e&1&&u("focus",function(){return i._handleFocus()})("keydown",function(o){return i._handleKeydown(o)}),e&2&&(T("aria-controls",i._getAriaControls())("aria-current",i._getAriaCurrent())("aria-disabled",i.disabled)("aria-selected",i._getAriaSelected())("id",i.id)("tabIndex",i._tabIndex())("role",i._getRole()),_("mat-mdc-tab-disabled",i.disabled)("mdc-tab--active",i.active))},inputs:{active:[2,"active","active",S],disabled:[2,"disabled","disabled",S],disableRipple:[2,"disableRipple","disableRipple",S],tabIndex:[2,"tabIndex","tabIndex",t=>t==null?0:bt(t)],id:"id"},exportAs:["matTabLink"],features:[q],attrs:bn,ngContentSelectors:Bt,decls:5,vars:2,consts:[[1,"mdc-tab__ripple"],["mat-ripple","",1,"mat-mdc-tab-ripple",3,"matRippleTrigger","matRippleDisabled"],[1,"mdc-tab__content"],[1,"mdc-tab__text-label"]],template:function(e,i){e&1&&(B(),M(0,"span",0)(1,"div",1),l(2,"span",2)(3,"span",3),y(4),d()()),e&2&&(c(),D("matRippleTrigger",i.elementRef.nativeElement)("matRippleDisabled",i.rippleDisabled))},dependencies:[gt],styles:[`.mat-mdc-tab-link{-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-decoration:none;background:none;height:var(--mat-tab-container-height, 48px);font-family:var(--mat-tab-label-text-font, var(--mat-sys-title-small-font));font-size:var(--mat-tab-label-text-size, var(--mat-sys-title-small-size));letter-spacing:var(--mat-tab-label-text-tracking, var(--mat-sys-title-small-tracking));line-height:var(--mat-tab-label-text-line-height, var(--mat-sys-title-small-line-height));font-weight:var(--mat-tab-label-text-weight, var(--mat-sys-title-small-weight))}.mat-mdc-tab-link.mdc-tab{flex-grow:0}.mat-mdc-tab-link .mdc-tab-indicator__content--underline{border-color:var(--mat-tab-active-indicator-color, var(--mat-sys-primary));border-top-width:var(--mat-tab-active-indicator-height, 2px);border-radius:var(--mat-tab-active-indicator-shape, 0)}.mat-mdc-tab-link:hover .mdc-tab__text-label{color:var(--mat-tab-inactive-hover-label-text-color, var(--mat-sys-on-surface))}.mat-mdc-tab-link:focus .mdc-tab__text-label{color:var(--mat-tab-inactive-focus-label-text-color, var(--mat-sys-on-surface))}.mat-mdc-tab-link.mdc-tab--active .mdc-tab__text-label{color:var(--mat-tab-active-label-text-color, var(--mat-sys-on-surface))}.mat-mdc-tab-link.mdc-tab--active .mdc-tab__ripple::before,.mat-mdc-tab-link.mdc-tab--active .mat-ripple-element{background-color:var(--mat-tab-active-ripple-color, var(--mat-sys-on-surface))}.mat-mdc-tab-link.mdc-tab--active:hover .mdc-tab__text-label{color:var(--mat-tab-active-hover-label-text-color, var(--mat-sys-on-surface))}.mat-mdc-tab-link.mdc-tab--active:hover .mdc-tab-indicator__content--underline{border-color:var(--mat-tab-active-hover-indicator-color, var(--mat-sys-primary))}.mat-mdc-tab-link.mdc-tab--active:focus .mdc-tab__text-label{color:var(--mat-tab-active-focus-label-text-color, var(--mat-sys-on-surface))}.mat-mdc-tab-link.mdc-tab--active:focus .mdc-tab-indicator__content--underline{border-color:var(--mat-tab-active-focus-indicator-color, var(--mat-sys-primary))}.mat-mdc-tab-link.mat-mdc-tab-disabled{opacity:.4;pointer-events:none}.mat-mdc-tab-link.mat-mdc-tab-disabled .mdc-tab__content{pointer-events:none}.mat-mdc-tab-link.mat-mdc-tab-disabled .mdc-tab__ripple::before,.mat-mdc-tab-link.mat-mdc-tab-disabled .mat-ripple-element{background-color:var(--mat-tab-disabled-ripple-color, var(--mat-sys-on-surface-variant))}.mat-mdc-tab-link .mdc-tab__ripple::before{content:"";display:block;position:absolute;top:0;left:0;right:0;bottom:0;opacity:0;pointer-events:none;background-color:var(--mat-tab-inactive-ripple-color, var(--mat-sys-on-surface))}.mat-mdc-tab-link .mdc-tab__text-label{color:var(--mat-tab-inactive-label-text-color, var(--mat-sys-on-surface));display:inline-flex;align-items:center}.mat-mdc-tab-link .mdc-tab__content{position:relative;pointer-events:auto}.mat-mdc-tab-link:hover .mdc-tab__ripple::before{opacity:.04}.mat-mdc-tab-link.cdk-program-focused .mdc-tab__ripple::before,.mat-mdc-tab-link.cdk-keyboard-focused .mdc-tab__ripple::before{opacity:.12}.mat-mdc-tab-link .mat-ripple-element{opacity:.12;background-color:var(--mat-tab-inactive-ripple-color, var(--mat-sys-on-surface))}.mat-mdc-tab-header.mat-mdc-tab-nav-bar-stretch-tabs .mat-mdc-tab-link{flex-grow:1}.mat-mdc-tab-link::before{margin:5px}@media(max-width: 599px){.mat-mdc-tab-link{min-width:72px}} -`],encapsulation:2,changeDetection:0})}return a})(),ss=(()=>{class a{id=r(U).getId("mat-tab-nav-panel-");_activeTabId;static \u0275fac=function(e){return new(e||a)};static \u0275cmp=C({type:a,selectors:[["mat-tab-nav-panel"]],hostAttrs:["role","tabpanel",1,"mat-mdc-tab-nav-panel"],hostVars:2,hostBindings:function(e,i){e&2&&T("aria-labelledby",i._activeTabId)("id",i.id)},inputs:{id:"id"},exportAs:["matTabNavPanel"],ngContentSelectors:Bt,decls:1,vars:0,template:function(e,i){e&1&&(B(),y(0))},encapsulation:2,changeDetection:0})}return a})(),ha=(()=>{class a{static \u0275fac=function(e){return new(e||a)};static \u0275mod=N({type:a});static \u0275inj=z({imports:[j]})}return a})();var In=["*",[["mat-option"],["ng-container"]]],Dn=["*","mat-option, ng-container"],En=["text"],Fn=[[["mat-icon"]],"*"],On=["mat-icon","*"];function Rn(a,s){if(a&1&&M(0,"mat-pseudo-checkbox",1),a&2){let t=p();D("disabled",t.disabled)("state",t.selected?"checked":"unchecked")}}function An(a,s){if(a&1&&M(0,"mat-pseudo-checkbox",3),a&2){let t=p();D("disabled",t.disabled)}}function Ln(a,s){if(a&1&&(l(0,"span",4),E(1),d()),a&2){let t=p();c(),ft("(",t.group.label,")")}}var fe=new w("MAT_OPTION_PARENT_COMPONENT"),ue=new w("MatOptgroup"),Pe=(()=>{class a{label;disabled=!1;_labelId=r(U).getId("mat-optgroup-label-");_inert;constructor(){let t=r(fe,{optional:!0});this._inert=t?.inertGroups??!1}static \u0275fac=function(e){return new(e||a)};static \u0275cmp=C({type:a,selectors:[["mat-optgroup"]],hostAttrs:[1,"mat-mdc-optgroup"],hostVars:3,hostBindings:function(e,i){e&2&&T("role",i._inert?null:"group")("aria-disabled",i._inert?null:i.disabled.toString())("aria-labelledby",i._inert?null:i._labelId)},inputs:{label:"label",disabled:[2,"disabled","disabled",S]},exportAs:["matOptgroup"],features:[$([{provide:ue,useExisting:a}])],ngContentSelectors:Dn,decls:5,vars:4,consts:[["role","presentation",1,"mat-mdc-optgroup-label",3,"id"],[1,"mdc-list-item__primary-text"]],template:function(e,i){e&1&&(B(In),kt(0,"span",0)(1,"span",1),E(2),y(3),Ot()(),y(4,1)),e&2&&(_("mdc-list-item--disabled",i.disabled),Rt("id",i._labelId),c(2),ft("",i.label," "))},styles:[`.mat-mdc-optgroup{color:var(--mat-optgroup-label-text-color, var(--mat-sys-on-surface-variant));font-family:var(--mat-optgroup-label-text-font, var(--mat-sys-title-small-font));line-height:var(--mat-optgroup-label-text-line-height, var(--mat-sys-title-small-line-height));font-size:var(--mat-optgroup-label-text-size, var(--mat-sys-title-small-size));letter-spacing:var(--mat-optgroup-label-text-tracking, var(--mat-sys-title-small-tracking));font-weight:var(--mat-optgroup-label-text-weight, var(--mat-sys-title-small-weight))}.mat-mdc-optgroup-label{display:flex;position:relative;align-items:center;justify-content:flex-start;overflow:hidden;min-height:48px;padding:0 16px;outline:none}.mat-mdc-optgroup-label.mdc-list-item--disabled{opacity:.38}.mat-mdc-optgroup-label .mdc-list-item__primary-text{font-size:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;font-family:inherit;text-decoration:inherit;text-transform:inherit;white-space:normal;color:inherit} -`],encapsulation:2,changeDetection:0})}return a})(),Le=class{source;isUserInput;constructor(s,t=!1){this.source=s,this.isUserInput=t}},zt=(()=>{class a{_element=r(O);_changeDetectorRef=r(W);_parent=r(fe,{optional:!0});group=r(ue,{optional:!0});_signalDisableRipple=!1;_selected=!1;_active=!1;_mostRecentViewValue="";get multiple(){return this._parent&&this._parent.multiple}get selected(){return this._selected}value;id=r(U).getId("mat-option-");get disabled(){return this.group&&this.group.disabled||this._disabled()}set disabled(t){this._disabled.set(t)}_disabled=Z(!1);get disableRipple(){return this._signalDisableRipple?this._parent.disableRipple():!!this._parent?.disableRipple}get hideSingleSelectionIndicator(){return!!(this._parent&&this._parent.hideSingleSelectionIndicator)}onSelectionChange=new F;_text;_stateChanges=new k;constructor(){let t=r(wt);t.load(Ct),t.load(zi),this._signalDisableRipple=!!this._parent&&oi(this._parent.disableRipple)}get active(){return this._active}get viewValue(){return(this._text?.nativeElement.textContent||"").trim()}select(t=!0){this._selected||(this._selected=!0,this._changeDetectorRef.markForCheck(),t&&this._emitSelectionChangeEvent())}deselect(t=!0){this._selected&&(this._selected=!1,this._changeDetectorRef.markForCheck(),t&&this._emitSelectionChangeEvent())}focus(t,e){let i=this._getHostElement();typeof i.focus=="function"&&i.focus(e)}setActiveStyles(){this._active||(this._active=!0,this._changeDetectorRef.markForCheck())}setInactiveStyles(){this._active&&(this._active=!1,this._changeDetectorRef.markForCheck())}getLabel(){return this.viewValue}_handleKeydown(t){(t.keyCode===13||t.keyCode===32)&&!ct(t)&&(this._selectViaInteraction(),t.preventDefault())}_selectViaInteraction(){this.disabled||(this._selected=this.multiple?!this._selected:!0,this._changeDetectorRef.markForCheck(),this._emitSelectionChangeEvent(!0))}_getTabIndex(){return this.disabled?"-1":"0"}_getHostElement(){return this._element.nativeElement}ngAfterViewChecked(){if(this._selected){let t=this.viewValue;t!==this._mostRecentViewValue&&(this._mostRecentViewValue&&this._stateChanges.next(),this._mostRecentViewValue=t)}}ngOnDestroy(){this._stateChanges.complete()}_emitSelectionChangeEvent(t=!1){this.onSelectionChange.emit(new Le(this,t))}static \u0275fac=function(e){return new(e||a)};static \u0275cmp=C({type:a,selectors:[["mat-option"]],viewQuery:function(e,i){if(e&1&&V(En,7),e&2){let n;m(n=h())&&(i._text=n.first)}},hostAttrs:["role","option",1,"mat-mdc-option","mdc-list-item"],hostVars:11,hostBindings:function(e,i){e&1&&u("click",function(){return i._selectViaInteraction()})("keydown",function(o){return i._handleKeydown(o)}),e&2&&(Rt("id",i.id),T("aria-selected",i.selected)("aria-disabled",i.disabled.toString()),_("mdc-list-item--selected",i.selected)("mat-mdc-option-multiple",i.multiple)("mat-mdc-option-active",i.active)("mdc-list-item--disabled",i.disabled))},inputs:{value:"value",id:"id",disabled:[2,"disabled","disabled",S]},outputs:{onSelectionChange:"onSelectionChange"},exportAs:["matOption"],ngContentSelectors:On,decls:8,vars:5,consts:[["text",""],["aria-hidden","true",1,"mat-mdc-option-pseudo-checkbox",3,"disabled","state"],[1,"mdc-list-item__primary-text"],["state","checked","aria-hidden","true","appearance","minimal",1,"mat-mdc-option-pseudo-checkbox",3,"disabled"],[1,"cdk-visually-hidden"],["aria-hidden","true","mat-ripple","",1,"mat-mdc-option-ripple","mat-focus-indicator",3,"matRippleTrigger","matRippleDisabled"]],template:function(e,i){e&1&&(B(Fn),g(0,Rn,1,2,"mat-pseudo-checkbox",1),y(1),l(2,"span",2,0),y(4,1),d(),g(5,An,1,1,"mat-pseudo-checkbox",3),g(6,Ln,2,1,"span",4),M(7,"div",5)),e&2&&(v(i.multiple?0:-1),c(5),v(!i.multiple&&i.selected&&!i.hideSingleSelectionIndicator?5:-1),c(),v(i.group&&i.group._inert?6:-1),c(),D("matRippleTrigger",i._getHostElement())("matRippleDisabled",i.disabled||i.disableRipple))},dependencies:[Ki,gt],styles:[`.mat-mdc-option{-webkit-user-select:none;user-select:none;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:flex;position:relative;align-items:center;justify-content:flex-start;overflow:hidden;min-height:48px;padding:0 16px;cursor:pointer;-webkit-tap-highlight-color:rgba(0,0,0,0);color:var(--mat-option-label-text-color, var(--mat-sys-on-surface));font-family:var(--mat-option-label-text-font, var(--mat-sys-label-large-font));line-height:var(--mat-option-label-text-line-height, var(--mat-sys-label-large-line-height));font-size:var(--mat-option-label-text-size, var(--mat-sys-body-large-size));letter-spacing:var(--mat-option-label-text-tracking, var(--mat-sys-label-large-tracking));font-weight:var(--mat-option-label-text-weight, var(--mat-sys-body-large-weight))}.mat-mdc-option:hover:not(.mdc-list-item--disabled){background-color:var(--mat-option-hover-state-layer-color, color-mix(in srgb, var(--mat-sys-on-surface) calc(var(--mat-sys-hover-state-layer-opacity) * 100%), transparent))}.mat-mdc-option:focus.mdc-list-item,.mat-mdc-option.mat-mdc-option-active.mdc-list-item{background-color:var(--mat-option-focus-state-layer-color, color-mix(in srgb, var(--mat-sys-on-surface) calc(var(--mat-sys-focus-state-layer-opacity) * 100%), transparent));outline:0}.mat-mdc-option.mdc-list-item--selected:not(.mdc-list-item--disabled):not(.mat-mdc-option-active,.mat-mdc-option-multiple,:focus,:hover){background-color:var(--mat-option-selected-state-layer-color, var(--mat-sys-secondary-container))}.mat-mdc-option.mdc-list-item--selected:not(.mdc-list-item--disabled):not(.mat-mdc-option-active,.mat-mdc-option-multiple,:focus,:hover) .mdc-list-item__primary-text{color:var(--mat-option-selected-state-label-text-color, var(--mat-sys-on-secondary-container))}.mat-mdc-option .mat-pseudo-checkbox{--mat-pseudo-checkbox-minimal-selected-checkmark-color: var(--mat-option-selected-state-label-text-color, var(--mat-sys-on-secondary-container))}.mat-mdc-option.mdc-list-item{align-items:center;background:rgba(0,0,0,0)}.mat-mdc-option.mdc-list-item--disabled{cursor:default;pointer-events:none}.mat-mdc-option.mdc-list-item--disabled .mat-mdc-option-pseudo-checkbox,.mat-mdc-option.mdc-list-item--disabled .mdc-list-item__primary-text,.mat-mdc-option.mdc-list-item--disabled>mat-icon{opacity:.38}.mat-mdc-optgroup .mat-mdc-option:not(.mat-mdc-option-multiple){padding-left:32px}[dir=rtl] .mat-mdc-optgroup .mat-mdc-option:not(.mat-mdc-option-multiple){padding-left:16px;padding-right:32px}.mat-mdc-option .mat-icon,.mat-mdc-option .mat-pseudo-checkbox-full{margin-right:16px;flex-shrink:0}[dir=rtl] .mat-mdc-option .mat-icon,[dir=rtl] .mat-mdc-option .mat-pseudo-checkbox-full{margin-right:0;margin-left:16px}.mat-mdc-option .mat-pseudo-checkbox-minimal{margin-left:16px;flex-shrink:0}[dir=rtl] .mat-mdc-option .mat-pseudo-checkbox-minimal{margin-right:16px;margin-left:0}.mat-mdc-option .mat-mdc-option-ripple{top:0;left:0;right:0;bottom:0;position:absolute;pointer-events:none}.mat-mdc-option .mdc-list-item__primary-text{white-space:normal;font-size:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;font-family:inherit;text-decoration:inherit;text-transform:inherit;margin-right:auto}[dir=rtl] .mat-mdc-option .mdc-list-item__primary-text{margin-right:0;margin-left:auto}@media(forced-colors: active){.mat-mdc-option.mdc-list-item--selected:not(:has(.mat-mdc-option-pseudo-checkbox))::after{content:"";position:absolute;top:50%;right:16px;transform:translateY(-50%);width:10px;height:0;border-bottom:solid 10px;border-radius:10px}[dir=rtl] .mat-mdc-option.mdc-list-item--selected:not(:has(.mat-mdc-option-pseudo-checkbox))::after{right:auto;left:16px}}.mat-mdc-option-multiple{--mat-list-list-item-selected-container-color: var(--mat-list-list-item-container-color, transparent)}.mat-mdc-option-active .mat-focus-indicator::before{content:""} -`],encapsulation:2,changeDetection:0})}return a})();function pa(a,s,t){if(t.length){let e=s.toArray(),i=t.toArray(),n=0;for(let o=0;ot+e?Math.max(0,a-e+s):t}var Pn=["notch"],Bn=["matFormFieldNotchedOutline",""],zn=["*"],ua=["iconPrefixContainer"],_a=["textPrefixContainer"],ba=["iconSuffixContainer"],ga=["textSuffixContainer"],Nn=["textField"],jn=["*",[["mat-label"]],[["","matPrefix",""],["","matIconPrefix",""]],[["","matTextPrefix",""]],[["","matTextSuffix",""]],[["","matSuffix",""],["","matIconSuffix",""]],[["mat-error"],["","matError",""]],[["mat-hint",3,"align","end"]],[["mat-hint","align","end"]]],Vn=["*","mat-label","[matPrefix], [matIconPrefix]","[matTextPrefix]","[matTextSuffix]","[matSuffix], [matIconSuffix]","mat-error, [matError]","mat-hint:not([align='end'])","mat-hint[align='end']"];function Hn(a,s){a&1&&M(0,"span",21)}function $n(a,s){if(a&1&&(l(0,"label",20),y(1,1),g(2,Hn,1,0,"span",21),d()),a&2){let t=p(2);D("floating",t._shouldLabelFloat())("monitorResize",t._hasOutline())("id",t._labelId),T("for",t._control.disableAutomaticLabeling?null:t._control.id),c(2),v(!t.hideRequiredMarker&&t._control.required?2:-1)}}function Wn(a,s){if(a&1&&g(0,$n,3,5,"label",20),a&2){let t=p();v(t._hasFloatingLabel()?0:-1)}}function Qn(a,s){a&1&&M(0,"div",7)}function Gn(a,s){}function qn(a,s){if(a&1&&tt(0,Gn,0,0,"ng-template",13),a&2){p(2);let t=ht(1);D("ngTemplateOutlet",t)}}function Un(a,s){if(a&1&&(l(0,"div",9),g(1,qn,1,1,null,13),d()),a&2){let t=p();D("matFormFieldNotchedOutlineOpen",t._shouldLabelFloat()),c(),v(t._forceDisplayInfixLabel()?-1:1)}}function Yn(a,s){a&1&&(l(0,"div",10,2),y(2,2),d())}function Kn(a,s){a&1&&(l(0,"div",11,3),y(2,3),d())}function Zn(a,s){}function Xn(a,s){if(a&1&&tt(0,Zn,0,0,"ng-template",13),a&2){p();let t=ht(1);D("ngTemplateOutlet",t)}}function Jn(a,s){a&1&&(l(0,"div",14,4),y(2,4),d())}function to(a,s){a&1&&(l(0,"div",15,5),y(2,5),d())}function eo(a,s){a&1&&M(0,"div",16)}function io(a,s){a&1&&(l(0,"div",18),y(1,6),d())}function ao(a,s){if(a&1&&(l(0,"mat-hint",22),E(1),d()),a&2){let t=p(2);D("id",t._hintLabelId),c(),pt(t.hintLabel)}}function no(a,s){if(a&1&&(l(0,"div",19),g(1,ao,2,2,"mat-hint",22),y(2,7),M(3,"div",23),y(4,8),d()),a&2){let t=p();c(),v(t.hintLabel?1:-1)}}var Be=(()=>{class a{static \u0275fac=function(e){return new(e||a)};static \u0275dir=x({type:a,selectors:[["mat-label"]]})}return a})(),Ta=new w("MatError"),oo=(()=>{class a{id=r(U).getId("mat-mdc-error-");constructor(){}static \u0275fac=function(e){return new(e||a)};static \u0275dir=x({type:a,selectors:[["mat-error"],["","matError",""]],hostAttrs:[1,"mat-mdc-form-field-error","mat-mdc-form-field-bottom-align"],hostVars:1,hostBindings:function(e,i){e&2&&Rt("id",i.id)},inputs:{id:"id"},features:[$([{provide:Ta,useExisting:a}])]})}return a})(),ze=(()=>{class a{align="start";id=r(U).getId("mat-mdc-hint-");static \u0275fac=function(e){return new(e||a)};static \u0275dir=x({type:a,selectors:[["mat-hint"]],hostAttrs:[1,"mat-mdc-form-field-hint","mat-mdc-form-field-bottom-align"],hostVars:4,hostBindings:function(e,i){e&2&&(Rt("id",i.id),T("align",null),_("mat-mdc-form-field-hint-end",i.align==="end"))},inputs:{align:"align",id:"id"}})}return a})(),Ma=new w("MatPrefix"),ro=(()=>{class a{set _isTextSelector(t){this._isText=!0}_isText=!1;static \u0275fac=function(e){return new(e||a)};static \u0275dir=x({type:a,selectors:[["","matPrefix",""],["","matIconPrefix",""],["","matTextPrefix",""]],inputs:{_isTextSelector:[0,"matTextPrefix","_isTextSelector"]},features:[$([{provide:Ma,useExisting:a}])]})}return a})(),Sa=new w("MatSuffix"),so=(()=>{class a{set _isTextSelector(t){this._isText=!0}_isText=!1;static \u0275fac=function(e){return new(e||a)};static \u0275dir=x({type:a,selectors:[["","matSuffix",""],["","matIconSuffix",""],["","matTextSuffix",""]],inputs:{_isTextSelector:[0,"matTextSuffix","_isTextSelector"]},features:[$([{provide:Sa,useExisting:a}])]})}return a})(),Ia=new w("FloatingLabelParent"),va=(()=>{class a{_elementRef=r(O);get floating(){return this._floating}set floating(t){this._floating=t,this.monitorResize&&this._handleResize()}_floating=!1;get monitorResize(){return this._monitorResize}set monitorResize(t){this._monitorResize=t,this._monitorResize?this._subscribeToResize():this._resizeSubscription.unsubscribe()}_monitorResize=!1;_resizeObserver=r(he);_ngZone=r(H);_parent=r(Ia);_resizeSubscription=new it;constructor(){}ngOnDestroy(){this._resizeSubscription.unsubscribe()}getWidth(){return lo(this._elementRef.nativeElement)}get element(){return this._elementRef.nativeElement}_handleResize(){setTimeout(()=>this._parent._handleLabelResized())}_subscribeToResize(){this._resizeSubscription.unsubscribe(),this._ngZone.runOutsideAngular(()=>{this._resizeSubscription=this._resizeObserver.observe(this._elementRef.nativeElement,{box:"border-box"}).subscribe(()=>this._handleResize())})}static \u0275fac=function(e){return new(e||a)};static \u0275dir=x({type:a,selectors:[["label","matFormFieldFloatingLabel",""]],hostAttrs:[1,"mdc-floating-label","mat-mdc-floating-label"],hostVars:2,hostBindings:function(e,i){e&2&&_("mdc-floating-label--float-above",i.floating)},inputs:{floating:"floating",monitorResize:"monitorResize"}})}return a})();function lo(a){let s=a;if(s.offsetParent!==null)return s.scrollWidth;let t=s.cloneNode(!0);t.style.setProperty("position","absolute"),t.style.setProperty("transform","translate(-9999px, -9999px)"),document.documentElement.appendChild(t);let e=t.scrollWidth;return t.remove(),e}var ya="mdc-line-ripple--active",_e="mdc-line-ripple--deactivating",xa=(()=>{class a{_elementRef=r(O);_cleanupTransitionEnd;constructor(){let t=r(H),e=r(st);t.runOutsideAngular(()=>{this._cleanupTransitionEnd=e.listen(this._elementRef.nativeElement,"transitionend",this._handleTransitionEnd)})}activate(){let t=this._elementRef.nativeElement.classList;t.remove(_e),t.add(ya)}deactivate(){this._elementRef.nativeElement.classList.add(_e)}_handleTransitionEnd=t=>{let e=this._elementRef.nativeElement.classList,i=e.contains(_e);t.propertyName==="opacity"&&i&&e.remove(ya,_e)};ngOnDestroy(){this._cleanupTransitionEnd()}static \u0275fac=function(e){return new(e||a)};static \u0275dir=x({type:a,selectors:[["div","matFormFieldLineRipple",""]],hostAttrs:[1,"mdc-line-ripple"]})}return a})(),ka=(()=>{class a{_elementRef=r(O);_ngZone=r(H);open=!1;_notch;ngAfterViewInit(){let t=this._elementRef.nativeElement,e=t.querySelector(".mdc-floating-label");e?(t.classList.add("mdc-notched-outline--upgraded"),typeof requestAnimationFrame=="function"&&(e.style.transitionDuration="0s",this._ngZone.runOutsideAngular(()=>{requestAnimationFrame(()=>e.style.transitionDuration="")}))):t.classList.add("mdc-notched-outline--no-label")}_setNotchWidth(t){let e=this._notch.nativeElement;!this.open||!t?e.style.width="":e.style.width=`calc(${t}px * var(--mat-mdc-form-field-floating-label-scale, 0.75) + 9px)`}_setMaxWidth(t){this._notch.nativeElement.style.setProperty("--mat-form-field-notch-max-width",`calc(100% - ${t}px)`)}static \u0275fac=function(e){return new(e||a)};static \u0275cmp=C({type:a,selectors:[["div","matFormFieldNotchedOutline",""]],viewQuery:function(e,i){if(e&1&&V(Pn,5),e&2){let n;m(n=h())&&(i._notch=n.first)}},hostAttrs:[1,"mdc-notched-outline"],hostVars:2,hostBindings:function(e,i){e&2&&_("mdc-notched-outline--notched",i.open)},inputs:{open:[0,"matFormFieldNotchedOutlineOpen","open"]},attrs:Bn,ngContentSelectors:zn,decls:5,vars:0,consts:[["notch",""],[1,"mat-mdc-notch-piece","mdc-notched-outline__leading"],[1,"mat-mdc-notch-piece","mdc-notched-outline__notch"],[1,"mat-mdc-notch-piece","mdc-notched-outline__trailing"]],template:function(e,i){e&1&&(B(),ke(0,"div",1),kt(1,"div",2,0),y(3),Ot(),ke(4,"div",3))},encapsulation:2,changeDetection:0})}return a})(),Ne=(()=>{class a{value=null;stateChanges;id;placeholder;ngControl=null;focused=!1;empty=!1;shouldLabelFloat=!1;required=!1;disabled=!1;errorState=!1;controlType;autofilled;userAriaDescribedBy;disableAutomaticLabeling;describedByIds;static \u0275fac=function(e){return new(e||a)};static \u0275dir=x({type:a})}return a})();var je=new w("MatFormField"),co=new w("MAT_FORM_FIELD_DEFAULT_OPTIONS"),wa="fill",mo="auto",Ca="fixed",ho="translateY(-50%)",Da=(()=>{class a{_elementRef=r(O);_changeDetectorRef=r(W);_platform=r(ot);_idGenerator=r(U);_ngZone=r(H);_defaults=r(co,{optional:!0});_currentDirection;_textField;_iconPrefixContainer;_textPrefixContainer;_iconSuffixContainer;_textSuffixContainer;_floatingLabel;_notchedOutline;_lineRipple;_iconPrefixContainerSignal=Qt("iconPrefixContainer");_textPrefixContainerSignal=Qt("textPrefixContainer");_iconSuffixContainerSignal=Qt("iconSuffixContainer");_textSuffixContainerSignal=Qt("textSuffixContainer");_prefixSuffixContainers=At(()=>[this._iconPrefixContainerSignal(),this._textPrefixContainerSignal(),this._iconSuffixContainerSignal(),this._textSuffixContainerSignal()].map(t=>t?.nativeElement).filter(t=>t!==void 0));_formFieldControl;_prefixChildren;_suffixChildren;_errorChildren;_hintChildren;_labelChild=bi(Be);get hideRequiredMarker(){return this._hideRequiredMarker}set hideRequiredMarker(t){this._hideRequiredMarker=K(t)}_hideRequiredMarker=!1;color="primary";get floatLabel(){return this._floatLabel||this._defaults?.floatLabel||mo}set floatLabel(t){t!==this._floatLabel&&(this._floatLabel=t,this._changeDetectorRef.markForCheck())}_floatLabel;get appearance(){return this._appearanceSignal()}set appearance(t){let e=t||this._defaults?.appearance||wa;this._appearanceSignal.set(e)}_appearanceSignal=Z(wa);get subscriptSizing(){return this._subscriptSizing||this._defaults?.subscriptSizing||Ca}set subscriptSizing(t){this._subscriptSizing=t||this._defaults?.subscriptSizing||Ca}_subscriptSizing=null;get hintLabel(){return this._hintLabel}set hintLabel(t){this._hintLabel=t,this._processHints()}_hintLabel="";_hasIconPrefix=!1;_hasTextPrefix=!1;_hasIconSuffix=!1;_hasTextSuffix=!1;_labelId=this._idGenerator.getId("mat-mdc-form-field-label-");_hintLabelId=this._idGenerator.getId("mat-mdc-hint-");_describedByIds;get _control(){return this._explicitFormFieldControl||this._formFieldControl}set _control(t){this._explicitFormFieldControl=t}_destroyed=new k;_isFocused=null;_explicitFormFieldControl;_previousControl=null;_previousControlValidatorFn=null;_stateChanges;_valueChanges;_describedByChanges;_outlineLabelOffsetResizeObserver=null;_animationsDisabled=Q();constructor(){let t=this._defaults,e=r(ut);t&&(t.appearance&&(this.appearance=t.appearance),this._hideRequiredMarker=!!t?.hideRequiredMarker,t.color&&(this.color=t.color)),ii(()=>this._currentDirection=e.valueSignal()),this._syncOutlineLabelOffset()}ngAfterViewInit(){this._updateFocusState(),this._animationsDisabled||this._ngZone.runOutsideAngular(()=>{setTimeout(()=>{this._elementRef.nativeElement.classList.add("mat-form-field-animations-enabled")},300)}),this._changeDetectorRef.detectChanges()}ngAfterContentInit(){this._assertFormFieldControl(),this._initializeSubscript(),this._initializePrefixAndSuffix()}ngAfterContentChecked(){this._assertFormFieldControl(),this._control!==this._previousControl&&(this._initializeControl(this._previousControl),this._control.ngControl&&this._control.ngControl.control&&(this._previousControlValidatorFn=this._control.ngControl.control.validator),this._previousControl=this._control),this._control.ngControl&&this._control.ngControl.control&&this._control.ngControl.control.validator!==this._previousControlValidatorFn&&this._changeDetectorRef.markForCheck()}ngOnDestroy(){this._outlineLabelOffsetResizeObserver?.disconnect(),this._stateChanges?.unsubscribe(),this._valueChanges?.unsubscribe(),this._describedByChanges?.unsubscribe(),this._destroyed.next(),this._destroyed.complete()}getLabelId=At(()=>this._hasFloatingLabel()?this._labelId:null);getConnectedOverlayOrigin(){return this._textField||this._elementRef}_animateAndLockLabel(){this._hasFloatingLabel()&&(this.floatLabel="always")}_initializeControl(t){let e=this._control,i="mat-mdc-form-field-type-";t&&this._elementRef.nativeElement.classList.remove(i+t.controlType),e.controlType&&this._elementRef.nativeElement.classList.add(i+e.controlType),this._stateChanges?.unsubscribe(),this._stateChanges=e.stateChanges.subscribe(()=>{this._updateFocusState(),this._changeDetectorRef.markForCheck()}),this._describedByChanges?.unsubscribe(),this._describedByChanges=e.stateChanges.pipe(at([void 0,void 0]),Ht(()=>[e.errorState,e.userAriaDescribedBy]),Je(),dt(([[n,o],[f,b]])=>n!==f||o!==b)).subscribe(()=>this._syncDescribedByIds()),this._valueChanges?.unsubscribe(),e.ngControl&&e.ngControl.valueChanges&&(this._valueChanges=e.ngControl.valueChanges.pipe(I(this._destroyed)).subscribe(()=>this._changeDetectorRef.markForCheck()))}_checkPrefixAndSuffixTypes(){this._hasIconPrefix=!!this._prefixChildren.find(t=>!t._isText),this._hasTextPrefix=!!this._prefixChildren.find(t=>t._isText),this._hasIconSuffix=!!this._suffixChildren.find(t=>!t._isText),this._hasTextSuffix=!!this._suffixChildren.find(t=>t._isText)}_initializePrefixAndSuffix(){this._checkPrefixAndSuffixTypes(),J(this._prefixChildren.changes,this._suffixChildren.changes).subscribe(()=>{this._checkPrefixAndSuffixTypes(),this._changeDetectorRef.markForCheck()})}_initializeSubscript(){this._hintChildren.changes.subscribe(()=>{this._processHints(),this._changeDetectorRef.markForCheck()}),this._errorChildren.changes.subscribe(()=>{this._syncDescribedByIds(),this._changeDetectorRef.markForCheck()}),this._validateHints(),this._syncDescribedByIds()}_assertFormFieldControl(){this._control}_updateFocusState(){let t=this._control.focused;t&&!this._isFocused?(this._isFocused=!0,this._lineRipple?.activate()):!t&&(this._isFocused||this._isFocused===null)&&(this._isFocused=!1,this._lineRipple?.deactivate()),this._elementRef.nativeElement.classList.toggle("mat-focused",t),this._textField?.nativeElement.classList.toggle("mdc-text-field--focused",t)}_syncOutlineLabelOffset(){gi({earlyRead:()=>{if(this._appearanceSignal()!=="outline")return this._outlineLabelOffsetResizeObserver?.disconnect(),null;if(globalThis.ResizeObserver){this._outlineLabelOffsetResizeObserver||=new globalThis.ResizeObserver(()=>{this._writeOutlinedLabelStyles(this._getOutlinedLabelOffset())});for(let t of this._prefixSuffixContainers())this._outlineLabelOffsetResizeObserver.observe(t,{box:"border-box"})}return this._getOutlinedLabelOffset()},write:t=>this._writeOutlinedLabelStyles(t())})}_shouldAlwaysFloat(){return this.floatLabel==="always"}_hasOutline(){return this.appearance==="outline"}_forceDisplayInfixLabel(){return!this._platform.isBrowser&&this._prefixChildren.length&&!this._shouldLabelFloat()}_hasFloatingLabel=At(()=>!!this._labelChild());_shouldLabelFloat(){return this._hasFloatingLabel()?this._control.shouldLabelFloat||this._shouldAlwaysFloat():!1}_shouldForward(t){let e=this._control?this._control.ngControl:null;return e&&e[t]}_getSubscriptMessageType(){return this._errorChildren&&this._errorChildren.length>0&&this._control.errorState?"error":"hint"}_handleLabelResized(){this._refreshOutlineNotchWidth()}_refreshOutlineNotchWidth(){!this._hasOutline()||!this._floatingLabel||!this._shouldLabelFloat()?this._notchedOutline?._setNotchWidth(0):this._notchedOutline?._setNotchWidth(this._floatingLabel.getWidth())}_processHints(){this._validateHints(),this._syncDescribedByIds()}_validateHints(){this._hintChildren}_syncDescribedByIds(){if(this._control){let t=[];if(this._control.userAriaDescribedBy&&typeof this._control.userAriaDescribedBy=="string"&&t.push(...this._control.userAriaDescribedBy.split(" ")),this._getSubscriptMessageType()==="hint"){let n=this._hintChildren?this._hintChildren.find(f=>f.align==="start"):null,o=this._hintChildren?this._hintChildren.find(f=>f.align==="end"):null;n?t.push(n.id):this._hintLabel&&t.push(this._hintLabelId),o&&t.push(o.id)}else this._errorChildren&&t.push(...this._errorChildren.map(n=>n.id));let e=this._control.describedByIds,i;if(e){let n=this._describedByIds||t;i=t.concat(e.filter(o=>o&&!n.includes(o)))}else i=t;this._control.setDescribedByIds(i),this._describedByIds=t}}_getOutlinedLabelOffset(){if(!this._hasOutline()||!this._floatingLabel)return null;if(!this._iconPrefixContainer&&!this._textPrefixContainer)return["",null];if(!this._isAttachedToDom())return null;let t=this._iconPrefixContainer?.nativeElement,e=this._textPrefixContainer?.nativeElement,i=this._iconSuffixContainer?.nativeElement,n=this._textSuffixContainer?.nativeElement,o=t?.getBoundingClientRect().width??0,f=e?.getBoundingClientRect().width??0,b=i?.getBoundingClientRect().width??0,xt=n?.getBoundingClientRect().width??0,Mt=this._currentDirection==="rtl"?"-1":"1",tn=`${o+f}px`,en=`calc(${Mt} * (${tn} + var(--mat-mdc-form-field-label-offset-x, 0px)))`,an=`var(--mat-mdc-form-field-label-transform, ${ho} translateX(${en}))`,nn=o+f+b+xt;return[an,nn]}_writeOutlinedLabelStyles(t){if(t!==null){let[e,i]=t;this._floatingLabel&&(this._floatingLabel.element.style.transform=e),i!==null&&this._notchedOutline?._setMaxWidth(i)}}_isAttachedToDom(){let t=this._elementRef.nativeElement;if(t.getRootNode){let e=t.getRootNode();return e&&e!==t}return document.documentElement.contains(t)}static \u0275fac=function(e){return new(e||a)};static \u0275cmp=C({type:a,selectors:[["mat-form-field"]],contentQueries:function(e,i,n){if(e&1&&(li(n,i._labelChild,Be,5),et(n,Ne,5)(n,Ma,5)(n,Sa,5)(n,Ta,5)(n,ze,5)),e&2){we();let o;m(o=h())&&(i._formFieldControl=o.first),m(o=h())&&(i._prefixChildren=o),m(o=h())&&(i._suffixChildren=o),m(o=h())&&(i._errorChildren=o),m(o=h())&&(i._hintChildren=o)}},viewQuery:function(e,i){if(e&1&&(ci(i._iconPrefixContainerSignal,ua,5)(i._textPrefixContainerSignal,_a,5)(i._iconSuffixContainerSignal,ba,5)(i._textSuffixContainerSignal,ga,5),V(Nn,5)(ua,5)(_a,5)(ba,5)(ga,5)(va,5)(ka,5)(xa,5)),e&2){we(4);let n;m(n=h())&&(i._textField=n.first),m(n=h())&&(i._iconPrefixContainer=n.first),m(n=h())&&(i._textPrefixContainer=n.first),m(n=h())&&(i._iconSuffixContainer=n.first),m(n=h())&&(i._textSuffixContainer=n.first),m(n=h())&&(i._floatingLabel=n.first),m(n=h())&&(i._notchedOutline=n.first),m(n=h())&&(i._lineRipple=n.first)}},hostAttrs:[1,"mat-mdc-form-field"],hostVars:38,hostBindings:function(e,i){e&2&&_("mat-mdc-form-field-label-always-float",i._shouldAlwaysFloat())("mat-mdc-form-field-has-icon-prefix",i._hasIconPrefix)("mat-mdc-form-field-has-icon-suffix",i._hasIconSuffix)("mat-form-field-invalid",i._control.errorState)("mat-form-field-disabled",i._control.disabled)("mat-form-field-autofilled",i._control.autofilled)("mat-form-field-appearance-fill",i.appearance=="fill")("mat-form-field-appearance-outline",i.appearance=="outline")("mat-form-field-hide-placeholder",i._hasFloatingLabel()&&!i._shouldLabelFloat())("mat-primary",i.color!=="accent"&&i.color!=="warn")("mat-accent",i.color==="accent")("mat-warn",i.color==="warn")("ng-untouched",i._shouldForward("untouched"))("ng-touched",i._shouldForward("touched"))("ng-pristine",i._shouldForward("pristine"))("ng-dirty",i._shouldForward("dirty"))("ng-valid",i._shouldForward("valid"))("ng-invalid",i._shouldForward("invalid"))("ng-pending",i._shouldForward("pending"))},inputs:{hideRequiredMarker:"hideRequiredMarker",color:"color",floatLabel:"floatLabel",appearance:"appearance",subscriptSizing:"subscriptSizing",hintLabel:"hintLabel"},exportAs:["matFormField"],features:[$([{provide:je,useExisting:a},{provide:Ia,useExisting:a}])],ngContentSelectors:Vn,decls:18,vars:21,consts:[["labelTemplate",""],["textField",""],["iconPrefixContainer",""],["textPrefixContainer",""],["textSuffixContainer",""],["iconSuffixContainer",""],[1,"mat-mdc-text-field-wrapper","mdc-text-field",3,"click"],[1,"mat-mdc-form-field-focus-overlay"],[1,"mat-mdc-form-field-flex"],["matFormFieldNotchedOutline","",3,"matFormFieldNotchedOutlineOpen"],[1,"mat-mdc-form-field-icon-prefix"],[1,"mat-mdc-form-field-text-prefix"],[1,"mat-mdc-form-field-infix"],[3,"ngTemplateOutlet"],[1,"mat-mdc-form-field-text-suffix"],[1,"mat-mdc-form-field-icon-suffix"],["matFormFieldLineRipple",""],["aria-atomic","true","aria-live","polite",1,"mat-mdc-form-field-subscript-wrapper","mat-mdc-form-field-bottom-align"],[1,"mat-mdc-form-field-error-wrapper"],[1,"mat-mdc-form-field-hint-wrapper"],["matFormFieldFloatingLabel","",3,"floating","monitorResize","id"],["aria-hidden","true",1,"mat-mdc-form-field-required-marker","mdc-floating-label--required"],[3,"id"],[1,"mat-mdc-form-field-hint-spacer"]],template:function(e,i){if(e&1&&(B(jn),tt(0,Wn,1,1,"ng-template",null,0,Te),l(2,"div",6,1),u("click",function(o){return i._control.onContainerClick(o)}),g(4,Qn,1,0,"div",7),l(5,"div",8),g(6,Un,2,2,"div",9),g(7,Yn,3,0,"div",10),g(8,Kn,3,0,"div",11),l(9,"div",12),g(10,Xn,1,1,null,13),y(11),d(),g(12,Jn,3,0,"div",14),g(13,to,3,0,"div",15),d(),g(14,eo,1,0,"div",16),d(),l(15,"div",17),g(16,io,2,0,"div",18)(17,no,5,1,"div",19),d()),e&2){let n;c(2),_("mdc-text-field--filled",!i._hasOutline())("mdc-text-field--outlined",i._hasOutline())("mdc-text-field--no-label",!i._hasFloatingLabel())("mdc-text-field--disabled",i._control.disabled)("mdc-text-field--invalid",i._control.errorState),c(2),v(!i._hasOutline()&&!i._control.disabled?4:-1),c(2),v(i._hasOutline()?6:-1),c(),v(i._hasIconPrefix?7:-1),c(),v(i._hasTextPrefix?8:-1),c(2),v(!i._hasOutline()||i._forceDisplayInfixLabel()?10:-1),c(2),v(i._hasTextSuffix?12:-1),c(),v(i._hasIconSuffix?13:-1),c(),v(i._hasOutline()?-1:14),c(),_("mat-mdc-form-field-subscript-dynamic-size",i.subscriptSizing==="dynamic");let o=i._getSubscriptMessageType();c(),v((n=o)==="error"?16:n==="hint"?17:-1)}},dependencies:[va,ka,xi,xa,ze],styles:[`.mdc-text-field{display:inline-flex;align-items:baseline;padding:0 16px;position:relative;box-sizing:border-box;overflow:hidden;will-change:opacity,transform,color;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.mdc-text-field__input{width:100%;min-width:0;border:none;border-radius:0;background:none;padding:0;-moz-appearance:none;-webkit-appearance:none;height:28px}.mdc-text-field__input::-webkit-calendar-picker-indicator,.mdc-text-field__input::-webkit-search-cancel-button{display:none}.mdc-text-field__input::-ms-clear{display:none}.mdc-text-field__input:focus{outline:none}.mdc-text-field__input:invalid{box-shadow:none}.mdc-text-field__input::placeholder{opacity:0}.mdc-text-field__input::-moz-placeholder{opacity:0}.mdc-text-field__input::-webkit-input-placeholder{opacity:0}.mdc-text-field__input:-ms-input-placeholder{opacity:0}.mdc-text-field--no-label .mdc-text-field__input::placeholder,.mdc-text-field--focused .mdc-text-field__input::placeholder{opacity:1}.mdc-text-field--no-label .mdc-text-field__input::-moz-placeholder,.mdc-text-field--focused .mdc-text-field__input::-moz-placeholder{opacity:1}.mdc-text-field--no-label .mdc-text-field__input::-webkit-input-placeholder,.mdc-text-field--focused .mdc-text-field__input::-webkit-input-placeholder{opacity:1}.mdc-text-field--no-label .mdc-text-field__input:-ms-input-placeholder,.mdc-text-field--focused .mdc-text-field__input:-ms-input-placeholder{opacity:1}.mdc-text-field--disabled:not(.mdc-text-field--no-label) .mdc-text-field__input.mat-mdc-input-disabled-interactive::placeholder{opacity:0}.mdc-text-field--disabled:not(.mdc-text-field--no-label) .mdc-text-field__input.mat-mdc-input-disabled-interactive::-moz-placeholder{opacity:0}.mdc-text-field--disabled:not(.mdc-text-field--no-label) .mdc-text-field__input.mat-mdc-input-disabled-interactive::-webkit-input-placeholder{opacity:0}.mdc-text-field--disabled:not(.mdc-text-field--no-label) .mdc-text-field__input.mat-mdc-input-disabled-interactive:-ms-input-placeholder{opacity:0}.mdc-text-field--outlined .mdc-text-field__input,.mdc-text-field--filled.mdc-text-field--no-label .mdc-text-field__input{height:100%}.mdc-text-field--outlined .mdc-text-field__input{display:flex;border:none !important;background-color:rgba(0,0,0,0)}.mdc-text-field--disabled .mdc-text-field__input{pointer-events:auto}.mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-text-field__input{color:var(--mat-form-field-filled-input-text-color, var(--mat-sys-on-surface));caret-color:var(--mat-form-field-filled-caret-color, var(--mat-sys-primary))}.mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-text-field__input::placeholder{color:var(--mat-form-field-filled-input-text-placeholder-color, var(--mat-sys-on-surface-variant))}.mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-text-field__input::-moz-placeholder{color:var(--mat-form-field-filled-input-text-placeholder-color, var(--mat-sys-on-surface-variant))}.mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-text-field__input::-webkit-input-placeholder{color:var(--mat-form-field-filled-input-text-placeholder-color, var(--mat-sys-on-surface-variant))}.mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-text-field__input:-ms-input-placeholder{color:var(--mat-form-field-filled-input-text-placeholder-color, var(--mat-sys-on-surface-variant))}.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-text-field__input{color:var(--mat-form-field-outlined-input-text-color, var(--mat-sys-on-surface));caret-color:var(--mat-form-field-outlined-caret-color, var(--mat-sys-primary))}.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-text-field__input::placeholder{color:var(--mat-form-field-outlined-input-text-placeholder-color, var(--mat-sys-on-surface-variant))}.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-text-field__input::-moz-placeholder{color:var(--mat-form-field-outlined-input-text-placeholder-color, var(--mat-sys-on-surface-variant))}.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-text-field__input::-webkit-input-placeholder{color:var(--mat-form-field-outlined-input-text-placeholder-color, var(--mat-sys-on-surface-variant))}.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-text-field__input:-ms-input-placeholder{color:var(--mat-form-field-outlined-input-text-placeholder-color, var(--mat-sys-on-surface-variant))}.mdc-text-field--filled.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-text-field__input{caret-color:var(--mat-form-field-filled-error-caret-color, var(--mat-sys-error))}.mdc-text-field--outlined.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-text-field__input{caret-color:var(--mat-form-field-outlined-error-caret-color, var(--mat-sys-error))}.mdc-text-field--filled.mdc-text-field--disabled .mdc-text-field__input{color:var(--mat-form-field-filled-disabled-input-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent))}.mdc-text-field--outlined.mdc-text-field--disabled .mdc-text-field__input{color:var(--mat-form-field-outlined-disabled-input-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent))}@media(forced-colors: active){.mdc-text-field--disabled .mdc-text-field__input{background-color:Window}}.mdc-text-field--filled{height:56px;border-bottom-right-radius:0;border-bottom-left-radius:0;border-top-left-radius:var(--mat-form-field-filled-container-shape, var(--mat-sys-corner-extra-small));border-top-right-radius:var(--mat-form-field-filled-container-shape, var(--mat-sys-corner-extra-small))}.mdc-text-field--filled:not(.mdc-text-field--disabled){background-color:var(--mat-form-field-filled-container-color, var(--mat-sys-surface-variant))}.mdc-text-field--filled.mdc-text-field--disabled{background-color:var(--mat-form-field-filled-disabled-container-color, color-mix(in srgb, var(--mat-sys-on-surface) 4%, transparent))}.mdc-text-field--outlined{height:56px;overflow:visible;padding-right:max(16px,var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small)));padding-left:max(16px,var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small)) + 4px)}[dir=rtl] .mdc-text-field--outlined{padding-right:max(16px,var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small)) + 4px);padding-left:max(16px,var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small)))}.mdc-floating-label{position:absolute;left:0;transform-origin:left top;line-height:1.15rem;text-align:left;text-overflow:ellipsis;white-space:nowrap;cursor:text;overflow:hidden;will-change:transform}[dir=rtl] .mdc-floating-label{right:0;left:auto;transform-origin:right top;text-align:right}.mdc-text-field .mdc-floating-label{top:50%;transform:translateY(-50%);pointer-events:none}.mdc-notched-outline .mdc-floating-label{display:inline-block;position:relative;max-width:100%}.mdc-text-field--outlined .mdc-floating-label{left:4px;right:auto}[dir=rtl] .mdc-text-field--outlined .mdc-floating-label{left:auto;right:4px}.mdc-text-field--filled .mdc-floating-label{left:16px;right:auto}[dir=rtl] .mdc-text-field--filled .mdc-floating-label{left:auto;right:16px}.mdc-text-field--disabled .mdc-floating-label{cursor:default}@media(forced-colors: active){.mdc-text-field--disabled .mdc-floating-label{z-index:1}}.mdc-text-field--filled.mdc-text-field--no-label .mdc-floating-label{display:none}.mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-floating-label{color:var(--mat-form-field-filled-label-text-color, var(--mat-sys-on-surface-variant))}.mdc-text-field--filled:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-floating-label{color:var(--mat-form-field-filled-focus-label-text-color, var(--mat-sys-primary))}.mdc-text-field--filled:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-floating-label{color:var(--mat-form-field-filled-hover-label-text-color, var(--mat-sys-on-surface-variant))}.mdc-text-field--filled.mdc-text-field--disabled .mdc-floating-label{color:var(--mat-form-field-filled-disabled-label-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent))}.mdc-text-field--filled:not(.mdc-text-field--disabled).mdc-text-field--invalid .mdc-floating-label{color:var(--mat-form-field-filled-error-label-text-color, var(--mat-sys-error))}.mdc-text-field--filled:not(.mdc-text-field--disabled).mdc-text-field--invalid.mdc-text-field--focused .mdc-floating-label{color:var(--mat-form-field-filled-error-focus-label-text-color, var(--mat-sys-error))}.mdc-text-field--filled:not(.mdc-text-field--disabled).mdc-text-field--invalid:not(.mdc-text-field--disabled):hover .mdc-floating-label{color:var(--mat-form-field-filled-error-hover-label-text-color, var(--mat-sys-on-error-container))}.mdc-text-field--filled .mdc-floating-label{font-family:var(--mat-form-field-filled-label-text-font, var(--mat-sys-body-large-font));font-size:var(--mat-form-field-filled-label-text-size, var(--mat-sys-body-large-size));font-weight:var(--mat-form-field-filled-label-text-weight, var(--mat-sys-body-large-weight));letter-spacing:var(--mat-form-field-filled-label-text-tracking, var(--mat-sys-body-large-tracking))}.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-floating-label{color:var(--mat-form-field-outlined-label-text-color, var(--mat-sys-on-surface-variant))}.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-floating-label{color:var(--mat-form-field-outlined-focus-label-text-color, var(--mat-sys-primary))}.mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-floating-label{color:var(--mat-form-field-outlined-hover-label-text-color, var(--mat-sys-on-surface))}.mdc-text-field--outlined.mdc-text-field--disabled .mdc-floating-label{color:var(--mat-form-field-outlined-disabled-label-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent))}.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--invalid .mdc-floating-label{color:var(--mat-form-field-outlined-error-label-text-color, var(--mat-sys-error))}.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--invalid.mdc-text-field--focused .mdc-floating-label{color:var(--mat-form-field-outlined-error-focus-label-text-color, var(--mat-sys-error))}.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--invalid:not(.mdc-text-field--disabled):hover .mdc-floating-label{color:var(--mat-form-field-outlined-error-hover-label-text-color, var(--mat-sys-on-error-container))}.mdc-text-field--outlined .mdc-floating-label{font-family:var(--mat-form-field-outlined-label-text-font, var(--mat-sys-body-large-font));font-size:var(--mat-form-field-outlined-label-text-size, var(--mat-sys-body-large-size));font-weight:var(--mat-form-field-outlined-label-text-weight, var(--mat-sys-body-large-weight));letter-spacing:var(--mat-form-field-outlined-label-text-tracking, var(--mat-sys-body-large-tracking))}.mdc-floating-label--float-above{cursor:auto;transform:translateY(-106%) scale(0.75)}.mdc-text-field--filled .mdc-floating-label--float-above{transform:translateY(-106%) scale(0.75)}.mdc-text-field--outlined .mdc-floating-label--float-above{transform:translateY(-37.25px) scale(1);font-size:.75rem}.mdc-notched-outline .mdc-floating-label--float-above{text-overflow:clip}.mdc-notched-outline--upgraded .mdc-floating-label--float-above{max-width:133.3333333333%}.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-34.75px) scale(0.75)}.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-floating-label--required:not(.mdc-floating-label--hide-required-marker)::after{margin-left:1px;margin-right:0;content:"*"}[dir=rtl] .mdc-floating-label--required:not(.mdc-floating-label--hide-required-marker)::after{margin-left:0;margin-right:1px}.mdc-notched-outline{display:flex;position:absolute;top:0;right:0;left:0;box-sizing:border-box;width:100%;max-width:100%;height:100%;text-align:left;pointer-events:none}[dir=rtl] .mdc-notched-outline{text-align:right}.mdc-text-field--outlined .mdc-notched-outline{z-index:1}.mat-mdc-notch-piece{box-sizing:border-box;height:100%;pointer-events:none;border:none;border-top:1px solid;border-bottom:1px solid}.mdc-text-field--focused .mat-mdc-notch-piece{border-width:2px}.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mat-mdc-notch-piece{border-color:var(--mat-form-field-outlined-outline-color, var(--mat-sys-outline));border-width:var(--mat-form-field-outlined-outline-width, 1px)}.mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mat-mdc-notch-piece{border-color:var(--mat-form-field-outlined-hover-outline-color, var(--mat-sys-on-surface))}.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--focused .mat-mdc-notch-piece{border-color:var(--mat-form-field-outlined-focus-outline-color, var(--mat-sys-primary))}.mdc-text-field--outlined.mdc-text-field--disabled .mat-mdc-notch-piece{border-color:var(--mat-form-field-outlined-disabled-outline-color, color-mix(in srgb, var(--mat-sys-on-surface) 12%, transparent))}.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--invalid .mat-mdc-notch-piece{border-color:var(--mat-form-field-outlined-error-outline-color, var(--mat-sys-error))}.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--invalid:not(.mdc-text-field--focused):hover .mdc-notched-outline .mat-mdc-notch-piece{border-color:var(--mat-form-field-outlined-error-hover-outline-color, var(--mat-sys-on-error-container))}.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--invalid.mdc-text-field--focused .mat-mdc-notch-piece{border-color:var(--mat-form-field-outlined-error-focus-outline-color, var(--mat-sys-error))}.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline .mat-mdc-notch-piece{border-width:var(--mat-form-field-outlined-focus-outline-width, 2px)}.mdc-notched-outline__leading{border-left:1px solid;border-right:none;border-top-right-radius:0;border-bottom-right-radius:0;border-top-left-radius:var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small));border-bottom-left-radius:var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small))}.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading{width:max(12px,var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small)))}[dir=rtl] .mdc-notched-outline__leading{border-left:none;border-right:1px solid;border-bottom-left-radius:0;border-top-left-radius:0;border-top-right-radius:var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small));border-bottom-right-radius:var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small))}.mdc-notched-outline__trailing{flex-grow:1;border-left:none;border-right:1px solid;border-top-left-radius:0;border-bottom-left-radius:0;border-top-right-radius:var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small));border-bottom-right-radius:var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small))}[dir=rtl] .mdc-notched-outline__trailing{border-left:1px solid;border-right:none;border-top-right-radius:0;border-bottom-right-radius:0;border-top-left-radius:var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small));border-bottom-left-radius:var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small))}.mdc-notched-outline__notch{flex:0 0 auto;width:auto}.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__notch{max-width:min(var(--mat-form-field-notch-max-width, 100%),calc(100% - max(12px, var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small))) * 2))}.mdc-text-field--outlined .mdc-notched-outline--notched .mdc-notched-outline__notch{max-width:min(100%,calc(100% - max(12px, var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small))) * 2))}.mdc-text-field--outlined .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:1px}.mdc-text-field--focused.mdc-text-field--outlined .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:2px}.mdc-notched-outline--notched .mdc-notched-outline__notch{padding-left:0;padding-right:8px;border-top:none}[dir=rtl] .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-left:8px;padding-right:0}.mdc-notched-outline--no-label .mdc-notched-outline__notch{display:none}.mdc-line-ripple::before,.mdc-line-ripple::after{position:absolute;bottom:0;left:0;width:100%;border-bottom-style:solid;content:""}.mdc-line-ripple::before{z-index:1;border-bottom-width:var(--mat-form-field-filled-active-indicator-height, 1px)}.mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-line-ripple::before{border-bottom-color:var(--mat-form-field-filled-active-indicator-color, var(--mat-sys-on-surface-variant))}.mdc-text-field--filled:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-line-ripple::before{border-bottom-color:var(--mat-form-field-filled-hover-active-indicator-color, var(--mat-sys-on-surface))}.mdc-text-field--filled.mdc-text-field--disabled .mdc-line-ripple::before{border-bottom-color:var(--mat-form-field-filled-disabled-active-indicator-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent))}.mdc-text-field--filled:not(.mdc-text-field--disabled).mdc-text-field--invalid .mdc-line-ripple::before{border-bottom-color:var(--mat-form-field-filled-error-active-indicator-color, var(--mat-sys-error))}.mdc-text-field--filled:not(.mdc-text-field--disabled).mdc-text-field--invalid:not(.mdc-text-field--focused):hover .mdc-line-ripple::before{border-bottom-color:var(--mat-form-field-filled-error-hover-active-indicator-color, var(--mat-sys-on-error-container))}.mdc-line-ripple::after{transform:scaleX(0);opacity:0;z-index:2}.mdc-text-field--filled .mdc-line-ripple::after{border-bottom-width:var(--mat-form-field-filled-focus-active-indicator-height, 2px)}.mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-line-ripple::after{border-bottom-color:var(--mat-form-field-filled-focus-active-indicator-color, var(--mat-sys-primary))}.mdc-text-field--filled.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-line-ripple::after{border-bottom-color:var(--mat-form-field-filled-error-focus-active-indicator-color, var(--mat-sys-error))}.mdc-line-ripple--active::after{transform:scaleX(1);opacity:1}.mdc-line-ripple--deactivating::after{opacity:0}.mdc-text-field--disabled{pointer-events:none}.mat-mdc-form-field-textarea-control{vertical-align:middle;resize:vertical;box-sizing:border-box;height:auto;margin:0;padding:0;border:none;overflow:auto}.mat-mdc-form-field-input-control.mat-mdc-form-field-input-control{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font:inherit;letter-spacing:inherit;text-decoration:inherit;text-transform:inherit;border:none}.mat-mdc-form-field .mat-mdc-floating-label.mdc-floating-label{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;line-height:normal;pointer-events:all;will-change:auto}.mat-mdc-form-field:not(.mat-form-field-disabled) .mat-mdc-floating-label.mdc-floating-label{cursor:inherit}.mdc-text-field--no-label:not(.mdc-text-field--textarea) .mat-mdc-form-field-input-control.mdc-text-field__input,.mat-mdc-text-field-wrapper .mat-mdc-form-field-input-control{height:auto}.mat-mdc-text-field-wrapper .mat-mdc-form-field-input-control.mdc-text-field__input[type=color]{height:23px}.mat-mdc-text-field-wrapper{height:auto;flex:auto;will-change:auto}.mat-mdc-form-field-has-icon-prefix .mat-mdc-text-field-wrapper{padding-left:0;--mat-mdc-form-field-label-offset-x: -16px}.mat-mdc-form-field-has-icon-suffix .mat-mdc-text-field-wrapper{padding-right:0}[dir=rtl] .mat-mdc-text-field-wrapper{padding-left:16px;padding-right:16px}[dir=rtl] .mat-mdc-form-field-has-icon-suffix .mat-mdc-text-field-wrapper{padding-left:0}[dir=rtl] .mat-mdc-form-field-has-icon-prefix .mat-mdc-text-field-wrapper{padding-right:0}.mat-form-field-disabled .mdc-text-field__input::placeholder{color:var(--mat-form-field-disabled-input-text-placeholder-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent))}.mat-form-field-disabled .mdc-text-field__input::-moz-placeholder{color:var(--mat-form-field-disabled-input-text-placeholder-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent))}.mat-form-field-disabled .mdc-text-field__input::-webkit-input-placeholder{color:var(--mat-form-field-disabled-input-text-placeholder-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent))}.mat-form-field-disabled .mdc-text-field__input:-ms-input-placeholder{color:var(--mat-form-field-disabled-input-text-placeholder-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent))}.mat-mdc-form-field-label-always-float .mdc-text-field__input::placeholder{transition-delay:40ms;transition-duration:110ms;opacity:1}.mat-mdc-text-field-wrapper .mat-mdc-form-field-infix .mat-mdc-floating-label{left:auto;right:auto}.mat-mdc-text-field-wrapper.mdc-text-field--outlined .mdc-text-field__input{display:inline-block}.mat-mdc-form-field .mat-mdc-text-field-wrapper.mdc-text-field .mdc-notched-outline__notch{padding-top:0}.mat-mdc-form-field.mat-mdc-form-field.mat-mdc-form-field.mat-mdc-form-field.mat-mdc-form-field.mat-mdc-form-field .mdc-notched-outline__notch{border-left:1px solid rgba(0,0,0,0)}[dir=rtl] .mat-mdc-form-field.mat-mdc-form-field.mat-mdc-form-field.mat-mdc-form-field.mat-mdc-form-field.mat-mdc-form-field .mdc-notched-outline__notch{border-left:none;border-right:1px solid rgba(0,0,0,0)}.mat-mdc-form-field-infix{min-height:var(--mat-form-field-container-height, 56px);padding-top:var(--mat-form-field-filled-with-label-container-padding-top, 24px);padding-bottom:var(--mat-form-field-filled-with-label-container-padding-bottom, 8px)}.mdc-text-field--outlined .mat-mdc-form-field-infix,.mdc-text-field--no-label .mat-mdc-form-field-infix{padding-top:var(--mat-form-field-container-vertical-padding, 16px);padding-bottom:var(--mat-form-field-container-vertical-padding, 16px)}.mat-mdc-text-field-wrapper .mat-mdc-form-field-flex .mat-mdc-floating-label{top:calc(var(--mat-form-field-container-height, 56px)/2)}.mdc-text-field--filled .mat-mdc-floating-label{display:var(--mat-form-field-filled-label-display, block)}.mat-mdc-text-field-wrapper.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{--mat-mdc-form-field-label-transform: translateY(calc(calc(6.75px + var(--mat-form-field-container-height, 56px) / 2) * -1)) scale(var(--mat-mdc-form-field-floating-label-scale, 0.75));transform:var(--mat-mdc-form-field-label-transform)}@keyframes _mat-form-field-subscript-animation{from{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:translateY(0)}}.mat-mdc-form-field-subscript-wrapper{box-sizing:border-box;width:100%;position:relative}.mat-mdc-form-field-hint-wrapper,.mat-mdc-form-field-error-wrapper{position:absolute;top:0;left:0;right:0;padding:0 16px;opacity:1;transform:translateY(0);animation:_mat-form-field-subscript-animation 0ms cubic-bezier(0.55, 0, 0.55, 0.2)}.mat-mdc-form-field-subscript-dynamic-size .mat-mdc-form-field-hint-wrapper,.mat-mdc-form-field-subscript-dynamic-size .mat-mdc-form-field-error-wrapper{position:static}.mat-mdc-form-field-bottom-align::before{content:"";display:inline-block;height:16px}.mat-mdc-form-field-bottom-align.mat-mdc-form-field-subscript-dynamic-size::before{content:unset}.mat-mdc-form-field-hint-end{order:1}.mat-mdc-form-field-hint-wrapper{display:flex}.mat-mdc-form-field-hint-spacer{flex:1 0 1em}.mat-mdc-form-field-error{display:block;color:var(--mat-form-field-error-text-color, var(--mat-sys-error))}.mat-mdc-form-field-subscript-wrapper,.mat-mdc-form-field-bottom-align::before{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:var(--mat-form-field-subscript-text-font, var(--mat-sys-body-small-font));line-height:var(--mat-form-field-subscript-text-line-height, var(--mat-sys-body-small-line-height));font-size:var(--mat-form-field-subscript-text-size, var(--mat-sys-body-small-size));letter-spacing:var(--mat-form-field-subscript-text-tracking, var(--mat-sys-body-small-tracking));font-weight:var(--mat-form-field-subscript-text-weight, var(--mat-sys-body-small-weight))}.mat-mdc-form-field-focus-overlay{top:0;left:0;right:0;bottom:0;position:absolute;opacity:0;pointer-events:none;background-color:var(--mat-form-field-state-layer-color, var(--mat-sys-on-surface))}.mat-mdc-text-field-wrapper:hover .mat-mdc-form-field-focus-overlay{opacity:var(--mat-form-field-hover-state-layer-opacity, var(--mat-sys-hover-state-layer-opacity))}.mat-mdc-form-field.mat-focused .mat-mdc-form-field-focus-overlay{opacity:var(--mat-form-field-focus-state-layer-opacity, 0)}select.mat-mdc-form-field-input-control{-moz-appearance:none;-webkit-appearance:none;background-color:rgba(0,0,0,0);display:inline-flex;box-sizing:border-box}select.mat-mdc-form-field-input-control:not(:disabled){cursor:pointer}select.mat-mdc-form-field-input-control:not(.mat-mdc-native-select-inline) option{color:var(--mat-form-field-select-option-text-color, var(--mat-sys-neutral10))}select.mat-mdc-form-field-input-control:not(.mat-mdc-native-select-inline) option:disabled{color:var(--mat-form-field-select-disabled-option-text-color, color-mix(in srgb, var(--mat-sys-neutral10) 38%, transparent))}.mat-mdc-form-field-type-mat-native-select .mat-mdc-form-field-infix::after{content:"";width:0;height:0;border-left:5px solid rgba(0,0,0,0);border-right:5px solid rgba(0,0,0,0);border-top:5px solid;position:absolute;right:0;top:50%;margin-top:-2.5px;pointer-events:none;color:var(--mat-form-field-enabled-select-arrow-color, var(--mat-sys-on-surface-variant))}[dir=rtl] .mat-mdc-form-field-type-mat-native-select .mat-mdc-form-field-infix::after{right:auto;left:0}.mat-mdc-form-field-type-mat-native-select.mat-focused .mat-mdc-form-field-infix::after{color:var(--mat-form-field-focus-select-arrow-color, var(--mat-sys-primary))}.mat-mdc-form-field-type-mat-native-select.mat-form-field-disabled .mat-mdc-form-field-infix::after{color:var(--mat-form-field-disabled-select-arrow-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent))}.mat-mdc-form-field-type-mat-native-select .mat-mdc-form-field-input-control{padding-right:15px}[dir=rtl] .mat-mdc-form-field-type-mat-native-select .mat-mdc-form-field-input-control{padding-right:0;padding-left:15px}@media(forced-colors: active){.mat-form-field-appearance-fill .mat-mdc-text-field-wrapper{outline:solid 1px}}@media(forced-colors: active){.mat-form-field-appearance-fill.mat-form-field-disabled .mat-mdc-text-field-wrapper{outline-color:GrayText}}@media(forced-colors: active){.mat-form-field-appearance-fill.mat-focused .mat-mdc-text-field-wrapper{outline:dashed 3px}}@media(forced-colors: active){.mat-mdc-form-field.mat-focused .mdc-notched-outline{border:dashed 3px}}.mat-mdc-form-field-input-control[type=date],.mat-mdc-form-field-input-control[type=datetime],.mat-mdc-form-field-input-control[type=datetime-local],.mat-mdc-form-field-input-control[type=month],.mat-mdc-form-field-input-control[type=week],.mat-mdc-form-field-input-control[type=time]{line-height:1}.mat-mdc-form-field-input-control::-webkit-datetime-edit{line-height:1;padding:0;margin-bottom:-2px}.mat-mdc-form-field{--mat-mdc-form-field-floating-label-scale: 0.75;display:inline-flex;flex-direction:column;min-width:0;text-align:left;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:var(--mat-form-field-container-text-font, var(--mat-sys-body-large-font));line-height:var(--mat-form-field-container-text-line-height, var(--mat-sys-body-large-line-height));font-size:var(--mat-form-field-container-text-size, var(--mat-sys-body-large-size));letter-spacing:var(--mat-form-field-container-text-tracking, var(--mat-sys-body-large-tracking));font-weight:var(--mat-form-field-container-text-weight, var(--mat-sys-body-large-weight))}.mat-mdc-form-field .mdc-text-field--outlined .mdc-floating-label--float-above{font-size:calc(var(--mat-form-field-outlined-label-text-populated-size)*var(--mat-mdc-form-field-floating-label-scale))}.mat-mdc-form-field .mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:var(--mat-form-field-outlined-label-text-populated-size)}[dir=rtl] .mat-mdc-form-field{text-align:right}.mat-mdc-form-field-flex{display:inline-flex;align-items:baseline;box-sizing:border-box;width:100%}.mat-mdc-text-field-wrapper{width:100%;z-index:0}.mat-mdc-form-field-icon-prefix,.mat-mdc-form-field-icon-suffix{align-self:center;line-height:0;pointer-events:auto;position:relative;z-index:1}.mat-mdc-form-field-icon-prefix>.mat-icon,.mat-mdc-form-field-icon-suffix>.mat-icon{padding:0 12px;box-sizing:content-box}.mat-mdc-form-field-icon-prefix{color:var(--mat-form-field-leading-icon-color, var(--mat-sys-on-surface-variant))}.mat-form-field-disabled .mat-mdc-form-field-icon-prefix{color:var(--mat-form-field-disabled-leading-icon-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent))}.mat-mdc-form-field-icon-suffix{color:var(--mat-form-field-trailing-icon-color, var(--mat-sys-on-surface-variant))}.mat-form-field-disabled .mat-mdc-form-field-icon-suffix{color:var(--mat-form-field-disabled-trailing-icon-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent))}.mat-form-field-invalid .mat-mdc-form-field-icon-suffix{color:var(--mat-form-field-error-trailing-icon-color, var(--mat-sys-error))}.mat-form-field-invalid:not(.mat-focused):not(.mat-form-field-disabled) .mat-mdc-text-field-wrapper:hover .mat-mdc-form-field-icon-suffix{color:var(--mat-form-field-error-hover-trailing-icon-color, var(--mat-sys-on-error-container))}.mat-form-field-invalid.mat-focused .mat-mdc-text-field-wrapper .mat-mdc-form-field-icon-suffix{color:var(--mat-form-field-error-focus-trailing-icon-color, var(--mat-sys-error))}.mat-mdc-form-field-icon-prefix,[dir=rtl] .mat-mdc-form-field-icon-suffix{padding:0 4px 0 0}.mat-mdc-form-field-icon-suffix,[dir=rtl] .mat-mdc-form-field-icon-prefix{padding:0 0 0 4px}.mat-mdc-form-field-subscript-wrapper .mat-icon,.mat-mdc-form-field label .mat-icon{width:1em;height:1em;font-size:inherit}.mat-mdc-form-field-infix{flex:auto;min-width:0;width:180px;position:relative;box-sizing:border-box}.mat-mdc-form-field-infix:has(textarea[cols]){width:auto}.mat-mdc-form-field .mdc-notched-outline__notch{margin-left:-1px;-webkit-clip-path:inset(-9em -999em -9em 1px);clip-path:inset(-9em -999em -9em 1px)}[dir=rtl] .mat-mdc-form-field .mdc-notched-outline__notch{margin-left:0;margin-right:-1px;-webkit-clip-path:inset(-9em 1px -9em -999em);clip-path:inset(-9em 1px -9em -999em)}.mat-mdc-form-field.mat-form-field-animations-enabled .mdc-floating-label{transition:transform 150ms cubic-bezier(0.4, 0, 0.2, 1),color 150ms cubic-bezier(0.4, 0, 0.2, 1)}.mat-mdc-form-field.mat-form-field-animations-enabled .mdc-text-field__input{transition:opacity 150ms cubic-bezier(0.4, 0, 0.2, 1)}.mat-mdc-form-field.mat-form-field-animations-enabled .mdc-text-field__input::placeholder{transition:opacity 67ms cubic-bezier(0.4, 0, 0.2, 1)}.mat-mdc-form-field.mat-form-field-animations-enabled .mdc-text-field__input::-moz-placeholder{transition:opacity 67ms cubic-bezier(0.4, 0, 0.2, 1)}.mat-mdc-form-field.mat-form-field-animations-enabled .mdc-text-field__input::-webkit-input-placeholder{transition:opacity 67ms cubic-bezier(0.4, 0, 0.2, 1)}.mat-mdc-form-field.mat-form-field-animations-enabled .mdc-text-field__input:-ms-input-placeholder{transition:opacity 67ms cubic-bezier(0.4, 0, 0.2, 1)}.mat-mdc-form-field.mat-form-field-animations-enabled.mdc-text-field--no-label .mdc-text-field__input::placeholder,.mat-mdc-form-field.mat-form-field-animations-enabled.mdc-text-field--focused .mdc-text-field__input::placeholder{transition-delay:40ms;transition-duration:110ms}.mat-mdc-form-field.mat-form-field-animations-enabled.mdc-text-field--no-label .mdc-text-field__input::-moz-placeholder,.mat-mdc-form-field.mat-form-field-animations-enabled.mdc-text-field--focused .mdc-text-field__input::-moz-placeholder{transition-delay:40ms;transition-duration:110ms}.mat-mdc-form-field.mat-form-field-animations-enabled.mdc-text-field--no-label .mdc-text-field__input::-webkit-input-placeholder,.mat-mdc-form-field.mat-form-field-animations-enabled.mdc-text-field--focused .mdc-text-field__input::-webkit-input-placeholder{transition-delay:40ms;transition-duration:110ms}.mat-mdc-form-field.mat-form-field-animations-enabled.mdc-text-field--no-label .mdc-text-field__input:-ms-input-placeholder,.mat-mdc-form-field.mat-form-field-animations-enabled.mdc-text-field--focused .mdc-text-field__input:-ms-input-placeholder{transition-delay:40ms;transition-duration:110ms}.mat-mdc-form-field.mat-form-field-animations-enabled .mdc-text-field--filled:not(.mdc-ripple-upgraded):focus .mdc-text-field__ripple::before{transition-duration:75ms}.mat-mdc-form-field.mat-form-field-animations-enabled .mdc-line-ripple::after{transition:transform 180ms cubic-bezier(0.4, 0, 0.2, 1),opacity 180ms cubic-bezier(0.4, 0, 0.2, 1)}.mat-mdc-form-field.mat-form-field-animations-enabled .mat-mdc-form-field-hint-wrapper,.mat-mdc-form-field.mat-form-field-animations-enabled .mat-mdc-form-field-error-wrapper{animation-duration:300ms}.mdc-notched-outline .mdc-floating-label{max-width:calc(100% + 1px)}.mdc-notched-outline--upgraded .mdc-floating-label--float-above{max-width:calc(133.3333333333% + 1px)} -`],encapsulation:2,changeDetection:0})}return a})();var Ea=(()=>{class a{isErrorState(t,e){return!!(t&&t.invalid&&(t.touched||e&&e.submitted))}static \u0275fac=function(e){return new(e||a)};static \u0275prov=P({token:a,factory:a.\u0275fac,providedIn:"root"})}return a})();var be=class{_defaultMatcher;ngControl;_parentFormGroup;_parentForm;_stateChanges;errorState=!1;matcher;constructor(s,t,e,i,n){this._defaultMatcher=s,this.ngControl=t,this._parentFormGroup=e,this._parentForm=i,this._stateChanges=n}updateErrorState(){let s=this.errorState,t=this._parentFormGroup||this._parentForm,e=this.matcher||this._defaultMatcher,i=this.ngControl?this.ngControl.control:null,n=e?.isErrorState(i,t)??!1;n!==s&&(this.errorState=n,this._stateChanges.next())}};var ge=(()=>{class a{static \u0275fac=function(e){return new(e||a)};static \u0275mod=N({type:a});static \u0275inj=z({imports:[j]})}return a})();var Nt=(()=>{class a{static \u0275fac=function(e){return new(e||a)};static \u0275mod=N({type:a});static \u0275inj=z({imports:[Kt,ge,zt,j]})}return a})();var Fa=(()=>{class a{static \u0275fac=function(e){return new(e||a)};static \u0275mod=N({type:a});static \u0275inj=z({imports:[ce,Da,j]})}return a})();var _o=["trigger"],bo=["panel"],go=[[["mat-select-trigger"]],"*"],vo=["mat-select-trigger","*"];function yo(a,s){if(a&1&&(l(0,"span",4),E(1),d()),a&2){let t=p();c(),pt(t.placeholder)}}function xo(a,s){a&1&&y(0)}function ko(a,s){if(a&1&&(l(0,"span",11),E(1),d()),a&2){let t=p(2);c(),pt(t.triggerValue)}}function wo(a,s){if(a&1&&(l(0,"span",5),g(1,xo,1,0)(2,ko,2,1,"span",11),d()),a&2){let t=p();c(),v(t.customTrigger?1:2)}}function Co(a,s){if(a&1){let t=lt();l(0,"div",12,1),u("keydown",function(i){R(t);let n=p();return A(n._handleKeydown(i))}),y(2,1),d()}if(a&2){let t=p();_t(t.panelClass),_("mat-select-panel-animations-enabled",!t._animationsDisabled)("mat-primary",(t._parentFormField==null?null:t._parentFormField.color)==="primary")("mat-accent",(t._parentFormField==null?null:t._parentFormField.color)==="accent")("mat-warn",(t._parentFormField==null?null:t._parentFormField.color)==="warn")("mat-undefined",!(t._parentFormField!=null&&t._parentFormField.color)),T("id",t.id+"-panel")("aria-multiselectable",t.multiple)("aria-label",t.ariaLabel||null)("aria-labelledby",t._getPanelAriaLabelledby())}}var To=new w("mat-select-scroll-strategy",{providedIn:"root",factory:()=>{let a=r(G);return()=>re(a)}}),Mo=new w("MAT_SELECT_CONFIG"),So=new w("MatSelectTrigger"),Ve=class{source;value;constructor(s,t){this.source=s,this.value=t}},Aa=(()=>{class a{_viewportRuler=r(ae);_changeDetectorRef=r(W);_elementRef=r(O);_dir=r(ut,{optional:!0});_idGenerator=r(U);_renderer=r(st);_parentFormField=r(je,{optional:!0});ngControl=r(Ai,{self:!0,optional:!0});_liveAnnouncer=r(de);_defaultOptions=r(Mo,{optional:!0});_animationsDisabled=Q();_popoverLocation;_initialized=new k;_cleanupDetach;options;optionGroups;customTrigger;_positions=[{originX:"start",originY:"bottom",overlayX:"start",overlayY:"top"},{originX:"end",originY:"bottom",overlayX:"end",overlayY:"top"},{originX:"start",originY:"top",overlayX:"start",overlayY:"bottom",panelClass:"mat-mdc-select-panel-above"},{originX:"end",originY:"top",overlayX:"end",overlayY:"bottom",panelClass:"mat-mdc-select-panel-above"}];_scrollOptionIntoView(t){let e=this.options.toArray()[t];if(e){let i=this.panel.nativeElement,n=pa(t,this.options,this.optionGroups),o=e._getHostElement();t===0&&n===1?i.scrollTop=0:i.scrollTop=fa(o.offsetTop,o.offsetHeight,i.scrollTop,i.offsetHeight)}}_positioningSettled(){this._scrollOptionIntoView(this._keyManager.activeItemIndex||0)}_getChangeEvent(t){return new Ve(this,t)}_scrollStrategyFactory=r(To);_panelOpen=!1;_compareWith=(t,e)=>t===e;_uid=this._idGenerator.getId("mat-select-");_triggerAriaLabelledBy=null;_previousControl;_destroy=new k;_errorStateTracker;stateChanges=new k;disableAutomaticLabeling=!0;userAriaDescribedBy;_selectionModel;_keyManager;_preferredOverlayOrigin;_overlayWidth;_onChange=()=>{};_onTouched=()=>{};_valueId=this._idGenerator.getId("mat-select-value-");_scrollStrategy;_overlayPanelClass=this._defaultOptions?.overlayPanelClass||"";get focused(){return this._focused||this._panelOpen}_focused=!1;controlType="mat-select";trigger;panel;_overlayDir;panelClass;disabled=!1;get disableRipple(){return this._disableRipple()}set disableRipple(t){this._disableRipple.set(t)}_disableRipple=Z(!1);tabIndex=0;get hideSingleSelectionIndicator(){return this._hideSingleSelectionIndicator}set hideSingleSelectionIndicator(t){this._hideSingleSelectionIndicator=t,this._syncParentProperties()}_hideSingleSelectionIndicator=this._defaultOptions?.hideSingleSelectionIndicator??!1;get placeholder(){return this._placeholder}set placeholder(t){this._placeholder=t,this.stateChanges.next()}_placeholder;get required(){return this._required??this.ngControl?.control?.hasValidator(Ri.required)??!1}set required(t){this._required=t,this.stateChanges.next()}_required;get multiple(){return this._multiple}set multiple(t){this._selectionModel,this._multiple=t}_multiple=!1;disableOptionCentering=this._defaultOptions?.disableOptionCentering??!1;get compareWith(){return this._compareWith}set compareWith(t){this._compareWith=t,this._selectionModel&&this._initializeSelection()}get value(){return this._value}set value(t){this._assignValue(t)&&this._onChange(t)}_value;ariaLabel="";ariaLabelledby;get errorStateMatcher(){return this._errorStateTracker.matcher}set errorStateMatcher(t){this._errorStateTracker.matcher=t}typeaheadDebounceInterval;sortComparator;get id(){return this._id}set id(t){this._id=t||this._uid,this.stateChanges.next()}_id;get errorState(){return this._errorStateTracker.errorState}set errorState(t){this._errorStateTracker.errorState=t}panelWidth=this._defaultOptions&&typeof this._defaultOptions.panelWidth<"u"?this._defaultOptions.panelWidth:"auto";canSelectNullableOptions=this._defaultOptions?.canSelectNullableOptions??!1;optionSelectionChanges=Ye(()=>{let t=this.options;return t?t.changes.pipe(at(t),$t(()=>J(...t.map(e=>e.onSelectionChange)))):this._initialized.pipe($t(()=>this.optionSelectionChanges))});openedChange=new F;_openedStream=this.openedChange.pipe(dt(t=>t),Ht(()=>{}));_closedStream=this.openedChange.pipe(dt(t=>!t),Ht(()=>{}));selectionChange=new F;valueChange=new F;constructor(){let t=r(Ea),e=r(Li,{optional:!0}),i=r(Pi,{optional:!0}),n=r(new ie("tabindex"),{optional:!0}),o=r(Oi,{optional:!0});this.ngControl&&(this.ngControl.valueAccessor=this),this._defaultOptions?.typeaheadDebounceInterval!=null&&(this.typeaheadDebounceInterval=this._defaultOptions.typeaheadDebounceInterval),this._errorStateTracker=new be(t,this.ngControl,i,e,this.stateChanges),this._scrollStrategy=this._scrollStrategyFactory(),this.tabIndex=n==null?0:parseInt(n)||0,this._popoverLocation=o?.usePopover===!1?null:"inline",this.id=this.id}ngOnInit(){this._selectionModel=new Yi(this.multiple),this.stateChanges.next(),this._viewportRuler.change().pipe(I(this._destroy)).subscribe(()=>{this.panelOpen&&(this._overlayWidth=this._getOverlayWidth(this._preferredOverlayOrigin),this._changeDetectorRef.detectChanges())})}ngAfterContentInit(){this._initialized.next(),this._initialized.complete(),this._initKeyManager(),this._selectionModel.changed.pipe(I(this._destroy)).subscribe(t=>{t.added.forEach(e=>e.select()),t.removed.forEach(e=>e.deselect())}),this.options.changes.pipe(at(null),I(this._destroy)).subscribe(()=>{this._resetOptions(),this._initializeSelection()})}ngDoCheck(){let t=this._getTriggerAriaLabelledby(),e=this.ngControl;if(t!==this._triggerAriaLabelledBy){let i=this._elementRef.nativeElement;this._triggerAriaLabelledBy=t,t?i.setAttribute("aria-labelledby",t):i.removeAttribute("aria-labelledby")}e&&(this._previousControl!==e.control&&(this._previousControl!==void 0&&e.disabled!==null&&e.disabled!==this.disabled&&(this.disabled=e.disabled),this._previousControl=e.control),this.updateErrorState())}ngOnChanges(t){(t.disabled||t.userAriaDescribedBy)&&this.stateChanges.next(),t.typeaheadDebounceInterval&&this._keyManager&&this._keyManager.withTypeAhead(this.typeaheadDebounceInterval),t.panelClass&&this.panelClass instanceof Set&&(this.panelClass=Array.from(this.panelClass))}ngOnDestroy(){this._cleanupDetach?.(),this._keyManager?.destroy(),this._destroy.next(),this._destroy.complete(),this.stateChanges.complete(),this._clearFromModal()}toggle(){this.panelOpen?this.close():this.open()}open(){this._canOpen()&&(this._parentFormField&&(this._preferredOverlayOrigin=this._parentFormField.getConnectedOverlayOrigin()),this._cleanupDetach?.(),this._overlayWidth=this._getOverlayWidth(this._preferredOverlayOrigin),this._applyModalPanelOwnership(),this._panelOpen=!0,this._overlayDir.positionChange.pipe(Xe(1)).subscribe(()=>{this._changeDetectorRef.detectChanges(),this._positioningSettled()}),this._overlayDir.attachOverlay(),this._keyManager.withHorizontalOrientation(null),this._highlightCorrectOption(),this._changeDetectorRef.markForCheck(),this.stateChanges.next(),Promise.resolve().then(()=>this.openedChange.emit(!0)))}_trackedModal=null;_applyModalPanelOwnership(){let t=this._elementRef.nativeElement.closest('body > .cdk-overlay-container [aria-modal="true"]');if(!t)return;let e=`${this.id}-panel`;this._trackedModal&&Ie(this._trackedModal,"aria-owns",e),Wi(t,"aria-owns",e),this._trackedModal=t}_clearFromModal(){if(!this._trackedModal)return;let t=`${this.id}-panel`;Ie(this._trackedModal,"aria-owns",t),this._trackedModal=null}close(){this._panelOpen&&(this._panelOpen=!1,this._exitAndDetach(),this._keyManager.withHorizontalOrientation(this._isRtl()?"rtl":"ltr"),this._changeDetectorRef.markForCheck(),this._onTouched(),this.stateChanges.next(),Promise.resolve().then(()=>this.openedChange.emit(!1)))}_exitAndDetach(){if(this._animationsDisabled||!this.panel){this._detachOverlay();return}this._cleanupDetach?.(),this._cleanupDetach=()=>{e(),clearTimeout(i),this._cleanupDetach=void 0};let t=this.panel.nativeElement,e=this._renderer.listen(t,"animationend",n=>{n.animationName==="_mat-select-exit"&&(this._cleanupDetach?.(),this._detachOverlay())}),i=setTimeout(()=>{this._cleanupDetach?.(),this._detachOverlay()},200);t.classList.add("mat-select-panel-exit")}_detachOverlay(){this._overlayDir.detachOverlay(),this._changeDetectorRef.markForCheck()}writeValue(t){this._assignValue(t)}registerOnChange(t){this._onChange=t}registerOnTouched(t){this._onTouched=t}setDisabledState(t){this.disabled=t,this._changeDetectorRef.markForCheck(),this.stateChanges.next()}get panelOpen(){return this._panelOpen}get selected(){return this.multiple?this._selectionModel?.selected||[]:this._selectionModel?.selected[0]}get triggerValue(){if(this.empty)return"";if(this._multiple){let t=this._selectionModel.selected.map(e=>e.viewValue);return this._isRtl()&&t.reverse(),t.join(", ")}return this._selectionModel.selected[0].viewValue}updateErrorState(){this._errorStateTracker.updateErrorState()}_isRtl(){return this._dir?this._dir.value==="rtl":!1}_handleKeydown(t){this.disabled||(this.panelOpen?this._handleOpenKeydown(t):this._handleClosedKeydown(t))}_handleClosedKeydown(t){let e=t.keyCode,i=e===40||e===38||e===37||e===39,n=e===13||e===32,o=this._keyManager;if(!o.isTyping()&&n&&!ct(t)||(this.multiple||t.altKey)&&i)t.preventDefault(),this.open();else if(!this.multiple){let f=this.selected;o.onKeydown(t);let b=this.selected;b&&f!==b&&this._liveAnnouncer.announce(b.viewValue,1e4)}}_handleOpenKeydown(t){let e=this._keyManager,i=t.keyCode,n=i===40||i===38,o=e.isTyping();if(n&&t.altKey)t.preventDefault(),this.close();else if(!o&&(i===13||i===32)&&e.activeItem&&!ct(t))t.preventDefault(),e.activeItem._selectViaInteraction();else if(!o&&this._multiple&&i===65&&t.ctrlKey){t.preventDefault();let f=this.options.some(b=>!b.disabled&&!b.selected);this.options.forEach(b=>{b.disabled||(f?b.select():b.deselect())})}else{let f=e.activeItemIndex;e.onKeydown(t),this._multiple&&n&&t.shiftKey&&e.activeItem&&e.activeItemIndex!==f&&e.activeItem._selectViaInteraction()}}_handleOverlayKeydown(t){t.keyCode===27&&!ct(t)&&(t.preventDefault(),this.close())}_onFocus(){this.disabled||(this._focused=!0,this.stateChanges.next())}_onBlur(){this._focused=!1,this._keyManager?.cancelTypeahead(),!this.disabled&&!this.panelOpen&&(this._onTouched(),this._changeDetectorRef.markForCheck(),this.stateChanges.next())}get empty(){return!this._selectionModel||this._selectionModel.isEmpty()}_initializeSelection(){Promise.resolve().then(()=>{this.ngControl&&(this._value=this.ngControl.value),this._setSelectionByValue(this._value),this.stateChanges.next()})}_setSelectionByValue(t){if(this.options.forEach(e=>e.setInactiveStyles()),this._selectionModel.clear(),this.multiple&&t)Array.isArray(t),t.forEach(e=>this._selectOptionByValue(e)),this._sortValues();else{let e=this._selectOptionByValue(t);e?this._keyManager.updateActiveItem(e):this.panelOpen||this._keyManager.updateActiveItem(-1)}this._changeDetectorRef.markForCheck()}_selectOptionByValue(t){let e=this.options.find(i=>{if(this._selectionModel.isSelected(i))return!1;try{return(i.value!=null||this.canSelectNullableOptions)&&this._compareWith(i.value,t)}catch{return!1}});return e&&this._selectionModel.select(e),e}_assignValue(t){return t!==this._value||this._multiple&&Array.isArray(t)?(this.options&&this._setSelectionByValue(t),this._value=t,!0):!1}_skipPredicate=t=>this.panelOpen?!1:t.disabled;_getOverlayWidth(t){return this.panelWidth==="auto"?(t instanceof Me?t.elementRef:t||this._elementRef).nativeElement.getBoundingClientRect().width:this.panelWidth===null?"":this.panelWidth}_syncParentProperties(){if(this.options)for(let t of this.options)t._changeDetectorRef.markForCheck()}_initKeyManager(){this._keyManager=new Hi(this.options).withTypeAhead(this.typeaheadDebounceInterval).withVerticalOrientation().withHorizontalOrientation(this._isRtl()?"rtl":"ltr").withHomeAndEnd().withPageUpDown().withAllowedModifierKeys(["shiftKey"]).skipPredicate(this._skipPredicate),this._keyManager.tabOut.subscribe(()=>{this.panelOpen&&(!this.multiple&&this._keyManager.activeItem&&this._keyManager.activeItem._selectViaInteraction(),this.focus(),this.close())}),this._keyManager.change.subscribe(()=>{this._panelOpen&&this.panel?this._scrollOptionIntoView(this._keyManager.activeItemIndex||0):!this._panelOpen&&!this.multiple&&this._keyManager.activeItem&&this._keyManager.activeItem._selectViaInteraction()})}_resetOptions(){let t=J(this.options.changes,this._destroy);this.optionSelectionChanges.pipe(I(t)).subscribe(e=>{this._onSelect(e.source,e.isUserInput),e.isUserInput&&!this.multiple&&this._panelOpen&&(this.close(),this.focus())}),J(...this.options.map(e=>e._stateChanges)).pipe(I(t)).subscribe(()=>{this._changeDetectorRef.detectChanges(),this.stateChanges.next()})}_onSelect(t,e){let i=this._selectionModel.isSelected(t);!this.canSelectNullableOptions&&t.value==null&&!this._multiple?(t.deselect(),this._selectionModel.clear(),this.value!=null&&this._propagateChanges(t.value)):(i!==t.selected&&(t.selected?this._selectionModel.select(t):this._selectionModel.deselect(t)),e&&this._keyManager.setActiveItem(t),this.multiple&&(this._sortValues(),e&&this.focus())),i!==this._selectionModel.isSelected(t)&&this._propagateChanges(),this.stateChanges.next()}_sortValues(){if(this.multiple){let t=this.options.toArray();this._selectionModel.sort((e,i)=>this.sortComparator?this.sortComparator(e,i,t):t.indexOf(e)-t.indexOf(i)),this.stateChanges.next()}}_propagateChanges(t){let e;this.multiple?e=this.selected.map(i=>i.value):e=this.selected?this.selected.value:t,this._value=e,this.valueChange.emit(e),this._onChange(e),this.selectionChange.emit(this._getChangeEvent(e)),this._changeDetectorRef.markForCheck()}_highlightCorrectOption(){if(this._keyManager)if(this.empty){let t=-1;for(let e=0;e0&&!!this._overlayDir}focus(t){this._elementRef.nativeElement.focus(t)}_getPanelAriaLabelledby(){if(this.ariaLabel)return null;let t=this._parentFormField?.getLabelId()||null,e=t?t+" ":"";return this.ariaLabelledby?e+this.ariaLabelledby:t}_getAriaActiveDescendant(){return this.panelOpen&&this._keyManager&&this._keyManager.activeItem?this._keyManager.activeItem.id:null}_getTriggerAriaLabelledby(){if(this.ariaLabel)return null;let t=this._parentFormField?.getLabelId()||"";return this.ariaLabelledby&&(t+=" "+this.ariaLabelledby),t||(t=this._valueId),t}get describedByIds(){return this._elementRef.nativeElement.getAttribute("aria-describedby")?.split(" ")||[]}setDescribedByIds(t){let e=this._elementRef.nativeElement;t.length?e.setAttribute("aria-describedby",t.join(" ")):e.removeAttribute("aria-describedby")}onContainerClick(t){let e=wi(t);e&&(e.tagName==="MAT-OPTION"||e.classList.contains("cdk-overlay-backdrop")||e.closest(".mat-mdc-select-panel"))||(this.focus(),this.open())}get shouldLabelFloat(){return this.panelOpen||!this.empty||this.focused&&!!this.placeholder}static \u0275fac=function(e){return new(e||a)};static \u0275cmp=C({type:a,selectors:[["mat-select"]],contentQueries:function(e,i,n){if(e&1&&et(n,So,5)(n,zt,5)(n,ue,5),e&2){let o;m(o=h())&&(i.customTrigger=o.first),m(o=h())&&(i.options=o),m(o=h())&&(i.optionGroups=o)}},viewQuery:function(e,i){if(e&1&&V(_o,5)(bo,5)(Se,5),e&2){let n;m(n=h())&&(i.trigger=n.first),m(n=h())&&(i.panel=n.first),m(n=h())&&(i._overlayDir=n.first)}},hostAttrs:["role","combobox","aria-haspopup","listbox",1,"mat-mdc-select"],hostVars:21,hostBindings:function(e,i){e&1&&u("keydown",function(o){return i._handleKeydown(o)})("focus",function(){return i._onFocus()})("blur",function(){return i._onBlur()}),e&2&&(T("id",i.id)("tabindex",i.disabled?-1:i.tabIndex)("aria-controls",i.panelOpen?i.id+"-panel":null)("aria-expanded",i.panelOpen)("aria-label",i.ariaLabel||null)("aria-required",i.required.toString())("aria-disabled",i.disabled.toString())("aria-invalid",i.errorState)("aria-activedescendant",i._getAriaActiveDescendant()),_("mat-mdc-select-disabled",i.disabled)("mat-mdc-select-invalid",i.errorState)("mat-mdc-select-required",i.required)("mat-mdc-select-empty",i.empty)("mat-mdc-select-multiple",i.multiple)("mat-select-open",i.panelOpen))},inputs:{userAriaDescribedBy:[0,"aria-describedby","userAriaDescribedBy"],panelClass:"panelClass",disabled:[2,"disabled","disabled",S],disableRipple:[2,"disableRipple","disableRipple",S],tabIndex:[2,"tabIndex","tabIndex",t=>t==null?0:bt(t)],hideSingleSelectionIndicator:[2,"hideSingleSelectionIndicator","hideSingleSelectionIndicator",S],placeholder:"placeholder",required:[2,"required","required",S],multiple:[2,"multiple","multiple",S],disableOptionCentering:[2,"disableOptionCentering","disableOptionCentering",S],compareWith:"compareWith",value:"value",ariaLabel:[0,"aria-label","ariaLabel"],ariaLabelledby:[0,"aria-labelledby","ariaLabelledby"],errorStateMatcher:"errorStateMatcher",typeaheadDebounceInterval:[2,"typeaheadDebounceInterval","typeaheadDebounceInterval",bt],sortComparator:"sortComparator",id:"id",panelWidth:"panelWidth",canSelectNullableOptions:[2,"canSelectNullableOptions","canSelectNullableOptions",S]},outputs:{openedChange:"openedChange",_openedStream:"opened",_closedStream:"closed",selectionChange:"selectionChange",valueChange:"valueChange"},exportAs:["matSelect"],features:[$([{provide:Ne,useExisting:a},{provide:fe,useExisting:a}]),Wt],ngContentSelectors:vo,decls:11,vars:10,consts:[["fallbackOverlayOrigin","cdkOverlayOrigin","trigger",""],["panel",""],["cdk-overlay-origin","",1,"mat-mdc-select-trigger",3,"click"],[1,"mat-mdc-select-value"],[1,"mat-mdc-select-placeholder","mat-mdc-select-min-line"],[1,"mat-mdc-select-value-text"],[1,"mat-mdc-select-arrow-wrapper"],[1,"mat-mdc-select-arrow"],["viewBox","0 0 24 24","width","24px","height","24px","focusable","false","aria-hidden","true"],["d","M7 10l5 5 5-5z"],["cdk-connected-overlay","","cdkConnectedOverlayHasBackdrop","","cdkConnectedOverlayBackdropClass","cdk-overlay-transparent-backdrop",3,"detach","backdropClick","overlayKeydown","cdkConnectedOverlayDisableClose","cdkConnectedOverlayPanelClass","cdkConnectedOverlayScrollStrategy","cdkConnectedOverlayOrigin","cdkConnectedOverlayPositions","cdkConnectedOverlayWidth","cdkConnectedOverlayFlexibleDimensions","cdkConnectedOverlayUsePopover"],[1,"mat-mdc-select-min-line"],["role","listbox","tabindex","-1",1,"mat-mdc-select-panel","mdc-menu-surface","mdc-menu-surface--open",3,"keydown"]],template:function(e,i){if(e&1&&(B(go),l(0,"div",2,0),u("click",function(){return i.open()}),l(3,"div",3),g(4,yo,2,1,"span",4)(5,wo,3,1,"span",5),d(),l(6,"div",6)(7,"div",7),mt(),l(8,"svg",8),M(9,"path",9),d()()()(),tt(10,Co,3,16,"ng-template",10),u("detach",function(){return i.close()})("backdropClick",function(){return i.close()})("overlayKeydown",function(o){return i._handleOverlayKeydown(o)})),e&2){let n=ht(1);c(3),T("id",i._valueId),c(),v(i.empty?4:5),c(6),D("cdkConnectedOverlayDisableClose",!0)("cdkConnectedOverlayPanelClass",i._overlayPanelClass)("cdkConnectedOverlayScrollStrategy",i._scrollStrategy)("cdkConnectedOverlayOrigin",i._preferredOverlayOrigin||n)("cdkConnectedOverlayPositions",i._positions)("cdkConnectedOverlayWidth",i._overlayWidth)("cdkConnectedOverlayFlexibleDimensions",!0)("cdkConnectedOverlayUsePopover",i._popoverLocation)}},dependencies:[Me,Se],styles:[`@keyframes _mat-select-enter{from{opacity:0;transform:scaleY(0.8)}to{opacity:1;transform:none}}@keyframes _mat-select-exit{from{opacity:1}to{opacity:0}}.mat-mdc-select{display:inline-block;width:100%;outline:none;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:var(--mat-select-enabled-trigger-text-color, var(--mat-sys-on-surface));font-family:var(--mat-select-trigger-text-font, var(--mat-sys-body-large-font));line-height:var(--mat-select-trigger-text-line-height, var(--mat-sys-body-large-line-height));font-size:var(--mat-select-trigger-text-size, var(--mat-sys-body-large-size));font-weight:var(--mat-select-trigger-text-weight, var(--mat-sys-body-large-weight));letter-spacing:var(--mat-select-trigger-text-tracking, var(--mat-sys-body-large-tracking))}div.mat-mdc-select-panel{box-shadow:var(--mat-select-container-elevation-shadow, 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12))}.mat-mdc-select-disabled{color:var(--mat-select-disabled-trigger-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent))}.mat-mdc-select-disabled .mat-mdc-select-placeholder{color:var(--mat-select-disabled-trigger-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent))}.mat-mdc-select-trigger{display:inline-flex;align-items:center;cursor:pointer;position:relative;box-sizing:border-box;width:100%}.mat-mdc-select-disabled .mat-mdc-select-trigger{-webkit-user-select:none;user-select:none;cursor:default}.mat-mdc-select-value{width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mat-mdc-select-value-text{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mat-mdc-select-arrow-wrapper{height:24px;flex-shrink:0;display:inline-flex;align-items:center}.mat-form-field-appearance-fill .mdc-text-field--no-label .mat-mdc-select-arrow-wrapper{transform:none}.mat-mdc-form-field .mat-mdc-select.mat-mdc-select-invalid .mat-mdc-select-arrow,.mat-form-field-invalid:not(.mat-form-field-disabled) .mat-mdc-form-field-infix::after{color:var(--mat-select-invalid-arrow-color, var(--mat-sys-error))}.mat-mdc-select-arrow{width:10px;height:5px;position:relative;color:var(--mat-select-enabled-arrow-color, var(--mat-sys-on-surface-variant))}.mat-mdc-form-field.mat-focused .mat-mdc-select-arrow{color:var(--mat-select-focused-arrow-color, var(--mat-sys-primary))}.mat-mdc-form-field .mat-mdc-select.mat-mdc-select-disabled .mat-mdc-select-arrow{color:var(--mat-select-disabled-arrow-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent))}.mat-select-open .mat-mdc-select-arrow{transform:rotate(180deg)}.mat-form-field-animations-enabled .mat-mdc-select-arrow{transition:transform 80ms linear}.mat-mdc-select-arrow svg{fill:currentColor;position:absolute;top:50%;left:50%;transform:translate(-50%, -50%)}@media(forced-colors: active){.mat-mdc-select-arrow svg{fill:CanvasText}.mat-mdc-select-disabled .mat-mdc-select-arrow svg{fill:GrayText}}div.mat-mdc-select-panel{width:100%;max-height:275px;outline:0;overflow:auto;padding:8px 0;border-radius:4px;box-sizing:border-box;position:relative;background-color:var(--mat-select-panel-background-color, var(--mat-sys-surface-container))}@media(forced-colors: active){div.mat-mdc-select-panel{outline:solid 1px}}.cdk-overlay-pane:not(.mat-mdc-select-panel-above) div.mat-mdc-select-panel{border-top-left-radius:0;border-top-right-radius:0;transform-origin:top center}.mat-mdc-select-panel-above div.mat-mdc-select-panel{border-bottom-left-radius:0;border-bottom-right-radius:0;transform-origin:bottom center}.mat-select-panel-animations-enabled{animation:_mat-select-enter 120ms cubic-bezier(0, 0, 0.2, 1)}.mat-select-panel-animations-enabled.mat-select-panel-exit{animation:_mat-select-exit 100ms linear}.mat-mdc-select-placeholder{transition:color 400ms 133.3333333333ms cubic-bezier(0.25, 0.8, 0.25, 1);color:var(--mat-select-placeholder-text-color, var(--mat-sys-on-surface-variant))}.mat-mdc-form-field:not(.mat-form-field-animations-enabled) .mat-mdc-select-placeholder,._mat-animation-noopable .mat-mdc-select-placeholder{transition:none}.mat-form-field-hide-placeholder .mat-mdc-select-placeholder{color:rgba(0,0,0,0);-webkit-text-fill-color:rgba(0,0,0,0);transition:none;display:block}.mat-mdc-form-field-type-mat-select:not(.mat-form-field-disabled) .mat-mdc-text-field-wrapper{cursor:pointer}.mat-mdc-form-field-type-mat-select.mat-form-field-appearance-fill .mat-mdc-floating-label{max-width:calc(100% - 18px)}.mat-mdc-form-field-type-mat-select.mat-form-field-appearance-fill .mdc-floating-label--float-above{max-width:calc(100%/0.75 - 24px)}.mat-mdc-form-field-type-mat-select.mat-form-field-appearance-outline .mdc-notched-outline__notch{max-width:calc(100% - 60px)}.mat-mdc-form-field-type-mat-select.mat-form-field-appearance-outline .mdc-text-field--label-floating .mdc-notched-outline__notch{max-width:calc(100% - 24px)}.mat-mdc-select-min-line:empty::before{content:" ";white-space:pre;width:1px;display:inline-block;visibility:hidden}.mat-form-field-appearance-fill .mat-mdc-select-arrow-wrapper{transform:var(--mat-select-arrow-transform, translateY(-8px))} -`],encapsulation:2,changeDetection:0})}return a})();var La=(()=>{class a{static \u0275fac=function(e){return new(e||a)};static \u0275mod=N({type:a});static \u0275inj=z({imports:[Pt,Nt,j,ne,Fa,Nt]})}return a})();var Do=["mat-internal-form-field",""],Eo=["*"],Fl=(()=>{class a{labelPosition="after";static \u0275fac=function(e){return new(e||a)};static \u0275cmp=C({type:a,selectors:[["div","mat-internal-form-field",""]],hostAttrs:[1,"mdc-form-field","mat-internal-form-field"],hostVars:2,hostBindings:function(e,i){e&2&&_("mdc-form-field--align-end",i.labelPosition==="before")},inputs:{labelPosition:"labelPosition"},attrs:Do,ngContentSelectors:Eo,decls:1,vars:0,template:function(e,i){e&1&&(B(),y(0))},styles:[`.mat-internal-form-field{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-flex;align-items:center;vertical-align:middle}.mat-internal-form-field>label{margin-left:0;margin-right:auto;padding-left:4px;padding-right:0;order:0}[dir=rtl] .mat-internal-form-field>label{margin-left:auto;margin-right:0;padding-left:0;padding-right:4px}.mdc-form-field--align-end>label{margin-left:auto;margin-right:0;padding-left:0;padding-right:4px;order:-1}[dir=rtl] .mdc-form-field--align-end .mdc-form-field--align-end label{margin-left:0;margin-right:auto;padding-left:4px;padding-right:0} -`],encapsulation:2,changeDetection:0})}return a})();var Al=(()=>{class a{static \u0275fac=function(e){return new(e||a)};static \u0275mod=N({type:a});static \u0275inj=z({imports:[j]})}return a})();var $e=(()=>{class a{get vertical(){return this._vertical}set vertical(t){this._vertical=K(t)}_vertical=!1;get inset(){return this._inset}set inset(t){this._inset=K(t)}_inset=!1;static \u0275fac=function(e){return new(e||a)};static \u0275cmp=C({type:a,selectors:[["mat-divider"]],hostAttrs:["role","separator",1,"mat-divider"],hostVars:7,hostBindings:function(e,i){e&2&&(T("aria-orientation",i.vertical?"vertical":"horizontal"),_("mat-divider-vertical",i.vertical)("mat-divider-horizontal",!i.vertical)("mat-divider-inset",i.inset))},inputs:{vertical:"vertical",inset:"inset"},decls:0,vars:0,template:function(e,i){},styles:[`.mat-divider{display:block;margin:0;border-top-style:solid;border-top-color:var(--mat-divider-color, var(--mat-sys-outline-variant));border-top-width:var(--mat-divider-width, 1px)}.mat-divider.mat-divider-vertical{border-top:0;border-right-style:solid;border-right-color:var(--mat-divider-color, var(--mat-sys-outline-variant));border-right-width:var(--mat-divider-width, 1px)}.mat-divider.mat-divider-inset{margin-left:80px}[dir=rtl] .mat-divider.mat-divider-inset{margin-left:auto;margin-right:80px} -`],encapsulation:2,changeDetection:0})}return a})(),ve=(()=>{class a{static \u0275fac=function(e){return new(e||a)};static \u0275mod=N({type:a});static \u0275inj=z({imports:[j]})}return a})();var Fo=["tooltip"],Oo=20;var Ro=new w("mat-tooltip-scroll-strategy",{providedIn:"root",factory:()=>{let a=r(G);return()=>re(a,{scrollThrottle:Oo})}}),Ao=new w("mat-tooltip-default-options",{providedIn:"root",factory:()=>({showDelay:0,hideDelay:0,touchendHideDelay:1500})});var Pa="tooltip-panel",Lo={passive:!0},Po=8,Bo=8,zo=24,No=200,We=(()=>{class a{_elementRef=r(O);_ngZone=r(H);_platform=r(ot);_ariaDescriber=r(Qi);_focusMonitor=r(le);_dir=r(ut);_injector=r(G);_viewContainerRef=r(ee);_mediaMatcher=r(Ni);_document=r(It);_renderer=r(st);_animationsDisabled=Q();_defaultOptions=r(Ao,{optional:!0});_overlayRef=null;_tooltipInstance=null;_overlayPanelClass;_portal;_position="below";_positionAtOrigin=!1;_disabled=!1;_tooltipClass;_viewInitialized=!1;_pointerExitEventsInitialized=!1;_tooltipComponent=Ba;_viewportMargin=8;_currentPosition;_cssClassPrefix="mat-mdc";_ariaDescriptionPending=!1;_dirSubscribed=!1;get position(){return this._position}set position(t){t!==this._position&&(this._position=t,this._overlayRef&&(this._updatePosition(this._overlayRef),this._tooltipInstance?.show(0),this._overlayRef.updatePosition()))}get positionAtOrigin(){return this._positionAtOrigin}set positionAtOrigin(t){this._positionAtOrigin=K(t),this._detach(),this._overlayRef=null}get disabled(){return this._disabled}set disabled(t){let e=K(t);this._disabled!==e&&(this._disabled=e,e?this.hide(0):this._setupPointerEnterEventsIfNeeded(),this._syncAriaDescription(this.message))}get showDelay(){return this._showDelay}set showDelay(t){this._showDelay=Gt(t)}_showDelay;get hideDelay(){return this._hideDelay}set hideDelay(t){this._hideDelay=Gt(t),this._tooltipInstance&&(this._tooltipInstance._mouseLeaveHideDelay=this._hideDelay)}_hideDelay;touchGestures="auto";get message(){return this._message}set message(t){let e=this._message;this._message=t!=null?String(t).trim():"",!this._message&&this._isTooltipVisible()?this.hide(0):(this._setupPointerEnterEventsIfNeeded(),this._updateTooltipMessage()),this._syncAriaDescription(e)}_message="";get tooltipClass(){return this._tooltipClass}set tooltipClass(t){this._tooltipClass=t,this._tooltipInstance&&this._setTooltipClass(this._tooltipClass)}_eventCleanups=[];_touchstartTimeout=null;_destroyed=new k;_isDestroyed=!1;constructor(){let t=this._defaultOptions;t&&(this._showDelay=t.showDelay,this._hideDelay=t.hideDelay,t.position&&(this.position=t.position),t.positionAtOrigin&&(this.positionAtOrigin=t.positionAtOrigin),t.touchGestures&&(this.touchGestures=t.touchGestures),t.tooltipClass&&(this.tooltipClass=t.tooltipClass)),this._viewportMargin=Po}ngAfterViewInit(){this._viewInitialized=!0,this._setupPointerEnterEventsIfNeeded(),this._focusMonitor.monitor(this._elementRef).pipe(I(this._destroyed)).subscribe(t=>{t?t==="keyboard"&&this._ngZone.run(()=>this.show()):this._ngZone.run(()=>this.hide(0))})}ngOnDestroy(){let t=this._elementRef.nativeElement;this._touchstartTimeout&&clearTimeout(this._touchstartTimeout),this._overlayRef&&(this._overlayRef.dispose(),this._tooltipInstance=null),this._eventCleanups.forEach(e=>e()),this._eventCleanups.length=0,this._destroyed.next(),this._destroyed.complete(),this._isDestroyed=!0,this._ariaDescriber.removeDescription(t,this.message,"tooltip"),this._focusMonitor.stopMonitoring(t)}show(t=this.showDelay,e){if(this.disabled||!this.message||this._isTooltipVisible()){this._tooltipInstance?._cancelPendingAnimations();return}let i=this._createOverlay(e);this._detach(),this._portal=this._portal||new qt(this._tooltipComponent,this._viewContainerRef);let n=this._tooltipInstance=i.attach(this._portal).instance;n._triggerElement=this._elementRef.nativeElement,n._mouseLeaveHideDelay=this._hideDelay,n.afterHidden().pipe(I(this._destroyed)).subscribe(()=>this._detach()),this._setTooltipClass(this._tooltipClass),this._updateTooltipMessage(),n.show(t)}hide(t=this.hideDelay){let e=this._tooltipInstance;e&&(e.isVisible()?e.hide(t):(e._cancelPendingAnimations(),this._detach()))}toggle(t){this._isTooltipVisible()?this.hide():this.show(void 0,t)}_isTooltipVisible(){return!!this._tooltipInstance&&this._tooltipInstance.isVisible()}_createOverlay(t){if(this._overlayRef){let o=this._overlayRef.getConfig().positionStrategy;if((!this.positionAtOrigin||!t)&&o._origin instanceof O)return this._overlayRef;this._detach()}let e=this._injector.get(Ci).getAncestorScrollContainers(this._elementRef),i=`${this._cssClassPrefix}-${Pa}`,n=Ei(this._injector,this.positionAtOrigin?t||this._elementRef:this._elementRef).withTransformOriginOn(`.${this._cssClassPrefix}-tooltip`).withFlexibleDimensions(!1).withViewportMargin(this._viewportMargin).withScrollableContainers(e).withPopoverLocation("global");return n.positionChanges.pipe(I(this._destroyed)).subscribe(o=>{this._updateCurrentPositionClass(o.connectionPair),this._tooltipInstance&&o.scrollableViewProperties.isOverlayClipped&&this._tooltipInstance.isVisible()&&this._ngZone.run(()=>this.hide(0))}),this._overlayRef=se(this._injector,{direction:this._dir,positionStrategy:n,panelClass:this._overlayPanelClass?[...this._overlayPanelClass,i]:i,scrollStrategy:this._injector.get(Ro)(),disableAnimations:this._animationsDisabled,eventPredicate:this._overlayEventPredicate}),this._updatePosition(this._overlayRef),this._overlayRef.detachments().pipe(I(this._destroyed)).subscribe(()=>this._detach()),this._overlayRef.outsidePointerEvents().pipe(I(this._destroyed)).subscribe(()=>this._tooltipInstance?._handleBodyInteraction()),this._overlayRef.keydownEvents().pipe(I(this._destroyed)).subscribe(o=>{o.preventDefault(),o.stopPropagation(),this._ngZone.run(()=>this.hide(0))}),this._defaultOptions?.disableTooltipInteractivity&&this._overlayRef.addPanelClass(`${this._cssClassPrefix}-tooltip-panel-non-interactive`),this._dirSubscribed||(this._dirSubscribed=!0,this._dir.change.pipe(I(this._destroyed)).subscribe(()=>{this._overlayRef&&this._updatePosition(this._overlayRef)})),this._overlayRef}_detach(){this._overlayRef&&this._overlayRef.hasAttached()&&this._overlayRef.detach(),this._tooltipInstance=null}_updatePosition(t){let e=t.getConfig().positionStrategy,i=this._getOrigin(),n=this._getOverlayPosition();e.withPositions([this._addOffset(X(X({},i.main),n.main)),this._addOffset(X(X({},i.fallback),n.fallback))])}_addOffset(t){let e=Bo,i=!this._dir||this._dir.value=="ltr";return t.originY==="top"?t.offsetY=-e:t.originY==="bottom"?t.offsetY=e:t.originX==="start"?t.offsetX=i?-e:e:t.originX==="end"&&(t.offsetX=i?e:-e),t}_getOrigin(){let t=!this._dir||this._dir.value=="ltr",e=this.position,i;e=="above"||e=="below"?i={originX:"center",originY:e=="above"?"top":"bottom"}:e=="before"||e=="left"&&t||e=="right"&&!t?i={originX:"start",originY:"center"}:(e=="after"||e=="right"&&t||e=="left"&&!t)&&(i={originX:"end",originY:"center"});let{x:n,y:o}=this._invertPosition(i.originX,i.originY);return{main:i,fallback:{originX:n,originY:o}}}_getOverlayPosition(){let t=!this._dir||this._dir.value=="ltr",e=this.position,i;e=="above"?i={overlayX:"center",overlayY:"bottom"}:e=="below"?i={overlayX:"center",overlayY:"top"}:e=="before"||e=="left"&&t||e=="right"&&!t?i={overlayX:"end",overlayY:"center"}:(e=="after"||e=="right"&&t||e=="left"&&!t)&&(i={overlayX:"start",overlayY:"center"});let{x:n,y:o}=this._invertPosition(i.overlayX,i.overlayY);return{main:i,fallback:{overlayX:n,overlayY:o}}}_updateTooltipMessage(){this._tooltipInstance&&(this._tooltipInstance.message=this.message,this._tooltipInstance._markForCheck(),rt(()=>{this._tooltipInstance&&this._overlayRef.updatePosition()},{injector:this._injector}))}_setTooltipClass(t){this._tooltipInstance&&(this._tooltipInstance.tooltipClass=t instanceof Set?Array.from(t):t,this._tooltipInstance._markForCheck())}_invertPosition(t,e){return this.position==="above"||this.position==="below"?e==="top"?e="bottom":e==="bottom"&&(e="top"):t==="end"?t="start":t==="start"&&(t="end"),{x:t,y:e}}_updateCurrentPositionClass(t){let{overlayY:e,originX:i,originY:n}=t,o;if(e==="center"?this._dir&&this._dir.value==="rtl"?o=i==="end"?"left":"right":o=i==="start"?"left":"right":o=e==="bottom"&&n==="top"?"above":"below",o!==this._currentPosition){let f=this._overlayRef;if(f){let b=`${this._cssClassPrefix}-${Pa}-`;f.removePanelClass(b+this._currentPosition),f.addPanelClass(b+o)}this._currentPosition=o}}_setupPointerEnterEventsIfNeeded(){this._disabled||!this.message||!this._viewInitialized||this._eventCleanups.length||(this._isTouchPlatform()?this.touchGestures!=="off"&&(this._disableNativeGesturesIfNecessary(),this._addListener("touchstart",t=>{let e=t.targetTouches?.[0],i=e?{x:e.clientX,y:e.clientY}:void 0;this._setupPointerExitEventsIfNeeded(),this._touchstartTimeout&&clearTimeout(this._touchstartTimeout);let n=500;this._touchstartTimeout=setTimeout(()=>{this._touchstartTimeout=null,this.show(void 0,i)},this._defaultOptions?.touchLongPressShowDelay??n)})):this._addListener("mouseenter",t=>{this._setupPointerExitEventsIfNeeded();let e;t.x!==void 0&&t.y!==void 0&&(e=t),this.show(void 0,e)}))}_setupPointerExitEventsIfNeeded(){if(!this._pointerExitEventsInitialized){if(this._pointerExitEventsInitialized=!0,!this._isTouchPlatform())this._addListener("mouseleave",t=>{let e=t.relatedTarget;(!e||!this._overlayRef?.overlayElement.contains(e))&&this.hide()}),this._addListener("wheel",t=>{if(this._isTooltipVisible()){let e=this._document.elementFromPoint(t.clientX,t.clientY),i=this._elementRef.nativeElement;e!==i&&!i.contains(e)&&this.hide()}});else if(this.touchGestures!=="off"){this._disableNativeGesturesIfNecessary();let t=()=>{this._touchstartTimeout&&clearTimeout(this._touchstartTimeout),this.hide(this._defaultOptions?.touchendHideDelay)};this._addListener("touchend",t),this._addListener("touchcancel",t)}}}_addListener(t,e){this._eventCleanups.push(this._renderer.listen(this._elementRef.nativeElement,t,e,Lo))}_isTouchPlatform(){return this._platform.IOS||this._platform.ANDROID?!0:this._platform.isBrowser?!!this._defaultOptions?.detectHoverCapability&&this._mediaMatcher.matchMedia("(any-hover: none)").matches:!1}_disableNativeGesturesIfNecessary(){let t=this.touchGestures;if(t!=="off"){let e=this._elementRef.nativeElement,i=e.style;(t==="on"||e.nodeName!=="INPUT"&&e.nodeName!=="TEXTAREA")&&(i.userSelect=i.msUserSelect=i.webkitUserSelect=i.MozUserSelect="none"),(t==="on"||!e.draggable)&&(i.webkitUserDrag="none"),i.touchAction="none",i.webkitTapHighlightColor="transparent"}}_syncAriaDescription(t){this._ariaDescriptionPending||(this._ariaDescriptionPending=!0,this._ariaDescriber.removeDescription(this._elementRef.nativeElement,t,"tooltip"),this._isDestroyed||rt({write:()=>{this._ariaDescriptionPending=!1,this.message&&!this.disabled&&this._ariaDescriber.describe(this._elementRef.nativeElement,this.message,"tooltip")}},{injector:this._injector}))}_overlayEventPredicate=t=>t.type==="keydown"?this._isTooltipVisible()&&t.keyCode===27&&!ct(t):!0;static \u0275fac=function(e){return new(e||a)};static \u0275dir=x({type:a,selectors:[["","matTooltip",""]],hostAttrs:[1,"mat-mdc-tooltip-trigger"],hostVars:2,hostBindings:function(e,i){e&2&&_("mat-mdc-tooltip-disabled",i.disabled)},inputs:{position:[0,"matTooltipPosition","position"],positionAtOrigin:[0,"matTooltipPositionAtOrigin","positionAtOrigin"],disabled:[0,"matTooltipDisabled","disabled"],showDelay:[0,"matTooltipShowDelay","showDelay"],hideDelay:[0,"matTooltipHideDelay","hideDelay"],touchGestures:[0,"matTooltipTouchGestures","touchGestures"],message:[0,"matTooltip","message"],tooltipClass:[0,"matTooltipClass","tooltipClass"]},exportAs:["matTooltip"]})}return a})(),Ba=(()=>{class a{_changeDetectorRef=r(W);_elementRef=r(O);_isMultiline=!1;message;tooltipClass;_showTimeoutId;_hideTimeoutId;_triggerElement;_mouseLeaveHideDelay;_animationsDisabled=Q();_tooltip;_closeOnInteraction=!1;_isVisible=!1;_onHide=new k;_showAnimation="mat-mdc-tooltip-show";_hideAnimation="mat-mdc-tooltip-hide";constructor(){}show(t){this._hideTimeoutId!=null&&clearTimeout(this._hideTimeoutId),this._showTimeoutId=setTimeout(()=>{this._toggleVisibility(!0),this._showTimeoutId=void 0},t)}hide(t){this._showTimeoutId!=null&&clearTimeout(this._showTimeoutId),this._hideTimeoutId=setTimeout(()=>{this._toggleVisibility(!1),this._hideTimeoutId=void 0},t)}afterHidden(){return this._onHide}isVisible(){return this._isVisible}ngOnDestroy(){this._cancelPendingAnimations(),this._onHide.complete(),this._triggerElement=null}_handleBodyInteraction(){this._closeOnInteraction&&this.hide(0)}_markForCheck(){this._changeDetectorRef.markForCheck()}_handleMouseLeave({relatedTarget:t}){(!t||!this._triggerElement.contains(t))&&(this.isVisible()?this.hide(this._mouseLeaveHideDelay):this._finalizeAnimation(!1))}_onShow(){this._isMultiline=this._isTooltipMultiline(),this._markForCheck()}_isTooltipMultiline(){let t=this._elementRef.nativeElement.getBoundingClientRect();return t.height>zo&&t.width>=No}_handleAnimationEnd({animationName:t}){(t===this._showAnimation||t===this._hideAnimation)&&this._finalizeAnimation(t===this._showAnimation)}_cancelPendingAnimations(){this._showTimeoutId!=null&&clearTimeout(this._showTimeoutId),this._hideTimeoutId!=null&&clearTimeout(this._hideTimeoutId),this._showTimeoutId=this._hideTimeoutId=void 0}_finalizeAnimation(t){t?this._closeOnInteraction=!0:this.isVisible()||this._onHide.next()}_toggleVisibility(t){let e=this._tooltip.nativeElement,i=this._showAnimation,n=this._hideAnimation;if(e.classList.remove(t?n:i),e.classList.add(t?i:n),this._isVisible!==t&&(this._isVisible=t,this._changeDetectorRef.markForCheck()),t&&!this._animationsDisabled&&typeof getComputedStyle=="function"){let o=getComputedStyle(e);(o.getPropertyValue("animation-duration")==="0s"||o.getPropertyValue("animation-name")==="none")&&(this._animationsDisabled=!0)}t&&this._onShow(),this._animationsDisabled&&(e.classList.add("_mat-animation-noopable"),this._finalizeAnimation(t))}static \u0275fac=function(e){return new(e||a)};static \u0275cmp=C({type:a,selectors:[["mat-tooltip-component"]],viewQuery:function(e,i){if(e&1&&V(Fo,7),e&2){let n;m(n=h())&&(i._tooltip=n.first)}},hostAttrs:["aria-hidden","true"],hostBindings:function(e,i){e&1&&u("mouseleave",function(o){return i._handleMouseLeave(o)})},decls:4,vars:5,consts:[["tooltip",""],[1,"mdc-tooltip","mat-mdc-tooltip",3,"animationend"],[1,"mat-mdc-tooltip-surface","mdc-tooltip__surface"]],template:function(e,i){e&1&&(kt(0,"div",1,0),si("animationend",function(o){return i._handleAnimationEnd(o)}),kt(2,"div",2),E(3),Ot()()),e&2&&(_t(i.tooltipClass),_("mdc-tooltip--multiline",i._isMultiline),c(3),pt(i.message))},styles:[`.mat-mdc-tooltip{position:relative;transform:scale(0);display:inline-flex}.mat-mdc-tooltip::before{content:"";top:0;right:0;bottom:0;left:0;z-index:-1;position:absolute}.mat-mdc-tooltip-panel-below .mat-mdc-tooltip::before{top:-8px}.mat-mdc-tooltip-panel-above .mat-mdc-tooltip::before{bottom:-8px}.mat-mdc-tooltip-panel-right .mat-mdc-tooltip::before{left:-8px}.mat-mdc-tooltip-panel-left .mat-mdc-tooltip::before{right:-8px}.mat-mdc-tooltip._mat-animation-noopable{animation:none;transform:scale(1)}.mat-mdc-tooltip-surface{word-break:normal;overflow-wrap:anywhere;padding:4px 8px;min-width:40px;max-width:200px;min-height:24px;max-height:40vh;box-sizing:border-box;overflow:hidden;text-align:center;will-change:transform,opacity;background-color:var(--mat-tooltip-container-color, var(--mat-sys-inverse-surface));color:var(--mat-tooltip-supporting-text-color, var(--mat-sys-inverse-on-surface));border-radius:var(--mat-tooltip-container-shape, var(--mat-sys-corner-extra-small));font-family:var(--mat-tooltip-supporting-text-font, var(--mat-sys-body-small-font));font-size:var(--mat-tooltip-supporting-text-size, var(--mat-sys-body-small-size));font-weight:var(--mat-tooltip-supporting-text-weight, var(--mat-sys-body-small-weight));line-height:var(--mat-tooltip-supporting-text-line-height, var(--mat-sys-body-small-line-height));letter-spacing:var(--mat-tooltip-supporting-text-tracking, var(--mat-sys-body-small-tracking))}.mat-mdc-tooltip-surface::before{position:absolute;box-sizing:border-box;width:100%;height:100%;top:0;left:0;border:1px solid rgba(0,0,0,0);border-radius:inherit;content:"";pointer-events:none}.mdc-tooltip--multiline .mat-mdc-tooltip-surface{text-align:left}[dir=rtl] .mdc-tooltip--multiline .mat-mdc-tooltip-surface{text-align:right}.mat-mdc-tooltip-panel{line-height:normal}.mat-mdc-tooltip-panel.mat-mdc-tooltip-panel-non-interactive{pointer-events:none}@keyframes mat-mdc-tooltip-show{0%{opacity:0;transform:scale(0.8)}100%{opacity:1;transform:scale(1)}}@keyframes mat-mdc-tooltip-hide{0%{opacity:1;transform:scale(1)}100%{opacity:0;transform:scale(0.8)}}.mat-mdc-tooltip-show{animation:mat-mdc-tooltip-show 150ms cubic-bezier(0, 0, 0.2, 1) forwards}.mat-mdc-tooltip-hide{animation:mat-mdc-tooltip-hide 75ms cubic-bezier(0.4, 0, 1, 1) forwards} -`],encapsulation:2,changeDetection:0})}return a})();var za=(()=>{class a{static \u0275fac=function(e){return new(e||a)};static \u0275mod=N({type:a});static \u0275inj=z({imports:[Vi,Pt,j,ne]})}return a})();var jt=class{data=[];dataChange=new St([]);itemUpdated=new k;getItems(){return this.data}add(s){this.findIndex(s)>=0?this.update(s):(this.data.push(s),this.dataChange.next(this.data))}set(s){s.forEach(e=>{let i=this.findIndex(e);if(i>=0){let n=Object.assign(this.data[i],e);this.data[i]=n}else this.data.push(e)}),this.data.filter(e=>s.filter(i=>this.getItemKey(i)===this.getItemKey(e)).length===0).forEach(e=>this.remove(e)),this.dataChange.next(this.data)}get(s){let t=this.data.findIndex(e=>this.getItemKey(e)===s);if(t>=0)return this.data[t]}update(s){let t=this.findIndex(s);if(t>=0){let e=Object.assign(this.data[t],s);this.data[t]=e,this.dataChange.next(this.data),this.itemUpdated.next(e)}}remove(s){let t=this.findIndex(s);t>=0&&(this.data.splice(t,1),this.dataChange.next(this.data))}get changes(){return this.dataChange}get itemChanged(){return this.itemUpdated}clear(){this.data=[],this.dataChange.next(this.data)}findIndex(s){return this.data.findIndex(t=>this.getItemKey(t)===this.getItemKey(s))}};var Na=(()=>{class a extends jt{getItemKey(t){return t.link_id}static \u0275fac=(()=>{let t;return function(i){return(t||(t=Y(a)))(i||a)}})();static \u0275prov=P({token:a,factory:a.\u0275fac})}return a})();var ja=(()=>{class a extends jt{getItemKey(t){return t.node_id}static \u0275fac=(()=>{let t;return function(i){return(t||(t=Y(a)))(i||a)}})();static \u0275prov=P({token:a,factory:a.\u0275fac})}return a})();var Va=(()=>{class a{httpController;constructor(t){this.httpController=t}getComputes(t){return this.httpController.get(t,"/computes")}getCompute(t,e){return this.httpController.get(t,`/computes/${e}`)}createCompute(t,e){return this.httpController.post(t,"/computes",e)}updateCompute(t,e,i){return this.httpController.put(t,`/computes/${e}`,i)}deleteCompute(t,e){return this.httpController.delete(t,`/computes/${e}`)}connectCompute(t,e){return this.httpController.post(t,`/computes/${e}/connect`,null)}getStatistics(t){return this.httpController.get(t,"/statistics")}static \u0275fac=function(e){return new(e||a)(nt(me))};static \u0275prov=P({token:a,factory:a.\u0275fac})}return a})();var Ha=(()=>{class a{settings={crash_reports:!0,console_command:void 0,anonymous_statistics:!0};reportsSettings="crash_reports";consoleSettings="console_command";statisticsSettings="statistics_command";constructor(){this.getItem(this.reportsSettings)&&(this.settings.crash_reports=this.getItem(this.reportsSettings)==="true"),this.getItem(this.consoleSettings)&&(this.settings.console_command=this.getItem(this.consoleSettings)),this.getItem(this.statisticsSettings)&&(this.settings.anonymous_statistics=this.getItem(this.statisticsSettings)==="true")}setReportsSettings(t){this.settings.crash_reports=t,this.removeItem(this.reportsSettings),t?this.setItem(this.reportsSettings,"true"):this.setItem(this.reportsSettings,"false")}setStatisticsSettings(t){this.settings.anonymous_statistics=t,this.removeItem(this.statisticsSettings),t?this.setItem(this.statisticsSettings,"true"):this.setItem(this.statisticsSettings,"false")}getReportsSettings(){return this.getItem(this.reportsSettings)==="true"}getStatisticsSettings(){return this.getItem(this.statisticsSettings)==="true"}setConsoleSettings(t){this.settings.console_command=t,this.removeItem(this.consoleSettings),this.setItem(this.consoleSettings,t)}getConsoleSettings(){return this.getItem(this.consoleSettings)}removeItem(t){localStorage.removeItem(t)}setItem(t,e){localStorage.setItem(t,e)}getItem(t){return localStorage.getItem(t)}getAll(){return this.settings}setAll(t){this.settings=t,this.setConsoleSettings(t.console_command),this.setReportsSettings(t.crash_reports),this.setStatisticsSettings(t.anonymous_statistics)}static \u0275fac=function(e){return new(e||a)};static \u0275prov=P({token:a,factory:a.\u0275fac,providedIn:"root"})}return a})();var $a=(()=>{class a{controllerId;projectId;controllerIdProjectList;setcontrollerId(t){this.controllerId=t}setProjectId(t){this.projectId=t}setcontrollerIdProjectList(t){this.controllerIdProjectList=t}getcontrollerId(){return this.controllerId}getProjectId(){return this.projectId}getcontrollerIdProjectList(){return this.controllerIdProjectList}removeData(){this.controllerId="",this.projectId=""}static \u0275fac=function(e){return new(e||a)};static \u0275prov=P({token:a,factory:a.\u0275fac})}return a})();var Wa=(()=>{class a{httpController;settingsService;recentlyOpenedProjectService;compression_methods=[{id:1,value:"none",name:"None"},{id:2,value:"zip",name:"Zip compression (deflate)"},{id:3,value:"bzip2",name:"Bzip2 compression"},{id:4,value:"lzma",name:"Lzma compression"},{id:5,value:"zstd",name:"Zstandard compression"}];compression_level_default_value=[{id:1,name:"none",value:"",selectionValues:[]},{id:2,name:"zip",value:6,selectionValues:[0,1,2,3,4,5,6,7,8,9]},{id:3,name:"bzip2",value:9,selectionValues:[1,2,3,4,5,6,7,8,9]},{id:4,name:"lzma",value:" ",selectionValues:[]},{id:5,name:"zstd",value:3,selectionValues:[1,2,3,4,5,6,7,8,9.1,11,12,13,14,15,16,17,18,19,20,21,22]}];projectListSubject=new k;projectLockIconSubject=new k;constructor(t,e,i){this.httpController=t,this.settingsService=e,this.recentlyOpenedProjectService=i}projectListUpdated(){this.projectListSubject.next(!0)}getReadmeFile(t,e){return this.httpController.getText(t,`/projects/${e}/files/README.txt`)}postReadmeFile(t,e,i){return this.httpController.post(t,`/projects/${e}/files/README.txt`,i)}get(t,e){return this.httpController.get(t,`/projects/${e}`)}open(t,e){return this.httpController.post(t,`/projects/${e}/open`,{})}close(t,e){return this.recentlyOpenedProjectService.removeData(),this.httpController.post(t,`/projects/${e}/close`,{})}list(t){return this.httpController.get(t,"/projects")}nodes(t,e){return this.httpController.get(t,`/projects/${e}/nodes`)}links(t,e){return this.httpController.get(t,`/projects/${e}/links`)}drawings(t,e){return this.httpController.get(t,`/projects/${e}/drawings`)}add(t,e,i){return this.httpController.post(t,"/projects",{name:e,project_id:i})}update(t,e){return this.httpController.put(t,`/projects/${e.project_id}`,{auto_close:e.auto_close,auto_open:e.auto_open,auto_start:e.auto_start,drawing_grid_size:e.drawing_grid_size,grid_size:e.grid_size,name:e.name,scene_width:e.scene_width,scene_height:e.scene_height,snap_to_grid:e.snap_to_grid,show_interface_labels:e.show_interface_labels,variables:e.variables})}delete(t,e){return this.httpController.delete(t,`/projects/${e}`)}getUploadPath(t,e,i){return`${t.protocol}//${t.host}:${t.port}/${vt.current_version}/projects/${e}/import?name=${i}`}getExportPath(t,e){return`${t.protocol}//${t.host}:${t.port}/${vt.current_version}/projects/${e.project_id}/export`}export(t,e){return this.httpController.get(t,`/projects/${e}/export`)}getStatistics(t,e){return this.httpController.get(t,`/projects/${e}/stats`)}duplicate(t,e,i){return this.httpController.post(t,`/projects/${e}/duplicate`,{name:i})}isReadOnly(t){return t.readonly?t.readonly:!1}getCompression(){return this.compression_methods}getCompressionLevel(){return this.compression_level_default_value}getexportPortableProjectPath(t,e,i={}){return i.compression_level!=null&&i.compression_level!=""?`${t.protocol}//${t.host}:${t.port}/${vt.current_version}/projects/${e}/export?include_snapshots=${i.include_snapshots}&include_images=${i.include_base_image}&reset_mac_addresses=${i.reset_mac_address}&compression=${i.compression}&compression_level=${i.compression_level}&token=${t.authToken}`:`${t.protocol}//${t.host}:${t.port}/${vt.current_version}/projects/${e}/export?include_snapshots=${i.include_snapshots}&include_images=${i.include_base_image}&reset_mac_addresses=${i.reset_mac_address}&compression=${i.compression}&token=${t.authToken}`}getProjectStatus(t,e){return this.get(t,`${e}/locked`)}projectUpdateLockIcon(){this.projectLockIconSubject.next(!0)}static \u0275fac=function(e){return new(e||a)(nt(me),nt(Ha),nt($a))};static \u0275prov=P({token:a,factory:a.\u0275fac})}return a})();var Wo=new w("DEFAULT_THEME_TOKEN",{providedIn:"root",factory:()=>"indigo-pink"}),Qa=(()=>{class a{document;_darkMode$=new St(!1);darkMode$=this._darkMode$.asObservable();themeChanged=new F;mapThemeChanged=new F;currentTheme="indigo-pink";currentMapTheme="auto";savedTheme="indigo-pink";savedMapTheme="auto";availableThemes=[{key:"deeppurple-amber",label:"Deep Purple & Amber",type:"light",primaryColor:"#6750A4"},{key:"indigo-pink",label:"Indigo & Pink",type:"light",primaryColor:"#3F51B5"},{key:"magenta-violet",label:"Magenta & Violet",type:"light",primaryColor:"#D81B60"},{key:"rose-red",label:"Rose & Red",type:"light",primaryColor:"#E91E63"},{key:"pink-bluegrey",label:"Pink & Bluegrey",type:"dark",primaryColor:"#E91E63"},{key:"purple-green",label:"Purple & Green",type:"dark",primaryColor:"#7E57C2"},{key:"azure-blue",label:"Azure & Blue",type:"dark",primaryColor:"#0078D4"},{key:"cyan-orange",label:"Cyan & Orange",type:"dark",primaryColor:"#00B7C3"}];availableMapBackgrounds=[{key:"auto",label:"Follow global theme",background:"",textColor:"",type:"light"},{key:"light-1",label:"Cyan Sky",background:"radial-gradient(ellipse at 20% 20%, #B2EBF2 0%, #E0F7FA 70%)",textColor:"#006064",type:"light"},{key:"light-2",label:"Blue Sky",background:"radial-gradient(ellipse at 20% 20%, #BBDEFB 0%, #E3F2FD 70%)",textColor:"#1565C0",type:"light"},{key:"light-3",label:"Cloud Gray",background:"radial-gradient(ellipse at 20% 20%, #F5F5F5 0%, #FAFAFA 70%)",textColor:"#424242",type:"light"},{key:"light-4",label:"Lavender",background:"radial-gradient(ellipse at 20% 20%, #E1BEE7 0%, #F3E5F5 70%)",textColor:"#4A148C",type:"light"},{key:"dark-1",label:"Deep Cyan",background:"linear-gradient(135deg, #006064 0%, #00838F 50%, #006064 100%)",textColor:"#FFFFFF",type:"dark"},{key:"dark-2",label:"Deep Blue",background:"linear-gradient(135deg, #1565C0 0%, #1976D2 50%, #1565C0 100%)",textColor:"#FFFFFF",type:"dark"},{key:"dark-3",label:"Charcoal",background:"linear-gradient(135deg, #424242 0%, #616161 50%, #424242 100%)",textColor:"#FFFFFF",type:"dark"},{key:"dark-4",label:"Deep Purple",background:"linear-gradient(135deg, #4A148C 0%, #6A1B9A 50%, #4A148C 100%)",textColor:"#FFFFFF",type:"dark"}];constructor(t,e){this.document=t;let i=localStorage.getItem("theme");this.currentTheme=i||e,this.savedTheme=this.currentTheme;let n=localStorage.getItem("mapTheme");this.currentMapTheme=n||"auto",this.savedMapTheme=this.currentMapTheme,this.applyTheme(this.currentTheme)}getCurrentTheme(){return this.currentTheme}getThemeType(){return this.isDarkTheme(this.currentTheme)?"dark":"light"}getActualMapTheme(){if(this.savedMapTheme==="auto")return this.getThemeType();let t=this.availableMapBackgrounds.find(e=>e.key===this.savedMapTheme);return t?t.type:"light"}getActualTheme(){return this.getThemeType()}isDarkTheme(t){return t==="pink-bluegrey"||t==="purple-green"||t==="azure-blue"||t==="cyan-orange"}setTheme(t){this.currentTheme!==t&&(this.currentTheme=t,this.savedTheme=t,this.applyTheme(t),this.saveThemePreference(t),this.themeChanged.emit(t),this.currentMapTheme==="auto"&&this.mapThemeChanged.emit(t),this._darkMode$.next(this.isDarkTheme(t)))}toggleTheme(){let t=this.getThemeType(),e;t==="dark"?e=this.availableThemes.find(i=>i.type==="light")?.key||"deeppurple-amber":e=this.availableThemes.find(i=>i.type==="dark")?.key||"pink-bluegrey",this.setTheme(e)}setDarkMode(t){let e=t?"pink-bluegrey":"indigo-pink";this.setTheme(e)}setMapTheme(t){this.currentMapTheme=t,this.savedMapTheme=t,localStorage.setItem("mapTheme",t),this.mapThemeChanged.emit(this.getActualMapTheme())}restoreTheme(){let t=localStorage.getItem("theme");t&&this.availableThemes.some(e=>e.key===t)&&this.setTheme(t)}applyTheme(t){let e=this.document.documentElement;e.classList.remove("theme-deeppurple-amber","theme-indigo-pink","theme-magenta-violet","theme-rose-red","theme-pink-bluegrey","theme-purple-green","theme-azure-blue","theme-cyan-orange"),e.classList.add(`theme-${t}`)}saveThemePreference(t){localStorage.setItem("theme",t)}isDarkMode(){return this.isDarkTheme(this.currentTheme)}isLightMode(){return!this.isDarkTheme(this.currentTheme)}getCanvasLabelColor(){return this.getActualMapTheme()==="dark"?"#FFFFFF":"#000000"}getCanvasLinkColor(){return this.getActualMapTheme()==="dark"?"#FFFFFF":"#000000"}static \u0275fac=function(e){return new(e||a)(nt(It),nt(Wo))};static \u0275prov=P({token:a,factory:a.\u0275fac,providedIn:"root"})}return a})();var Ga=(()=>{class a{ws;currentController;computeNotificationEmitter=new F;computeCache=new Map;computeCacheUpdated=new F;notificationsPath(t){let e="ws";return t.protocol==="https:"&&(e="wss"),`${e}://${t.host}:${t.port}/${vt.current_version}/notifications/ws?token=${t.authToken}`}projectNotificationsPath(t,e){let i="ws";return t.protocol==="https:"&&(i="wss"),`${i}://${t.host}:${t.port}/${vt.current_version}/projects/${e}/notifications/ws?token=${t.authToken}`}connectToComputeNotifications(t){this.ws&&this.currentController===t||(this.disconnect(),this.currentController=t,this.ws=new WebSocket(this.notificationsPath(t)),this.ws.onmessage=e=>{let i=JSON.parse(e.data);this.handleMessage(i)},this.ws.onerror=()=>{console.error("Compute notifications WebSocket error")},this.ws.onclose=()=>{this.ws=null})}disconnect(){this.ws&&(this.ws.close(),this.ws=null,this.currentController=null,this.computeCache.clear())}getCachedComputes(){return Array.from(this.computeCache.values())}hasCachedData(){return this.computeCache.size>0}setInitialComputes(t){this.computeCache.clear(),t.forEach(e=>{this.computeCache.set(e.compute_id,e)}),this.computeCacheUpdated.emit(this.getCachedComputes())}handleMessage(t){switch(t.action){case"compute.created":this.computeCache.set(t.event.compute_id,t.event),this.computeNotificationEmitter.emit(t),this.computeCacheUpdated.emit(this.getCachedComputes());break;case"compute.updated":this.computeCache.set(t.event.compute_id,t.event),this.computeNotificationEmitter.emit(t),this.computeCacheUpdated.emit(this.getCachedComputes());break;case"compute.deleted":this.computeCache.delete(t.event.compute_id),this.computeNotificationEmitter.emit(t),this.computeCacheUpdated.emit(this.getCachedComputes());break}}static \u0275fac=function(e){return new(e||a)};static \u0275prov=P({token:a,factory:a.\u0275fac})}return a})();function Qo(a,s){if(a&1){let t=lt();l(0,"div",1)(1,"button",2),u("click",function(){R(t);let i=p();return A(i.action())}),E(2),d()()}if(a&2){let t=p();c(2),ft(" ",t.data.action," ")}}var Go=["label"];function qo(a,s){}var Uo=Math.pow(2,31)-1,Zt=class{_overlayRef;instance;containerInstance;_afterDismissed=new k;_afterOpened=new k;_onAction=new k;_durationTimeoutId;_dismissedByAction=!1;constructor(s,t){this._overlayRef=t,this.containerInstance=s,s._onExit.subscribe(()=>this._finishDismiss())}dismiss(){this._afterDismissed.closed||this.containerInstance.exit(),clearTimeout(this._durationTimeoutId)}dismissWithAction(){this._onAction.closed||(this._dismissedByAction=!0,this._onAction.next(),this._onAction.complete(),this.dismiss()),clearTimeout(this._durationTimeoutId)}closeWithAction(){this.dismissWithAction()}_dismissAfter(s){this._durationTimeoutId=setTimeout(()=>this.dismiss(),Math.min(s,Uo))}_open(){this._afterOpened.closed||(this._afterOpened.next(),this._afterOpened.complete())}_finishDismiss(){this._overlayRef.dispose(),this._onAction.closed||this._onAction.complete(),this._afterDismissed.next({dismissedByAction:this._dismissedByAction}),this._afterDismissed.complete(),this._dismissedByAction=!1}afterDismissed(){return this._afterDismissed}afterOpened(){return this.containerInstance._onEnter}onAction(){return this._onAction}},qa=new w("MatSnackBarData"),Vt=class{politeness="polite";announcementMessage="";viewContainerRef;duration=0;panelClass;direction;data=null;horizontalPosition="center";verticalPosition="bottom"},Yo=(()=>{class a{static \u0275fac=function(e){return new(e||a)};static \u0275dir=x({type:a,selectors:[["","matSnackBarLabel",""]],hostAttrs:[1,"mat-mdc-snack-bar-label","mdc-snackbar__label"]})}return a})(),Ko=(()=>{class a{static \u0275fac=function(e){return new(e||a)};static \u0275dir=x({type:a,selectors:[["","matSnackBarActions",""]],hostAttrs:[1,"mat-mdc-snack-bar-actions","mdc-snackbar__actions"]})}return a})(),Zo=(()=>{class a{static \u0275fac=function(e){return new(e||a)};static \u0275dir=x({type:a,selectors:[["","matSnackBarAction",""]],hostAttrs:[1,"mat-mdc-snack-bar-action","mdc-snackbar__action"]})}return a})(),Ua=(()=>{class a{snackBarRef=r(Zt);data=r(qa);constructor(){}action(){this.snackBarRef.dismissWithAction()}get hasAction(){return!!this.data.action}static \u0275fac=function(e){return new(e||a)};static \u0275cmp=C({type:a,selectors:[["simple-snack-bar"]],hostAttrs:[1,"mat-mdc-simple-snack-bar"],exportAs:["matSnackBar"],decls:3,vars:2,consts:[["matSnackBarLabel",""],["matSnackBarActions",""],["matButton","","matSnackBarAction","",3,"click"]],template:function(e,i){e&1&&(l(0,"div",0),E(1),d(),g(2,Qo,3,1,"div",1)),e&2&&(c(),ft(" ",i.data.message,` -`),c(),v(i.hasAction?2:-1))},dependencies:[qi,Yo,Ko,Zo],styles:[`.mat-mdc-simple-snack-bar{display:flex}.mat-mdc-simple-snack-bar .mat-mdc-snack-bar-label{max-height:50vh;overflow:auto} -`],encapsulation:2,changeDetection:0})}return a})(),Qe="_mat-snack-bar-enter",Ge="_mat-snack-bar-exit",Xo=(()=>{class a extends Mi{_ngZone=r(H);_elementRef=r(O);_changeDetectorRef=r(W);_platform=r(ot);_animationsDisabled=Q();snackBarConfig=r(Vt);_document=r(It);_trackedModals=new Set;_enterFallback;_exitFallback;_injector=r(G);_announceDelay=150;_announceTimeoutId;_destroyed=!1;_portalOutlet;_onAnnounce=new k;_onExit=new k;_onEnter=new k;_animationState="void";_live;_label;_role;_liveElementId=r(U).getId("mat-snack-bar-container-live-");constructor(){super();let t=this.snackBarConfig;t.politeness==="assertive"&&!t.announcementMessage?this._live="assertive":t.politeness==="off"?this._live="off":this._live="polite",this._platform.FIREFOX&&(this._live==="polite"&&(this._role="status"),this._live==="assertive"&&(this._role="alert"))}attachComponentPortal(t){this._assertNotAttached();let e=this._portalOutlet.attachComponentPortal(t);return this._afterPortalAttached(),e}attachTemplatePortal(t){this._assertNotAttached();let e=this._portalOutlet.attachTemplatePortal(t);return this._afterPortalAttached(),e}attachDomPortal=t=>{this._assertNotAttached();let e=this._portalOutlet.attachDomPortal(t);return this._afterPortalAttached(),e};onAnimationEnd(t){t===Ge?this._completeExit():t===Qe&&(clearTimeout(this._enterFallback),this._ngZone.run(()=>{this._onEnter.next(),this._onEnter.complete()}))}enter(){this._destroyed||(this._animationState="visible",this._changeDetectorRef.markForCheck(),this._changeDetectorRef.detectChanges(),this._screenReaderAnnounce(),this._animationsDisabled?rt(()=>{this._ngZone.run(()=>queueMicrotask(()=>this.onAnimationEnd(Qe)))},{injector:this._injector}):(clearTimeout(this._enterFallback),this._enterFallback=setTimeout(()=>{this._elementRef.nativeElement.classList.add("mat-snack-bar-fallback-visible"),this.onAnimationEnd(Qe)},200)))}exit(){return this._destroyed?te(void 0):(this._ngZone.run(()=>{this._animationState="hidden",this._changeDetectorRef.markForCheck(),this._elementRef.nativeElement.setAttribute("mat-exit",""),clearTimeout(this._announceTimeoutId),this._animationsDisabled?rt(()=>{this._ngZone.run(()=>queueMicrotask(()=>this.onAnimationEnd(Ge)))},{injector:this._injector}):(clearTimeout(this._exitFallback),this._exitFallback=setTimeout(()=>this.onAnimationEnd(Ge),200))}),this._onExit)}ngOnDestroy(){this._destroyed=!0,this._clearFromModals(),this._completeExit()}_completeExit(){clearTimeout(this._exitFallback),queueMicrotask(()=>{this._onExit.next(),this._onExit.complete()})}_afterPortalAttached(){let t=this._elementRef.nativeElement,e=this.snackBarConfig.panelClass;e&&(Array.isArray(e)?e.forEach(o=>t.classList.add(o)):t.classList.add(e)),this._exposeToModals();let i=this._label.nativeElement,n="mdc-snackbar__label";i.classList.toggle(n,!i.querySelector(`.${n}`))}_exposeToModals(){let t=this._liveElementId,e=this._document.querySelectorAll('body > .cdk-overlay-container [aria-modal="true"]');for(let i=0;i{let e=t.getAttribute("aria-owns");if(e){let i=e.replace(this._liveElementId,"").trim();i.length>0?t.setAttribute("aria-owns",i):t.removeAttribute("aria-owns")}}),this._trackedModals.clear()}_assertNotAttached(){this._portalOutlet.hasAttached()}_screenReaderAnnounce(){this._announceTimeoutId||this._ngZone.runOutsideAngular(()=>{this._announceTimeoutId=setTimeout(()=>{if(this._destroyed)return;let t=this._elementRef.nativeElement,e=t.querySelector("[aria-hidden]"),i=t.querySelector("[aria-live]");if(e&&i){let n=null;this._platform.isBrowser&&document.activeElement instanceof HTMLElement&&e.contains(document.activeElement)&&(n=document.activeElement),e.removeAttribute("aria-hidden"),i.appendChild(e),n?.focus(),this._onAnnounce.next(),this._onAnnounce.complete()}},this._announceDelay)})}static \u0275fac=function(e){return new(e||a)};static \u0275cmp=C({type:a,selectors:[["mat-snack-bar-container"]],viewQuery:function(e,i){if(e&1&&V(Lt,7)(Go,7),e&2){let n;m(n=h())&&(i._portalOutlet=n.first),m(n=h())&&(i._label=n.first)}},hostAttrs:[1,"mdc-snackbar","mat-mdc-snack-bar-container"],hostVars:6,hostBindings:function(e,i){e&1&&u("animationend",function(o){return i.onAnimationEnd(o.animationName)})("animationcancel",function(o){return i.onAnimationEnd(o.animationName)}),e&2&&_("mat-snack-bar-container-enter",i._animationState==="visible")("mat-snack-bar-container-exit",i._animationState==="hidden")("mat-snack-bar-container-animations-enabled",!i._animationsDisabled)},features:[q],decls:6,vars:3,consts:[["label",""],[1,"mdc-snackbar__surface","mat-mdc-snackbar-surface"],[1,"mat-mdc-snack-bar-label"],["aria-hidden","true"],["cdkPortalOutlet",""]],template:function(e,i){e&1&&(l(0,"div",1)(1,"div",2,0)(3,"div",3),tt(4,qo,0,0,"ng-template",4),d(),M(5,"div"),d()()),e&2&&(c(5),T("aria-live",i._live)("role",i._role)("id",i._liveElementId))},dependencies:[Lt],styles:[`@keyframes _mat-snack-bar-enter{from{transform:scale(0.8);opacity:0}to{transform:scale(1);opacity:1}}@keyframes _mat-snack-bar-exit{from{opacity:1}to{opacity:0}}.mat-mdc-snack-bar-container{display:flex;align-items:center;justify-content:center;box-sizing:border-box;-webkit-tap-highlight-color:rgba(0,0,0,0);margin:8px}.mat-mdc-snack-bar-handset .mat-mdc-snack-bar-container{width:100vw}.mat-snack-bar-container-animations-enabled{opacity:0}.mat-snack-bar-container-animations-enabled.mat-snack-bar-fallback-visible{opacity:1}.mat-snack-bar-container-animations-enabled.mat-snack-bar-container-enter{animation:_mat-snack-bar-enter 150ms cubic-bezier(0, 0, 0.2, 1) forwards}.mat-snack-bar-container-animations-enabled.mat-snack-bar-container-exit{animation:_mat-snack-bar-exit 75ms cubic-bezier(0.4, 0, 1, 1) forwards}.mat-mdc-snackbar-surface{box-shadow:0px 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0, 0, 0, 0.12);display:flex;align-items:center;justify-content:flex-start;box-sizing:border-box;padding-left:0;padding-right:8px}[dir=rtl] .mat-mdc-snackbar-surface{padding-right:0;padding-left:8px}.mat-mdc-snack-bar-container .mat-mdc-snackbar-surface{min-width:344px;max-width:672px}.mat-mdc-snack-bar-handset .mat-mdc-snackbar-surface{width:100%;min-width:0}@media(forced-colors: active){.mat-mdc-snackbar-surface{outline:solid 1px}}.mat-mdc-snack-bar-container .mat-mdc-snackbar-surface{color:var(--mat-snack-bar-supporting-text-color, var(--mat-sys-inverse-on-surface));border-radius:var(--mat-snack-bar-container-shape, var(--mat-sys-corner-extra-small));background-color:var(--mat-snack-bar-container-color, var(--mat-sys-inverse-surface))}.mdc-snackbar__label{width:100%;flex-grow:1;box-sizing:border-box;margin:0;padding:14px 8px 14px 16px}[dir=rtl] .mdc-snackbar__label{padding-left:8px;padding-right:16px}.mat-mdc-snack-bar-container .mdc-snackbar__label{font-family:var(--mat-snack-bar-supporting-text-font, var(--mat-sys-body-medium-font));font-size:var(--mat-snack-bar-supporting-text-size, var(--mat-sys-body-medium-size));font-weight:var(--mat-snack-bar-supporting-text-weight, var(--mat-sys-body-medium-weight));line-height:var(--mat-snack-bar-supporting-text-line-height, var(--mat-sys-body-medium-line-height))}.mat-mdc-snack-bar-actions{display:flex;flex-shrink:0;align-items:center;box-sizing:border-box}.mat-mdc-snack-bar-handset,.mat-mdc-snack-bar-container,.mat-mdc-snack-bar-label{flex:1 1 auto}.mat-mdc-snack-bar-container .mat-mdc-button.mat-mdc-snack-bar-action:not(:disabled).mat-unthemed{color:var(--mat-snack-bar-button-color, var(--mat-sys-inverse-primary))}.mat-mdc-snack-bar-container .mat-mdc-button.mat-mdc-snack-bar-action:not(:disabled){--mat-button-text-state-layer-color: currentColor;--mat-button-text-ripple-color: currentColor}.mat-mdc-snack-bar-container .mat-mdc-button.mat-mdc-snack-bar-action:not(:disabled) .mat-ripple-element{opacity:.1} -`],encapsulation:2})}return a})(),Jo=new w("mat-snack-bar-default-options",{providedIn:"root",factory:()=>new Vt}),qe=(()=>{class a{_live=r(de);_injector=r(G);_breakpointObserver=r(ji);_parentSnackBar=r(a,{optional:!0,skipSelf:!0});_defaultConfig=r(Jo);_animationsDisabled=Q();_snackBarRefAtThisLevel=null;simpleSnackBarComponent=Ua;snackBarContainerComponent=Xo;handsetCssClass="mat-mdc-snack-bar-handset";get _openedSnackBarRef(){let t=this._parentSnackBar;return t?t._openedSnackBarRef:this._snackBarRefAtThisLevel}set _openedSnackBarRef(t){this._parentSnackBar?this._parentSnackBar._openedSnackBarRef=t:this._snackBarRefAtThisLevel=t}constructor(){}openFromComponent(t,e){return this._attach(t,e)}openFromTemplate(t,e){return this._attach(t,e)}open(t,e="",i){let n=X(X({},this._defaultConfig),i);return n.data={message:t,action:e},n.announcementMessage===t&&(n.announcementMessage=void 0),this.openFromComponent(this.simpleSnackBarComponent,n)}dismiss(){this._openedSnackBarRef&&this._openedSnackBarRef.dismiss()}ngOnDestroy(){this._snackBarRefAtThisLevel&&this._snackBarRefAtThisLevel.dismiss()}_attachSnackBarContainer(t,e){let i=e&&e.viewContainerRef&&e.viewContainerRef.injector,n=G.create({parent:i||this._injector,providers:[{provide:Vt,useValue:e}]}),o=new qt(this.snackBarContainerComponent,e.viewContainerRef,n),f=t.attach(o);return f.instance.snackBarConfig=e,f.instance}_attach(t,e){let i=X(X(X({},new Vt),this._defaultConfig),e),n=this._createOverlay(i),o=this._attachSnackBarContainer(n,i),f=new Zt(o,n);if(t instanceof Dt){let b=new oe(t,null,{$implicit:i.data,snackBarRef:f});f.instance=o.attachTemplatePortal(b)}else{let b=this._createInjector(i,f),xt=new qt(t,void 0,b),Mt=o.attachComponentPortal(xt);f.instance=Mt.instance}return this._breakpointObserver.observe(Gi.HandsetPortrait).pipe(I(n.detachments())).subscribe(b=>{n.overlayElement.classList.toggle(this.handsetCssClass,b.matches)}),i.announcementMessage&&o._onAnnounce.subscribe(()=>{this._live.announce(i.announcementMessage,i.politeness)}),this._animateSnackBar(f,i),this._openedSnackBarRef=f,this._openedSnackBarRef}_animateSnackBar(t,e){t.afterDismissed().subscribe(()=>{this._openedSnackBarRef==t&&(this._openedSnackBarRef=null),e.announcementMessage&&this._live.clear()}),e.duration&&e.duration>0&&t.afterOpened().subscribe(()=>t._dismissAfter(e.duration)),this._openedSnackBarRef?(this._openedSnackBarRef.afterDismissed().subscribe(()=>{t.containerInstance.enter()}),this._openedSnackBarRef.dismiss()):t.containerInstance.enter()}_createOverlay(t){let e=new Di;e.direction=t.direction;let i=Fi(this._injector),n=t.direction==="rtl",o=t.horizontalPosition==="left"||t.horizontalPosition==="start"&&!n||t.horizontalPosition==="end"&&n,f=!o&&t.horizontalPosition!=="center";return o?i.left("0"):f?i.right("0"):i.centerHorizontally(),t.verticalPosition==="top"?i.top("0"):i.bottom("0"),e.positionStrategy=i,e.disableAnimations=this._animationsDisabled,se(this._injector,e)}_createInjector(t,e){let i=t&&t.viewContainerRef&&t.viewContainerRef.injector;return G.create({parent:i||this._injector,providers:[{provide:Zt,useValue:e},{provide:qa,useValue:t.data}]})}static \u0275fac=function(e){return new(e||a)};static \u0275prov=P({token:a,factory:a.\u0275fac,providedIn:"root"})}return a})();var ad=(()=>{class a{static \u0275fac=function(e){return new(e||a)};static \u0275mod=N({type:a});static \u0275inj=z({providers:[qe],imports:[Pt,Ii,Ui,Ua,j]})}return a})();var Ya=(()=>{class a{snackbar;snackBarConfigForSuccess={duration:4e3,panelClass:["snackabar-success"],MatSnackBarHorizontalPosition:"center",MatSnackBarVerticalPosition:"bottom"};snackBarConfigForWarning={duration:4e3,panelClass:["snackabar-warning"],MatSnackBarHorizontalPosition:"center",MatSnackBarVerticalPosition:"bottom"};snackBarConfigForError={duration:1e4,panelClass:["snackabar-error"],MatSnackBarHorizontalPosition:"center",MatSnackBarVerticalPosition:"bottom"};constructor(t){this.snackbar=t}error(t){console.error(t),this.snackbar.open(t,"Close",this.snackBarConfigForError)}warning(t){this.snackbar.open(t,"Close",this.snackBarConfigForWarning)}success(t){this.snackbar.open(t,"Close",this.snackBarConfigForSuccess)}static \u0275fac=function(e){return new(e||a)(nt(qe))};static \u0275prov=P({token:a,factory:a.\u0275fac})}return a})();var Ka=["*"],Za=`.mdc-list{margin:0;padding:8px 0;list-style-type:none}.mdc-list:focus{outline:none}.mdc-list-item{display:flex;position:relative;justify-content:flex-start;overflow:hidden;padding:0;align-items:stretch;cursor:pointer;padding-left:16px;padding-right:16px;background-color:var(--mat-list-list-item-container-color, transparent);border-radius:var(--mat-list-list-item-container-shape, var(--mat-sys-corner-none))}.mdc-list-item.mdc-list-item--selected{background-color:var(--mat-list-list-item-selected-container-color)}.mdc-list-item:focus{outline:0}.mdc-list-item.mdc-list-item--disabled{cursor:auto}.mdc-list-item.mdc-list-item--with-one-line{height:var(--mat-list-list-item-one-line-container-height, 48px)}.mdc-list-item.mdc-list-item--with-one-line .mdc-list-item__start{align-self:center;margin-top:0}.mdc-list-item.mdc-list-item--with-one-line .mdc-list-item__end{align-self:center;margin-top:0}.mdc-list-item.mdc-list-item--with-two-lines{height:var(--mat-list-list-item-two-line-container-height, 64px)}.mdc-list-item.mdc-list-item--with-two-lines .mdc-list-item__start{align-self:flex-start;margin-top:16px}.mdc-list-item.mdc-list-item--with-two-lines .mdc-list-item__end{align-self:center;margin-top:0}.mdc-list-item.mdc-list-item--with-three-lines{height:var(--mat-list-list-item-three-line-container-height, 88px)}.mdc-list-item.mdc-list-item--with-three-lines .mdc-list-item__start{align-self:flex-start;margin-top:16px}.mdc-list-item.mdc-list-item--with-three-lines .mdc-list-item__end{align-self:flex-start;margin-top:16px}.mdc-list-item.mdc-list-item--selected::before,.mdc-list-item.mdc-list-item--selected:focus::before,.mdc-list-item:not(.mdc-list-item--selected):focus::before{position:absolute;box-sizing:border-box;width:100%;height:100%;top:0;left:0;content:"";pointer-events:none}a.mdc-list-item{color:inherit;text-decoration:none}.mdc-list-item__start{fill:currentColor;flex-shrink:0;pointer-events:none}.mdc-list-item--with-leading-icon .mdc-list-item__start{color:var(--mat-list-list-item-leading-icon-color, var(--mat-sys-on-surface-variant));width:var(--mat-list-list-item-leading-icon-size, 24px);height:var(--mat-list-list-item-leading-icon-size, 24px);margin-left:16px;margin-right:32px}[dir=rtl] .mdc-list-item--with-leading-icon .mdc-list-item__start{margin-left:32px;margin-right:16px}.mdc-list-item--with-leading-icon:hover .mdc-list-item__start{color:var(--mat-list-list-item-hover-leading-icon-color)}.mdc-list-item--with-leading-avatar .mdc-list-item__start{width:var(--mat-list-list-item-leading-avatar-size, 40px);height:var(--mat-list-list-item-leading-avatar-size, 40px);margin-left:16px;margin-right:16px;border-radius:50%}.mdc-list-item--with-leading-avatar .mdc-list-item__start,[dir=rtl] .mdc-list-item--with-leading-avatar .mdc-list-item__start{margin-left:16px;margin-right:16px;border-radius:50%}.mdc-list-item__end{flex-shrink:0;pointer-events:none}.mdc-list-item--with-trailing-meta .mdc-list-item__end{font-family:var(--mat-list-list-item-trailing-supporting-text-font, var(--mat-sys-label-small-font));line-height:var(--mat-list-list-item-trailing-supporting-text-line-height, var(--mat-sys-label-small-line-height));font-size:var(--mat-list-list-item-trailing-supporting-text-size, var(--mat-sys-label-small-size));font-weight:var(--mat-list-list-item-trailing-supporting-text-weight, var(--mat-sys-label-small-weight));letter-spacing:var(--mat-list-list-item-trailing-supporting-text-tracking, var(--mat-sys-label-small-tracking))}.mdc-list-item--with-trailing-icon .mdc-list-item__end{color:var(--mat-list-list-item-trailing-icon-color, var(--mat-sys-on-surface-variant));width:var(--mat-list-list-item-trailing-icon-size, 24px);height:var(--mat-list-list-item-trailing-icon-size, 24px)}.mdc-list-item--with-trailing-icon:hover .mdc-list-item__end{color:var(--mat-list-list-item-hover-trailing-icon-color)}.mdc-list-item.mdc-list-item--with-trailing-meta .mdc-list-item__end{color:var(--mat-list-list-item-trailing-supporting-text-color, var(--mat-sys-on-surface-variant))}.mdc-list-item--selected.mdc-list-item--with-trailing-icon .mdc-list-item__end{color:var(--mat-list-list-item-selected-trailing-icon-color, var(--mat-sys-primary))}.mdc-list-item__content{text-overflow:ellipsis;white-space:nowrap;overflow:hidden;align-self:center;flex:1;pointer-events:none}.mdc-list-item--with-two-lines .mdc-list-item__content,.mdc-list-item--with-three-lines .mdc-list-item__content{align-self:stretch}.mdc-list-item__primary-text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden;color:var(--mat-list-list-item-label-text-color, var(--mat-sys-on-surface));font-family:var(--mat-list-list-item-label-text-font, var(--mat-sys-body-large-font));line-height:var(--mat-list-list-item-label-text-line-height, var(--mat-sys-body-large-line-height));font-size:var(--mat-list-list-item-label-text-size, var(--mat-sys-body-large-size));font-weight:var(--mat-list-list-item-label-text-weight, var(--mat-sys-body-large-weight));letter-spacing:var(--mat-list-list-item-label-text-tracking, var(--mat-sys-body-large-tracking))}.mdc-list-item:hover .mdc-list-item__primary-text{color:var(--mat-list-list-item-hover-label-text-color, var(--mat-sys-on-surface))}.mdc-list-item:focus .mdc-list-item__primary-text{color:var(--mat-list-list-item-focus-label-text-color, var(--mat-sys-on-surface))}.mdc-list-item--with-two-lines .mdc-list-item__primary-text,.mdc-list-item--with-three-lines .mdc-list-item__primary-text{display:block;margin-top:0;line-height:normal;margin-bottom:-20px}.mdc-list-item--with-two-lines .mdc-list-item__primary-text::before,.mdc-list-item--with-three-lines .mdc-list-item__primary-text::before{display:inline-block;width:0;height:28px;content:"";vertical-align:0}.mdc-list-item--with-two-lines .mdc-list-item__primary-text::after,.mdc-list-item--with-three-lines .mdc-list-item__primary-text::after{display:inline-block;width:0;height:20px;content:"";vertical-align:-20px}.mdc-list-item__secondary-text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden;display:block;margin-top:0;color:var(--mat-list-list-item-supporting-text-color, var(--mat-sys-on-surface-variant));font-family:var(--mat-list-list-item-supporting-text-font, var(--mat-sys-body-medium-font));line-height:var(--mat-list-list-item-supporting-text-line-height, var(--mat-sys-body-medium-line-height));font-size:var(--mat-list-list-item-supporting-text-size, var(--mat-sys-body-medium-size));font-weight:var(--mat-list-list-item-supporting-text-weight, var(--mat-sys-body-medium-weight));letter-spacing:var(--mat-list-list-item-supporting-text-tracking, var(--mat-sys-body-medium-tracking))}.mdc-list-item__secondary-text::before{display:inline-block;width:0;height:20px;content:"";vertical-align:0}.mdc-list-item--with-three-lines .mdc-list-item__secondary-text{white-space:normal;line-height:20px}.mdc-list-item--with-overline .mdc-list-item__secondary-text{white-space:nowrap;line-height:auto}.mdc-list-item--with-leading-radio.mdc-list-item,.mdc-list-item--with-leading-checkbox.mdc-list-item,.mdc-list-item--with-leading-icon.mdc-list-item,.mdc-list-item--with-leading-avatar.mdc-list-item{padding-left:0;padding-right:16px}[dir=rtl] .mdc-list-item--with-leading-radio.mdc-list-item,[dir=rtl] .mdc-list-item--with-leading-checkbox.mdc-list-item,[dir=rtl] .mdc-list-item--with-leading-icon.mdc-list-item,[dir=rtl] .mdc-list-item--with-leading-avatar.mdc-list-item{padding-left:16px;padding-right:0}.mdc-list-item--with-leading-radio.mdc-list-item--with-two-lines .mdc-list-item__primary-text,.mdc-list-item--with-leading-checkbox.mdc-list-item--with-two-lines .mdc-list-item__primary-text,.mdc-list-item--with-leading-icon.mdc-list-item--with-two-lines .mdc-list-item__primary-text,.mdc-list-item--with-leading-avatar.mdc-list-item--with-two-lines .mdc-list-item__primary-text{display:block;margin-top:0;line-height:normal;margin-bottom:-20px}.mdc-list-item--with-leading-radio.mdc-list-item--with-two-lines .mdc-list-item__primary-text::before,.mdc-list-item--with-leading-checkbox.mdc-list-item--with-two-lines .mdc-list-item__primary-text::before,.mdc-list-item--with-leading-icon.mdc-list-item--with-two-lines .mdc-list-item__primary-text::before,.mdc-list-item--with-leading-avatar.mdc-list-item--with-two-lines .mdc-list-item__primary-text::before{display:inline-block;width:0;height:32px;content:"";vertical-align:0}.mdc-list-item--with-leading-radio.mdc-list-item--with-two-lines .mdc-list-item__primary-text::after,.mdc-list-item--with-leading-checkbox.mdc-list-item--with-two-lines .mdc-list-item__primary-text::after,.mdc-list-item--with-leading-icon.mdc-list-item--with-two-lines .mdc-list-item__primary-text::after,.mdc-list-item--with-leading-avatar.mdc-list-item--with-two-lines .mdc-list-item__primary-text::after{display:inline-block;width:0;height:20px;content:"";vertical-align:-20px}.mdc-list-item--with-leading-radio.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end,.mdc-list-item--with-leading-checkbox.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end,.mdc-list-item--with-leading-icon.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end,.mdc-list-item--with-leading-avatar.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end{display:block;margin-top:0;line-height:normal}.mdc-list-item--with-leading-radio.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end::before,.mdc-list-item--with-leading-checkbox.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end::before,.mdc-list-item--with-leading-icon.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end::before,.mdc-list-item--with-leading-avatar.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end::before{display:inline-block;width:0;height:32px;content:"";vertical-align:0}.mdc-list-item--with-trailing-icon.mdc-list-item,[dir=rtl] .mdc-list-item--with-trailing-icon.mdc-list-item{padding-left:0;padding-right:0}.mdc-list-item--with-trailing-icon .mdc-list-item__end{margin-left:16px;margin-right:16px}.mdc-list-item--with-trailing-meta.mdc-list-item{padding-left:16px;padding-right:0}[dir=rtl] .mdc-list-item--with-trailing-meta.mdc-list-item{padding-left:0;padding-right:16px}.mdc-list-item--with-trailing-meta .mdc-list-item__end{-webkit-user-select:none;user-select:none;margin-left:28px;margin-right:16px}[dir=rtl] .mdc-list-item--with-trailing-meta .mdc-list-item__end{margin-left:16px;margin-right:28px}.mdc-list-item--with-trailing-meta.mdc-list-item--with-three-lines .mdc-list-item__end,.mdc-list-item--with-trailing-meta.mdc-list-item--with-two-lines .mdc-list-item__end{display:block;line-height:normal;align-self:flex-start;margin-top:0}.mdc-list-item--with-trailing-meta.mdc-list-item--with-three-lines .mdc-list-item__end::before,.mdc-list-item--with-trailing-meta.mdc-list-item--with-two-lines .mdc-list-item__end::before{display:inline-block;width:0;height:28px;content:"";vertical-align:0}.mdc-list-item--with-leading-radio .mdc-list-item__start,.mdc-list-item--with-leading-checkbox .mdc-list-item__start{margin-left:8px;margin-right:24px}[dir=rtl] .mdc-list-item--with-leading-radio .mdc-list-item__start,[dir=rtl] .mdc-list-item--with-leading-checkbox .mdc-list-item__start{margin-left:24px;margin-right:8px}.mdc-list-item--with-leading-radio.mdc-list-item--with-two-lines .mdc-list-item__start,.mdc-list-item--with-leading-checkbox.mdc-list-item--with-two-lines .mdc-list-item__start{align-self:flex-start;margin-top:8px}.mdc-list-item--with-trailing-radio.mdc-list-item,.mdc-list-item--with-trailing-checkbox.mdc-list-item{padding-left:16px;padding-right:0}[dir=rtl] .mdc-list-item--with-trailing-radio.mdc-list-item,[dir=rtl] .mdc-list-item--with-trailing-checkbox.mdc-list-item{padding-left:0;padding-right:16px}.mdc-list-item--with-trailing-radio.mdc-list-item--with-leading-icon,.mdc-list-item--with-trailing-radio.mdc-list-item--with-leading-avatar,.mdc-list-item--with-trailing-checkbox.mdc-list-item--with-leading-icon,.mdc-list-item--with-trailing-checkbox.mdc-list-item--with-leading-avatar{padding-left:0}[dir=rtl] .mdc-list-item--with-trailing-radio.mdc-list-item--with-leading-icon,[dir=rtl] .mdc-list-item--with-trailing-radio.mdc-list-item--with-leading-avatar,[dir=rtl] .mdc-list-item--with-trailing-checkbox.mdc-list-item--with-leading-icon,[dir=rtl] .mdc-list-item--with-trailing-checkbox.mdc-list-item--with-leading-avatar{padding-right:0}.mdc-list-item--with-trailing-radio .mdc-list-item__end,.mdc-list-item--with-trailing-checkbox .mdc-list-item__end{margin-left:24px;margin-right:8px}[dir=rtl] .mdc-list-item--with-trailing-radio .mdc-list-item__end,[dir=rtl] .mdc-list-item--with-trailing-checkbox .mdc-list-item__end{margin-left:8px;margin-right:24px}.mdc-list-item--with-trailing-radio.mdc-list-item--with-three-lines .mdc-list-item__end,.mdc-list-item--with-trailing-checkbox.mdc-list-item--with-three-lines .mdc-list-item__end{align-self:flex-start;margin-top:8px}.mdc-list-group__subheader{margin:.75rem 16px}.mdc-list-item--disabled .mdc-list-item__start,.mdc-list-item--disabled .mdc-list-item__content,.mdc-list-item--disabled .mdc-list-item__end{opacity:1}.mdc-list-item--disabled .mdc-list-item__primary-text,.mdc-list-item--disabled .mdc-list-item__secondary-text{opacity:var(--mat-list-list-item-disabled-label-text-opacity, 0.3)}.mdc-list-item--disabled.mdc-list-item--with-leading-icon .mdc-list-item__start{color:var(--mat-list-list-item-disabled-leading-icon-color, var(--mat-sys-on-surface));opacity:var(--mat-list-list-item-disabled-leading-icon-opacity, 0.38)}.mdc-list-item--disabled.mdc-list-item--with-trailing-icon .mdc-list-item__end{color:var(--mat-list-list-item-disabled-trailing-icon-color, var(--mat-sys-on-surface));opacity:var(--mat-list-list-item-disabled-trailing-icon-opacity, 0.38)}.mat-mdc-list-item.mat-mdc-list-item-both-leading-and-trailing,[dir=rtl] .mat-mdc-list-item.mat-mdc-list-item-both-leading-and-trailing{padding-left:0;padding-right:0}.mdc-list-item.mdc-list-item--disabled .mdc-list-item__primary-text{color:var(--mat-list-list-item-disabled-label-text-color, var(--mat-sys-on-surface))}.mdc-list-item:hover::before{background-color:var(--mat-list-list-item-hover-state-layer-color, var(--mat-sys-on-surface));opacity:var(--mat-list-list-item-hover-state-layer-opacity, var(--mat-sys-hover-state-layer-opacity))}.mdc-list-item.mdc-list-item--disabled::before{background-color:var(--mat-list-list-item-disabled-state-layer-color, var(--mat-sys-on-surface));opacity:var(--mat-list-list-item-disabled-state-layer-opacity, var(--mat-sys-focus-state-layer-opacity))}.mdc-list-item:focus::before{background-color:var(--mat-list-list-item-focus-state-layer-color, var(--mat-sys-on-surface));opacity:var(--mat-list-list-item-focus-state-layer-opacity, var(--mat-sys-focus-state-layer-opacity))}.mdc-list-item--disabled .mdc-radio,.mdc-list-item--disabled .mdc-checkbox{opacity:var(--mat-list-list-item-disabled-label-text-opacity, 0.3)}.mdc-list-item--with-leading-avatar .mat-mdc-list-item-avatar{border-radius:var(--mat-list-list-item-leading-avatar-shape, var(--mat-sys-corner-full));background-color:var(--mat-list-list-item-leading-avatar-color, var(--mat-sys-primary-container))}.mat-mdc-list-item-icon{font-size:var(--mat-list-list-item-leading-icon-size, 24px)}@media(forced-colors: active){a.mdc-list-item--activated::after{content:"";position:absolute;top:50%;right:16px;transform:translateY(-50%);width:10px;height:0;border-bottom:solid 10px;border-radius:10px}a.mdc-list-item--activated [dir=rtl]::after{right:auto;left:16px}}.mat-mdc-list-base{display:block}.mat-mdc-list-base .mdc-list-item__start,.mat-mdc-list-base .mdc-list-item__end,.mat-mdc-list-base .mdc-list-item__content{pointer-events:auto}.mat-mdc-list-item,.mat-mdc-list-option{width:100%;box-sizing:border-box;-webkit-tap-highlight-color:rgba(0,0,0,0)}.mat-mdc-list-item:not(.mat-mdc-list-item-interactive),.mat-mdc-list-option:not(.mat-mdc-list-item-interactive){cursor:default}.mat-mdc-list-item .mat-divider-inset,.mat-mdc-list-option .mat-divider-inset{position:absolute;left:0;right:0;bottom:0}.mat-mdc-list-item .mat-mdc-list-item-avatar~.mat-divider-inset,.mat-mdc-list-option .mat-mdc-list-item-avatar~.mat-divider-inset{margin-left:72px}[dir=rtl] .mat-mdc-list-item .mat-mdc-list-item-avatar~.mat-divider-inset,[dir=rtl] .mat-mdc-list-option .mat-mdc-list-item-avatar~.mat-divider-inset{margin-right:72px}.mat-mdc-list-item-interactive::before{top:0;left:0;right:0;bottom:0;position:absolute;content:"";opacity:0;pointer-events:none;border-radius:inherit}.mat-mdc-list-item>.mat-focus-indicator{top:0;left:0;right:0;bottom:0;position:absolute;pointer-events:none}.mat-mdc-list-item:focus-visible>.mat-focus-indicator::before{content:""}.mat-mdc-list-item.mdc-list-item--with-three-lines .mat-mdc-list-item-line.mdc-list-item__secondary-text{white-space:nowrap;line-height:normal}.mat-mdc-list-item.mdc-list-item--with-three-lines .mat-mdc-list-item-unscoped-content.mdc-list-item__secondary-text{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}mat-action-list button{background:none;color:inherit;border:none;font:inherit;outline:inherit;-webkit-tap-highlight-color:rgba(0,0,0,0);text-align:start}mat-action-list button::-moz-focus-inner{border:0}.mdc-list-item--with-leading-icon .mdc-list-item__start{margin-inline-start:var(--mat-list-list-item-leading-icon-start-space, 16px);margin-inline-end:var(--mat-list-list-item-leading-icon-end-space, 16px)}.mat-mdc-nav-list .mat-mdc-list-item{border-radius:var(--mat-list-active-indicator-shape, var(--mat-sys-corner-full));--mat-focus-indicator-border-radius: var(--mat-list-active-indicator-shape, var(--mat-sys-corner-full))}.mat-mdc-nav-list .mat-mdc-list-item.mdc-list-item--activated{background-color:var(--mat-list-active-indicator-color, var(--mat-sys-secondary-container))} -`,er=["unscopedContent"],ir=["text"],ar=[[["","matListItemAvatar",""],["","matListItemIcon",""]],[["","matListItemTitle",""]],[["","matListItemLine",""]],"*",[["","matListItemMeta",""]],[["mat-divider"]]],nr=["[matListItemAvatar],[matListItemIcon]","[matListItemTitle]","[matListItemLine]","*","[matListItemMeta]","mat-divider"];var or=new w("ListOption"),rr=(()=>{class a{_elementRef=r(O);constructor(){}static \u0275fac=function(e){return new(e||a)};static \u0275dir=x({type:a,selectors:[["","matListItemTitle",""]],hostAttrs:[1,"mat-mdc-list-item-title","mdc-list-item__primary-text"]})}return a})(),sr=(()=>{class a{_elementRef=r(O);constructor(){}static \u0275fac=function(e){return new(e||a)};static \u0275dir=x({type:a,selectors:[["","matListItemLine",""]],hostAttrs:[1,"mat-mdc-list-item-line","mdc-list-item__secondary-text"]})}return a})(),lr=(()=>{class a{static \u0275fac=function(e){return new(e||a)};static \u0275dir=x({type:a,selectors:[["","matListItemMeta",""]],hostAttrs:[1,"mat-mdc-list-item-meta","mdc-list-item__end"]})}return a})(),Xa=(()=>{class a{_listOption=r(or,{optional:!0});constructor(){}_isAlignedAtStart(){return!this._listOption||this._listOption?._getTogglePosition()==="after"}static \u0275fac=function(e){return new(e||a)};static \u0275dir=x({type:a,hostVars:4,hostBindings:function(e,i){e&2&&_("mdc-list-item__start",i._isAlignedAtStart())("mdc-list-item__end",!i._isAlignedAtStart())}})}return a})(),cr=(()=>{class a extends Xa{static \u0275fac=(()=>{let t;return function(i){return(t||(t=Y(a)))(i||a)}})();static \u0275dir=x({type:a,selectors:[["","matListItemAvatar",""]],hostAttrs:[1,"mat-mdc-list-item-avatar"],features:[q]})}return a})(),dr=(()=>{class a extends Xa{static \u0275fac=(()=>{let t;return function(i){return(t||(t=Y(a)))(i||a)}})();static \u0275dir=x({type:a,selectors:[["","matListItemIcon",""]],hostAttrs:[1,"mat-mdc-list-item-icon"],features:[q]})}return a})(),mr=new w("MAT_LIST_CONFIG"),Xt=(()=>{class a{_isNonInteractive=!0;get disableRipple(){return this._disableRipple}set disableRipple(t){this._disableRipple=K(t)}_disableRipple=!1;get disabled(){return this._disabled()}set disabled(t){this._disabled.set(K(t))}_disabled=Z(!1);_defaultOptions=r(mr,{optional:!0});static \u0275fac=function(e){return new(e||a)};static \u0275dir=x({type:a,hostVars:1,hostBindings:function(e,i){e&2&&T("aria-disabled",i.disabled)},inputs:{disableRipple:"disableRipple",disabled:"disabled"}})}return a})(),hr=(()=>{class a{_elementRef=r(O);_ngZone=r(H);_listBase=r(Xt,{optional:!0});_platform=r(ot);_hostElement;_isButtonElement;_noopAnimations=Q();_avatars;_icons;set lines(t){this._explicitLines=Gt(t,null),this._updateItemLines(!1)}_explicitLines=null;get disableRipple(){return this.disabled||this._disableRipple||this._noopAnimations||!!this._listBase?.disableRipple}set disableRipple(t){this._disableRipple=K(t)}_disableRipple=!1;get disabled(){return this._disabled()||!!this._listBase?.disabled}set disabled(t){this._disabled.set(K(t))}_disabled=Z(!1);_subscriptions=new it;_rippleRenderer=null;_hasUnscopedTextContent=!1;rippleConfig;get rippleDisabled(){return this.disableRipple||!!this.rippleConfig.disabled}constructor(){r(wt).load(Ct);let t=r(Yt,{optional:!0});this.rippleConfig=t||{},this._hostElement=this._elementRef.nativeElement,this._isButtonElement=this._hostElement.nodeName.toLowerCase()==="button",this._listBase&&!this._listBase._isNonInteractive&&this._initInteractiveListItem(),this._isButtonElement&&!this._hostElement.hasAttribute("type")&&this._hostElement.setAttribute("type","button")}ngAfterViewInit(){this._monitorProjectedLinesAndTitle(),this._updateItemLines(!0)}ngOnDestroy(){this._subscriptions.unsubscribe(),this._rippleRenderer!==null&&this._rippleRenderer._removeTriggerEvents()}_hasIconOrAvatar(){return!!(this._avatars.length||this._icons.length)}_initInteractiveListItem(){this._hostElement.classList.add("mat-mdc-list-item-interactive"),this._rippleRenderer=new De(this,this._ngZone,this._hostElement,this._platform,r(G)),this._rippleRenderer.setupTriggerEvents(this._hostElement)}_monitorProjectedLinesAndTitle(){this._ngZone.runOutsideAngular(()=>{this._subscriptions.add(J(this._lines.changes,this._titles.changes).subscribe(()=>this._updateItemLines(!1)))})}_updateItemLines(t){if(!this._lines||!this._titles||!this._unscopedContent)return;t&&this._checkDomForUnscopedTextContent();let e=this._explicitLines??this._inferLinesFromContent(),i=this._unscopedContent.nativeElement;if(this._hostElement.classList.toggle("mat-mdc-list-item-single-line",e<=1),this._hostElement.classList.toggle("mdc-list-item--with-one-line",e<=1),this._hostElement.classList.toggle("mdc-list-item--with-two-lines",e===2),this._hostElement.classList.toggle("mdc-list-item--with-three-lines",e===3),this._hasUnscopedTextContent){let n=this._titles.length===0&&e===1;i.classList.toggle("mdc-list-item__primary-text",n),i.classList.toggle("mdc-list-item__secondary-text",!n)}else i.classList.remove("mdc-list-item__primary-text"),i.classList.remove("mdc-list-item__secondary-text")}_inferLinesFromContent(){let t=this._titles.length+this._lines.length;return this._hasUnscopedTextContent&&(t+=1),t}_checkDomForUnscopedTextContent(){this._hasUnscopedTextContent=Array.from(this._unscopedContent.nativeElement.childNodes).filter(t=>t.nodeType!==t.COMMENT_NODE).some(t=>!!(t.textContent&&t.textContent.trim()))}static \u0275fac=function(e){return new(e||a)};static \u0275dir=x({type:a,contentQueries:function(e,i,n){if(e&1&&et(n,cr,4)(n,dr,4),e&2){let o;m(o=h())&&(i._avatars=o),m(o=h())&&(i._icons=o)}},hostVars:4,hostBindings:function(e,i){e&2&&(T("aria-disabled",i.disabled)("disabled",i._isButtonElement&&i.disabled||null),_("mdc-list-item--disabled",i.disabled))},inputs:{lines:"lines",disableRipple:"disableRipple",disabled:"disabled"}})}return a})();var Fd=(()=>{class a extends Xt{static \u0275fac=(()=>{let t;return function(i){return(t||(t=Y(a)))(i||a)}})();static \u0275cmp=C({type:a,selectors:[["mat-list"]],hostAttrs:[1,"mat-mdc-list","mat-mdc-list-base","mdc-list"],exportAs:["matList"],features:[$([{provide:Xt,useExisting:a}]),q],ngContentSelectors:Ka,decls:1,vars:0,template:function(e,i){e&1&&(B(),y(0))},styles:[Za],encapsulation:2,changeDetection:0})}return a})(),Od=(()=>{class a extends hr{_lines;_titles;_meta;_unscopedContent;_itemText;get activated(){return this._activated}set activated(t){this._activated=K(t)}_activated=!1;_getAriaCurrent(){return this._hostElement.nodeName==="A"&&this._activated?"page":null}_hasBothLeadingAndTrailing(){return this._meta.length!==0&&(this._avatars.length!==0||this._icons.length!==0)}static \u0275fac=(()=>{let t;return function(i){return(t||(t=Y(a)))(i||a)}})();static \u0275cmp=C({type:a,selectors:[["mat-list-item"],["a","mat-list-item",""],["button","mat-list-item",""]],contentQueries:function(e,i,n){if(e&1&&et(n,sr,5)(n,rr,5)(n,lr,5),e&2){let o;m(o=h())&&(i._lines=o),m(o=h())&&(i._titles=o),m(o=h())&&(i._meta=o)}},viewQuery:function(e,i){if(e&1&&V(er,5)(ir,5),e&2){let n;m(n=h())&&(i._unscopedContent=n.first),m(n=h())&&(i._itemText=n.first)}},hostAttrs:[1,"mat-mdc-list-item","mdc-list-item"],hostVars:13,hostBindings:function(e,i){e&2&&(T("aria-current",i._getAriaCurrent()),_("mdc-list-item--activated",i.activated)("mdc-list-item--with-leading-avatar",i._avatars.length!==0)("mdc-list-item--with-leading-icon",i._icons.length!==0)("mdc-list-item--with-trailing-meta",i._meta.length!==0)("mat-mdc-list-item-both-leading-and-trailing",i._hasBothLeadingAndTrailing())("_mat-animation-noopable",i._noopAnimations))},inputs:{activated:"activated"},exportAs:["matListItem"],features:[q],ngContentSelectors:nr,decls:10,vars:0,consts:[["unscopedContent",""],[1,"mdc-list-item__content"],[1,"mat-mdc-list-item-unscoped-content",3,"cdkObserveContent"],[1,"mat-focus-indicator"]],template:function(e,i){e&1&&(B(ar),y(0),l(1,"span",1),y(2,1),y(3,2),l(4,"span",2,0),u("cdkObserveContent",function(){return i._updateItemLines(!0)}),y(6,3),d()(),y(7,4),y(8,5),M(9,"div",3))},dependencies:[Ut],encapsulation:2,changeDetection:0})}return a})();var Rd=(()=>{class a extends Xt{_isNonInteractive=!1;static \u0275fac=(()=>{let t;return function(i){return(t||(t=Y(a)))(i||a)}})();static \u0275cmp=C({type:a,selectors:[["mat-nav-list"]],hostAttrs:["role","navigation",1,"mat-mdc-nav-list","mat-mdc-list-base","mdc-list"],exportAs:["matNavList"],features:[$([{provide:Xt,useExisting:a}]),q],ngContentSelectors:Ka,decls:1,vars:0,template:function(e,i){e&1&&(B(),y(0))},styles:[Za],encapsulation:2,changeDetection:0})}return a})();var Ad=(()=>{class a{static \u0275fac=function(e){return new(e||a)};static \u0275mod=N({type:a});static \u0275inj=z({imports:[ce,Kt,ge,j,ve]})}return a})();var fr=a=>({lightTheme:a}),ur=()=>({right:!0,left:!0,bottom:!0,top:!0}),Ja=(a,s)=>s.name;function _r(a,s){if(a&1){let t=lt();l(0,"div",1),u("mousemove",function(i){R(t);let n=p();return A(n.dragWidget(i))},ye)("mouseup",function(){R(t);let i=p();return A(i.toggleDragging(!1))},ye),d()}}function br(a,s){a&1&&(mt(),l(0,"svg",25),M(1,"rect",26),d())}function gr(a,s){a&1&&(mt(),l(0,"svg",25),M(1,"rect",27),d())}function vr(a,s){a&1&&(mt(),l(0,"svg",25),M(1,"rect",28),d())}function yr(a,s){if(a&1&&(l(0,"div"),E(1),d()),a&2){let t=p().$implicit;c(),mi("",t.console_type,"://",t.console_host,":",t.console)}}function xr(a,s){a&1&&(l(0,"div"),E(1,"none"),d())}function kr(a,s){if(a&1&&(l(0,"div",21)(1,"div"),g(2,br,2,0,":svg:svg",25),g(3,gr,2,0,":svg:svg",25),g(4,vr,2,0,":svg:svg",25),E(5),d(),g(6,yr,2,3,"div"),g(7,xr,2,0,"div"),d()),a&2){let t=s.$implicit;c(2),v(t.status==="started"?2:-1),c(),v(t.status==="suspended"?3:-1),c(),v(t.status==="stopped"?4:-1),c(),ft(" ",t.name," "),c(),v(t.console!==null&&t.console!==void 0&&t.console_type!=="none"?6:-1),c(),v(t.console===null||t.console===void 0||t.console_type==="none"?7:-1)}}function wr(a,s){a&1&&(mt(),l(0,"svg",25),M(1,"rect",26),d())}function Cr(a,s){a&1&&(mt(),l(0,"svg",25),M(1,"rect",28),d())}function Tr(a,s){if(a&1&&(l(0,"div",24)(1,"div"),g(2,wr,2,0,":svg:svg",25),g(3,Cr,2,0,":svg:svg",25),E(4),d(),l(5,"div"),E(6),d()()),a&2){let t=s.$implicit,e=p(2);D("matTooltip",e.getComputeTooltip(t)),c(2),v(t.connected?2:-1),c(),v(t.connected?-1:3),c(),ft(" ",e.truncateComputeName(t.name)," "),c(2),di(" ",t.host,":",t.port," ")}}function Mr(a,s){if(a&1){let t=lt();l(0,"div",2),u("mousedown",function(){R(t);let i=p();return A(i.toggleDragging(!0))})("resizeStart",function(){R(t);let i=p();return A(i.toggleDragging(!1))})("resizeEnd",function(i){R(t);let n=p();return A(n.onResizeEnd(i))}),l(1,"div",3)(2,"mat-tab-group")(3,"mat-tab",4),u("click",function(){R(t);let i=p();return A(i.toggleTopologyVisibility(!0))}),l(4,"div",5)(5,"div",6)(6,"mat-select",7)(7,"mat-optgroup",8)(8,"mat-option",9),u("onSelectionChange",function(){R(t);let i=p();return A(i.applyStatusFilter("started"))}),E(9,"started"),d(),l(10,"mat-option",10),u("onSelectionChange",function(){R(t);let i=p();return A(i.applyStatusFilter("suspended"))}),E(11,"suspended"),d(),l(12,"mat-option",11),u("onSelectionChange",function(){R(t);let i=p();return A(i.applyStatusFilter("stopped"))}),E(13,"stopped"),d()(),l(14,"mat-optgroup",12)(15,"mat-option",13),u("onSelectionChange",function(){R(t);let i=p();return A(i.applyCaptureFilter("capture"))}),E(16,"active capture(s)"),d(),l(17,"mat-option",14),u("onSelectionChange",function(){R(t);let i=p();return A(i.applyCaptureFilter("packet"))}),E(18,"active packet captures"),d()()()(),l(19,"div",15)(20,"mat-select",16),u("selectionChange",function(){R(t);let i=p();return A(i.setSortingOrder())}),fi("valueChange",function(i){R(t);let n=p();return pi(n.sortingOrder,i)||(n.sortingOrder=i),A(i)}),l(21,"mat-option",17),E(22,"sort by name ascending"),d(),l(23,"mat-option",18),E(24,"sort by name descending"),d()()(),M(25,"mat-divider",19),l(26,"div",20),Et(27,kr,8,6,"div",21,Ja),d()()(),l(29,"mat-tab",22),u("click",function(){R(t);let i=p();return A(i.toggleTopologyVisibility(!1))}),l(30,"div",5)(31,"div",23),Et(32,Tr,7,6,"div",24,Ja),d()()()()()()}if(a&2){let t=p();D("ngStyle",t.style)("ngClass",_i(7,fr,t.isLightThemeEnabled))("validateResize",t.validate)("resizeEdges",ui(9,ur))("enableGhostResize",!0),c(20),hi("value",t.sortingOrder),c(6),D("ngStyle",t.styleInside),c(),Ft(t.filteredNodes),c(5),Ft(t.computes)}}var em=(()=>{class a{nodesDataSource=r(ja);projectService=r(Wa);computeService=r(Va);linksDataSource=r(Na);themeService=r(Qa);notificationService=r(Ga);toasterService=r(Ya);cd=r(W);controller;project;computesInitialized=!1;closeTopologySummary=new F;style={};styleInside={height:"280px"};subscriptions=[];projectsStatistics;nodes=[];filteredNodes=[];sortingOrder="asc";startedStatusFilterEnabled=!1;suspendedStatusFilterEnabled=!1;stoppedStatusFilterEnabled=!1;captureFilterEnabled=!1;packetFilterEnabled=!1;computes=[];isTopologyVisible=!0;isDraggingEnabled=!1;isLightThemeEnabled=!1;constructor(){}ngOnInit(){this.themeService.getActualTheme()==="light"?this.isLightThemeEnabled=!0:this.isLightThemeEnabled=!1,this.subscriptions.push(this.nodesDataSource.changes.subscribe(t=>{this.nodes=t,this.nodes.forEach(e=>{(e.console_host==="0.0.0.0"||e.console_host==="0:0:0:0:0:0:0:0"||e.console_host==="::")&&(e.console_host=this.controller?.host)}),this.sortingOrder==="asc"?this.filteredNodes=t.sort(this.compareAsc):this.filteredNodes=t.sort(this.compareDesc),this.cd.markForCheck()})),setTimeout(()=>{this.initializeComputesAndNotifications()},0),this.revertPosition()}initializeComputesAndNotifications(){if(!(!this.controller||!this.project||this.computesInitialized)){if(this.computesInitialized=!0,this.projectService.getStatistics(this.controller,this.project.project_id).subscribe({next:t=>{this.projectsStatistics=t,this.cd.markForCheck()},error:t=>{let e=t.error?.message||t.message||"Failed to load project statistics";this.toasterService.error(e),this.cd.markForCheck()}}),this.notificationService.hasCachedData()){let t=this.notificationService.getCachedComputes();this.computes=t,this.cd.markForCheck()}else this.computeService.getComputes(this.controller).subscribe({next:t=>{this.notificationService.setInitialComputes(t),this.computes=t,this.cd.markForCheck()},error:t=>{let e=t.error?.message||t.message||"Failed to load computes";this.toasterService.error(e),this.cd.markForCheck()}});this.subscriptions.push(this.notificationService.computeNotificationEmitter.subscribe(t=>{this.handleComputeNotification(t)})),this.subscriptions.push(this.notificationService.computeCacheUpdated.subscribe(t=>{this.computes=t,this.cd.markForCheck()}))}}revertPosition(){let t=localStorage.getItem("leftPosition"),e=localStorage.getItem("rightPosition"),i=localStorage.getItem("topPosition"),n=localStorage.getItem("widthOfWidget"),o=localStorage.getItem("heightOfWidget");i?this.style={position:"fixed",left:`${+t}px`,right:`${+e}px`,top:`${+i}px`,width:`${+n}px`,height:`${+o}px`}:this.style={top:"60px",right:"0px",width:"320px",height:"400px"}}toggleDragging(t){this.isDraggingEnabled=t}dragWidget(t){let e=Number(t.movementX),i=Number(t.movementY),n=Number(this.style.width.split("px")[0]),o=Number(this.style.height.split("px")[0]),f=Number(this.style.top.split("px")[0])+i;if(this.style.left){let b=Number(this.style.left.split("px")[0])+e;this.style={position:"fixed",left:`${b}px`,top:`${f}px`,width:`${n}px`,height:`${o}px`},localStorage.setItem("leftPosition",b.toString()),localStorage.setItem("topPosition",f.toString()),localStorage.setItem("widthOfWidget",n.toString()),localStorage.setItem("heightOfWidget",o.toString())}else{let b=Number(this.style.right.split("px")[0])-e;this.style={position:"fixed",right:`${b}px`,top:`${f}px`,width:`${n}px`,height:`${o}px`},localStorage.setItem("rightPosition",b.toString()),localStorage.setItem("topPosition",f.toString()),localStorage.setItem("widthOfWidget",n.toString()),localStorage.setItem("heightOfWidget",o.toString())}}validate(t){return!(t.rectangle.width&&t.rectangle.height&&(t.rectangle.width<290||t.rectangle.height<260))}onResizeEnd(t){this.style={position:"fixed",left:`${t.rectangle.left}px`,top:`${t.rectangle.top}px`,width:`${t.rectangle.width}px`,height:`${t.rectangle.height}px`},this.styleInside={height:`${t.rectangle.height-120}px`}}toggleTopologyVisibility(t){this.isTopologyVisible=t,this.revertPosition()}compareAsc(t,e){return t.namei.compute_id===t.event.compute_id)!==-1?this.computes=this.computes.map(i=>i.compute_id===t.event.compute_id?t.event:i):this.computes=[...this.computes,t.event];break;case"compute.deleted":this.computes=this.computes.filter(i=>i.compute_id!==t.event.compute_id);break}this.cd.markForCheck()}ngOnDestroy(){this.subscriptions.forEach(t=>t.unsubscribe())}setSortingOrder(){this.sortingOrder==="asc"?this.filteredNodes=this.filteredNodes.sort(this.compareAsc):this.filteredNodes=this.filteredNodes.sort(this.compareDesc)}applyStatusFilter(t){t==="started"?this.startedStatusFilterEnabled=!this.startedStatusFilterEnabled:t==="stopped"?this.stoppedStatusFilterEnabled=!this.stoppedStatusFilterEnabled:t==="suspended"&&(this.suspendedStatusFilterEnabled=!this.suspendedStatusFilterEnabled),this.applyFilters()}applyCaptureFilter(t){t==="capture"?this.captureFilterEnabled=!this.captureFilterEnabled:t==="packet"&&(this.packetFilterEnabled=!this.packetFilterEnabled),this.applyFilters()}applyFilters(){let t=[];this.startedStatusFilterEnabled&&(t=t.concat(this.nodes.filter(e=>e.status==="started"))),this.stoppedStatusFilterEnabled&&(t=t.concat(this.nodes.filter(e=>e.status==="stopped"))),this.suspendedStatusFilterEnabled&&(t=t.concat(this.nodes.filter(e=>e.status==="suspended"))),!this.startedStatusFilterEnabled&&!this.stoppedStatusFilterEnabled&&!this.suspendedStatusFilterEnabled&&(t=t.concat(this.nodes)),this.captureFilterEnabled&&(t=this.checkCapturing(t)),this.packetFilterEnabled&&(t=this.checkPacketFilters(t)),this.sortingOrder==="asc"?this.filteredNodes=t.sort(this.compareAsc):this.filteredNodes=t.sort(this.compareDesc)}checkCapturing(t){let e=this.linksDataSource.getItems(),i=[];e.forEach(o=>{o.capturing&&o.nodes.forEach(f=>{i.push(f.node_id)})});let n=[];return t.forEach(o=>{i.includes(o.node_id)&&n.push(o)}),n}checkPacketFilters(t){let e=this.linksDataSource.getItems(),i=[];e.forEach(o=>{(o.filters.bpf||o.filters.corrupt||o.filters.corrupt||o.filters.packet_loss||o.filters.frequency_drop)&&o.nodes.forEach(f=>{i.push(f.node_id)})});let n=[];return t.forEach(o=>{i.includes(o.node_id)&&n.push(o)}),n}close(){this.closeTopologySummary.emit(!1)}truncateComputeName(t){if(!t)return"";let e=15;return t.length<=e?t:t.substring(0,e)+"..."}getComputeTooltip(t){return t?[`Name: ${t.name||"N/A"}`,`Host: ${t.host}:${t.port}`,`Connected: ${t.connected?"Yes":"No"}`,t.cpu_usage_percent!=null?`CPU: ${t.cpu_usage_percent.toFixed(1)}%`:null,t.memory_usage_percent!=null?`Memory: ${t.memory_usage_percent.toFixed(1)}%`:null].filter(Boolean).join(` -`):""}static \u0275fac=function(e){return new(e||a)};static \u0275cmp=C({type:a,selectors:[["app-topology-summary"]],inputs:{controller:"controller",project:"project"},outputs:{closeTopologySummary:"closeTopologySummary"},decls:2,vars:2,consts:[["mwlResizable","",1,"summaryWrapper",3,"ngStyle","ngClass","validateResize","resizeEdges","enableGhostResize"],[3,"mousemove","mouseup"],["mwlResizable","",1,"summaryWrapper",3,"mousedown","resizeStart","resizeEnd","ngStyle","ngClass","validateResize","resizeEdges","enableGhostResize"],[1,"summaryHeader"],["label","Map topology",3,"click"],[1,"tabContent"],[1,"summaryFilters"],["placeholder","Filter nodes","multiple",""],["label","Status filter"],["value","started",3,"onSelectionChange"],["value","suspended",3,"onSelectionChange"],["value","stopped",3,"onSelectionChange"],["label","Capture filter"],["value","capture",3,"onSelectionChange"],["value","packet",3,"onSelectionChange"],[1,"summarySorting"],["placeholder","Sorting",3,"selectionChange","valueChange","value"],["value","asc"],["value","desc"],[1,"divider"],[1,"summaryContent",3,"ngStyle"],[1,"nodeRow"],["label","Computes",3,"click"],[1,"summaryContentComputes"],["matTooltipPosition","above",1,"nodeRow",3,"matTooltip"],["width","10","height","10"],["x","0","y","0","width","10","height","10","fill","green",1,"status_started"],["x","0","y","0","width","10","height","10","fill","yellow",1,"status_suspended"],["x","0","y","0","width","10","height","10","fill","red",1,"status_stopped"]],template:function(e,i){e&1&&(g(0,_r,1,0,"div"),g(1,Mr,34,10,"div",0)),e&2&&(v(i.isDraggingEnabled?0:-1),c(),v(i.projectsStatistics?1:-1))},dependencies:[ki,vi,yi,ha,Ae,ma,La,Aa,zt,Pe,Nt,ve,$e,za,We],styles:["@media screen and (max-width:600px){.summaryWrapper[_ngcontent-%COMP%]{visibility:hidden}}mat-tab-group[_ngcontent-%COMP%]{width:100%}.summaryWrapper[_ngcontent-%COMP%]{box-shadow:0 4px 16px color-mix(in srgb,var(--mat-sys-shadow) 25%,transparent);position:fixed;top:60px;right:0;height:400px;width:320px;background:var(--mat-sys-surface);color:var(--mat-sys-on-surface);overflow:hidden;font-size:12px;margin:16px;border-radius:4px}.summaryHeaderMenu[_ngcontent-%COMP%]{height:24px}.summaryHeader[_ngcontent-%COMP%]{width:100%;display:flex}.summaryFilters[_ngcontent-%COMP%], .summarySorting[_ngcontent-%COMP%]{height:25px;margin-left:8px;margin-right:8px}.tabContent[_ngcontent-%COMP%]{padding:10px}.summaryContent[_ngcontent-%COMP%]{overflow:auto;scrollbar-color:darkgrey var(--mat-sys-surface);scrollbar-width:thin}.summaryContentComputes[_ngcontent-%COMP%]{max-height:350px;overflow:auto;scrollbar-color:darkgrey var(--mat-sys-surface);scrollbar-width:thin}.titleButton[_ngcontent-%COMP%]{margin-left:8px;margin-top:4px;outline:none;border-radius:0}.marked[_ngcontent-%COMP%]{color:var(--mat-sys-primary);border-bottom:2px solid var(--mat-sys-primary)}.divider[_ngcontent-%COMP%]{margin-top:8px;margin-bottom:8px;width:100%;height:2px}.nodeRow[_ngcontent-%COMP%]{width:100%;display:flex;justify-content:space-between;padding-right:8px}.nodeRow[_ngcontent-%COMP%] > div[_ngcontent-%COMP%]{display:flex;align-items:center;gap:6px}.nodeRow[_ngcontent-%COMP%] svg[_ngcontent-%COMP%]{flex-shrink:0;vertical-align:middle}.radio-group-wrapper[_ngcontent-%COMP%]{margin-top:8px}.radio-group[_ngcontent-%COMP%]{display:flex;justify-content:space-between}.closeButton[_ngcontent-%COMP%]{cursor:pointer;font-size:18px;margin-top:8px;margin-right:8px}.filterBox[_ngcontent-%COMP%]{display:flex;justify-content:space-between}.notvisible[_ngcontent-%COMP%]{display:none}"],changeDetection:0})}return a})();export{We as a,za as b,Be as c,oo as d,ze as e,ro as f,so as g,Ne as h,je as i,Da as j,Fa as k,Ea as l,be as m,fe as n,ue as o,Pe as p,Le as q,zt as r,pa as s,fa as t,Nt as u,Aa as v,La as w,Va as x,Ga as y,Zt as z,qa as A,qe as B,ad as C,Ya as D,Fl as E,Al as F,$e as G,ve as H,Fd as I,Od as J,Rd as K,Ad as L,Qa as M,vn as N,xn as O,Ae as P,ma as Q,Tn as R,Mn as S,ss as T,ha as U,Ha as V,$a as W,Wa as X,jt as Y,Na as Z,ja as _,em as $}; diff --git a/gns3server/static/web-ui/chunk-ERB75MEN.js b/gns3server/static/web-ui/chunk-ERB75MEN.js new file mode 100644 index 000000000..bbc0b1527 --- /dev/null +++ b/gns3server/static/web-ui/chunk-ERB75MEN.js @@ -0,0 +1,685 @@ +import{$ as D,$a as fe,Bd as Mt,Cb as kt,D as ie,Db as wt,Dc as Ce,Dd as Oe,E as rt,Ea as k,Eb as O,Fa as ue,Fb as Tt,G as f,Ga as me,Gd as Ft,Ge as Pe,He as ut,Ia as ge,Id as Me,Jc as At,L as N,Ma as A,Na as E,Nc as Se,Oa as L,Oc as Ie,P as ae,Qa as he,Qb as be,Qc as Et,Ra as Z,S as re,Sa as Y,Uc as $t,Ue as Le,V as pt,We as Be,Xa as m,Xc as ke,Y as ft,Yc as lt,Ye as je,Za as pe,_ as se,_b as ve,a as M,aa as x,b as Xt,ba as h,bf as Nt,ca as S,cc as Dt,cd as j,cf as ze,da as l,eb as B,ef as Ge,fb as v,fd as we,g as te,gb as w,h as Ht,hb as X,i as T,ia as Ut,j as ee,ja as le,k as Vt,ka as F,kc as st,ke as Rt,la as J,ld as z,mc as y,na as P,nb as yt,nc as xt,nd as Qt,ne as ot,oa as ce,od as Te,p as Q,pa as _t,pb as Wt,pd as De,q as g,qa as bt,qb as tt,sa as de,sb as Ct,sd as ct,se as Fe,t as ne,ta as vt,tb as St,td as dt,te as Re,u as q,ua as I,ub as It,uc as ye,ud as nt,vb as _e,vd as Ot,ve as Ne,wb as et,xb as U,xd as xe,y as ht,ya as K,yb as W,yd as Ae,z as oe,zd as Ee}from"./chunk-TYGV4UPE.js";var dn=["determinateSpinner"];function un(o,s){if(o&1&&(Ut(),v(0,"svg",11),X(1,"circle",12),w()),o&2){let t=Ct();m("viewBox",t._viewBox()),k(),wt("stroke-dasharray",t._strokeCircumference(),"px")("stroke-dashoffset",t._strokeCircumference()/2,"px")("stroke-width",t._circleStrokeWidth(),"%"),m("r",t._circleRadius())}}var mn=new h("mat-progress-spinner-default-options",{providedIn:"root",factory:()=>({diameter:He})}),He=100,gn=10,io=(()=>{class o{_elementRef=l(I);_noopAnimations;get color(){return this._color||this._defaultColor}set color(t){this._color=t}_color;_defaultColor="primary";_determinateCircle;constructor(){let t=l(mn),e=Pe(),n=this._elementRef.nativeElement;this._noopAnimations=e==="di-disabled"&&!!t&&!t._forceAnimations,this.mode=n.nodeName.toLowerCase()==="mat-spinner"?"indeterminate":"determinate",!this._noopAnimations&&e==="reduced-motion"&&n.classList.add("mat-progress-spinner-reduced-motion"),t&&(t.color&&(this.color=this._defaultColor=t.color),t.diameter&&(this.diameter=t.diameter),t.strokeWidth&&(this.strokeWidth=t.strokeWidth))}mode;get value(){return this.mode==="determinate"?this._value:0}set value(t){this._value=Math.max(0,Math.min(100,t||0))}_value=0;get diameter(){return this._diameter}set diameter(t){this._diameter=t||0}_diameter=He;get strokeWidth(){return this._strokeWidth??this.diameter/10}set strokeWidth(t){this._strokeWidth=t||0}_strokeWidth;_circleRadius(){return(this.diameter-gn)/2}_viewBox(){let t=this._circleRadius()*2+this.strokeWidth;return`0 0 ${t} ${t}`}_strokeCircumference(){return 2*Math.PI*this._circleRadius()}_strokeDashOffset(){return this.mode==="determinate"?this._strokeCircumference()*(100-this._value)/100:null}_circleStrokeWidth(){return this.strokeWidth/this.diameter*100}static \u0275fac=function(e){return new(e||o)};static \u0275cmp=A({type:o,selectors:[["mat-progress-spinner"],["mat-spinner"]],viewQuery:function(e,n){if(e&1&&et(dn,5),e&2){let i;U(i=W())&&(n._determinateCircle=i.first)}},hostAttrs:["role","progressbar","tabindex","-1",1,"mat-mdc-progress-spinner","mdc-circular-progress"],hostVars:18,hostBindings:function(e,n){e&2&&(m("aria-valuemin",0)("aria-valuemax",100)("aria-valuenow",n.mode==="determinate"?n.value:null)("mode",n.mode),Tt("mat-"+n.color),wt("width",n.diameter,"px")("height",n.diameter,"px")("--mat-progress-spinner-size",n.diameter+"px")("--mat-progress-spinner-active-indicator-width",n.diameter+"px"),O("_mat-animation-noopable",n._noopAnimations)("mdc-circular-progress--indeterminate",n.mode==="indeterminate"))},inputs:{color:"color",mode:"mode",value:[2,"value","value",xt],diameter:[2,"diameter","diameter",xt],strokeWidth:[2,"strokeWidth","strokeWidth",xt]},exportAs:["matProgressSpinner"],decls:14,vars:11,consts:[["circle",""],["determinateSpinner",""],["aria-hidden","true",1,"mdc-circular-progress__determinate-container"],["xmlns","http://www.w3.org/2000/svg","focusable","false",1,"mdc-circular-progress__determinate-circle-graphic"],["cx","50%","cy","50%",1,"mdc-circular-progress__determinate-circle"],["aria-hidden","true",1,"mdc-circular-progress__indeterminate-container"],[1,"mdc-circular-progress__spinner-layer"],[1,"mdc-circular-progress__circle-clipper","mdc-circular-progress__circle-left"],[3,"ngTemplateOutlet"],[1,"mdc-circular-progress__gap-patch"],[1,"mdc-circular-progress__circle-clipper","mdc-circular-progress__circle-right"],["xmlns","http://www.w3.org/2000/svg","focusable","false",1,"mdc-circular-progress__indeterminate-circle-graphic"],["cx","50%","cy","50%"]],template:function(e,n){if(e&1&&(Y(0,un,2,8,"ng-template",null,0,ve),v(2,"div",2,1),Ut(),v(4,"svg",3),X(5,"circle",4),w()(),le(),v(6,"div",5)(7,"div",6)(8,"div",7),yt(9,8),w(),v(10,"div",9),yt(11,8),w(),v(12,"div",10),yt(13,8),w()()()),e&2){let i=kt(1);k(4),m("viewBox",n._viewBox()),k(),wt("stroke-dasharray",n._strokeCircumference(),"px")("stroke-dashoffset",n._strokeDashOffset(),"px")("stroke-width",n._circleStrokeWidth(),"%"),m("r",n._circleRadius()),k(4),B("ngTemplateOutlet",i),k(2),B("ngTemplateOutlet",i),k(2),B("ngTemplateOutlet",i)}},dependencies:[ye],styles:[`.mat-mdc-progress-spinner { + --mat-progress-spinner-animation-multiplier: 1; + display: block; + overflow: hidden; + line-height: 0; + position: relative; + direction: ltr; + transition: opacity 250ms cubic-bezier(0.4, 0, 0.6, 1); +} +.mat-mdc-progress-spinner circle { + stroke-width: var(--mat-progress-spinner-active-indicator-width, 4px); +} +.mat-mdc-progress-spinner._mat-animation-noopable, .mat-mdc-progress-spinner._mat-animation-noopable .mdc-circular-progress__determinate-circle { + transition: none !important; +} +.mat-mdc-progress-spinner._mat-animation-noopable .mdc-circular-progress__indeterminate-circle-graphic, +.mat-mdc-progress-spinner._mat-animation-noopable .mdc-circular-progress__spinner-layer, +.mat-mdc-progress-spinner._mat-animation-noopable .mdc-circular-progress__indeterminate-container { + animation: none !important; +} +.mat-mdc-progress-spinner._mat-animation-noopable .mdc-circular-progress__indeterminate-container circle { + stroke-dasharray: 0 !important; +} +@media (forced-colors: active) { + .mat-mdc-progress-spinner .mdc-circular-progress__indeterminate-circle-graphic, + .mat-mdc-progress-spinner .mdc-circular-progress__determinate-circle { + stroke: currentColor; + stroke: CanvasText; + } +} + +.mat-progress-spinner-reduced-motion { + --mat-progress-spinner-animation-multiplier: 1.25; +} + +.mdc-circular-progress__determinate-container, +.mdc-circular-progress__indeterminate-circle-graphic, +.mdc-circular-progress__indeterminate-container, +.mdc-circular-progress__spinner-layer { + position: absolute; + width: 100%; + height: 100%; +} + +.mdc-circular-progress__determinate-container { + transform: rotate(-90deg); +} +.mdc-circular-progress--indeterminate .mdc-circular-progress__determinate-container { + opacity: 0; +} + +.mdc-circular-progress__indeterminate-container { + font-size: 0; + letter-spacing: 0; + white-space: nowrap; + opacity: 0; +} +.mdc-circular-progress--indeterminate .mdc-circular-progress__indeterminate-container { + opacity: 1; + animation: mdc-circular-progress-container-rotate calc(1568.2352941176ms * var(--mat-progress-spinner-animation-multiplier)) linear infinite; +} + +.mdc-circular-progress__determinate-circle-graphic, +.mdc-circular-progress__indeterminate-circle-graphic { + fill: transparent; +} + +.mat-mdc-progress-spinner .mdc-circular-progress__determinate-circle, +.mat-mdc-progress-spinner .mdc-circular-progress__indeterminate-circle-graphic { + stroke: var(--mat-progress-spinner-active-indicator-color, var(--mat-sys-primary)); +} +@media (forced-colors: active) { + .mat-mdc-progress-spinner .mdc-circular-progress__determinate-circle, + .mat-mdc-progress-spinner .mdc-circular-progress__indeterminate-circle-graphic { + stroke: CanvasText; + } +} + +.mdc-circular-progress__determinate-circle { + transition: stroke-dashoffset 500ms cubic-bezier(0, 0, 0.2, 1); +} + +.mdc-circular-progress__gap-patch { + position: absolute; + top: 0; + left: 47.5%; + box-sizing: border-box; + width: 5%; + height: 100%; + overflow: hidden; +} + +.mdc-circular-progress__gap-patch .mdc-circular-progress__indeterminate-circle-graphic { + left: -900%; + width: 2000%; + transform: rotate(180deg); +} +.mdc-circular-progress__circle-clipper .mdc-circular-progress__indeterminate-circle-graphic { + width: 200%; +} +.mdc-circular-progress__circle-right .mdc-circular-progress__indeterminate-circle-graphic { + left: -100%; +} +.mdc-circular-progress--indeterminate .mdc-circular-progress__circle-left .mdc-circular-progress__indeterminate-circle-graphic { + animation: mdc-circular-progress-left-spin calc(1333ms * var(--mat-progress-spinner-animation-multiplier)) cubic-bezier(0.4, 0, 0.2, 1) infinite both; +} +.mdc-circular-progress--indeterminate .mdc-circular-progress__circle-right .mdc-circular-progress__indeterminate-circle-graphic { + animation: mdc-circular-progress-right-spin calc(1333ms * var(--mat-progress-spinner-animation-multiplier)) cubic-bezier(0.4, 0, 0.2, 1) infinite both; +} + +.mdc-circular-progress__circle-clipper { + display: inline-flex; + position: relative; + width: 50%; + height: 100%; + overflow: hidden; +} + +.mdc-circular-progress--indeterminate .mdc-circular-progress__spinner-layer { + animation: mdc-circular-progress-spinner-layer-rotate calc(5332ms * var(--mat-progress-spinner-animation-multiplier)) cubic-bezier(0.4, 0, 0.2, 1) infinite both; +} + +@keyframes mdc-circular-progress-container-rotate { + to { + transform: rotate(360deg); + } +} +@keyframes mdc-circular-progress-spinner-layer-rotate { + 12.5% { + transform: rotate(135deg); + } + 25% { + transform: rotate(270deg); + } + 37.5% { + transform: rotate(405deg); + } + 50% { + transform: rotate(540deg); + } + 62.5% { + transform: rotate(675deg); + } + 75% { + transform: rotate(810deg); + } + 87.5% { + transform: rotate(945deg); + } + 100% { + transform: rotate(1080deg); + } +} +@keyframes mdc-circular-progress-left-spin { + from { + transform: rotate(265deg); + } + 50% { + transform: rotate(130deg); + } + to { + transform: rotate(265deg); + } +} +@keyframes mdc-circular-progress-right-spin { + from { + transform: rotate(-265deg); + } + 50% { + transform: rotate(-130deg); + } + to { + transform: rotate(-265deg); + } +} +`],encapsulation:2,changeDetection:0})}return o})();var ao=(()=>{class o{static \u0275fac=function(e){return new(e||o)};static \u0275mod=E({type:o});static \u0275inj=x({imports:[j]})}return o})();function Ve(o){return Error(`Unable to find icon with the name "${o}"`)}function pn(){return Error("Could not find HttpClient for use with Angular Material icons. Please add provideHttpClient() to your providers.")}function Ue(o){return Error(`The URL provided to MatIconRegistry was not trusted as a resource URL via Angular's DomSanitizer. Attempted URL was "${o}".`)}function We(o){return Error(`The literal provided to MatIconRegistry was not trusted as safe HTML by Angular's DomSanitizer. Attempted literal was "${o}".`)}var R=class{url;svgText;options;svgElement=null;constructor(s,t,e){this.url=s,this.svgText=t,this.options=e}},Qe=(()=>{class o{_httpClient;_sanitizer;_errorHandler;_document;_svgIconConfigs=new Map;_iconSetConfigs=new Map;_cachedIconsByUrl=new Map;_inProgressUrlFetches=new Map;_fontCssClassesByAlias=new Map;_resolvers=[];_defaultFontSetClass=["material-icons","mat-ligature-font"];constructor(t,e,n,i){this._httpClient=t,this._sanitizer=e,this._errorHandler=i,this._document=n}addSvgIcon(t,e,n){return this.addSvgIconInNamespace("",t,e,n)}addSvgIconLiteral(t,e,n){return this.addSvgIconLiteralInNamespace("",t,e,n)}addSvgIconInNamespace(t,e,n,i){return this._addSvgIconConfig(t,e,new R(n,null,i))}addSvgIconResolver(t){return this._resolvers.push(t),this}addSvgIconLiteralInNamespace(t,e,n,i){let a=this._sanitizer.sanitize(K.HTML,n);if(!a)throw We(n);let r=ot(a);return this._addSvgIconConfig(t,e,new R("",r,i))}addSvgIconSet(t,e){return this.addSvgIconSetInNamespace("",t,e)}addSvgIconSetLiteral(t,e){return this.addSvgIconSetLiteralInNamespace("",t,e)}addSvgIconSetInNamespace(t,e,n){return this._addSvgIconSetConfig(t,new R(e,null,n))}addSvgIconSetLiteralInNamespace(t,e,n){let i=this._sanitizer.sanitize(K.HTML,e);if(!i)throw We(e);let a=ot(i);return this._addSvgIconSetConfig(t,new R("",a,n))}registerFontClassAlias(t,e=t){return this._fontCssClassesByAlias.set(t,e),this}classNameForFontAlias(t){return this._fontCssClassesByAlias.get(t)||t}setDefaultFontSetClass(...t){return this._defaultFontSetClass=t,this}getDefaultFontSetClass(){return this._defaultFontSetClass}getSvgIconFromUrl(t){let e=this._sanitizer.sanitize(K.RESOURCE_URL,t);if(!e)throw Ue(t);let n=this._cachedIconsByUrl.get(e);return n?Q(Pt(n)):this._loadSvgIconFromConfig(new R(t,null)).pipe(ft(i=>this._cachedIconsByUrl.set(e,i)),q(i=>Pt(i)))}getNamedSvgIcon(t,e=""){let n=$e(e,t),i=this._svgIconConfigs.get(n);if(i)return this._getSvgFromConfig(i);if(i=this._getIconConfigFromResolvers(e,t),i)return this._svgIconConfigs.set(n,i),this._getSvgFromConfig(i);let a=this._iconSetConfigs.get(e);return a?this._getSvgFromIconSetConfigs(t,a):g(Ve(n))}ngOnDestroy(){this._resolvers=[],this._svgIconConfigs.clear(),this._iconSetConfigs.clear(),this._cachedIconsByUrl.clear()}_getSvgFromConfig(t){return t.svgText?Q(Pt(this._svgElementFromConfig(t))):this._loadSvgIconFromConfig(t).pipe(q(e=>Pt(e)))}_getSvgFromIconSetConfigs(t,e){let n=this._extractIconWithNameFromAnySet(t,e);if(n)return Q(n);let i=e.filter(a=>!a.svgText).map(a=>this._loadSvgIconSetFromConfig(a).pipe(f(r=>{let c=`Loading icon set URL: ${this._sanitizer.sanitize(K.RESOURCE_URL,a.url)} failed: ${r.message}`;return this._errorHandler.handleError(new Error(c)),Q(null)})));return oe(i).pipe(q(()=>{let a=this._extractIconWithNameFromAnySet(t,e);if(!a)throw Ve(t);return a}))}_extractIconWithNameFromAnySet(t,e){for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.svgText&&i.svgText.toString().indexOf(t)>-1){let a=this._svgElementFromConfig(i),r=this._extractSvgIconFromSet(a,t,i.options);if(r)return r}}return null}_loadSvgIconFromConfig(t){return this._fetchIcon(t).pipe(ft(e=>t.svgText=e),q(()=>this._svgElementFromConfig(t)))}_loadSvgIconSetFromConfig(t){return t.svgText?Q(null):this._fetchIcon(t).pipe(ft(e=>t.svgText=e))}_extractSvgIconFromSet(t,e,n){let i=t.querySelector(`[id="${e}"]`);if(!i)return null;let a=i.cloneNode(!0);if(a.removeAttribute("id"),a.nodeName.toLowerCase()==="svg")return this._setSvgAttributes(a,n);if(a.nodeName.toLowerCase()==="symbol")return this._setSvgAttributes(this._toSvgElement(a),n);let r=this._svgElementFromString(ot(""));return r.appendChild(a),this._setSvgAttributes(r,n)}_svgElementFromString(t){let e=this._document.createElement("DIV");e.innerHTML=t;let n=e.querySelector("svg");if(!n)throw Error(" tag not found");return n}_toSvgElement(t){let e=this._svgElementFromString(ot("")),n=t.attributes;for(let i=0;iot(c)),ae(()=>this._inProgressUrlFetches.delete(a)),re());return this._inProgressUrlFetches.set(a,d),d}_addSvgIconConfig(t,e,n){return this._svgIconConfigs.set($e(t,e),n),this}_addSvgIconSetConfig(t,e){let n=this._iconSetConfigs.get(t);return n?n.push(e):this._iconSetConfigs.set(t,[e]),this}_svgElementFromConfig(t){if(!t.svgElement){let e=this._svgElementFromString(t.svgText);this._setSvgAttributes(e,t.options),t.svgElement=e}return t.svgElement}_getIconConfigFromResolvers(t,e){for(let n=0;n{let o=l(J),s=o?o.location:null;return{getPathname:()=>s?s.pathname+s.search:""}}}),qe=["clip-path","color-profile","src","cursor","fill","filter","marker","marker-start","marker-mid","marker-end","mask","stroke"],yn=qe.map(o=>`[${o}]`).join(", "),Cn=/^url\(['"]?#(.*?)['"]?\)$/,Do=(()=>{class o{_elementRef=l(I);_iconRegistry=l(Qe);_location=l(vn);_errorHandler=l(_t);_defaultColor;get color(){return this._color||this._defaultColor}set color(t){this._color=t}_color;inline=!1;get svgIcon(){return this._svgIcon}set svgIcon(t){t!==this._svgIcon&&(t?this._updateSvgIcon(t):this._svgIcon&&this._clearSvgElement(),this._svgIcon=t)}_svgIcon;get fontSet(){return this._fontSet}set fontSet(t){let e=this._cleanupFontValue(t);e!==this._fontSet&&(this._fontSet=e,this._updateFontIconClasses())}_fontSet;get fontIcon(){return this._fontIcon}set fontIcon(t){let e=this._cleanupFontValue(t);e!==this._fontIcon&&(this._fontIcon=e,this._updateFontIconClasses())}_fontIcon;_previousFontSetClass=[];_previousFontIconClass;_svgName=null;_svgNamespace=null;_previousPath;_elementsWithExternalReferences;_currentIconFetch=te.EMPTY;constructor(){let t=l(new Dt("aria-hidden"),{optional:!0}),e=l(bn,{optional:!0});e&&(e.color&&(this.color=this._defaultColor=e.color),e.fontSet&&(this.fontSet=e.fontSet)),t||this._elementRef.nativeElement.setAttribute("aria-hidden","true")}_splitIconName(t){if(!t)return["",""];let e=t.split(":");switch(e.length){case 1:return["",e[0]];case 2:return e;default:throw Error(`Invalid icon name: "${t}"`)}}ngOnInit(){this._updateFontIconClasses()}ngAfterViewChecked(){let t=this._elementsWithExternalReferences;if(t&&t.size){let e=this._location.getPathname();e!==this._previousPath&&(this._previousPath=e,this._prependPathToReferences(e))}}ngOnDestroy(){this._currentIconFetch.unsubscribe(),this._elementsWithExternalReferences&&this._elementsWithExternalReferences.clear()}_usingFontIcon(){return!this.svgIcon}_setSvgElement(t){this._clearSvgElement();let e=this._location.getPathname();this._previousPath=e,this._cacheChildrenWithExternalReferences(t),this._prependPathToReferences(e),this._elementRef.nativeElement.appendChild(t)}_clearSvgElement(){let t=this._elementRef.nativeElement,e=t.childNodes.length;for(this._elementsWithExternalReferences&&this._elementsWithExternalReferences.clear();e--;){let n=t.childNodes[e];(n.nodeType!==1||n.nodeName.toLowerCase()==="svg")&&n.remove()}}_updateFontIconClasses(){if(!this._usingFontIcon())return;let t=this._elementRef.nativeElement,e=(this.fontSet?this._iconRegistry.classNameForFontAlias(this.fontSet).split(/ +/):this._iconRegistry.getDefaultFontSetClass()).filter(n=>n.length>0);this._previousFontSetClass.forEach(n=>t.classList.remove(n)),e.forEach(n=>t.classList.add(n)),this._previousFontSetClass=e,this.fontIcon!==this._previousFontIconClass&&!e.includes("mat-ligature-font")&&(this._previousFontIconClass&&t.classList.remove(this._previousFontIconClass),this.fontIcon&&t.classList.add(this.fontIcon),this._previousFontIconClass=this.fontIcon)}_cleanupFontValue(t){return typeof t=="string"?t.trim().split(" ")[0]:t}_prependPathToReferences(t){let e=this._elementsWithExternalReferences;e&&e.forEach((n,i)=>{n.forEach(a=>{i.setAttribute(a.name,`url('${t}#${a.value}')`)})})}_cacheChildrenWithExternalReferences(t){let e=t.querySelectorAll(yn),n=this._elementsWithExternalReferences=this._elementsWithExternalReferences||new Map;for(let i=0;i{let r=e[i],d=r.getAttribute(a),c=d?d.match(Cn):null;if(c){let C=n.get(r);C||(C=[],n.set(r,C)),C.push({name:a,value:c[1]})}})}_updateSvgIcon(t){if(this._svgNamespace=null,this._svgName=null,this._currentIconFetch.unsubscribe(),t){let[e,n]=this._splitIconName(t);e&&(this._svgNamespace=e),n&&(this._svgName=n),this._currentIconFetch=this._iconRegistry.getNamedSvgIcon(n,e).pipe(N(1)).subscribe(i=>this._setSvgElement(i),i=>{let a=`Error retrieving icon ${e}:${n}! ${i.message}`;this._errorHandler.handleError(new Error(a))})}}static \u0275fac=function(e){return new(e||o)};static \u0275cmp=A({type:o,selectors:[["mat-icon"]],hostAttrs:["role","img",1,"mat-icon","notranslate"],hostVars:10,hostBindings:function(e,n){e&2&&(m("data-mat-icon-type",n._usingFontIcon()?"font":"svg")("data-mat-icon-name",n._svgName||n.fontIcon)("data-mat-icon-namespace",n._svgNamespace||n.fontSet)("fontIcon",n._usingFontIcon()?n.fontIcon:null),Tt(n.color?"mat-"+n.color:""),O("mat-icon-inline",n.inline)("mat-icon-no-color",n.color!=="primary"&&n.color!=="accent"&&n.color!=="warn"))},inputs:{color:"color",inline:[2,"inline","inline",y],svgIcon:"svgIcon",fontSet:"fontSet",fontIcon:"fontIcon"},exportAs:["matIcon"],ngContentSelectors:_n,decls:1,vars:0,template:function(e,n){e&1&&(St(),It(0))},styles:[`mat-icon, mat-icon.mat-primary, mat-icon.mat-accent, mat-icon.mat-warn { + color: var(--mat-icon-color, inherit); +} + +.mat-icon { + -webkit-user-select: none; + user-select: none; + background-repeat: no-repeat; + display: inline-block; + fill: currentColor; + height: 24px; + width: 24px; + overflow: hidden; +} +.mat-icon.mat-icon-inline { + font-size: inherit; + height: inherit; + line-height: inherit; + width: inherit; +} +.mat-icon.mat-ligature-font[fontIcon]::before { + content: attr(fontIcon); +} + +[dir=rtl] .mat-icon-rtl-mirror { + transform: scale(-1, 1); +} + +.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-prefix .mat-icon, +.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-suffix .mat-icon { + display: block; +} +.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-prefix .mat-icon-button .mat-icon, +.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-suffix .mat-icon-button .mat-icon { + margin: auto; +} +`],encapsulation:2,changeDetection:0})}return o})(),xo=(()=>{class o{static \u0275fac=function(e){return new(e||o)};static \u0275mod=E({type:o});static \u0275inj=x({imports:[j]})}return o})();function Sn(o,s){}var G=class{viewContainerRef;injector;id;role="dialog";panelClass="";hasBackdrop=!0;backdropClass="";disableClose=!1;closePredicate;width="";height="";minWidth;minHeight;maxWidth;maxHeight;positionStrategy;data=null;direction;ariaDescribedBy=null;ariaLabelledBy=null;ariaLabel=null;ariaModal=!1;autoFocus="first-tabbable";restoreFocus=!0;scrollStrategy;closeOnNavigation=!0;closeOnDestroy=!0;closeOnOverlayDetachments=!0;disableAnimations=!1;providers;container;templateContext};var Jt=(()=>{class o extends De{_elementRef=l(I);_focusTrapFactory=l(Re);_config;_interactivityChecker=l(Fe);_ngZone=l(ce);_focusMonitor=l(Rt);_renderer=l(ge);_changeDetectorRef=l(st);_injector=l(F);_platform=l(ke);_document=l(J);_portalOutlet;_focusTrapped=new T;_focusTrap=null;_elementFocusedBeforeDialogWasOpened=null;_closeInteractionType=null;_ariaLabelledByQueue=[];_isDestroyed=!1;constructor(){super(),this._config=l(G,{optional:!0})||new G,this._config.ariaLabelledBy&&this._ariaLabelledByQueue.push(this._config.ariaLabelledBy)}_addAriaLabelledBy(t){this._ariaLabelledByQueue.push(t),this._changeDetectorRef.markForCheck()}_removeAriaLabelledBy(t){let e=this._ariaLabelledByQueue.indexOf(t);e>-1&&(this._ariaLabelledByQueue.splice(e,1),this._changeDetectorRef.markForCheck())}_contentAttached(){this._initializeFocusTrap(),this._captureInitialFocus()}_captureInitialFocus(){this._trapFocus()}ngOnDestroy(){this._focusTrapped.complete(),this._isDestroyed=!0,this._restoreFocus()}attachComponentPortal(t){this._portalOutlet.hasAttached();let e=this._portalOutlet.attachComponentPortal(t);return this._contentAttached(),e}attachTemplatePortal(t){this._portalOutlet.hasAttached();let e=this._portalOutlet.attachTemplatePortal(t);return this._contentAttached(),e}attachDomPortal=t=>{this._portalOutlet.hasAttached();let e=this._portalOutlet.attachDomPortal(t);return this._contentAttached(),e};_recaptureFocus(){this._containsFocus()||this._trapFocus()}_forceFocus(t,e){this._interactivityChecker.isFocusable(t)||(t.tabIndex=-1,this._ngZone.runOutsideAngular(()=>{let n=()=>{i(),a(),t.removeAttribute("tabindex")},i=this._renderer.listen(t,"blur",n),a=this._renderer.listen(t,"mousedown",n)})),t.focus(e)}_focusByCssSelector(t,e){let n=this._elementRef.nativeElement.querySelector(t);n&&this._forceFocus(n,e)}_trapFocus(t){this._isDestroyed||ue(()=>{let e=this._elementRef.nativeElement;switch(this._config.autoFocus){case!1:case"dialog":this._containsFocus()||e.focus(t);break;case!0:case"first-tabbable":this._focusTrap?.focusInitialElement(t)||this._focusDialogContainer(t);break;case"first-heading":this._focusByCssSelector('h1, h2, h3, h4, h5, h6, [role="heading"]',t);break;default:this._focusByCssSelector(this._config.autoFocus,t);break}this._focusTrapped.next()},{injector:this._injector})}_restoreFocus(){let t=this._config.restoreFocus,e=null;if(typeof t=="string"?e=this._document.querySelector(t):typeof t=="boolean"?e=t?this._elementFocusedBeforeDialogWasOpened:null:t&&(e=t),this._config.restoreFocus&&e&&typeof e.focus=="function"){let n=Et(),i=this._elementRef.nativeElement;(!n||n===this._document.body||n===i||i.contains(n))&&(this._focusMonitor?(this._focusMonitor.focusVia(e,this._closeInteractionType),this._closeInteractionType=null):e.focus())}this._focusTrap&&this._focusTrap.destroy()}_focusDialogContainer(t){this._elementRef.nativeElement.focus?.(t)}_containsFocus(){let t=this._elementRef.nativeElement,e=Et();return t===e||t.contains(e)}_initializeFocusTrap(){this._platform.isBrowser&&(this._focusTrap=this._focusTrapFactory.create(this._elementRef.nativeElement),this._document&&(this._elementFocusedBeforeDialogWasOpened=Et()))}static \u0275fac=function(e){return new(e||o)};static \u0275cmp=A({type:o,selectors:[["cdk-dialog-container"]],viewQuery:function(e,n){if(e&1&&et(ct,7),e&2){let i;U(i=W())&&(n._portalOutlet=i.first)}},hostAttrs:["tabindex","-1",1,"cdk-dialog-container"],hostVars:6,hostBindings:function(e,n){e&2&&m("id",n._config.id||null)("role",n._config.role)("aria-modal",n._config.ariaModal)("aria-labelledby",n._config.ariaLabel?null:n._ariaLabelledByQueue[0])("aria-label",n._config.ariaLabel)("aria-describedby",n._config.ariaDescribedBy||null)},features:[Z],decls:1,vars:0,consts:[["cdkPortalOutlet",""]],template:function(e,n){e&1&&Y(0,Sn,0,0,"ng-template",0)},dependencies:[ct],styles:[`.cdk-dialog-container { + display: block; + width: 100%; + height: 100%; + min-height: inherit; + max-height: inherit; +} +`],encapsulation:2})}return o})(),mt=class{overlayRef;config;componentInstance=null;componentRef=null;containerInstance;disableClose;closed=new T;backdropClick;keydownEvents;outsidePointerEvents;id;_detachSubscription;constructor(s,t){this.overlayRef=s,this.config=t,this.disableClose=t.disableClose,this.backdropClick=s.backdropClick(),this.keydownEvents=s.keydownEvents(),this.outsidePointerEvents=s.outsidePointerEvents(),this.id=t.id,this.keydownEvents.subscribe(e=>{e.keyCode===27&&!this.disableClose&&!nt(e)&&(e.preventDefault(),this.close(void 0,{focusOrigin:"keyboard"}))}),this.backdropClick.subscribe(()=>{!this.disableClose&&this._canClose()?this.close(void 0,{focusOrigin:"mouse"}):this.containerInstance._recaptureFocus?.()}),this._detachSubscription=s.detachments().subscribe(()=>{t.closeOnOverlayDetachments!==!1&&this.close()})}close(s,t){if(this._canClose(s)){let e=this.closed;this.containerInstance._closeInteractionType=t?.focusOrigin||"program",this._detachSubscription.unsubscribe(),this.overlayRef.dispose(),e.next(s),e.complete(),this.componentInstance=this.containerInstance=null}}updatePosition(){return this.overlayRef.updatePosition(),this}updateSize(s="",t=""){return this.overlayRef.updateSize({width:s,height:t}),this}addPanelClass(s){return this.overlayRef.addPanelClass(s),this}removePanelClass(s){return this.overlayRef.removePanelClass(s),this}_canClose(s){let t=this.config;return!!this.containerInstance&&(!t.closePredicate||t.closePredicate(s,t,this.componentInstance))}},In=new h("DialogScrollStrategy",{providedIn:"root",factory:()=>{let o=l(F);return()=>Ot(o)}}),kn=new h("DialogData"),wn=new h("DefaultDialogConfig");function Tn(o){let s=bt(o),t=new P;return{valueSignal:s,get value(){return s()},change:t,ngOnDestroy(){t.complete()}}}var Kt=(()=>{class o{_injector=l(F);_defaultOptions=l(wn,{optional:!0});_parentDialog=l(o,{optional:!0,skipSelf:!0});_overlayContainer=l(Ae);_idGenerator=l(z);_openDialogsAtThisLevel=[];_afterAllClosedAtThisLevel=new T;_afterOpenedAtThisLevel=new T;_ariaHiddenElements=new Map;_scrollStrategy=l(In);get openDialogs(){return this._parentDialog?this._parentDialog.openDialogs:this._openDialogsAtThisLevel}get afterOpened(){return this._parentDialog?this._parentDialog.afterOpened:this._afterOpenedAtThisLevel}afterAllClosed=ht(()=>this.openDialogs.length?this._getAfterAllClosed():this._getAfterAllClosed().pipe(pt(void 0)));constructor(){}open(t,e){let n=this._defaultOptions||new G;e=M(M({},n),e),e.id=e.id||this._idGenerator.getId("cdk-dialog-"),e.id&&this.getDialogById(e.id);let i=this._getOverlayConfig(e),a=Oe(this._injector,i),r=new mt(a,e),d=this._attachContainer(a,r,e);if(r.containerInstance=d,!this.openDialogs.length){let c=this._overlayContainer.getContainerElement();d._focusTrapped?d._focusTrapped.pipe(N(1)).subscribe(()=>{this._hideNonDialogContentFromAssistiveTechnology(c)}):this._hideNonDialogContentFromAssistiveTechnology(c)}return this._attachDialogContent(t,r,d,e),this.openDialogs.push(r),r.closed.subscribe(()=>this._removeOpenDialog(r,!0)),this.afterOpened.next(r),r}closeAll(){qt(this.openDialogs,t=>t.close())}getDialogById(t){return this.openDialogs.find(e=>e.id===t)}ngOnDestroy(){qt(this._openDialogsAtThisLevel,t=>{t.config.closeOnDestroy===!1&&this._removeOpenDialog(t,!1)}),qt(this._openDialogsAtThisLevel,t=>t.close()),this._afterAllClosedAtThisLevel.complete(),this._afterOpenedAtThisLevel.complete(),this._openDialogsAtThisLevel=[]}_getOverlayConfig(t){let e=new xe({positionStrategy:t.positionStrategy||Mt().centerHorizontally().centerVertically(),scrollStrategy:t.scrollStrategy||this._scrollStrategy(),panelClass:t.panelClass,hasBackdrop:t.hasBackdrop,direction:t.direction,minWidth:t.minWidth,minHeight:t.minHeight,maxWidth:t.maxWidth,maxHeight:t.maxHeight,width:t.width,height:t.height,disposeOnNavigation:t.closeOnNavigation,disableAnimations:t.disableAnimations});return t.backdropClass&&(e.backdropClass=t.backdropClass),e}_attachContainer(t,e,n){let i=n.injector||n.viewContainerRef?.injector,a=[{provide:G,useValue:n},{provide:mt,useValue:e},{provide:Ee,useValue:t}],r;n.container?typeof n.container=="function"?r=n.container:(r=n.container.type,a.push(...n.container.providers(n))):r=Jt;let d=new Qt(r,n.viewContainerRef,F.create({parent:i||this._injector,providers:a}));return t.attach(d).instance}_attachDialogContent(t,e,n,i){if(t instanceof me){let a=this._createInjector(i,e,n,void 0),r={$implicit:i.data,dialogRef:e};i.templateContext&&(r=M(M({},r),typeof i.templateContext=="function"?i.templateContext():i.templateContext)),n.attachTemplatePortal(new Te(t,null,r,a))}else{let a=this._createInjector(i,e,n,this._injector),r=n.attachComponentPortal(new Qt(t,i.viewContainerRef,a));e.componentRef=r,e.componentInstance=r.instance}}_createInjector(t,e,n,i){let a=t.injector||t.viewContainerRef?.injector,r=[{provide:kn,useValue:t.data},{provide:mt,useValue:e}];return t.providers&&(typeof t.providers=="function"?r.push(...t.providers(e,t,n)):r.push(...t.providers)),t.direction&&(!a||!a.get(lt,null,{optional:!0}))&&r.push({provide:lt,useValue:Tn(t.direction)}),F.create({parent:a||i,providers:r})}_removeOpenDialog(t,e){let n=this.openDialogs.indexOf(t);n>-1&&(this.openDialogs.splice(n,1),this.openDialogs.length||(this._ariaHiddenElements.forEach((i,a)=>{i?a.setAttribute("aria-hidden",i):a.removeAttribute("aria-hidden")}),this._ariaHiddenElements.clear(),e&&this._getAfterAllClosed().next()))}_hideNonDialogContentFromAssistiveTechnology(t){if(t.parentElement){let e=t.parentElement.children;for(let n=e.length-1;n>-1;n--){let i=e[n];i!==t&&i.nodeName!=="SCRIPT"&&i.nodeName!=="STYLE"&&!i.hasAttribute("aria-live")&&!i.hasAttribute("popover")&&(this._ariaHiddenElements.set(i,i.getAttribute("aria-hidden")),i.setAttribute("aria-hidden","true"))}}}_getAfterAllClosed(){let t=this._parentDialog;return t?t._getAfterAllClosed():this._afterAllClosedAtThisLevel}static \u0275fac=function(e){return new(e||o)};static \u0275prov=D({token:o,factory:o.\u0275fac,providedIn:"root"})}return o})();function qt(o,s){let t=o.length;for(;t--;)s(o[t])}var Ke=(()=>{class o{static \u0275fac=function(e){return new(e||o)};static \u0275mod=E({type:o});static \u0275inj=x({providers:[Kt],imports:[Ft,dt,Ne,dt]})}return o})();function Dn(o,s){}var Bt=class{viewContainerRef;injector;id;role="dialog";panelClass="";hasBackdrop=!0;backdropClass="";disableClose=!1;closePredicate;width="";height="";minWidth;minHeight;maxWidth;maxHeight;position;data=null;direction;ariaDescribedBy=null;ariaLabelledBy=null;ariaLabel=null;ariaModal=!1;autoFocus="first-tabbable";restoreFocus=!0;delayFocusTrap=!0;scrollStrategy;closeOnNavigation=!0;enterAnimationDuration;exitAnimationDuration},Zt="mdc-dialog--open",Ze="mdc-dialog--opening",Ye="mdc-dialog--closing",xn=150,An=75,En=(()=>{class o extends Jt{_animationStateChanged=new P;_animationsEnabled=!ut();_actionSectionCount=0;_hostElement=this._elementRef.nativeElement;_enterAnimationDuration=this._animationsEnabled?tn(this._config.enterAnimationDuration)??xn:0;_exitAnimationDuration=this._animationsEnabled?tn(this._config.exitAnimationDuration)??An:0;_animationTimer=null;_contentAttached(){super._contentAttached(),this._startOpenAnimation()}_startOpenAnimation(){this._animationStateChanged.emit({state:"opening",totalTime:this._enterAnimationDuration}),this._animationsEnabled?(this._hostElement.style.setProperty(Xe,`${this._enterAnimationDuration}ms`),this._requestAnimationFrame(()=>this._hostElement.classList.add(Ze,Zt)),this._waitForAnimationToComplete(this._enterAnimationDuration,this._finishDialogOpen)):(this._hostElement.classList.add(Zt),Promise.resolve().then(()=>this._finishDialogOpen()))}_startExitAnimation(){this._animationStateChanged.emit({state:"closing",totalTime:this._exitAnimationDuration}),this._hostElement.classList.remove(Zt),this._animationsEnabled?(this._hostElement.style.setProperty(Xe,`${this._exitAnimationDuration}ms`),this._requestAnimationFrame(()=>this._hostElement.classList.add(Ye)),this._waitForAnimationToComplete(this._exitAnimationDuration,this._finishDialogClose)):Promise.resolve().then(()=>this._finishDialogClose())}_updateActionSectionCount(t){this._actionSectionCount+=t,this._changeDetectorRef.markForCheck()}_finishDialogOpen=()=>{this._clearAnimationClasses(),this._openAnimationDone(this._enterAnimationDuration)};_finishDialogClose=()=>{this._clearAnimationClasses(),this._animationStateChanged.emit({state:"closed",totalTime:this._exitAnimationDuration})};_clearAnimationClasses(){this._hostElement.classList.remove(Ze,Ye)}_waitForAnimationToComplete(t,e){this._animationTimer!==null&&clearTimeout(this._animationTimer),this._animationTimer=setTimeout(e,t)}_requestAnimationFrame(t){this._ngZone.runOutsideAngular(()=>{typeof requestAnimationFrame=="function"?requestAnimationFrame(t):t()})}_captureInitialFocus(){this._config.delayFocusTrap||this._trapFocus()}_openAnimationDone(t){this._config.delayFocusTrap&&this._trapFocus(),this._animationStateChanged.next({state:"opened",totalTime:t})}ngOnDestroy(){super.ngOnDestroy(),this._animationTimer!==null&&clearTimeout(this._animationTimer)}attachComponentPortal(t){let e=super.attachComponentPortal(t);return e.location.nativeElement.classList.add("mat-mdc-dialog-component-host"),e}static \u0275fac=(()=>{let t;return function(n){return(t||(t=vt(o)))(n||o)}})();static \u0275cmp=A({type:o,selectors:[["mat-dialog-container"]],hostAttrs:["tabindex","-1",1,"mat-mdc-dialog-container","mdc-dialog"],hostVars:10,hostBindings:function(e,n){e&2&&(Wt("id",n._config.id),m("aria-modal",n._config.ariaModal)("role",n._config.role)("aria-labelledby",n._config.ariaLabel?null:n._ariaLabelledByQueue[0])("aria-label",n._config.ariaLabel)("aria-describedby",n._config.ariaDescribedBy||null),O("_mat-animation-noopable",!n._animationsEnabled)("mat-mdc-dialog-container-with-actions",n._actionSectionCount>0))},features:[Z],decls:3,vars:0,consts:[[1,"mat-mdc-dialog-inner-container","mdc-dialog__container"],[1,"mat-mdc-dialog-surface","mdc-dialog__surface"],["cdkPortalOutlet",""]],template:function(e,n){e&1&&(v(0,"div",0)(1,"div",1),Y(2,Dn,0,0,"ng-template",2),w()())},dependencies:[ct],styles:[`.mat-mdc-dialog-container { + width: 100%; + height: 100%; + display: block; + box-sizing: border-box; + max-height: inherit; + min-height: inherit; + min-width: inherit; + max-width: inherit; + outline: 0; +} + +.cdk-overlay-pane.mat-mdc-dialog-panel { + max-width: var(--mat-dialog-container-max-width, 560px); + min-width: var(--mat-dialog-container-min-width, 280px); +} +@media (max-width: 599px) { + .cdk-overlay-pane.mat-mdc-dialog-panel { + max-width: var(--mat-dialog-container-small-max-width, calc(100vw - 32px)); + } +} + +.mat-mdc-dialog-inner-container { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-around; + box-sizing: border-box; + height: 100%; + opacity: 0; + transition: opacity linear var(--mat-dialog-transition-duration, 0ms); + max-height: inherit; + min-height: inherit; + min-width: inherit; + max-width: inherit; +} +.mdc-dialog--closing .mat-mdc-dialog-inner-container { + transition: opacity 75ms linear; + transform: none; +} +.mdc-dialog--open .mat-mdc-dialog-inner-container { + opacity: 1; +} +._mat-animation-noopable .mat-mdc-dialog-inner-container { + transition: none; +} + +.mat-mdc-dialog-surface { + display: flex; + flex-direction: column; + flex-grow: 0; + flex-shrink: 0; + box-sizing: border-box; + width: 100%; + height: 100%; + position: relative; + overflow-y: auto; + outline: 0; + transform: scale(0.8); + transition: transform var(--mat-dialog-transition-duration, 0ms) cubic-bezier(0, 0, 0.2, 1); + max-height: inherit; + min-height: inherit; + min-width: inherit; + max-width: inherit; + box-shadow: var(--mat-dialog-container-elevation-shadow, none); + border-radius: var(--mat-dialog-container-shape, var(--mat-sys-corner-extra-large, 4px)); + background-color: var(--mat-dialog-container-color, var(--mat-sys-surface, white)); +} +[dir=rtl] .mat-mdc-dialog-surface { + text-align: right; +} +.mdc-dialog--open .mat-mdc-dialog-surface, .mdc-dialog--closing .mat-mdc-dialog-surface { + transform: none; +} +._mat-animation-noopable .mat-mdc-dialog-surface { + transition: none; +} +.mat-mdc-dialog-surface::before { + position: absolute; + box-sizing: border-box; + width: 100%; + height: 100%; + top: 0; + left: 0; + border: 2px solid transparent; + border-radius: inherit; + content: ""; + pointer-events: none; +} + +.mat-mdc-dialog-title { + display: block; + position: relative; + flex-shrink: 0; + box-sizing: border-box; + margin: 0 0 1px; + padding: var(--mat-dialog-headline-padding, 6px 24px 13px); +} +.mat-mdc-dialog-title::before { + display: inline-block; + width: 0; + height: 40px; + content: ""; + vertical-align: 0; +} +[dir=rtl] .mat-mdc-dialog-title { + text-align: right; +} +.mat-mdc-dialog-container .mat-mdc-dialog-title { + color: var(--mat-dialog-subhead-color, var(--mat-sys-on-surface, rgba(0, 0, 0, 0.87))); + font-family: var(--mat-dialog-subhead-font, var(--mat-sys-headline-small-font, inherit)); + line-height: var(--mat-dialog-subhead-line-height, var(--mat-sys-headline-small-line-height, 1.5rem)); + font-size: var(--mat-dialog-subhead-size, var(--mat-sys-headline-small-size, 1rem)); + font-weight: var(--mat-dialog-subhead-weight, var(--mat-sys-headline-small-weight, 400)); + letter-spacing: var(--mat-dialog-subhead-tracking, var(--mat-sys-headline-small-tracking, 0.03125em)); +} + +.mat-mdc-dialog-content { + display: block; + flex-grow: 1; + box-sizing: border-box; + margin: 0; + overflow: auto; + max-height: 65vh; +} +.mat-mdc-dialog-content > :first-child { + margin-top: 0; +} +.mat-mdc-dialog-content > :last-child { + margin-bottom: 0; +} +.mat-mdc-dialog-container .mat-mdc-dialog-content { + color: var(--mat-dialog-supporting-text-color, var(--mat-sys-on-surface-variant, rgba(0, 0, 0, 0.6))); + font-family: var(--mat-dialog-supporting-text-font, var(--mat-sys-body-medium-font, inherit)); + line-height: var(--mat-dialog-supporting-text-line-height, var(--mat-sys-body-medium-line-height, 1.5rem)); + font-size: var(--mat-dialog-supporting-text-size, var(--mat-sys-body-medium-size, 1rem)); + font-weight: var(--mat-dialog-supporting-text-weight, var(--mat-sys-body-medium-weight, 400)); + letter-spacing: var(--mat-dialog-supporting-text-tracking, var(--mat-sys-body-medium-tracking, 0.03125em)); +} +.mat-mdc-dialog-container .mat-mdc-dialog-content { + padding: var(--mat-dialog-content-padding, 20px 24px); +} +.mat-mdc-dialog-container-with-actions .mat-mdc-dialog-content { + padding: var(--mat-dialog-with-actions-content-padding, 20px 24px 0); +} +.mat-mdc-dialog-container .mat-mdc-dialog-title + .mat-mdc-dialog-content { + padding-top: 0; +} + +.mat-mdc-dialog-actions { + display: flex; + position: relative; + flex-shrink: 0; + flex-wrap: wrap; + align-items: center; + box-sizing: border-box; + min-height: 52px; + margin: 0; + border-top: 1px solid transparent; + padding: var(--mat-dialog-actions-padding, 16px 24px); + justify-content: var(--mat-dialog-actions-alignment, flex-end); +} +@media (forced-colors: active) { + .mat-mdc-dialog-actions { + border-top-color: CanvasText; + } +} +.mat-mdc-dialog-actions.mat-mdc-dialog-actions-align-start, .mat-mdc-dialog-actions[align=start] { + justify-content: start; +} +.mat-mdc-dialog-actions.mat-mdc-dialog-actions-align-center, .mat-mdc-dialog-actions[align=center] { + justify-content: center; +} +.mat-mdc-dialog-actions.mat-mdc-dialog-actions-align-end, .mat-mdc-dialog-actions[align=end] { + justify-content: flex-end; +} +.mat-mdc-dialog-actions .mat-button-base + .mat-button-base, +.mat-mdc-dialog-actions .mat-mdc-button-base + .mat-mdc-button-base { + margin-left: 8px; +} +[dir=rtl] .mat-mdc-dialog-actions .mat-button-base + .mat-button-base, +[dir=rtl] .mat-mdc-dialog-actions .mat-mdc-button-base + .mat-mdc-button-base { + margin-left: 0; + margin-right: 8px; +} + +.mat-mdc-dialog-component-host { + display: contents; +} +`],encapsulation:2})}return o})(),Xe="--mat-dialog-transition-duration";function tn(o){return o==null?null:typeof o=="number"?o:o.endsWith("ms")?$t(o.substring(0,o.length-2)):o.endsWith("s")?$t(o.substring(0,o.length-1))*1e3:o==="0"?0:null}var Lt=(function(o){return o[o.OPEN=0]="OPEN",o[o.CLOSING=1]="CLOSING",o[o.CLOSED=2]="CLOSED",o})(Lt||{}),gt=class{_ref;_config;_containerInstance;componentInstance;componentRef=null;disableClose;id;_afterOpened=new Vt(1);_beforeClosed=new Vt(1);_result;_closeFallbackTimeout;_state=Lt.OPEN;_closeInteractionType;constructor(s,t,e){this._ref=s,this._config=t,this._containerInstance=e,this.disableClose=t.disableClose,this.id=s.id,s.addPanelClass("mat-mdc-dialog-panel"),e._animationStateChanged.pipe(rt(n=>n.state==="opened"),N(1)).subscribe(()=>{this._afterOpened.next(),this._afterOpened.complete()}),e._animationStateChanged.pipe(rt(n=>n.state==="closed"),N(1)).subscribe(()=>{clearTimeout(this._closeFallbackTimeout),this._finishDialogClose()}),s.overlayRef.detachments().subscribe(()=>{this._beforeClosed.next(this._result),this._beforeClosed.complete(),this._finishDialogClose()}),ie(this.backdropClick(),this.keydownEvents().pipe(rt(n=>n.keyCode===27&&!this.disableClose&&!nt(n)))).subscribe(n=>{this.disableClose||(n.preventDefault(),en(this,n.type==="keydown"?"keyboard":"mouse"))})}close(s){let t=this._config.closePredicate;t&&!t(s,this._config,this.componentInstance)||(this._result=s,this._containerInstance._animationStateChanged.pipe(rt(e=>e.state==="closing"),N(1)).subscribe(e=>{this._beforeClosed.next(s),this._beforeClosed.complete(),this._ref.overlayRef.detachBackdrop(),this._closeFallbackTimeout=setTimeout(()=>this._finishDialogClose(),e.totalTime+100)}),this._state=Lt.CLOSING,this._containerInstance._startExitAnimation())}afterOpened(){return this._afterOpened}afterClosed(){return this._ref.closed}beforeClosed(){return this._beforeClosed}backdropClick(){return this._ref.backdropClick}keydownEvents(){return this._ref.keydownEvents}updatePosition(s){let t=this._ref.config.positionStrategy;return s&&(s.left||s.right)?s.left?t.left(s.left):t.right(s.right):t.centerHorizontally(),s&&(s.top||s.bottom)?s.top?t.top(s.top):t.bottom(s.bottom):t.centerVertically(),this._ref.updatePosition(),this}updateSize(s="",t=""){return this._ref.updateSize(s,t),this}addPanelClass(s){return this._ref.addPanelClass(s),this}removePanelClass(s){return this._ref.removePanelClass(s),this}getState(){return this._state}_finishDialogClose(){this._state=Lt.CLOSED,this._ref.close(this._result,{focusOrigin:this._closeInteractionType}),this.componentInstance=null}};function en(o,s,t){return o._closeInteractionType=s,o.close(t)}var On=new h("MatMdcDialogData"),Mn=new h("mat-mdc-dialog-default-options"),Fn=new h("mat-mdc-dialog-scroll-strategy",{providedIn:"root",factory:()=>{let o=l(F);return()=>Ot(o)}}),Yt=(()=>{class o{_defaultOptions=l(Mn,{optional:!0});_scrollStrategy=l(Fn);_parentDialog=l(o,{optional:!0,skipSelf:!0});_idGenerator=l(z);_injector=l(F);_dialog=l(Kt);_animationsDisabled=ut();_openDialogsAtThisLevel=[];_afterAllClosedAtThisLevel=new T;_afterOpenedAtThisLevel=new T;dialogConfigClass=Bt;_dialogRefConstructor;_dialogContainerType;_dialogDataToken;get openDialogs(){return this._parentDialog?this._parentDialog.openDialogs:this._openDialogsAtThisLevel}get afterOpened(){return this._parentDialog?this._parentDialog.afterOpened:this._afterOpenedAtThisLevel}_getAfterAllClosed(){let t=this._parentDialog;return t?t._getAfterAllClosed():this._afterAllClosedAtThisLevel}afterAllClosed=ht(()=>this.openDialogs.length?this._getAfterAllClosed():this._getAfterAllClosed().pipe(pt(void 0)));constructor(){this._dialogRefConstructor=gt,this._dialogContainerType=En,this._dialogDataToken=On}open(t,e){let n;e=M(M({},this._defaultOptions||new Bt),e),e.id=e.id||this._idGenerator.getId("mat-mdc-dialog-"),e.scrollStrategy=e.scrollStrategy||this._scrollStrategy();let i=this._dialog.open(t,Xt(M({},e),{positionStrategy:Mt(this._injector).centerHorizontally().centerVertically(),disableClose:!0,closePredicate:void 0,closeOnDestroy:!1,closeOnOverlayDetachments:!1,disableAnimations:this._animationsDisabled||e.enterAnimationDuration?.toLocaleString()==="0"||e.exitAnimationDuration?.toString()==="0",container:{type:this._dialogContainerType,providers:()=>[{provide:this.dialogConfigClass,useValue:e},{provide:G,useValue:e}]},templateContext:()=>({dialogRef:n}),providers:(a,r,d)=>(n=new this._dialogRefConstructor(a,e,d),n.updatePosition(e?.position),[{provide:this._dialogContainerType,useValue:d},{provide:this._dialogDataToken,useValue:r.data},{provide:this._dialogRefConstructor,useValue:n}])}));return n.componentRef=i.componentRef,n.componentInstance=i.componentInstance,this.openDialogs.push(n),this.afterOpened.next(n),n.afterClosed().subscribe(()=>{let a=this.openDialogs.indexOf(n);a>-1&&(this.openDialogs.splice(a,1),this.openDialogs.length||this._getAfterAllClosed().next())}),n}closeAll(){this._closeDialogs(this.openDialogs)}getDialogById(t){return this.openDialogs.find(e=>e.id===t)}ngOnDestroy(){this._closeDialogs(this._openDialogsAtThisLevel),this._afterAllClosedAtThisLevel.complete(),this._afterOpenedAtThisLevel.complete()}_closeDialogs(t){let e=t.length;for(;e--;)t[e].close()}static \u0275fac=function(e){return new(e||o)};static \u0275prov=D({token:o,factory:o.\u0275fac,providedIn:"root"})}return o})(),pi=(()=>{class o{dialogRef=l(gt,{optional:!0});_elementRef=l(I);_dialog=l(Yt);ariaLabel;type="button";dialogResult;_matDialogClose;constructor(){}ngOnInit(){this.dialogRef||(this.dialogRef=on(this._elementRef,this._dialog.openDialogs))}ngOnChanges(t){let e=t._matDialogClose||t._matDialogCloseResult;e&&(this.dialogResult=e.currentValue)}_onButtonClick(t){en(this.dialogRef,t.screenX===0&&t.screenY===0?"keyboard":"mouse",this.dialogResult)}static \u0275fac=function(e){return new(e||o)};static \u0275dir=L({type:o,selectors:[["","mat-dialog-close",""],["","matDialogClose",""]],hostVars:2,hostBindings:function(e,n){e&1&&tt("click",function(a){return n._onButtonClick(a)}),e&2&&m("aria-label",n.ariaLabel||null)("type",n.type)},inputs:{ariaLabel:[0,"aria-label","ariaLabel"],type:"type",dialogResult:[0,"mat-dialog-close","dialogResult"],_matDialogClose:[0,"matDialogClose","_matDialogClose"]},exportAs:["matDialogClose"],features:[de]})}return o})(),nn=(()=>{class o{_dialogRef=l(gt,{optional:!0});_elementRef=l(I);_dialog=l(Yt);constructor(){}ngOnInit(){this._dialogRef||(this._dialogRef=on(this._elementRef,this._dialog.openDialogs)),this._dialogRef&&Promise.resolve().then(()=>{this._onAdd()})}ngOnDestroy(){this._dialogRef?._containerInstance&&Promise.resolve().then(()=>{this._onRemove()})}static \u0275fac=function(e){return new(e||o)};static \u0275dir=L({type:o})}return o})(),fi=(()=>{class o extends nn{id=l(z).getId("mat-mdc-dialog-title-");_onAdd(){this._dialogRef._containerInstance?._addAriaLabelledBy?.(this.id)}_onRemove(){this._dialogRef?._containerInstance?._removeAriaLabelledBy?.(this.id)}static \u0275fac=(()=>{let t;return function(n){return(t||(t=vt(o)))(n||o)}})();static \u0275dir=L({type:o,selectors:[["","mat-dialog-title",""],["","matDialogTitle",""]],hostAttrs:[1,"mat-mdc-dialog-title","mdc-dialog__title"],hostVars:1,hostBindings:function(e,n){e&2&&Wt("id",n.id)},inputs:{id:"id"},exportAs:["matDialogTitle"],features:[Z]})}return o})(),_i=(()=>{class o{static \u0275fac=function(e){return new(e||o)};static \u0275dir=L({type:o,selectors:[["","mat-dialog-content",""],["mat-dialog-content"],["","matDialogContent",""]],hostAttrs:[1,"mat-mdc-dialog-content","mdc-dialog__content"],features:[he([we])]})}return o})(),bi=(()=>{class o extends nn{align;_onAdd(){this._dialogRef._containerInstance?._updateActionSectionCount?.(1)}_onRemove(){this._dialogRef._containerInstance?._updateActionSectionCount?.(-1)}static \u0275fac=(()=>{let t;return function(n){return(t||(t=vt(o)))(n||o)}})();static \u0275dir=L({type:o,selectors:[["","mat-dialog-actions",""],["mat-dialog-actions"],["","matDialogActions",""]],hostAttrs:[1,"mat-mdc-dialog-actions","mdc-dialog__actions"],hostVars:6,hostBindings:function(e,n){e&2&&O("mat-mdc-dialog-actions-align-start",n.align==="start")("mat-mdc-dialog-actions-align-center",n.align==="center")("mat-mdc-dialog-actions-align-end",n.align==="end")},inputs:{align:"align"},features:[Z]})}return o})();function on(o,s){let t=o.nativeElement.parentElement;for(;t&&!t.classList.contains("mat-mdc-dialog-container");)t=t.parentElement;return t?s.find(e=>e.id===t.id):null}var vi=(()=>{class o{static \u0275fac=function(e){return new(e||o)};static \u0275mod=E({type:o});static \u0275inj=x({providers:[Yt],imports:[Ke,Ft,dt,j]})}return o})();var zn=["button"],Gn=["*"];function Hn(o,s){if(o&1&&(v(0,"div",2),X(1,"mat-pseudo-checkbox",6),w()),o&2){let t=Ct();k(),B("disabled",t.disabled)}}var an=new h("MAT_BUTTON_TOGGLE_DEFAULT_OPTIONS",{providedIn:"root",factory:()=>({hideSingleSelectionIndicator:!1,hideMultipleSelectionIndicator:!1,disabledInteractive:!1})}),rn=new h("MatButtonToggleGroup"),Vn={provide:Me,useExisting:se(()=>Un),multi:!0},jt=class{source;value;constructor(s,t){this.source=s,this.value=t}},Un=(()=>{class o{_changeDetector=l(st);_dir=l(lt,{optional:!0});_multiple=!1;_disabled=!1;_disabledInteractive=!1;_selectionModel;_rawValue;_controlValueAccessorChangeFn=()=>{};_onTouched=()=>{};_buttonToggles;appearance;get name(){return this._name}set name(t){this._name=t,this._markButtonsForCheck()}_name=l(z).getId("mat-button-toggle-group-");vertical=!1;get value(){let t=this._selectionModel?this._selectionModel.selected:[];return this.multiple?t.map(e=>e.value):t[0]?t[0].value:void 0}set value(t){this._setSelectionByValue(t),this.valueChange.emit(this.value)}valueChange=new P;get selected(){let t=this._selectionModel?this._selectionModel.selected:[];return this.multiple?t:t[0]||null}get multiple(){return this._multiple}set multiple(t){this._multiple=t,this._markButtonsForCheck()}get disabled(){return this._disabled}set disabled(t){this._disabled=t,this._markButtonsForCheck()}get disabledInteractive(){return this._disabledInteractive}set disabledInteractive(t){this._disabledInteractive=t,this._markButtonsForCheck()}get dir(){return this._dir&&this._dir.value==="rtl"?"rtl":"ltr"}change=new P;get hideSingleSelectionIndicator(){return this._hideSingleSelectionIndicator}set hideSingleSelectionIndicator(t){this._hideSingleSelectionIndicator=t,this._markButtonsForCheck()}_hideSingleSelectionIndicator;get hideMultipleSelectionIndicator(){return this._hideMultipleSelectionIndicator}set hideMultipleSelectionIndicator(t){this._hideMultipleSelectionIndicator=t,this._markButtonsForCheck()}_hideMultipleSelectionIndicator;constructor(){let t=l(an,{optional:!0});this.appearance=t&&t.appearance?t.appearance:"standard",this._hideSingleSelectionIndicator=t?.hideSingleSelectionIndicator??!1,this._hideMultipleSelectionIndicator=t?.hideMultipleSelectionIndicator??!1}ngOnInit(){this._selectionModel=new ze(this.multiple,void 0,!1)}ngAfterContentInit(){this._selectionModel.select(...this._buttonToggles.filter(t=>t.checked)),this.multiple||this._initializeTabIndex()}writeValue(t){this.value=t,this._changeDetector.markForCheck()}registerOnChange(t){this._controlValueAccessorChangeFn=t}registerOnTouched(t){this._onTouched=t}setDisabledState(t){this.disabled=t}_keydown(t){if(this.multiple||this.disabled||nt(t))return;let n=t.target.id,i=this._buttonToggles.toArray().findIndex(r=>r.buttonId===n),a=null;switch(t.keyCode){case 32:case 13:a=this._buttonToggles.get(i)||null;break;case 38:a=this._getNextButton(i,-1);break;case 37:a=this._getNextButton(i,this.dir==="ltr"?-1:1);break;case 40:a=this._getNextButton(i,1);break;case 39:a=this._getNextButton(i,this.dir==="ltr"?1:-1);break;default:return}a&&(t.preventDefault(),a._onButtonClick(),a.focus())}_emitChangeEvent(t){let e=new jt(t,this.value);this._rawValue=e.value,this._controlValueAccessorChangeFn(e.value),this.change.emit(e)}_syncButtonToggle(t,e,n=!1,i=!1){!this.multiple&&this.selected&&!t.checked&&(this.selected.checked=!1),this._selectionModel?e?this._selectionModel.select(t):this._selectionModel.deselect(t):i=!0,i?Promise.resolve().then(()=>this._updateModelValue(t,n)):this._updateModelValue(t,n)}_isSelected(t){return this._selectionModel&&this._selectionModel.isSelected(t)}_isPrechecked(t){return typeof this._rawValue>"u"?!1:this.multiple&&Array.isArray(this._rawValue)?this._rawValue.some(e=>t.value!=null&&e===t.value):t.value===this._rawValue}_initializeTabIndex(){if(this._buttonToggles.forEach(t=>{t.tabIndex=-1}),this.selected)this.selected.tabIndex=0;else for(let t=0;tthis._selectValue(n,e))):(this._clearSelection(),this._selectValue(t,e)),!this.multiple&&e.every(n=>n.tabIndex===-1)){for(let n of e)if(!n.disabled){n.tabIndex=0;break}}}_clearSelection(){this._selectionModel.clear(),this._buttonToggles.forEach(t=>{t.checked=!1,this.multiple||(t.tabIndex=-1)})}_selectValue(t,e){for(let n of e)if(n.value===t){n.checked=!0,this._selectionModel.select(n),this.multiple||(n.tabIndex=0);break}}_updateModelValue(t,e){e&&this._emitChangeEvent(t),this.valueChange.emit(this.value)}_markButtonsForCheck(){this._buttonToggles?.forEach(t=>t._markForCheck())}static \u0275fac=function(e){return new(e||o)};static \u0275dir=L({type:o,selectors:[["mat-button-toggle-group"]],contentQueries:function(e,n,i){if(e&1&&_e(i,sn,5),e&2){let a;U(a=W())&&(n._buttonToggles=a)}},hostAttrs:[1,"mat-button-toggle-group"],hostVars:6,hostBindings:function(e,n){e&1&&tt("keydown",function(a){return n._keydown(a)}),e&2&&(m("role",n.multiple?"group":"radiogroup")("aria-disabled",n.disabled),O("mat-button-toggle-vertical",n.vertical)("mat-button-toggle-group-appearance-standard",n.appearance==="standard"))},inputs:{appearance:"appearance",name:"name",vertical:[2,"vertical","vertical",y],value:"value",multiple:[2,"multiple","multiple",y],disabled:[2,"disabled","disabled",y],disabledInteractive:[2,"disabledInteractive","disabledInteractive",y],hideSingleSelectionIndicator:[2,"hideSingleSelectionIndicator","hideSingleSelectionIndicator",y],hideMultipleSelectionIndicator:[2,"hideMultipleSelectionIndicator","hideMultipleSelectionIndicator",y]},outputs:{valueChange:"valueChange",change:"change"},exportAs:["matButtonToggleGroup"],features:[be([Vn,{provide:rn,useExisting:o}])]})}return o})(),sn=(()=>{class o{_changeDetectorRef=l(st);_elementRef=l(I);_focusMonitor=l(Rt);_idGenerator=l(z);_animationDisabled=ut();_checked=!1;ariaLabel;ariaLabelledby=null;_buttonElement;buttonToggleGroup;get buttonId(){return`${this.id}-button`}id;name;value;get tabIndex(){return this._tabIndex()}set tabIndex(t){this._tabIndex.set(t)}_tabIndex;disableRipple=!1;get appearance(){return this.buttonToggleGroup?this.buttonToggleGroup.appearance:this._appearance}set appearance(t){this._appearance=t}_appearance;get checked(){return this.buttonToggleGroup?this.buttonToggleGroup._isSelected(this):this._checked}set checked(t){t!==this._checked&&(this._checked=t,this.buttonToggleGroup&&this.buttonToggleGroup._syncButtonToggle(this,this._checked),this._changeDetectorRef.markForCheck())}get disabled(){return this._disabled||this.buttonToggleGroup&&this.buttonToggleGroup.disabled}set disabled(t){this._disabled=t}_disabled=!1;get disabledInteractive(){return this._disabledInteractive||this.buttonToggleGroup!==null&&this.buttonToggleGroup.disabledInteractive}set disabledInteractive(t){this._disabledInteractive=t}_disabledInteractive;change=new P;constructor(){l(Ie).load(Be);let t=l(rn,{optional:!0}),e=l(new Dt("tabindex"),{optional:!0})||"",n=l(an,{optional:!0});this._tabIndex=bt(parseInt(e)||0),this.buttonToggleGroup=t,this._appearance=n&&n.appearance?n.appearance:"standard",this._disabledInteractive=n?.disabledInteractive??!1}ngOnInit(){let t=this.buttonToggleGroup;this.id=this.id||this._idGenerator.getId("mat-button-toggle-"),t&&(t._isPrechecked(this)?this.checked=!0:t._isSelected(this)!==this._checked&&t._syncButtonToggle(this,this._checked))}ngAfterViewInit(){this._animationDisabled||this._elementRef.nativeElement.classList.add("mat-button-toggle-animations-enabled"),this._focusMonitor.monitor(this._elementRef,!0)}ngOnDestroy(){let t=this.buttonToggleGroup;this._focusMonitor.stopMonitoring(this._elementRef),t&&t._isSelected(this)&&t._syncButtonToggle(this,!1,!1,!0)}focus(t){this._buttonElement.nativeElement.focus(t)}_onButtonClick(){if(this.disabled)return;let t=this.isSingleSelector()?!0:!this._checked;if(t!==this._checked&&(this._checked=t,this.buttonToggleGroup&&(this.buttonToggleGroup._syncButtonToggle(this,this._checked,!0),this.buttonToggleGroup._onTouched())),this.isSingleSelector()){let e=this.buttonToggleGroup._buttonToggles.find(n=>n.tabIndex===0);e&&(e.tabIndex=-1),this.tabIndex=0}this.change.emit(new jt(this,this.value))}_markForCheck(){this._changeDetectorRef.markForCheck()}_getButtonName(){return this.isSingleSelector()?this.buttonToggleGroup.name:this.name||null}isSingleSelector(){return this.buttonToggleGroup&&!this.buttonToggleGroup.multiple}static \u0275fac=function(e){return new(e||o)};static \u0275cmp=A({type:o,selectors:[["mat-button-toggle"]],viewQuery:function(e,n){if(e&1&&et(zn,5),e&2){let i;U(i=W())&&(n._buttonElement=i.first)}},hostAttrs:["role","presentation",1,"mat-button-toggle"],hostVars:14,hostBindings:function(e,n){e&1&&tt("focus",function(){return n.focus()}),e&2&&(m("aria-label",null)("aria-labelledby",null)("id",n.id)("name",null),O("mat-button-toggle-standalone",!n.buttonToggleGroup)("mat-button-toggle-checked",n.checked)("mat-button-toggle-disabled",n.disabled)("mat-button-toggle-disabled-interactive",n.disabledInteractive)("mat-button-toggle-appearance-standard",n.appearance==="standard"))},inputs:{ariaLabel:[0,"aria-label","ariaLabel"],ariaLabelledby:[0,"aria-labelledby","ariaLabelledby"],id:"id",name:"name",value:"value",tabIndex:"tabIndex",disableRipple:[2,"disableRipple","disableRipple",y],appearance:"appearance",checked:[2,"checked","checked",y],disabled:[2,"disabled","disabled",y],disabledInteractive:[2,"disabledInteractive","disabledInteractive",y]},outputs:{change:"change"},exportAs:["matButtonToggle"],ngContentSelectors:Gn,decls:7,vars:13,consts:[["button",""],["type","button",1,"mat-button-toggle-button","mat-focus-indicator",3,"click","id","disabled"],[1,"mat-button-toggle-checkbox-wrapper"],[1,"mat-button-toggle-label-content"],[1,"mat-button-toggle-focus-overlay"],["matRipple","",1,"mat-button-toggle-ripple",3,"matRippleTrigger","matRippleDisabled"],["state","checked","aria-hidden","true","appearance","minimal",3,"disabled"]],template:function(e,n){if(e&1&&(St(),v(0,"button",1,0),tt("click",function(){return n._onButtonClick()}),pe(2,Hn,2,1,"div",2),v(3,"span",3),It(4),w()(),X(5,"span",4)(6,"span",5)),e&2){let i=kt(1);B("id",n.buttonId)("disabled",n.disabled&&!n.disabledInteractive||null),m("role",n.isSingleSelector()?"radio":"button")("tabindex",n.disabled&&!n.disabledInteractive?-1:n.tabIndex)("aria-pressed",n.isSingleSelector()?null:n.checked)("aria-checked",n.isSingleSelector()?n.checked:null)("name",n._getButtonName())("aria-label",n.ariaLabel)("aria-labelledby",n.ariaLabelledby)("aria-disabled",n.disabled&&n.disabledInteractive?"true":null),k(2),fe(n.buttonToggleGroup&&(!n.buttonToggleGroup.multiple&&!n.buttonToggleGroup.hideSingleSelectionIndicator||n.buttonToggleGroup.multiple&&!n.buttonToggleGroup.hideMultipleSelectionIndicator)?2:-1),k(4),B("matRippleTrigger",i)("matRippleDisabled",n.disableRipple||n.disabled)}},dependencies:[Le,Ge],styles:[`.mat-button-toggle-standalone, +.mat-button-toggle-group { + position: relative; + display: inline-flex; + flex-direction: row; + white-space: nowrap; + overflow: hidden; + -webkit-tap-highlight-color: transparent; + border-radius: var(--mat-button-toggle-legacy-shape); + transform: translateZ(0); +} +.mat-button-toggle-standalone:not([class*=mat-elevation-z]), +.mat-button-toggle-group:not([class*=mat-elevation-z]) { + box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12); +} +@media (forced-colors: active) { + .mat-button-toggle-standalone, + .mat-button-toggle-group { + outline: solid 1px; + } +} + +.mat-button-toggle-standalone.mat-button-toggle-appearance-standard, +.mat-button-toggle-group-appearance-standard { + border-radius: var(--mat-button-toggle-shape, var(--mat-sys-corner-extra-large)); + border: solid 1px var(--mat-button-toggle-divider-color, var(--mat-sys-outline)); +} +.mat-button-toggle-standalone.mat-button-toggle-appearance-standard .mat-pseudo-checkbox, +.mat-button-toggle-group-appearance-standard .mat-pseudo-checkbox { + --mat-pseudo-checkbox-minimal-selected-checkmark-color: var(--mat-button-toggle-selected-state-text-color, var(--mat-sys-on-secondary-container)); +} +.mat-button-toggle-standalone.mat-button-toggle-appearance-standard:not([class*=mat-elevation-z]), +.mat-button-toggle-group-appearance-standard:not([class*=mat-elevation-z]) { + box-shadow: none; +} +@media (forced-colors: active) { + .mat-button-toggle-standalone.mat-button-toggle-appearance-standard, + .mat-button-toggle-group-appearance-standard { + outline: 0; + } +} + +.mat-button-toggle-vertical { + flex-direction: column; +} +.mat-button-toggle-vertical .mat-button-toggle-label-content { + display: block; +} + +.mat-button-toggle { + white-space: nowrap; + position: relative; + color: var(--mat-button-toggle-legacy-text-color); + font-family: var(--mat-button-toggle-legacy-label-text-font); + font-size: var(--mat-button-toggle-legacy-label-text-size); + line-height: var(--mat-button-toggle-legacy-label-text-line-height); + font-weight: var(--mat-button-toggle-legacy-label-text-weight); + letter-spacing: var(--mat-button-toggle-legacy-label-text-tracking); + --mat-pseudo-checkbox-minimal-selected-checkmark-color: var(--mat-button-toggle-legacy-selected-state-text-color); +} +.mat-button-toggle.cdk-keyboard-focused .mat-button-toggle-focus-overlay { + opacity: var(--mat-button-toggle-legacy-focus-state-layer-opacity); +} +.mat-button-toggle .mat-icon svg { + vertical-align: top; +} + +.mat-button-toggle-checkbox-wrapper { + display: inline-block; + justify-content: flex-start; + align-items: center; + width: 0; + height: 18px; + line-height: 18px; + overflow: hidden; + box-sizing: border-box; + position: absolute; + top: 50%; + left: 16px; + transform: translate3d(0, -50%, 0); +} +[dir=rtl] .mat-button-toggle-checkbox-wrapper { + left: auto; + right: 16px; +} +.mat-button-toggle-appearance-standard .mat-button-toggle-checkbox-wrapper { + left: 12px; +} +[dir=rtl] .mat-button-toggle-appearance-standard .mat-button-toggle-checkbox-wrapper { + left: auto; + right: 12px; +} +.mat-button-toggle-checked .mat-button-toggle-checkbox-wrapper { + width: 18px; +} +.mat-button-toggle-animations-enabled .mat-button-toggle-checkbox-wrapper { + transition: width 150ms 45ms cubic-bezier(0.4, 0, 0.2, 1); +} +.mat-button-toggle-vertical .mat-button-toggle-checkbox-wrapper { + transition: none; +} + +.mat-button-toggle-checked { + color: var(--mat-button-toggle-legacy-selected-state-text-color); + background-color: var(--mat-button-toggle-legacy-selected-state-background-color); +} + +.mat-button-toggle-disabled { + pointer-events: none; + color: var(--mat-button-toggle-legacy-disabled-state-text-color); + background-color: var(--mat-button-toggle-legacy-disabled-state-background-color); + --mat-pseudo-checkbox-minimal-disabled-selected-checkmark-color: var(--mat-button-toggle-legacy-disabled-state-text-color); +} +.mat-button-toggle-disabled.mat-button-toggle-checked { + background-color: var(--mat-button-toggle-legacy-disabled-selected-state-background-color); +} + +.mat-button-toggle-disabled-interactive { + pointer-events: auto; +} + +.mat-button-toggle-appearance-standard { + color: var(--mat-button-toggle-text-color, var(--mat-sys-on-surface)); + background-color: var(--mat-button-toggle-background-color, transparent); + font-family: var(--mat-button-toggle-label-text-font, var(--mat-sys-label-large-font)); + font-size: var(--mat-button-toggle-label-text-size, var(--mat-sys-label-large-size)); + line-height: var(--mat-button-toggle-label-text-line-height, var(--mat-sys-label-large-line-height)); + font-weight: var(--mat-button-toggle-label-text-weight, var(--mat-sys-label-large-weight)); + letter-spacing: var(--mat-button-toggle-label-text-tracking, var(--mat-sys-label-large-tracking)); +} +.mat-button-toggle-group-appearance-standard .mat-button-toggle-appearance-standard + .mat-button-toggle-appearance-standard { + border-left: solid 1px var(--mat-button-toggle-divider-color, var(--mat-sys-outline)); +} +[dir=rtl] .mat-button-toggle-group-appearance-standard .mat-button-toggle-appearance-standard + .mat-button-toggle-appearance-standard { + border-left: none; + border-right: solid 1px var(--mat-button-toggle-divider-color, var(--mat-sys-outline)); +} +.mat-button-toggle-group-appearance-standard.mat-button-toggle-vertical .mat-button-toggle-appearance-standard + .mat-button-toggle-appearance-standard { + border-left: none; + border-right: none; + border-top: solid 1px var(--mat-button-toggle-divider-color, var(--mat-sys-outline)); +} +.mat-button-toggle-appearance-standard.mat-button-toggle-checked { + color: var(--mat-button-toggle-selected-state-text-color, var(--mat-sys-on-secondary-container)); + background-color: var(--mat-button-toggle-selected-state-background-color, var(--mat-sys-secondary-container)); +} +.mat-button-toggle-appearance-standard.mat-button-toggle-disabled { + color: var(--mat-button-toggle-disabled-state-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); + background-color: var(--mat-button-toggle-disabled-state-background-color, transparent); +} +.mat-button-toggle-appearance-standard.mat-button-toggle-disabled .mat-pseudo-checkbox { + --mat-pseudo-checkbox-minimal-disabled-selected-checkmark-color: var(--mat-button-toggle-disabled-selected-state-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); +} +.mat-button-toggle-appearance-standard.mat-button-toggle-disabled.mat-button-toggle-checked { + color: var(--mat-button-toggle-disabled-selected-state-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); + background-color: var(--mat-button-toggle-disabled-selected-state-background-color, color-mix(in srgb, var(--mat-sys-on-surface) 12%, transparent)); +} +.mat-button-toggle-appearance-standard .mat-button-toggle-focus-overlay { + background-color: var(--mat-button-toggle-state-layer-color, var(--mat-sys-on-surface)); +} +.mat-button-toggle-appearance-standard:hover .mat-button-toggle-focus-overlay { + opacity: var(--mat-button-toggle-hover-state-layer-opacity, var(--mat-sys-hover-state-layer-opacity)); +} +.mat-button-toggle-appearance-standard.cdk-keyboard-focused .mat-button-toggle-focus-overlay { + opacity: var(--mat-button-toggle-focus-state-layer-opacity, var(--mat-sys-focus-state-layer-opacity)); +} +@media (hover: none) { + .mat-button-toggle-appearance-standard:hover .mat-button-toggle-focus-overlay { + display: none; + } +} + +.mat-button-toggle-label-content { + -webkit-user-select: none; + user-select: none; + display: inline-block; + padding: 0 16px; + line-height: var(--mat-button-toggle-legacy-height); + position: relative; +} +.mat-button-toggle-appearance-standard .mat-button-toggle-label-content { + padding: 0 12px; + line-height: var(--mat-button-toggle-height, 40px); +} + +.mat-button-toggle-label-content > * { + vertical-align: middle; +} + +.mat-button-toggle-focus-overlay { + top: 0; + left: 0; + right: 0; + bottom: 0; + position: absolute; + border-radius: inherit; + pointer-events: none; + opacity: 0; + background-color: var(--mat-button-toggle-legacy-state-layer-color); +} + +@media (forced-colors: active) { + .mat-button-toggle-checked .mat-button-toggle-focus-overlay { + border-bottom: solid 500px; + opacity: 0.5; + height: 0; + } + .mat-button-toggle-checked:hover .mat-button-toggle-focus-overlay { + opacity: 0.6; + } + .mat-button-toggle-checked.mat-button-toggle-appearance-standard .mat-button-toggle-focus-overlay { + border-bottom: solid 500px; + } +} +.mat-button-toggle .mat-button-toggle-ripple { + top: 0; + left: 0; + right: 0; + bottom: 0; + position: absolute; + pointer-events: none; +} + +.mat-button-toggle-button { + border: 0; + background: none; + color: inherit; + padding: 0; + margin: 0; + font: inherit; + outline: none; + width: 100%; + cursor: pointer; +} +.mat-button-toggle-animations-enabled .mat-button-toggle-button { + transition: padding 150ms 45ms cubic-bezier(0.4, 0, 0.2, 1); +} +.mat-button-toggle-vertical .mat-button-toggle-button { + transition: none; +} +.mat-button-toggle-disabled .mat-button-toggle-button { + cursor: default; +} +.mat-button-toggle-button::-moz-focus-inner { + border: 0; +} +.mat-button-toggle-checked .mat-button-toggle-button:has(.mat-button-toggle-checkbox-wrapper) { + padding-left: 30px; +} +[dir=rtl] .mat-button-toggle-checked .mat-button-toggle-button:has(.mat-button-toggle-checkbox-wrapper) { + padding-left: 0; + padding-right: 30px; +} + +.mat-button-toggle-standalone.mat-button-toggle-appearance-standard { + --mat-focus-indicator-border-radius: var(--mat-button-toggle-shape, var(--mat-sys-corner-extra-large)); +} + +.mat-button-toggle-group-appearance-standard:not(.mat-button-toggle-vertical) .mat-button-toggle:last-of-type .mat-button-toggle-button::before { + border-top-right-radius: var(--mat-button-toggle-shape, var(--mat-sys-corner-extra-large)); + border-bottom-right-radius: var(--mat-button-toggle-shape, var(--mat-sys-corner-extra-large)); +} +.mat-button-toggle-group-appearance-standard:not(.mat-button-toggle-vertical) .mat-button-toggle:first-of-type .mat-button-toggle-button::before { + border-top-left-radius: var(--mat-button-toggle-shape, var(--mat-sys-corner-extra-large)); + border-bottom-left-radius: var(--mat-button-toggle-shape, var(--mat-sys-corner-extra-large)); +} + +.mat-button-toggle-group-appearance-standard.mat-button-toggle-vertical .mat-button-toggle:last-of-type .mat-button-toggle-button::before { + border-bottom-right-radius: var(--mat-button-toggle-shape, var(--mat-sys-corner-extra-large)); + border-bottom-left-radius: var(--mat-button-toggle-shape, var(--mat-sys-corner-extra-large)); +} +.mat-button-toggle-group-appearance-standard.mat-button-toggle-vertical .mat-button-toggle:first-of-type .mat-button-toggle-button::before { + border-top-right-radius: var(--mat-button-toggle-shape, var(--mat-sys-corner-extra-large)); + border-top-left-radius: var(--mat-button-toggle-shape, var(--mat-sys-corner-extra-large)); +} +`],encapsulation:2,changeDetection:0})}return o})(),Vi=(()=>{class o{static \u0275fac=function(e){return new(e||o)};static \u0275mod=E({type:o});static \u0275inj=x({imports:[je,sn,j]})}return o})();function Wn(o){return o.metadata&&(typeof o.metadata=="string"?JSON.parse(o.metadata):o.metadata).copilot_mode||null}function Wi(o){return Wn(o)==="troubleshooting_injection"}var it=(function(o){return o.NETWORK_ERROR="NETWORK_ERROR",o.PROJECT_NOT_OPENED="PROJECT_NOT_OPENED",o.LLM_NOT_CONFIGURED="LLM_NOT_CONFIGURED",o.SESSION_NOT_FOUND="SESSION_NOT_FOUND",o.UNAUTHORIZED="UNAUTHORIZED",o.UNKNOWN_ERROR="UNKNOWN_ERROR",o})(it||{});var zt=class{authToken;id;name;location;host;port;path;ubridge_path;status="stopped";protocol;username;password;tokenExpired=!1};var ln=(()=>{class o{httpController;controllerIds=[];serviceInitialized=new T;isServiceInitialized;constructor(t){this.httpController=t,this.controllerIds=this.getcontrollerIds(),this.isServiceInitialized=!0,this.serviceInitialized.next(this.isServiceInitialized)}getcontrollerIds(){let t=localStorage.getItem("controllerIds");if(t?.length>0){let e=t.split(",");return[...new Set(e)].filter(n=>n&&n.trim().length>0)}return[]}updatecontrollerIds(){localStorage.removeItem("controllerIds"),localStorage.setItem("controllerIds",this.controllerIds.toString())}get(t){let e=JSON.parse(localStorage.getItem(`controller-${t}`));return new Promise(i=>{i(e)})}create(t){if(this.findAllSync().some(r=>r.name===t.name))return Promise.reject(new Error(`Controller with name "${t.name}" already exists`));let n=this.controllerIds.map(r=>parseInt(r.replace("controller-",""),10)).filter(r=>!isNaN(r)),i=n.length>0?Math.max(...n):0;return t.id=i+1,localStorage.setItem(`controller-${t.id}`,JSON.stringify(t)),this.controllerIds.push(`controller-${t.id}`),this.updatecontrollerIds(),new Promise(r=>{r(t)})}findAllSync(){let t=[];return this.controllerIds.forEach(e=>{let n=localStorage.getItem(e);n&&t.push(JSON.parse(n))}),t}isControllerNameTaken(t){return this.findAllSync().some(n=>n.name===t)}update(t){return localStorage.removeItem(`controller-${t.id}`),localStorage.setItem(`controller-${t.id}`,JSON.stringify(t)),new Promise(n=>{n(t)})}findAll(){return new Promise(e=>{let n=[];this.controllerIds.forEach(i=>{let a=localStorage.getItem(i);if(a){let r=JSON.parse(a);n.push(r)}}),e(n)})}delete(t){return localStorage.removeItem(`controller-${t.id}`),this.controllerIds=this.controllerIds.filter(n=>n!==`controller-${t.id}`),this.updatecontrollerIds(),new Promise(n=>{n(t.id)})}getControllerUrl(t){return`${t.protocol}//${t.host}:${t.port}/`}checkControllerVersion(t){return this.httpController.get(t,"/version").pipe(ne(5e3),f(e=>{if(e.name==="TimeoutError"){let n=new Error("Connection timeout");return g(()=>n)}return g(()=>e)}))}getLocalController(t,e){return new Promise((i,a)=>{this.findAll().then(r=>{let d=r.find(c=>c.location==="bundled");if(d)d.host=t,d.port=e,d.protocol=location.protocol,this.update(d).then(c=>{i(c)},a);else{let c=new zt;c.name="local",c.host=t,c.port=e,c.location="bundled",c.protocol=location.protocol,this.create(c).then(C=>{i(C)},a)}},a)})}static \u0275fac=function(e){return new(e||o)(S(Nt))};static \u0275prov=D({token:o,factory:o.\u0275fac})}return o})();var ea=(()=>{class o{http;httpController;controllerService;currentProjectId=null;currentSessionId=null;isStreaming=new ee(!1);constructor(t,e,n){this.http=t,this.httpController=e,this.controllerService=n}injectFault(t,e,n){let i=`${this.getControllerUrl(t)}/v3/copilot/projects/${e}/chat/inject`,a=this.getAuthHeaders(t),r={"Content-Type":"application/json"};return a.keys().forEach(d=>{let c=a.get(d);c&&(r[d]=c)}),new Ht(d=>(fetch(i,{method:"POST",headers:r,body:JSON.stringify({message:n})}).then(async c=>{if(!c.ok){let u=`HTTP error! status: ${c.status}`;try{let b=await c.json();b.message&&(u=b.message)}catch{c.statusText&&(u=c.statusText)}let _=new Error(u);throw _.status=c.status,_.statusText=c.statusText,_.error={message:u},_}if(!c.body)throw new Error("Response body is null");let C=c.body.getReader(),Gt=new TextDecoder,H="";(async()=>{try{for(;;){let{done:u,value:_}=await C.read();if(u){d.complete();break}H+=Gt.decode(_,{stream:!0});let b=H.split(` +`);H=b.pop()||"";for(let at of b)if(at.startsWith("data: ")){let V=at.slice(6).trim();if(V)try{let p=JSON.parse(V);if(p.type==="heartbeat")continue;if(d.next(p),p.type==="done"||p.type==="error"){d.complete();break}}catch(p){console.error("Failed to parse SSE data:",V,p)}}if(d.closed)break}}catch(u){console.error("Stream processing error:",u),d.error(u)}finally{C.cancel()}})()}).catch(c=>{console.error("Fetch error:",c),d.error(c)}),()=>{})).pipe(f(d=>g(()=>d)))}streamChat(t,e,n){this.currentProjectId=e,this.isStreaming.next(!0);let i=`${this.getControllerUrl(t)}/v3/copilot/projects/${e}/chat/stream`,a=this.getAuthHeaders(t),r={"Content-Type":"application/json"};return a.keys().forEach(d=>{let c=a.get(d);c&&(r[d]=c)}),new Ht(d=>(fetch(i,{method:"POST",headers:r,body:JSON.stringify(n)}).then(async c=>{if(!c.ok){let u=`HTTP error! status: ${c.status}`;try{let b=await c.json();b.message&&(u=b.message)}catch{c.statusText&&(u=c.statusText)}let _=new Error(u);throw _.status=c.status,_.statusText=c.statusText,_.error={message:u},_}if(!c.body)throw new Error("Response body is null");let C=c.body.getReader(),Gt=new TextDecoder,H="";(async()=>{try{for(;;){let{done:u,value:_}=await C.read();if(u){d.complete();break}H+=Gt.decode(_,{stream:!0});let b=H.split(` +`);H=b.pop()||"";for(let at of b)if(at.startsWith("data: ")){let V=at.slice(6).trim();if(V)try{let p=JSON.parse(V);if(p.type==="heartbeat")continue;if(p.session_id&&(this.currentSessionId=p.session_id),d.next(p),p.type==="done"||p.type==="error"){d.complete();break}}catch(p){console.error("Failed to parse SSE data:",V,p)}}if(d.closed)break}}catch(u){console.error("Stream processing error:",u),d.error(u)}finally{C.cancel()}})()}).catch(c=>{console.error("Fetch error:",c),d.error(c)}).finally(()=>{this.isStreaming.next(!1)}),()=>{this.isStreaming.next(!1)})).pipe(f(d=>(this.isStreaming.next(!1),g(()=>d))))}getSessions(t,e){return this.httpController.get(t,`/copilot/projects/${e}/chat/sessions`).pipe(f(n=>(console.error("Failed to get sessions:",n),g(()=>n))))}getSessionHistory(t,e,n,i=100){let a=i?{limit:i}:void 0;return this.httpController.get(t,`/copilot/projects/${e}/chat/sessions/${n}/history`).pipe(f(r=>(console.error("Failed to get session history:",r),g(()=>r))))}renameSession(t,e,n,i){let a={title:i};return this.httpController.patch(t,`/copilot/projects/${e}/chat/sessions/${n}`,a).pipe(f(r=>(console.error("Failed to rename session:",r),g(()=>r))))}deleteSession(t,e,n){return this.httpController.delete(t,`/copilot/projects/${e}/chat/sessions/${n}`).pipe(f(i=>(console.error("Failed to delete session:",i),g(()=>i))))}pinSession(t,e,n){return this.httpController.put(t,`/copilot/projects/${e}/chat/sessions/${n}/pin`,null).pipe(f(i=>(console.error("Failed to pin session:",i),g(()=>i))))}unpinSession(t,e,n){return this.httpController.delete(t,`/copilot/projects/${e}/chat/sessions/${n}/pin`).pipe(f(i=>(console.error("Failed to unpin session:",i),g(()=>i))))}getStreamingState(){return this.isStreaming.asObservable()}abortChat(t,e,n){return this.httpController.post(t,`/copilot/projects/${e}/chat/sessions/${n}/abort`,null).pipe(f(i=>(console.error("Failed to abort chat:",i),g(()=>i))))}getCurrentSessionId(){return this.currentSessionId}resetCurrentSession(){this.currentSessionId=null,this.currentProjectId=null}reloadSkills(t){return this.httpController.post(t,"/copilot/reload/skills",null).pipe(f(e=>(console.error("Failed to reload skills:",e),g(()=>e))))}getControllerUrl(t){return`${t.protocol==="https:"?"https":"http"}://${t.host}:${t.port}`}getAuthHeaders(t){let e=new Ce;if(t.authToken)return e.set("Authorization",`Bearer ${t.authToken}`);if(t.username&&t.password){let n=btoa(`${t.username}:${t.password}`);return e.set("Authorization",`Basic ${n}`)}return e}createChatError(t){return t.status===401?{type:it.UNAUTHORIZED,message:"Unauthorized access",details:t}:t.status===404?{type:it.SESSION_NOT_FOUND,message:"Session not found",details:t}:t.message&&t.message.includes("fetch")?{type:it.NETWORK_ERROR,message:"Network connection failed",details:t}:{type:it.UNKNOWN_ERROR,message:t.message||"Unknown error",details:t}}static \u0275fac=function(e){return new(e||o)(S(At),S(Nt),S(ln))};static \u0275prov=D({token:o,factory:o.\u0275fac,providedIn:"root"})}return o})();export{io as a,ao as b,Qe as c,Do as d,xo as e,zt as f,ln as g,Jt as h,kn as i,Kt as j,Ke as k,gt as l,On as m,Yt as n,pi as o,fi as p,_i as q,bi as r,vi as s,Un as t,sn as u,Vi as v,Wi as w,ea as x}; diff --git a/gns3server/static/web-ui/chunk-EXPJ4N52.js b/gns3server/static/web-ui/chunk-EXPJ4N52.js new file mode 100644 index 000000000..5d95f3b08 --- /dev/null +++ b/gns3server/static/web-ui/chunk-EXPJ4N52.js @@ -0,0 +1,2825 @@ +import{$ as P,$a as v,$e as _t,Ab as ci,Ad as Ei,Ae as Wi,B as Ke,Bb as we,Bd as Fi,Be as Ie,Cb as ht,Cd as Oi,Ce as Qi,D as J,Da as ye,Db as Ce,Dd as se,De as K,E as dt,Ea as c,Eb as _,Ed as Me,Fa as rt,Fb as bt,Fd as Se,Fe as Gi,Ga as Dt,Gb as E,Gd as Pt,Hb as pt,He as Q,Ia as st,Ib as ft,Jb as di,K as Ze,Kb as mi,L as Xe,La as ee,Lb as hi,Ld as Ri,Ma as C,Mb as pi,Na as N,Nb as fi,Nd as Ai,Oa as x,Oc as wt,Qb as $,R as Je,Ra as q,Rb as ui,Rc as wi,Sa as tt,Sb as _i,Sd as Li,Se as De,T as ti,Ta as ai,Te as Yt,U as ei,Ua as oi,Uc as Gt,Ue as vt,V as nt,W as $t,Wa as ri,We as Ct,X as I,Xa as T,Xc as ot,Yc as ut,Ye as Kt,Za as g,Ze as qi,_b as Te,_e as Ui,a as X,aa as z,ae as Pi,ba as w,bb as xe,bc as At,bf as me,ca as at,cb as Et,cc as ie,cd as j,cf as Yi,da as r,db as Ft,eb as D,ed as Ci,ef as Ki,fb as l,fd as Ti,g as it,ga as R,gb as d,gc as Qt,gd as ne,h as Jt,ha as A,hb as M,i as k,ia as mt,ib as kt,ic as bi,j as St,jb as Ot,jd as ae,ka as G,kb as ke,kc as W,ke as le,la as It,ld as U,le as Bi,mc as S,me as zi,n as Ue,na as F,nc as gt,nd as qt,oa as H,ob as lt,oc as gi,od as oe,oe as Ni,p as te,pb as Rt,pd as Mi,pe as ji,qa as Z,qb as u,qc as vi,qe as Ut,ra as ii,rb as si,rd as Si,re as ce,sa as Wt,sb as p,sd as Lt,ta as Y,tb as B,tc as yi,td as Ii,u as Ht,ua as O,ub as y,uc as xi,ud as ct,ue as de,va as ni,vb as et,ve as Vi,wb as V,wd as re,we as Hi,xb as m,xd as Di,xe as $i,y as Ye,yb as h,yc as ki,zb as li}from"./chunk-TYGV4UPE.js";var Ee=class{_box;_destroyed=new k;_resizeSubject=new k;_resizeObserver;_elementObservables=new Map;constructor(s){this._box=s,typeof ResizeObserver<"u"&&(this._resizeObserver=new ResizeObserver(t=>this._resizeSubject.next(t)))}observe(s){return this._elementObservables.has(s)||this._elementObservables.set(s,new Jt(t=>{let e=this._resizeSubject.subscribe(t);return this._resizeObserver?.observe(s,{box:this._box}),()=>{this._resizeObserver?.unobserve(s),e.unsubscribe(),this._elementObservables.delete(s)}}).pipe(dt(t=>t.some(e=>e.target===s)),ti({bufferSize:1,refCount:!0}),I(this._destroyed))),this._elementObservables.get(s)}destroy(){this._destroyed.next(),this._destroyed.complete(),this._resizeSubject.complete(),this._elementObservables.clear()}},he=(()=>{class n{_cleanupErrorListener;_observers=new Map;_ngZone=r(H);constructor(){typeof ResizeObserver<"u"}ngOnDestroy(){for(let[,t]of this._observers)t.destroy();this._observers.clear(),this._cleanupErrorListener?.()}observe(t,e){let i=e?.box||"content-box";return this._observers.has(i)||this._observers.set(i,new Ee(i)),this._observers.get(i).observe(t)}static \u0275fac=function(e){return new(e||n)};static \u0275prov=P({token:n,factory:n.\u0275fac,providedIn:"root"})}return n})();var Bt=["*"];function ra(n,s){n&1&&y(0)}var tn=["tabListContainer"],en=["tabList"],nn=["tabListInner"],an=["nextPaginator"],on=["previousPaginator"],sa=["content"];function la(n,s){}var ca=["tabBodyWrapper"],da=["tabHeader"];function ma(n,s){}function ha(n,s){if(n&1&&tt(0,ma,0,0,"ng-template",12),n&2){let t=p().$implicit;D("cdkPortalOutlet",t.templateLabel)}}function pa(n,s){if(n&1&&E(0),n&2){let t=p().$implicit;pt(t.textLabel)}}function fa(n,s){if(n&1){let t=lt();l(0,"div",7,2),u("click",function(){let i=R(t),a=i.$implicit,o=i.$index,f=p(),b=ht(1);return A(f._handleClick(a,b,o))})("cdkFocusChange",function(i){let a=R(t).$index,o=p();return A(o._tabFocusChanged(i,a))}),M(2,"span",8)(3,"div",9),l(4,"span",10)(5,"span",11),g(6,ha,1,1,null,12)(7,pa,1,1),d()()()}if(n&2){let t=s.$implicit,e=s.$index,i=ht(1),a=p();bt(t.labelClass),_("mdc-tab--active",a.selectedIndex===e),D("id",a._getTabLabelId(t,e))("disabled",t.disabled)("fitInkBarToContent",a.fitInkBarToContent),T("tabIndex",a._getTabIndex(e))("aria-posinset",e+1)("aria-setsize",a._tabs.length)("aria-controls",a._getTabContentId(e))("aria-selected",a.selectedIndex===e)("aria-label",t.ariaLabel||null)("aria-labelledby",!t.ariaLabel&&t.ariaLabelledby?t.ariaLabelledby:null),c(3),D("matRippleTrigger",i)("matRippleDisabled",t.disabled||a.disableRipple),c(3),v(t.templateLabel?6:7)}}function ua(n,s){n&1&&y(0)}function _a(n,s){if(n&1){let t=lt();l(0,"mat-tab-body",13),u("_onCentered",function(){R(t);let i=p();return A(i._removeTabBodyWrapperHeight())})("_onCentering",function(i){R(t);let a=p();return A(a._setTabBodyWrapperHeight(i))})("_beforeCentering",function(i){R(t);let a=p();return A(a._bodyCentered(i))}),d()}if(n&2){let t=s.$implicit,e=s.$index,i=p();bt(t.bodyClass),D("id",i._getTabContentId(e))("content",t.content)("position",t.position)("animationDuration",i.animationDuration)("preserveContent",i.preserveContent),T("tabindex",i.contentTabIndex!=null&&i.selectedIndex===e?i.contentTabIndex:null)("aria-labelledby",i._getTabLabelId(t,e))("aria-hidden",i.selectedIndex!==e)}}var ba=["mat-tab-nav-bar",""],ga=["mat-tab-link",""],va=new w("MatTabContent"),ya=(()=>{class n{template=r(Dt);constructor(){}static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n,selectors:[["","matTabContent",""]],features:[$([{provide:va,useExisting:n}])]})}return n})(),xa=new w("MatTabLabel"),rn=new w("MAT_TAB"),ka=(()=>{class n extends Si{_closestTab=r(rn,{optional:!0});static \u0275fac=(()=>{let t;return function(i){return(t||(t=Y(n)))(i||n)}})();static \u0275dir=x({type:n,selectors:[["","mat-tab-label",""],["","matTabLabel",""]],features:[$([{provide:xa,useExisting:n}]),q]})}return n})(),sn=new w("MAT_TAB_GROUP"),Ae=(()=>{class n{_viewContainerRef=r(ee);_closestTabGroup=r(sn,{optional:!0});disabled=!1;get templateLabel(){return this._templateLabel}set templateLabel(t){this._setTemplateLabelInput(t)}_templateLabel;_explicitContent=void 0;_implicitContent;textLabel="";ariaLabel;ariaLabelledby;labelClass;bodyClass;id=null;_contentPortal=null;get content(){return this._contentPortal}_stateChanges=new k;position=null;origin=null;isActive=!1;constructor(){r(wt).load(Ct)}ngOnChanges(t){(t.hasOwnProperty("textLabel")||t.hasOwnProperty("disabled"))&&this._stateChanges.next()}ngOnDestroy(){this._stateChanges.complete()}ngOnInit(){this._contentPortal=new oe(this._explicitContent||this._implicitContent,this._viewContainerRef)}_setTemplateLabelInput(t){t&&t._closestTab===this&&(this._templateLabel=t)}static \u0275fac=function(e){return new(e||n)};static \u0275cmp=C({type:n,selectors:[["mat-tab"]],contentQueries:function(e,i,a){if(e&1&&et(a,ka,5)(a,ya,7,Dt),e&2){let o;m(o=h())&&(i.templateLabel=o.first),m(o=h())&&(i._explicitContent=o.first)}},viewQuery:function(e,i){if(e&1&&V(Dt,7),e&2){let a;m(a=h())&&(i._implicitContent=a.first)}},hostAttrs:["hidden",""],hostVars:1,hostBindings:function(e,i){e&2&&T("id",null)},inputs:{disabled:[2,"disabled","disabled",S],textLabel:[0,"label","textLabel"],ariaLabel:[0,"aria-label","ariaLabel"],ariaLabelledby:[0,"aria-labelledby","ariaLabelledby"],labelClass:"labelClass",bodyClass:"bodyClass",id:"id"},exportAs:["matTab"],features:[$([{provide:rn,useExisting:n}]),Wt],ngContentSelectors:Bt,decls:1,vars:0,template:function(e,i){e&1&&(B(),ai(0,ra,1,0,"ng-template"))},encapsulation:2})}return n})(),Fe="mdc-tab-indicator--active",Zi="mdc-tab-indicator--no-transition",pe=class{_items;_currentItem;constructor(s){this._items=s}hide(){this._items.forEach(s=>s.deactivateInkBar()),this._currentItem=void 0}alignToElement(s){let t=this._items.find(i=>i.elementRef.nativeElement===s),e=this._currentItem;if(t!==e&&(e?.deactivateInkBar(),t)){let i=e?.elementRef.nativeElement.getBoundingClientRect?.();t.activateInkBar(i),this._currentItem=t}}},ln=(()=>{class n{_elementRef=r(O);_inkBarElement=null;_inkBarContentElement=null;_fitToContent=!1;get fitInkBarToContent(){return this._fitToContent}set fitInkBarToContent(t){this._fitToContent!==t&&(this._fitToContent=t,this._inkBarElement&&this._appendInkBarElement())}activateInkBar(t){let e=this._elementRef.nativeElement;if(!t||!e.getBoundingClientRect||!this._inkBarContentElement){e.classList.add(Fe);return}let i=e.getBoundingClientRect(),a=t.width/i.width,o=t.left-i.left;e.classList.add(Zi),this._inkBarContentElement.style.setProperty("transform",`translateX(${o}px) scaleX(${a})`),e.getBoundingClientRect(),e.classList.remove(Zi),e.classList.add(Fe),this._inkBarContentElement.style.setProperty("transform","")}deactivateInkBar(){this._elementRef.nativeElement.classList.remove(Fe)}ngOnInit(){this._createInkBarElement()}ngOnDestroy(){this._inkBarElement?.remove(),this._inkBarElement=this._inkBarContentElement=null}_createInkBarElement(){let t=this._elementRef.nativeElement.ownerDocument||document,e=this._inkBarElement=t.createElement("span"),i=this._inkBarContentElement=t.createElement("span");e.className="mdc-tab-indicator",i.className="mdc-tab-indicator__content mdc-tab-indicator__content--underline",e.appendChild(this._inkBarContentElement),this._appendInkBarElement()}_appendInkBarElement(){this._inkBarElement;let t=this._fitToContent?this._elementRef.nativeElement.querySelector(".mdc-tab__content"):this._elementRef.nativeElement;t.appendChild(this._inkBarElement)}static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n,inputs:{fitInkBarToContent:[2,"fitInkBarToContent","fitInkBarToContent",S]}})}return n})();var cn=(()=>{class n extends ln{elementRef=r(O);disabled=!1;focus(){this.elementRef.nativeElement.focus()}getOffsetLeft(){return this.elementRef.nativeElement.offsetLeft}getOffsetWidth(){return this.elementRef.nativeElement.offsetWidth}static \u0275fac=(()=>{let t;return function(i){return(t||(t=Y(n)))(i||n)}})();static \u0275dir=x({type:n,selectors:[["","matTabLabelWrapper",""]],hostVars:3,hostBindings:function(e,i){e&2&&(T("aria-disabled",!!i.disabled),_("mat-mdc-tab-disabled",i.disabled))},inputs:{disabled:[2,"disabled","disabled",S]},features:[q]})}return n})(),Xi={passive:!0},wa=650,Ca=100,dn=(()=>{class n{_elementRef=r(O);_changeDetectorRef=r(W);_viewportRuler=r(ne);_dir=r(ut,{optional:!0});_ngZone=r(H);_platform=r(ot);_sharedResizeObserver=r(he);_injector=r(G);_renderer=r(st);_animationsDisabled=Q();_eventCleanups;_scrollDistance=0;_selectedIndexChanged=!1;_destroyed=new k;_showPaginationControls=!1;_disableScrollAfter=!0;_disableScrollBefore=!0;_tabLabelCount;_scrollDistanceChanged=!1;_keyManager;_currentTextContent;_stopScrolling=new k;disablePagination=!1;get selectedIndex(){return this._selectedIndex}set selectedIndex(t){let e=isNaN(t)?0:t;this._selectedIndex!=e&&(this._selectedIndexChanged=!0,this._selectedIndex=e,this._keyManager&&this._keyManager.updateActiveItem(e))}_selectedIndex=0;selectFocusedIndex=new F;indexFocused=new F;constructor(){this._eventCleanups=this._ngZone.runOutsideAngular(()=>[this._renderer.listen(this._elementRef.nativeElement,"mouseleave",()=>this._stopInterval())])}ngAfterViewInit(){this._eventCleanups.push(this._renderer.listen(this._previousPaginator.nativeElement,"touchstart",()=>this._handlePaginatorPress("before"),Xi),this._renderer.listen(this._nextPaginator.nativeElement,"touchstart",()=>this._handlePaginatorPress("after"),Xi))}ngAfterContentInit(){let t=this._dir?this._dir.change:te("ltr"),e=this._sharedResizeObserver.observe(this._elementRef.nativeElement).pipe(Ze(32),I(this._destroyed)),i=this._viewportRuler.change(150).pipe(I(this._destroyed)),a=()=>{this.updatePagination(),this._alignInkBarToSelectedTab()};this._keyManager=new $i(this._items).withHorizontalOrientation(this._getLayoutDirection()).withHomeAndEnd().withWrap().skipPredicate(()=>!1),this._keyManager.updateActiveItem(Math.max(this._selectedIndex,0)),rt(a,{injector:this._injector}),J(t,i,e,this._items.changes,this._itemsResized()).pipe(I(this._destroyed)).subscribe(()=>{this._ngZone.run(()=>{Promise.resolve().then(()=>{this._scrollDistance=Math.max(0,Math.min(this._getMaxScrollDistance(),this._scrollDistance)),a()})}),this._keyManager?.withHorizontalOrientation(this._getLayoutDirection())}),this._keyManager.change.subscribe(o=>{this.indexFocused.emit(o),this._setTabFocus(o)})}_itemsResized(){return typeof ResizeObserver!="function"?Ue:this._items.changes.pipe(nt(this._items),$t(t=>new Jt(e=>this._ngZone.runOutsideAngular(()=>{let i=new ResizeObserver(a=>e.next(a));return t.forEach(a=>i.observe(a.elementRef.nativeElement)),()=>{i.disconnect()}}))),ei(1),dt(t=>t.some(e=>e.contentRect.width>0&&e.contentRect.height>0)))}ngAfterContentChecked(){this._tabLabelCount!=this._items.length&&(this.updatePagination(),this._tabLabelCount=this._items.length,this._changeDetectorRef.markForCheck()),this._selectedIndexChanged&&(this._scrollToLabel(this._selectedIndex),this._checkScrollingControls(),this._alignInkBarToSelectedTab(),this._selectedIndexChanged=!1,this._changeDetectorRef.markForCheck()),this._scrollDistanceChanged&&(this._updateTabScrollPosition(),this._scrollDistanceChanged=!1,this._changeDetectorRef.markForCheck())}ngOnDestroy(){this._eventCleanups.forEach(t=>t()),this._keyManager?.destroy(),this._destroyed.next(),this._destroyed.complete(),this._stopScrolling.complete()}_handleKeydown(t){if(!ct(t))switch(t.keyCode){case 13:case 32:if(this.focusIndex!==this.selectedIndex){let e=this._items.get(this.focusIndex);e&&!e.disabled&&(this.selectFocusedIndex.emit(this.focusIndex),this._itemSelected(t))}break;default:this._keyManager?.onKeydown(t)}}_onContentChanges(){let t=this._elementRef.nativeElement.textContent;t!==this._currentTextContent&&(this._currentTextContent=t||"",this._ngZone.run(()=>{this.updatePagination(),this._alignInkBarToSelectedTab(),this._changeDetectorRef.markForCheck()}))}updatePagination(){this._checkPaginationEnabled(),this._checkScrollingControls(),this._updateTabScrollPosition()}get focusIndex(){return this._keyManager?this._keyManager.activeItemIndex:0}set focusIndex(t){!this._isValidIndex(t)||this.focusIndex===t||!this._keyManager||this._keyManager.setActiveItem(t)}_isValidIndex(t){return this._items?!!this._items.toArray()[t]:!0}_setTabFocus(t){if(this._showPaginationControls&&this._scrollToLabel(t),this._items&&this._items.length){this._items.toArray()[t].focus();let e=this._tabListContainer.nativeElement;this._getLayoutDirection()=="ltr"?e.scrollLeft=0:e.scrollLeft=e.scrollWidth-e.offsetWidth}}_getLayoutDirection(){return this._dir&&this._dir.value==="rtl"?"rtl":"ltr"}_updateTabScrollPosition(){if(this.disablePagination)return;let t=this.scrollDistance,e=this._getLayoutDirection()==="ltr"?-t:t;this._tabList.nativeElement.style.transform=`translateX(${Math.round(e)}px)`,(this._platform.TRIDENT||this._platform.EDGE)&&(this._tabListContainer.nativeElement.scrollLeft=0)}get scrollDistance(){return this._scrollDistance}set scrollDistance(t){this._scrollTo(t)}_scrollHeader(t){let e=this._tabListContainer.nativeElement.offsetWidth,i=(t=="before"?-1:1)*e/3;return this._scrollTo(this._scrollDistance+i)}_handlePaginatorClick(t){this._stopInterval(),this._scrollHeader(t)}_scrollToLabel(t){if(this.disablePagination)return;let e=this._items?this._items.toArray()[t]:null;if(!e)return;let i=this._tabListContainer.nativeElement.offsetWidth,{offsetLeft:a,offsetWidth:o}=e.elementRef.nativeElement,f,b;this._getLayoutDirection()=="ltr"?(f=a,b=f+o):(b=this._tabListInner.nativeElement.offsetWidth-a,f=b-o);let xt=this.scrollDistance,Mt=this.scrollDistance+i;fMt&&(this.scrollDistance+=Math.min(b-Mt,f-xt))}_checkPaginationEnabled(){if(this.disablePagination)this._showPaginationControls=!1;else{let t=this._tabListInner.nativeElement.scrollWidth,e=this._elementRef.nativeElement.offsetWidth,i=t-e>=5;i||(this.scrollDistance=0),i!==this._showPaginationControls&&(this._showPaginationControls=i,this._changeDetectorRef.markForCheck())}}_checkScrollingControls(){this.disablePagination?this._disableScrollAfter=this._disableScrollBefore=!0:(this._disableScrollBefore=this.scrollDistance==0,this._disableScrollAfter=this.scrollDistance==this._getMaxScrollDistance(),this._changeDetectorRef.markForCheck())}_getMaxScrollDistance(){let t=this._tabListInner.nativeElement.scrollWidth,e=this._tabListContainer.nativeElement.offsetWidth;return t-e||0}_alignInkBarToSelectedTab(){let t=this._items&&this._items.length?this._items.toArray()[this.selectedIndex]:null,e=t?t.elementRef.nativeElement:null;e?this._inkBar.alignToElement(e):this._inkBar.hide()}_stopInterval(){this._stopScrolling.next()}_handlePaginatorPress(t,e){e&&e.button!=null&&e.button!==0||(this._stopInterval(),Ke(wa,Ca).pipe(I(J(this._stopScrolling,this._destroyed))).subscribe(()=>{let{maxScrollDistance:i,distance:a}=this._scrollHeader(t);(a===0||a>=i)&&this._stopInterval()}))}_scrollTo(t){if(this.disablePagination)return{maxScrollDistance:0,distance:0};let e=this._getMaxScrollDistance();return this._scrollDistance=Math.max(0,Math.min(e,t)),this._scrollDistanceChanged=!0,this._checkScrollingControls(),{maxScrollDistance:e,distance:this._scrollDistance}}static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n,inputs:{disablePagination:[2,"disablePagination","disablePagination",S],selectedIndex:[2,"selectedIndex","selectedIndex",gt]},outputs:{selectFocusedIndex:"selectFocusedIndex",indexFocused:"indexFocused"}})}return n})(),Ta=(()=>{class n extends dn{_items;_tabListContainer;_tabList;_tabListInner;_nextPaginator;_previousPaginator;_inkBar;ariaLabel;ariaLabelledby;disableRipple=!1;ngAfterContentInit(){this._inkBar=new pe(this._items),super.ngAfterContentInit()}_itemSelected(t){t.preventDefault()}static \u0275fac=(()=>{let t;return function(i){return(t||(t=Y(n)))(i||n)}})();static \u0275cmp=C({type:n,selectors:[["mat-tab-header"]],contentQueries:function(e,i,a){if(e&1&&et(a,cn,4),e&2){let o;m(o=h())&&(i._items=o)}},viewQuery:function(e,i){if(e&1&&V(tn,7)(en,7)(nn,7)(an,5)(on,5),e&2){let a;m(a=h())&&(i._tabListContainer=a.first),m(a=h())&&(i._tabList=a.first),m(a=h())&&(i._tabListInner=a.first),m(a=h())&&(i._nextPaginator=a.first),m(a=h())&&(i._previousPaginator=a.first)}},hostAttrs:[1,"mat-mdc-tab-header"],hostVars:4,hostBindings:function(e,i){e&2&&_("mat-mdc-tab-header-pagination-controls-enabled",i._showPaginationControls)("mat-mdc-tab-header-rtl",i._getLayoutDirection()=="rtl")},inputs:{ariaLabel:[0,"aria-label","ariaLabel"],ariaLabelledby:[0,"aria-labelledby","ariaLabelledby"],disableRipple:[2,"disableRipple","disableRipple",S]},features:[q],ngContentSelectors:Bt,decls:13,vars:10,consts:[["previousPaginator",""],["tabListContainer",""],["tabList",""],["tabListInner",""],["nextPaginator",""],["mat-ripple","",1,"mat-mdc-tab-header-pagination","mat-mdc-tab-header-pagination-before",3,"click","mousedown","touchend","matRippleDisabled"],[1,"mat-mdc-tab-header-pagination-chevron"],[1,"mat-mdc-tab-label-container",3,"keydown"],["role","tablist",1,"mat-mdc-tab-list",3,"cdkObserveContent"],[1,"mat-mdc-tab-labels"],["mat-ripple","",1,"mat-mdc-tab-header-pagination","mat-mdc-tab-header-pagination-after",3,"mousedown","click","touchend","matRippleDisabled"]],template:function(e,i){e&1&&(B(),l(0,"div",5,0),u("click",function(){return i._handlePaginatorClick("before")})("mousedown",function(o){return i._handlePaginatorPress("before",o)})("touchend",function(){return i._stopInterval()}),M(2,"div",6),d(),l(3,"div",7,1),u("keydown",function(o){return i._handleKeydown(o)}),l(5,"div",8,2),u("cdkObserveContent",function(){return i._onContentChanges()}),l(7,"div",9,3),y(9),d()()(),l(10,"div",10,4),u("mousedown",function(o){return i._handlePaginatorPress("after",o)})("click",function(){return i._handlePaginatorClick("after")})("touchend",function(){return i._stopInterval()}),M(12,"div",6),d()),e&2&&(_("mat-mdc-tab-header-pagination-disabled",i._disableScrollBefore),D("matRippleDisabled",i._disableScrollBefore||i.disableRipple),c(3),_("_mat-animation-noopable",i._animationsDisabled),c(2),T("aria-label",i.ariaLabel||null)("aria-labelledby",i.ariaLabelledby||null),c(5),_("mat-mdc-tab-header-pagination-disabled",i._disableScrollAfter),D("matRippleDisabled",i._disableScrollAfter||i.disableRipple))},dependencies:[vt,Ut],styles:[`.mat-mdc-tab-header { + display: flex; + overflow: hidden; + position: relative; + flex-shrink: 0; +} + +.mdc-tab-indicator .mdc-tab-indicator__content { + transition-duration: var(--mat-tab-animation-duration, 250ms); +} + +.mat-mdc-tab-header-pagination { + -webkit-user-select: none; + user-select: none; + position: relative; + display: none; + justify-content: center; + align-items: center; + min-width: 32px; + cursor: pointer; + z-index: 2; + -webkit-tap-highlight-color: transparent; + touch-action: none; + box-sizing: content-box; + outline: 0; +} +.mat-mdc-tab-header-pagination::-moz-focus-inner { + border: 0; +} +.mat-mdc-tab-header-pagination .mat-ripple-element { + opacity: 0.12; + background-color: var(--mat-tab-inactive-ripple-color, var(--mat-sys-on-surface)); +} +.mat-mdc-tab-header-pagination-controls-enabled .mat-mdc-tab-header-pagination { + display: flex; +} + +.mat-mdc-tab-header-pagination-before, +.mat-mdc-tab-header-rtl .mat-mdc-tab-header-pagination-after { + padding-left: 4px; +} +.mat-mdc-tab-header-pagination-before .mat-mdc-tab-header-pagination-chevron, +.mat-mdc-tab-header-rtl .mat-mdc-tab-header-pagination-after .mat-mdc-tab-header-pagination-chevron { + transform: rotate(-135deg); +} + +.mat-mdc-tab-header-rtl .mat-mdc-tab-header-pagination-before, +.mat-mdc-tab-header-pagination-after { + padding-right: 4px; +} +.mat-mdc-tab-header-rtl .mat-mdc-tab-header-pagination-before .mat-mdc-tab-header-pagination-chevron, +.mat-mdc-tab-header-pagination-after .mat-mdc-tab-header-pagination-chevron { + transform: rotate(45deg); +} + +.mat-mdc-tab-header-pagination-chevron { + border-style: solid; + border-width: 2px 2px 0 0; + height: 8px; + width: 8px; + border-color: var(--mat-tab-pagination-icon-color, var(--mat-sys-on-surface)); +} + +.mat-mdc-tab-header-pagination-disabled { + box-shadow: none; + cursor: default; + pointer-events: none; +} +.mat-mdc-tab-header-pagination-disabled .mat-mdc-tab-header-pagination-chevron { + opacity: 0.4; +} + +.mat-mdc-tab-list { + flex-grow: 1; + position: relative; + transition: transform 500ms cubic-bezier(0.35, 0, 0.25, 1); +} +._mat-animation-noopable .mat-mdc-tab-list { + transition: none; +} + +.mat-mdc-tab-label-container { + display: flex; + flex-grow: 1; + overflow: hidden; + z-index: 1; + border-bottom-style: solid; + border-bottom-width: var(--mat-tab-divider-height, 1px); + border-bottom-color: var(--mat-tab-divider-color, var(--mat-sys-surface-variant)); +} +.mat-mdc-tab-group-inverted-header .mat-mdc-tab-label-container { + border-bottom: none; + border-top-style: solid; + border-top-width: var(--mat-tab-divider-height, 1px); + border-top-color: var(--mat-tab-divider-color, var(--mat-sys-surface-variant)); +} + +.mat-mdc-tab-labels { + display: flex; + flex: 1 0 auto; +} +[mat-align-tabs=center] > .mat-mdc-tab-header .mat-mdc-tab-labels { + justify-content: center; +} +[mat-align-tabs=end] > .mat-mdc-tab-header .mat-mdc-tab-labels { + justify-content: flex-end; +} +.cdk-drop-list .mat-mdc-tab-labels, .mat-mdc-tab-labels.cdk-drop-list { + min-height: var(--mat-tab-container-height, 48px); +} + +.mat-mdc-tab::before { + margin: 5px; +} +@media (forced-colors: active) { + .mat-mdc-tab[aria-disabled=true] { + color: GrayText; + } +} +`],encapsulation:2})}return n})(),mn=new w("MAT_TABS_CONFIG"),Ji=(()=>{class n extends Lt{_host=r(Oe);_ngZone=r(H);_centeringSub=it.EMPTY;_leavingSub=it.EMPTY;constructor(){super()}ngOnInit(){super.ngOnInit(),this._centeringSub=this._host._beforeCentering.pipe(nt(this._host._isCenterPosition())).subscribe(t=>{this._host._content&&t&&!this.hasAttached()&&this._ngZone.run(()=>{Promise.resolve().then(),this.attach(this._host._content)})}),this._leavingSub=this._host._afterLeavingCenter.subscribe(()=>{this._host.preserveContent||this._ngZone.run(()=>this.detach())})}ngOnDestroy(){super.ngOnDestroy(),this._centeringSub.unsubscribe(),this._leavingSub.unsubscribe()}static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n,selectors:[["","matTabBodyHost",""]],features:[q]})}return n})(),Oe=(()=>{class n{_elementRef=r(O);_dir=r(ut,{optional:!0});_ngZone=r(H);_injector=r(G);_renderer=r(st);_diAnimationsDisabled=Q();_eventCleanups;_initialized=!1;_fallbackTimer;_positionIndex;_dirChangeSubscription=it.EMPTY;_position;_previousPosition;_onCentering=new F;_beforeCentering=new F;_afterLeavingCenter=new F;_onCentered=new F(!0);_portalHost;_contentElement;_content;animationDuration="500ms";preserveContent=!1;set position(t){this._positionIndex=t,this._computePositionAnimationState()}constructor(){if(this._dir){let t=r(W);this._dirChangeSubscription=this._dir.change.subscribe(e=>{this._computePositionAnimationState(e),t.markForCheck()})}}ngOnInit(){this._bindTransitionEvents(),this._position==="center"&&(this._setActiveClass(!0),rt(()=>this._onCentering.emit(this._elementRef.nativeElement.clientHeight),{injector:this._injector})),this._initialized=!0}ngOnDestroy(){clearTimeout(this._fallbackTimer),this._eventCleanups?.forEach(t=>t()),this._dirChangeSubscription.unsubscribe()}_bindTransitionEvents(){this._ngZone.runOutsideAngular(()=>{let t=this._elementRef.nativeElement,e=i=>{i.target===this._contentElement?.nativeElement&&(this._elementRef.nativeElement.classList.remove("mat-tab-body-animating"),i.type==="transitionend"&&this._transitionDone())};this._eventCleanups=[this._renderer.listen(t,"transitionstart",i=>{i.target===this._contentElement?.nativeElement&&(this._elementRef.nativeElement.classList.add("mat-tab-body-animating"),this._transitionStarted())}),this._renderer.listen(t,"transitionend",e),this._renderer.listen(t,"transitioncancel",e)]})}_transitionStarted(){clearTimeout(this._fallbackTimer);let t=this._position==="center";this._beforeCentering.emit(t),t&&this._onCentering.emit(this._elementRef.nativeElement.clientHeight)}_transitionDone(){this._position==="center"?this._onCentered.emit():this._previousPosition==="center"&&this._afterLeavingCenter.emit()}_setActiveClass(t){this._elementRef.nativeElement.classList.toggle("mat-mdc-tab-body-active",t)}_getLayoutDirection(){return this._dir&&this._dir.value==="rtl"?"rtl":"ltr"}_isCenterPosition(){return this._positionIndex===0}_computePositionAnimationState(t=this._getLayoutDirection()){this._previousPosition=this._position,this._positionIndex<0?this._position=t=="ltr"?"left":"right":this._positionIndex>0?this._position=t=="ltr"?"right":"left":this._position="center",this._animationsDisabled()?this._simulateTransitionEvents():this._initialized&&(this._position==="center"||this._previousPosition==="center")&&(clearTimeout(this._fallbackTimer),this._fallbackTimer=this._ngZone.runOutsideAngular(()=>setTimeout(()=>this._simulateTransitionEvents(),100)))}_simulateTransitionEvents(){this._transitionStarted(),rt(()=>this._transitionDone(),{injector:this._injector})}_animationsDisabled(){return this._diAnimationsDisabled||this.animationDuration==="0ms"||this.animationDuration==="0s"}static \u0275fac=function(e){return new(e||n)};static \u0275cmp=C({type:n,selectors:[["mat-tab-body"]],viewQuery:function(e,i){if(e&1&&V(Ji,5)(sa,5),e&2){let a;m(a=h())&&(i._portalHost=a.first),m(a=h())&&(i._contentElement=a.first)}},hostAttrs:[1,"mat-mdc-tab-body"],hostVars:1,hostBindings:function(e,i){e&2&&T("inert",i._position==="center"?null:"")},inputs:{_content:[0,"content","_content"],animationDuration:"animationDuration",preserveContent:"preserveContent",position:"position"},outputs:{_onCentering:"_onCentering",_beforeCentering:"_beforeCentering",_onCentered:"_onCentered"},decls:3,vars:6,consts:[["content",""],["cdkScrollable","",1,"mat-mdc-tab-body-content"],["matTabBodyHost",""]],template:function(e,i){e&1&&(l(0,"div",1,0),tt(2,la,0,0,"ng-template",2),d()),e&2&&_("mat-tab-body-content-left",i._position==="left")("mat-tab-body-content-right",i._position==="right")("mat-tab-body-content-can-animate",i._position==="center"||i._previousPosition==="center")},dependencies:[Ji,Ti],styles:[`.mat-mdc-tab-body { + top: 0; + left: 0; + right: 0; + bottom: 0; + position: absolute; + display: block; + overflow: hidden; + outline: 0; + flex-basis: 100%; +} +.mat-mdc-tab-body.mat-mdc-tab-body-active { + position: relative; + overflow-x: hidden; + overflow-y: auto; + z-index: 1; + flex-grow: 1; +} +.mat-mdc-tab-group.mat-mdc-tab-group-dynamic-height .mat-mdc-tab-body.mat-mdc-tab-body-active { + overflow-y: hidden; +} + +.mat-mdc-tab-body-content { + height: 100%; + overflow: auto; + transform: none; + visibility: hidden; +} +.mat-tab-body-animating > .mat-mdc-tab-body-content, .mat-mdc-tab-body-active > .mat-mdc-tab-body-content { + visibility: visible; +} +.mat-tab-body-animating > .mat-mdc-tab-body-content { + min-height: 1px; +} +.mat-mdc-tab-group-dynamic-height .mat-mdc-tab-body-content { + overflow: hidden; +} + +.mat-tab-body-content-can-animate { + transition: transform var(--mat-tab-animation-duration) 1ms cubic-bezier(0.35, 0, 0.25, 1); +} +.mat-mdc-tab-body-wrapper._mat-animation-noopable .mat-tab-body-content-can-animate { + transition: none; +} + +.mat-tab-body-content-left { + transform: translate3d(-100%, 0, 0); +} + +.mat-tab-body-content-right { + transform: translate3d(100%, 0, 0); +} +`],encapsulation:2})}return n})(),hn=(()=>{class n{_elementRef=r(O);_changeDetectorRef=r(W);_ngZone=r(H);_tabsSubscription=it.EMPTY;_tabLabelSubscription=it.EMPTY;_tabBodySubscription=it.EMPTY;_diAnimationsDisabled=Q();_allTabs;_tabBodies;_tabBodyWrapper;_tabHeader;_tabs=new ni;_indexToSelect=0;_lastFocusedTabIndex=null;_tabBodyWrapperHeight=0;color;get fitInkBarToContent(){return this._fitInkBarToContent}set fitInkBarToContent(t){this._fitInkBarToContent=t,this._changeDetectorRef.markForCheck()}_fitInkBarToContent=!1;stretchTabs=!0;alignTabs=null;dynamicHeight=!1;get selectedIndex(){return this._selectedIndex}set selectedIndex(t){this._indexToSelect=isNaN(t)?null:t}_selectedIndex=null;headerPosition="above";get animationDuration(){return this._animationDuration}set animationDuration(t){let e=t+"";this._animationDuration=/^\d+$/.test(e)?t+"ms":e}_animationDuration;get contentTabIndex(){return this._contentTabIndex}set contentTabIndex(t){this._contentTabIndex=isNaN(t)?null:t}_contentTabIndex=null;disablePagination=!1;disableRipple=!1;preserveContent=!1;get backgroundColor(){return this._backgroundColor}set backgroundColor(t){let e=this._elementRef.nativeElement.classList;e.remove("mat-tabs-with-background",`mat-background-${this.backgroundColor}`),t&&e.add("mat-tabs-with-background",`mat-background-${t}`),this._backgroundColor=t}_backgroundColor;ariaLabel;ariaLabelledby;selectedIndexChange=new F;focusChange=new F;animationDone=new F;selectedTabChange=new F(!0);_groupId;_isServer=!r(ot).isBrowser;constructor(){let t=r(mn,{optional:!0});this._groupId=r(U).getId("mat-tab-group-"),this.animationDuration=t&&t.animationDuration?t.animationDuration:"500ms",this.disablePagination=t&&t.disablePagination!=null?t.disablePagination:!1,this.dynamicHeight=t&&t.dynamicHeight!=null?t.dynamicHeight:!1,t?.contentTabIndex!=null&&(this.contentTabIndex=t.contentTabIndex),this.preserveContent=!!t?.preserveContent,this.fitInkBarToContent=t&&t.fitInkBarToContent!=null?t.fitInkBarToContent:!1,this.stretchTabs=t&&t.stretchTabs!=null?t.stretchTabs:!0,this.alignTabs=t&&t.alignTabs!=null?t.alignTabs:null}ngAfterContentChecked(){let t=this._indexToSelect=this._clampTabIndex(this._indexToSelect);if(this._selectedIndex!=t){let e=this._selectedIndex==null;if(!e){this.selectedTabChange.emit(this._createChangeEvent(t));let i=this._tabBodyWrapper.nativeElement;i.style.minHeight=i.clientHeight+"px"}Promise.resolve().then(()=>{this._tabs.forEach((i,a)=>i.isActive=a===t),e||(this.selectedIndexChange.emit(t),this._tabBodyWrapper.nativeElement.style.minHeight="")})}this._tabs.forEach((e,i)=>{e.position=i-t,this._selectedIndex!=null&&e.position==0&&!e.origin&&(e.origin=t-this._selectedIndex)}),this._selectedIndex!==t&&(this._selectedIndex=t,this._lastFocusedTabIndex=null,this._changeDetectorRef.markForCheck())}ngAfterContentInit(){this._subscribeToAllTabChanges(),this._subscribeToTabLabels(),this._tabsSubscription=this._tabs.changes.subscribe(()=>{let t=this._clampTabIndex(this._indexToSelect);if(t===this._selectedIndex){let e=this._tabs.toArray(),i;for(let a=0;a{e[t].isActive=!0,this.selectedTabChange.emit(this._createChangeEvent(t))})}this._changeDetectorRef.markForCheck()})}ngAfterViewInit(){this._tabBodySubscription=this._tabBodies.changes.subscribe(()=>this._bodyCentered(!0))}_subscribeToAllTabChanges(){this._allTabs.changes.pipe(nt(this._allTabs)).subscribe(t=>{this._tabs.reset(t.filter(e=>e._closestTabGroup===this||!e._closestTabGroup)),this._tabs.notifyOnChanges()})}ngOnDestroy(){this._tabs.destroy(),this._tabsSubscription.unsubscribe(),this._tabLabelSubscription.unsubscribe(),this._tabBodySubscription.unsubscribe()}realignInkBar(){this._tabHeader&&this._tabHeader._alignInkBarToSelectedTab()}updatePagination(){this._tabHeader&&this._tabHeader.updatePagination()}focusTab(t){let e=this._tabHeader;e&&(e.focusIndex=t)}_focusChanged(t){this._lastFocusedTabIndex=t,this.focusChange.emit(this._createChangeEvent(t))}_createChangeEvent(t){let e=new Re;return e.index=t,this._tabs&&this._tabs.length&&(e.tab=this._tabs.toArray()[t]),e}_subscribeToTabLabels(){this._tabLabelSubscription&&this._tabLabelSubscription.unsubscribe(),this._tabLabelSubscription=J(...this._tabs.map(t=>t._stateChanges)).subscribe(()=>this._changeDetectorRef.markForCheck())}_clampTabIndex(t){return Math.min(this._tabs.length-1,Math.max(t||0,0))}_getTabLabelId(t,e){return t.id||`${this._groupId}-label-${e}`}_getTabContentId(t){return`${this._groupId}-content-${t}`}_setTabBodyWrapperHeight(t){if(!this.dynamicHeight||!this._tabBodyWrapperHeight){this._tabBodyWrapperHeight=t;return}let e=this._tabBodyWrapper.nativeElement;e.style.height=this._tabBodyWrapperHeight+"px",this._tabBodyWrapper.nativeElement.offsetHeight&&(e.style.height=t+"px")}_removeTabBodyWrapperHeight(){let t=this._tabBodyWrapper.nativeElement;this._tabBodyWrapperHeight=t.clientHeight,t.style.height="",this._ngZone.run(()=>this.animationDone.emit())}_handleClick(t,e,i){e.focusIndex=i,t.disabled||(this.selectedIndex=i)}_getTabIndex(t){let e=this._lastFocusedTabIndex??this.selectedIndex;return t===e?0:-1}_tabFocusChanged(t,e){t&&t!=="mouse"&&t!=="touch"&&(this._tabHeader.focusIndex=e)}_bodyCentered(t){t&&this._tabBodies?.forEach((e,i)=>e._setActiveClass(i===this._selectedIndex))}_animationsDisabled(){return this._diAnimationsDisabled||this.animationDuration==="0"||this.animationDuration==="0ms"}static \u0275fac=function(e){return new(e||n)};static \u0275cmp=C({type:n,selectors:[["mat-tab-group"]],contentQueries:function(e,i,a){if(e&1&&et(a,Ae,5),e&2){let o;m(o=h())&&(i._allTabs=o)}},viewQuery:function(e,i){if(e&1&&V(ca,5)(da,5)(Oe,5),e&2){let a;m(a=h())&&(i._tabBodyWrapper=a.first),m(a=h())&&(i._tabHeader=a.first),m(a=h())&&(i._tabBodies=a)}},hostAttrs:[1,"mat-mdc-tab-group"],hostVars:11,hostBindings:function(e,i){e&2&&(T("mat-align-tabs",i.alignTabs),bt("mat-"+(i.color||"primary")),Ce("--mat-tab-animation-duration",i.animationDuration),_("mat-mdc-tab-group-dynamic-height",i.dynamicHeight)("mat-mdc-tab-group-inverted-header",i.headerPosition==="below")("mat-mdc-tab-group-stretch-tabs",i.stretchTabs))},inputs:{color:"color",fitInkBarToContent:[2,"fitInkBarToContent","fitInkBarToContent",S],stretchTabs:[2,"mat-stretch-tabs","stretchTabs",S],alignTabs:[0,"mat-align-tabs","alignTabs"],dynamicHeight:[2,"dynamicHeight","dynamicHeight",S],selectedIndex:[2,"selectedIndex","selectedIndex",gt],headerPosition:"headerPosition",animationDuration:"animationDuration",contentTabIndex:[2,"contentTabIndex","contentTabIndex",gt],disablePagination:[2,"disablePagination","disablePagination",S],disableRipple:[2,"disableRipple","disableRipple",S],preserveContent:[2,"preserveContent","preserveContent",S],backgroundColor:"backgroundColor",ariaLabel:[0,"aria-label","ariaLabel"],ariaLabelledby:[0,"aria-labelledby","ariaLabelledby"]},outputs:{selectedIndexChange:"selectedIndexChange",focusChange:"focusChange",animationDone:"animationDone",selectedTabChange:"selectedTabChange"},exportAs:["matTabGroup"],features:[$([{provide:sn,useExisting:n}])],ngContentSelectors:Bt,decls:9,vars:8,consts:[["tabHeader",""],["tabBodyWrapper",""],["tabNode",""],[3,"indexFocused","selectFocusedIndex","selectedIndex","disableRipple","disablePagination","aria-label","aria-labelledby"],["role","tab","matTabLabelWrapper","","cdkMonitorElementFocus","",1,"mdc-tab","mat-mdc-tab","mat-focus-indicator",3,"id","mdc-tab--active","class","disabled","fitInkBarToContent"],[1,"mat-mdc-tab-body-wrapper"],["role","tabpanel",3,"id","class","content","position","animationDuration","preserveContent"],["role","tab","matTabLabelWrapper","","cdkMonitorElementFocus","",1,"mdc-tab","mat-mdc-tab","mat-focus-indicator",3,"click","cdkFocusChange","id","disabled","fitInkBarToContent"],[1,"mdc-tab__ripple"],["mat-ripple","",1,"mat-mdc-tab-ripple",3,"matRippleTrigger","matRippleDisabled"],[1,"mdc-tab__content"],[1,"mdc-tab__text-label"],[3,"cdkPortalOutlet"],["role","tabpanel",3,"_onCentered","_onCentering","_beforeCentering","id","content","position","animationDuration","preserveContent"]],template:function(e,i){e&1&&(B(),l(0,"mat-tab-header",3,0),u("indexFocused",function(o){return i._focusChanged(o)})("selectFocusedIndex",function(o){return i.selectedIndex=o}),Et(2,fa,8,17,"div",4,xe),d(),g(4,ua,1,0),l(5,"div",5,1),Et(7,_a,1,10,"mat-tab-body",6,xe),d()),e&2&&(D("selectedIndex",i.selectedIndex||0)("disableRipple",i.disableRipple)("disablePagination",i.disablePagination),ri("aria-label",i.ariaLabel)("aria-labelledby",i.ariaLabelledby),c(2),Ft(i._tabs),c(2),v(i._isServer?4:-1),c(),_("_mat-animation-noopable",i._animationsDisabled()),c(2),Ft(i._tabs))},dependencies:[Ta,cn,Bi,vt,Lt,Oe],styles:[`.mdc-tab { + min-width: 90px; + padding: 0 24px; + display: flex; + flex: 1 0 auto; + justify-content: center; + box-sizing: border-box; + border: none; + outline: none; + text-align: center; + white-space: nowrap; + cursor: pointer; + z-index: 1; + touch-action: manipulation; +} + +.mdc-tab__content { + display: flex; + align-items: center; + justify-content: center; + height: inherit; + pointer-events: none; +} + +.mdc-tab__text-label { + transition: 150ms color linear; + display: inline-block; + line-height: 1; + z-index: 2; +} + +.mdc-tab--active .mdc-tab__text-label { + transition-delay: 100ms; +} + +._mat-animation-noopable .mdc-tab__text-label { + transition: none; +} + +.mdc-tab-indicator { + display: flex; + position: absolute; + top: 0; + left: 0; + justify-content: center; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 1; +} + +.mdc-tab-indicator__content { + transition: var(--mat-tab-animation-duration, 250ms) transform cubic-bezier(0.4, 0, 0.2, 1); + transform-origin: left; + opacity: 0; +} + +.mdc-tab-indicator__content--underline { + align-self: flex-end; + box-sizing: border-box; + width: 100%; + border-top-style: solid; +} + +.mdc-tab-indicator--active .mdc-tab-indicator__content { + opacity: 1; +} + +._mat-animation-noopable .mdc-tab-indicator__content, .mdc-tab-indicator--no-transition .mdc-tab-indicator__content { + transition: none; +} + +.mat-mdc-tab-ripple.mat-mdc-tab-ripple { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + pointer-events: none; +} + +.mat-mdc-tab { + -webkit-tap-highlight-color: transparent; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-decoration: none; + background: none; + height: var(--mat-tab-container-height, 48px); + font-family: var(--mat-tab-label-text-font, var(--mat-sys-title-small-font)); + font-size: var(--mat-tab-label-text-size, var(--mat-sys-title-small-size)); + letter-spacing: var(--mat-tab-label-text-tracking, var(--mat-sys-title-small-tracking)); + line-height: var(--mat-tab-label-text-line-height, var(--mat-sys-title-small-line-height)); + font-weight: var(--mat-tab-label-text-weight, var(--mat-sys-title-small-weight)); +} +.mat-mdc-tab.mdc-tab { + flex-grow: 0; +} +.mat-mdc-tab .mdc-tab-indicator__content--underline { + border-color: var(--mat-tab-active-indicator-color, var(--mat-sys-primary)); + border-top-width: var(--mat-tab-active-indicator-height, 2px); + border-radius: var(--mat-tab-active-indicator-shape, 0); +} +.mat-mdc-tab:hover .mdc-tab__text-label { + color: var(--mat-tab-inactive-hover-label-text-color, var(--mat-sys-on-surface)); +} +.mat-mdc-tab:focus .mdc-tab__text-label { + color: var(--mat-tab-inactive-focus-label-text-color, var(--mat-sys-on-surface)); +} +.mat-mdc-tab.mdc-tab--active .mdc-tab__text-label { + color: var(--mat-tab-active-label-text-color, var(--mat-sys-on-surface)); +} +.mat-mdc-tab.mdc-tab--active .mdc-tab__ripple::before, +.mat-mdc-tab.mdc-tab--active .mat-ripple-element { + background-color: var(--mat-tab-active-ripple-color, var(--mat-sys-on-surface)); +} +.mat-mdc-tab.mdc-tab--active:hover .mdc-tab__text-label { + color: var(--mat-tab-active-hover-label-text-color, var(--mat-sys-on-surface)); +} +.mat-mdc-tab.mdc-tab--active:hover .mdc-tab-indicator__content--underline { + border-color: var(--mat-tab-active-hover-indicator-color, var(--mat-sys-primary)); +} +.mat-mdc-tab.mdc-tab--active:focus .mdc-tab__text-label { + color: var(--mat-tab-active-focus-label-text-color, var(--mat-sys-on-surface)); +} +.mat-mdc-tab.mdc-tab--active:focus .mdc-tab-indicator__content--underline { + border-color: var(--mat-tab-active-focus-indicator-color, var(--mat-sys-primary)); +} +.mat-mdc-tab.mat-mdc-tab-disabled { + opacity: 0.4; + pointer-events: none; +} +.mat-mdc-tab.mat-mdc-tab-disabled .mdc-tab__content { + pointer-events: none; +} +.mat-mdc-tab.mat-mdc-tab-disabled .mdc-tab__ripple::before, +.mat-mdc-tab.mat-mdc-tab-disabled .mat-ripple-element { + background-color: var(--mat-tab-disabled-ripple-color, var(--mat-sys-on-surface-variant)); +} +.mat-mdc-tab .mdc-tab__ripple::before { + content: ""; + display: block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + opacity: 0; + pointer-events: none; + background-color: var(--mat-tab-inactive-ripple-color, var(--mat-sys-on-surface)); +} +.mat-mdc-tab .mdc-tab__text-label { + color: var(--mat-tab-inactive-label-text-color, var(--mat-sys-on-surface)); + display: inline-flex; + align-items: center; +} +.mat-mdc-tab .mdc-tab__content { + position: relative; + pointer-events: auto; +} +.mat-mdc-tab:hover .mdc-tab__ripple::before { + opacity: 0.04; +} +.mat-mdc-tab.cdk-program-focused .mdc-tab__ripple::before, .mat-mdc-tab.cdk-keyboard-focused .mdc-tab__ripple::before { + opacity: 0.12; +} +.mat-mdc-tab .mat-ripple-element { + opacity: 0.12; + background-color: var(--mat-tab-inactive-ripple-color, var(--mat-sys-on-surface)); +} +.mat-mdc-tab-group.mat-mdc-tab-group-stretch-tabs > .mat-mdc-tab-header .mat-mdc-tab { + flex-grow: 1; +} + +.mat-mdc-tab-group { + display: flex; + flex-direction: column; + max-width: 100%; +} +.mat-mdc-tab-group.mat-tabs-with-background > .mat-mdc-tab-header, .mat-mdc-tab-group.mat-tabs-with-background > .mat-mdc-tab-header-pagination { + background-color: var(--mat-tab-background-color); +} +.mat-mdc-tab-group.mat-tabs-with-background.mat-primary > .mat-mdc-tab-header .mat-mdc-tab .mdc-tab__text-label { + color: var(--mat-tab-foreground-color); +} +.mat-mdc-tab-group.mat-tabs-with-background.mat-primary > .mat-mdc-tab-header .mdc-tab-indicator__content--underline { + border-color: var(--mat-tab-foreground-color); +} +.mat-mdc-tab-group.mat-tabs-with-background:not(.mat-primary) > .mat-mdc-tab-header .mat-mdc-tab:not(.mdc-tab--active) .mdc-tab__text-label { + color: var(--mat-tab-foreground-color); +} +.mat-mdc-tab-group.mat-tabs-with-background:not(.mat-primary) > .mat-mdc-tab-header .mat-mdc-tab:not(.mdc-tab--active) .mdc-tab-indicator__content--underline { + border-color: var(--mat-tab-foreground-color); +} +.mat-mdc-tab-group.mat-tabs-with-background > .mat-mdc-tab-header .mat-mdc-tab-header-pagination-chevron, +.mat-mdc-tab-group.mat-tabs-with-background > .mat-mdc-tab-header .mat-focus-indicator::before, .mat-mdc-tab-group.mat-tabs-with-background > .mat-mdc-tab-header-pagination .mat-mdc-tab-header-pagination-chevron, +.mat-mdc-tab-group.mat-tabs-with-background > .mat-mdc-tab-header-pagination .mat-focus-indicator::before { + border-color: var(--mat-tab-foreground-color); +} +.mat-mdc-tab-group.mat-tabs-with-background > .mat-mdc-tab-header .mat-ripple-element, .mat-mdc-tab-group.mat-tabs-with-background > .mat-mdc-tab-header .mdc-tab__ripple::before, .mat-mdc-tab-group.mat-tabs-with-background > .mat-mdc-tab-header-pagination .mat-ripple-element, .mat-mdc-tab-group.mat-tabs-with-background > .mat-mdc-tab-header-pagination .mdc-tab__ripple::before { + background-color: var(--mat-tab-foreground-color); +} +.mat-mdc-tab-group.mat-tabs-with-background > .mat-mdc-tab-header .mat-mdc-tab-header-pagination-chevron, .mat-mdc-tab-group.mat-tabs-with-background > .mat-mdc-tab-header-pagination .mat-mdc-tab-header-pagination-chevron { + color: var(--mat-tab-foreground-color); +} +.mat-mdc-tab-group.mat-mdc-tab-group-inverted-header { + flex-direction: column-reverse; +} +.mat-mdc-tab-group.mat-mdc-tab-group-inverted-header .mdc-tab-indicator__content--underline { + align-self: flex-start; +} + +.mat-mdc-tab-body-wrapper { + position: relative; + overflow: hidden; + display: flex; + transition: height 500ms cubic-bezier(0.35, 0, 0.25, 1); +} +.mat-mdc-tab-body-wrapper._mat-animation-noopable { + transition: none !important; + animation: none !important; +} +`],encapsulation:2})}return n})(),Re=class{index;tab},Ma=(()=>{class n extends dn{_focusedItem=Z(null);get fitInkBarToContent(){return this._fitInkBarToContent.value}set fitInkBarToContent(t){this._fitInkBarToContent.next(t),this._changeDetectorRef.markForCheck()}_fitInkBarToContent=new St(!1);stretchTabs=!0;get animationDuration(){return this._animationDuration}set animationDuration(t){let e=t+"";this._animationDuration=/^\d+$/.test(e)?t+"ms":e}_animationDuration;_items;get backgroundColor(){return this._backgroundColor}set backgroundColor(t){let e=this._elementRef.nativeElement.classList;e.remove("mat-tabs-with-background",`mat-background-${this.backgroundColor}`),t&&e.add("mat-tabs-with-background",`mat-background-${t}`),this._backgroundColor=t}_backgroundColor;get disableRipple(){return this._disableRipple()}set disableRipple(t){this._disableRipple.set(t)}_disableRipple=Z(!1);color="primary";tabPanel;_tabListContainer;_tabList;_tabListInner;_nextPaginator;_previousPaginator;_inkBar;constructor(){let t=r(mn,{optional:!0});super(),this.disablePagination=t&&t.disablePagination!=null?t.disablePagination:!1,this.fitInkBarToContent=t&&t.fitInkBarToContent!=null?t.fitInkBarToContent:!1,this.stretchTabs=t&&t.stretchTabs!=null?t.stretchTabs:!0}_itemSelected(){}ngAfterContentInit(){this._inkBar=new pe(this._items),this._items.changes.pipe(nt(null),I(this._destroyed)).subscribe(()=>this.updateActiveLink()),super.ngAfterContentInit(),this._keyManager.change.pipe(nt(null),I(this._destroyed)).subscribe(()=>this._focusedItem.set(this._keyManager?.activeItem||null))}ngAfterViewInit(){this.tabPanel,super.ngAfterViewInit()}updateActiveLink(){if(!this._items)return;let t=this._items.toArray();for(let e=0;e .mat-mdc-tab-link-container .mat-mdc-tab-links { + justify-content: center; +} +[mat-align-tabs=end] > .mat-mdc-tab-link-container .mat-mdc-tab-links { + justify-content: flex-end; +} +.cdk-drop-list .mat-mdc-tab-links, .mat-mdc-tab-links.cdk-drop-list { + min-height: var(--mat-tab-container-height, 48px); +} + +.mat-mdc-tab-link-container { + display: flex; + flex-grow: 1; + overflow: hidden; + z-index: 1; + border-bottom-style: solid; + border-bottom-width: var(--mat-tab-divider-height, 1px); + border-bottom-color: var(--mat-tab-divider-color, var(--mat-sys-surface-variant)); +} + +.mat-mdc-tab-nav-bar.mat-tabs-with-background > .mat-mdc-tab-link-container, .mat-mdc-tab-nav-bar.mat-tabs-with-background > .mat-mdc-tab-header-pagination { + background-color: var(--mat-tab-background-color); +} +.mat-mdc-tab-nav-bar.mat-tabs-with-background.mat-primary > .mat-mdc-tab-link-container .mat-mdc-tab-link .mdc-tab__text-label { + color: var(--mat-tab-foreground-color); +} +.mat-mdc-tab-nav-bar.mat-tabs-with-background.mat-primary > .mat-mdc-tab-link-container .mdc-tab-indicator__content--underline { + border-color: var(--mat-tab-foreground-color); +} +.mat-mdc-tab-nav-bar.mat-tabs-with-background:not(.mat-primary) > .mat-mdc-tab-link-container .mat-mdc-tab-link:not(.mdc-tab--active) .mdc-tab__text-label { + color: var(--mat-tab-foreground-color); +} +.mat-mdc-tab-nav-bar.mat-tabs-with-background:not(.mat-primary) > .mat-mdc-tab-link-container .mat-mdc-tab-link:not(.mdc-tab--active) .mdc-tab-indicator__content--underline { + border-color: var(--mat-tab-foreground-color); +} +.mat-mdc-tab-nav-bar.mat-tabs-with-background > .mat-mdc-tab-link-container .mat-mdc-tab-header-pagination-chevron, +.mat-mdc-tab-nav-bar.mat-tabs-with-background > .mat-mdc-tab-link-container .mat-focus-indicator::before, .mat-mdc-tab-nav-bar.mat-tabs-with-background > .mat-mdc-tab-header-pagination .mat-mdc-tab-header-pagination-chevron, +.mat-mdc-tab-nav-bar.mat-tabs-with-background > .mat-mdc-tab-header-pagination .mat-focus-indicator::before { + border-color: var(--mat-tab-foreground-color); +} +.mat-mdc-tab-nav-bar.mat-tabs-with-background > .mat-mdc-tab-link-container .mat-ripple-element, .mat-mdc-tab-nav-bar.mat-tabs-with-background > .mat-mdc-tab-link-container .mdc-tab__ripple::before, .mat-mdc-tab-nav-bar.mat-tabs-with-background > .mat-mdc-tab-header-pagination .mat-ripple-element, .mat-mdc-tab-nav-bar.mat-tabs-with-background > .mat-mdc-tab-header-pagination .mdc-tab__ripple::before { + background-color: var(--mat-tab-foreground-color); +} +.mat-mdc-tab-nav-bar.mat-tabs-with-background > .mat-mdc-tab-link-container .mat-mdc-tab-header-pagination-chevron, .mat-mdc-tab-nav-bar.mat-tabs-with-background > .mat-mdc-tab-header-pagination .mat-mdc-tab-header-pagination-chevron { + color: var(--mat-tab-foreground-color); +} +`],encapsulation:2})}return n})(),Sa=(()=>{class n extends ln{_tabNavBar=r(Ma);elementRef=r(O);_focusMonitor=r(le);_destroyed=new k;_isActive=!1;_tabIndex=At(()=>this._tabNavBar._focusedItem()===this?this.tabIndex:-1);get active(){return this._isActive}set active(t){t!==this._isActive&&(this._isActive=t,this._tabNavBar.updateActiveLink())}disabled=!1;get disableRipple(){return this._disableRipple()}set disableRipple(t){this._disableRipple.set(t)}_disableRipple=Z(!1);tabIndex=0;rippleConfig;get rippleDisabled(){return this.disabled||this.disableRipple||this._tabNavBar.disableRipple||!!this.rippleConfig.disabled}id=r(U).getId("mat-tab-link-");constructor(){super(),r(wt).load(Ct);let t=r(Yt,{optional:!0}),e=r(new ie("tabindex"),{optional:!0});this.rippleConfig=t||{},this.tabIndex=e==null?0:parseInt(e)||0,Q()&&(this.rippleConfig.animation={enterDuration:0,exitDuration:0}),this._tabNavBar._fitInkBarToContent.pipe(I(this._destroyed)).subscribe(i=>{this.fitInkBarToContent=i})}focus(){this.elementRef.nativeElement.focus()}ngAfterViewInit(){this._focusMonitor.monitor(this.elementRef)}ngOnDestroy(){this._destroyed.next(),this._destroyed.complete(),super.ngOnDestroy(),this._focusMonitor.stopMonitoring(this.elementRef)}_handleFocus(){this._tabNavBar.focusIndex=this._tabNavBar._items.toArray().indexOf(this)}_handleKeydown(t){(t.keyCode===32||t.keyCode===13)&&(this.disabled?t.preventDefault():this._tabNavBar.tabPanel&&(t.keyCode===32&&t.preventDefault(),this.elementRef.nativeElement.click()))}_getAriaControls(){return this._tabNavBar.tabPanel?this._tabNavBar.tabPanel?.id:this.elementRef.nativeElement.getAttribute("aria-controls")}_getAriaSelected(){return this._tabNavBar.tabPanel?this.active?"true":"false":this.elementRef.nativeElement.getAttribute("aria-selected")}_getAriaCurrent(){return this.active&&!this._tabNavBar.tabPanel?"page":null}_getRole(){return this._tabNavBar.tabPanel?"tab":this.elementRef.nativeElement.getAttribute("role")}static \u0275fac=function(e){return new(e||n)};static \u0275cmp=C({type:n,selectors:[["","mat-tab-link",""],["","matTabLink",""]],hostAttrs:[1,"mdc-tab","mat-mdc-tab-link","mat-focus-indicator"],hostVars:11,hostBindings:function(e,i){e&1&&u("focus",function(){return i._handleFocus()})("keydown",function(o){return i._handleKeydown(o)}),e&2&&(T("aria-controls",i._getAriaControls())("aria-current",i._getAriaCurrent())("aria-disabled",i.disabled)("aria-selected",i._getAriaSelected())("id",i.id)("tabIndex",i._tabIndex())("role",i._getRole()),_("mat-mdc-tab-disabled",i.disabled)("mdc-tab--active",i.active))},inputs:{active:[2,"active","active",S],disabled:[2,"disabled","disabled",S],disableRipple:[2,"disableRipple","disableRipple",S],tabIndex:[2,"tabIndex","tabIndex",t=>t==null?0:gt(t)],id:"id"},exportAs:["matTabLink"],features:[q],attrs:ga,ngContentSelectors:Bt,decls:5,vars:2,consts:[[1,"mdc-tab__ripple"],["mat-ripple","",1,"mat-mdc-tab-ripple",3,"matRippleTrigger","matRippleDisabled"],[1,"mdc-tab__content"],[1,"mdc-tab__text-label"]],template:function(e,i){e&1&&(B(),M(0,"span",0)(1,"div",1),l(2,"span",2)(3,"span",3),y(4),d()()),e&2&&(c(),D("matRippleTrigger",i.elementRef.nativeElement)("matRippleDisabled",i.rippleDisabled))},dependencies:[vt],styles:[`.mat-mdc-tab-link { + -webkit-tap-highlight-color: transparent; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-decoration: none; + background: none; + height: var(--mat-tab-container-height, 48px); + font-family: var(--mat-tab-label-text-font, var(--mat-sys-title-small-font)); + font-size: var(--mat-tab-label-text-size, var(--mat-sys-title-small-size)); + letter-spacing: var(--mat-tab-label-text-tracking, var(--mat-sys-title-small-tracking)); + line-height: var(--mat-tab-label-text-line-height, var(--mat-sys-title-small-line-height)); + font-weight: var(--mat-tab-label-text-weight, var(--mat-sys-title-small-weight)); +} +.mat-mdc-tab-link.mdc-tab { + flex-grow: 0; +} +.mat-mdc-tab-link .mdc-tab-indicator__content--underline { + border-color: var(--mat-tab-active-indicator-color, var(--mat-sys-primary)); + border-top-width: var(--mat-tab-active-indicator-height, 2px); + border-radius: var(--mat-tab-active-indicator-shape, 0); +} +.mat-mdc-tab-link:hover .mdc-tab__text-label { + color: var(--mat-tab-inactive-hover-label-text-color, var(--mat-sys-on-surface)); +} +.mat-mdc-tab-link:focus .mdc-tab__text-label { + color: var(--mat-tab-inactive-focus-label-text-color, var(--mat-sys-on-surface)); +} +.mat-mdc-tab-link.mdc-tab--active .mdc-tab__text-label { + color: var(--mat-tab-active-label-text-color, var(--mat-sys-on-surface)); +} +.mat-mdc-tab-link.mdc-tab--active .mdc-tab__ripple::before, +.mat-mdc-tab-link.mdc-tab--active .mat-ripple-element { + background-color: var(--mat-tab-active-ripple-color, var(--mat-sys-on-surface)); +} +.mat-mdc-tab-link.mdc-tab--active:hover .mdc-tab__text-label { + color: var(--mat-tab-active-hover-label-text-color, var(--mat-sys-on-surface)); +} +.mat-mdc-tab-link.mdc-tab--active:hover .mdc-tab-indicator__content--underline { + border-color: var(--mat-tab-active-hover-indicator-color, var(--mat-sys-primary)); +} +.mat-mdc-tab-link.mdc-tab--active:focus .mdc-tab__text-label { + color: var(--mat-tab-active-focus-label-text-color, var(--mat-sys-on-surface)); +} +.mat-mdc-tab-link.mdc-tab--active:focus .mdc-tab-indicator__content--underline { + border-color: var(--mat-tab-active-focus-indicator-color, var(--mat-sys-primary)); +} +.mat-mdc-tab-link.mat-mdc-tab-disabled { + opacity: 0.4; + pointer-events: none; +} +.mat-mdc-tab-link.mat-mdc-tab-disabled .mdc-tab__content { + pointer-events: none; +} +.mat-mdc-tab-link.mat-mdc-tab-disabled .mdc-tab__ripple::before, +.mat-mdc-tab-link.mat-mdc-tab-disabled .mat-ripple-element { + background-color: var(--mat-tab-disabled-ripple-color, var(--mat-sys-on-surface-variant)); +} +.mat-mdc-tab-link .mdc-tab__ripple::before { + content: ""; + display: block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + opacity: 0; + pointer-events: none; + background-color: var(--mat-tab-inactive-ripple-color, var(--mat-sys-on-surface)); +} +.mat-mdc-tab-link .mdc-tab__text-label { + color: var(--mat-tab-inactive-label-text-color, var(--mat-sys-on-surface)); + display: inline-flex; + align-items: center; +} +.mat-mdc-tab-link .mdc-tab__content { + position: relative; + pointer-events: auto; +} +.mat-mdc-tab-link:hover .mdc-tab__ripple::before { + opacity: 0.04; +} +.mat-mdc-tab-link.cdk-program-focused .mdc-tab__ripple::before, .mat-mdc-tab-link.cdk-keyboard-focused .mdc-tab__ripple::before { + opacity: 0.12; +} +.mat-mdc-tab-link .mat-ripple-element { + opacity: 0.12; + background-color: var(--mat-tab-inactive-ripple-color, var(--mat-sys-on-surface)); +} +.mat-mdc-tab-header.mat-mdc-tab-nav-bar-stretch-tabs .mat-mdc-tab-link { + flex-grow: 1; +} +.mat-mdc-tab-link::before { + margin: 5px; +} + +@media (max-width: 599px) { + .mat-mdc-tab-link { + min-width: 72px; + } +} +`],encapsulation:2,changeDetection:0})}return n})(),ss=(()=>{class n{id=r(U).getId("mat-tab-nav-panel-");_activeTabId;static \u0275fac=function(e){return new(e||n)};static \u0275cmp=C({type:n,selectors:[["mat-tab-nav-panel"]],hostAttrs:["role","tabpanel",1,"mat-mdc-tab-nav-panel"],hostVars:2,hostBindings:function(e,i){e&2&&T("aria-labelledby",i._activeTabId)("id",i.id)},inputs:{id:"id"},exportAs:["matTabNavPanel"],ngContentSelectors:Bt,decls:1,vars:0,template:function(e,i){e&1&&(B(),y(0))},encapsulation:2,changeDetection:0})}return n})(),pn=(()=>{class n{static \u0275fac=function(e){return new(e||n)};static \u0275mod=N({type:n});static \u0275inj=z({imports:[j]})}return n})();var Da=["notch"],Ea=["matFormFieldNotchedOutline",""],Fa=["*"],fn=["iconPrefixContainer"],un=["textPrefixContainer"],_n=["iconSuffixContainer"],bn=["textSuffixContainer"],Oa=["textField"],Ra=["*",[["mat-label"]],[["","matPrefix",""],["","matIconPrefix",""]],[["","matTextPrefix",""]],[["","matTextSuffix",""]],[["","matSuffix",""],["","matIconSuffix",""]],[["mat-error"],["","matError",""]],[["mat-hint",3,"align","end"]],[["mat-hint","align","end"]]],Aa=["*","mat-label","[matPrefix], [matIconPrefix]","[matTextPrefix]","[matTextSuffix]","[matSuffix], [matIconSuffix]","mat-error, [matError]","mat-hint:not([align='end'])","mat-hint[align='end']"];function La(n,s){n&1&&M(0,"span",21)}function Pa(n,s){if(n&1&&(l(0,"label",20),y(1,1),g(2,La,1,0,"span",21),d()),n&2){let t=p(2);D("floating",t._shouldLabelFloat())("monitorResize",t._hasOutline())("id",t._labelId),T("for",t._control.disableAutomaticLabeling?null:t._control.id),c(2),v(!t.hideRequiredMarker&&t._control.required?2:-1)}}function Ba(n,s){if(n&1&&g(0,Pa,3,5,"label",20),n&2){let t=p();v(t._hasFloatingLabel()?0:-1)}}function za(n,s){n&1&&M(0,"div",7)}function Na(n,s){}function ja(n,s){if(n&1&&tt(0,Na,0,0,"ng-template",13),n&2){p(2);let t=ht(1);D("ngTemplateOutlet",t)}}function Va(n,s){if(n&1&&(l(0,"div",9),g(1,ja,1,1,null,13),d()),n&2){let t=p();D("matFormFieldNotchedOutlineOpen",t._shouldLabelFloat()),c(),v(t._forceDisplayInfixLabel()?-1:1)}}function Ha(n,s){n&1&&(l(0,"div",10,2),y(2,2),d())}function $a(n,s){n&1&&(l(0,"div",11,3),y(2,3),d())}function Wa(n,s){}function Qa(n,s){if(n&1&&tt(0,Wa,0,0,"ng-template",13),n&2){p();let t=ht(1);D("ngTemplateOutlet",t)}}function Ga(n,s){n&1&&(l(0,"div",14,4),y(2,4),d())}function qa(n,s){n&1&&(l(0,"div",15,5),y(2,5),d())}function Ua(n,s){n&1&&M(0,"div",16)}function Ya(n,s){n&1&&(l(0,"div",18),y(1,6),d())}function Ka(n,s){if(n&1&&(l(0,"mat-hint",22),E(1),d()),n&2){let t=p(2);D("id",t._hintLabelId),c(),pt(t.hintLabel)}}function Za(n,s){if(n&1&&(l(0,"div",19),g(1,Ka,2,2,"mat-hint",22),y(2,7),M(3,"div",23),y(4,8),d()),n&2){let t=p();c(),v(t.hintLabel?1:-1)}}var Le=(()=>{class n{static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n,selectors:[["mat-label"]]})}return n})(),Cn=new w("MatError"),Xa=(()=>{class n{id=r(U).getId("mat-mdc-error-");constructor(){}static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n,selectors:[["mat-error"],["","matError",""]],hostAttrs:[1,"mat-mdc-form-field-error","mat-mdc-form-field-bottom-align"],hostVars:1,hostBindings:function(e,i){e&2&&Rt("id",i.id)},inputs:{id:"id"},features:[$([{provide:Cn,useExisting:n}])]})}return n})(),Pe=(()=>{class n{align="start";id=r(U).getId("mat-mdc-hint-");static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n,selectors:[["mat-hint"]],hostAttrs:[1,"mat-mdc-form-field-hint","mat-mdc-form-field-bottom-align"],hostVars:4,hostBindings:function(e,i){e&2&&(Rt("id",i.id),T("align",null),_("mat-mdc-form-field-hint-end",i.align==="end"))},inputs:{align:"align",id:"id"}})}return n})(),Tn=new w("MatPrefix"),Ja=(()=>{class n{set _isTextSelector(t){this._isText=!0}_isText=!1;static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n,selectors:[["","matPrefix",""],["","matIconPrefix",""],["","matTextPrefix",""]],inputs:{_isTextSelector:[0,"matTextPrefix","_isTextSelector"]},features:[$([{provide:Tn,useExisting:n}])]})}return n})(),Mn=new w("MatSuffix"),to=(()=>{class n{set _isTextSelector(t){this._isText=!0}_isText=!1;static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n,selectors:[["","matSuffix",""],["","matIconSuffix",""],["","matTextSuffix",""]],inputs:{_isTextSelector:[0,"matTextSuffix","_isTextSelector"]},features:[$([{provide:Mn,useExisting:n}])]})}return n})(),Sn=new w("FloatingLabelParent"),gn=(()=>{class n{_elementRef=r(O);get floating(){return this._floating}set floating(t){this._floating=t,this.monitorResize&&this._handleResize()}_floating=!1;get monitorResize(){return this._monitorResize}set monitorResize(t){this._monitorResize=t,this._monitorResize?this._subscribeToResize():this._resizeSubscription.unsubscribe()}_monitorResize=!1;_resizeObserver=r(he);_ngZone=r(H);_parent=r(Sn);_resizeSubscription=new it;constructor(){}ngOnDestroy(){this._resizeSubscription.unsubscribe()}getWidth(){return eo(this._elementRef.nativeElement)}get element(){return this._elementRef.nativeElement}_handleResize(){setTimeout(()=>this._parent._handleLabelResized())}_subscribeToResize(){this._resizeSubscription.unsubscribe(),this._ngZone.runOutsideAngular(()=>{this._resizeSubscription=this._resizeObserver.observe(this._elementRef.nativeElement,{box:"border-box"}).subscribe(()=>this._handleResize())})}static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n,selectors:[["label","matFormFieldFloatingLabel",""]],hostAttrs:[1,"mdc-floating-label","mat-mdc-floating-label"],hostVars:2,hostBindings:function(e,i){e&2&&_("mdc-floating-label--float-above",i.floating)},inputs:{floating:"floating",monitorResize:"monitorResize"}})}return n})();function eo(n){let s=n;if(s.offsetParent!==null)return s.scrollWidth;let t=s.cloneNode(!0);t.style.setProperty("position","absolute"),t.style.setProperty("transform","translate(-9999px, -9999px)"),document.documentElement.appendChild(t);let e=t.scrollWidth;return t.remove(),e}var vn="mdc-line-ripple--active",fe="mdc-line-ripple--deactivating",yn=(()=>{class n{_elementRef=r(O);_cleanupTransitionEnd;constructor(){let t=r(H),e=r(st);t.runOutsideAngular(()=>{this._cleanupTransitionEnd=e.listen(this._elementRef.nativeElement,"transitionend",this._handleTransitionEnd)})}activate(){let t=this._elementRef.nativeElement.classList;t.remove(fe),t.add(vn)}deactivate(){this._elementRef.nativeElement.classList.add(fe)}_handleTransitionEnd=t=>{let e=this._elementRef.nativeElement.classList,i=e.contains(fe);t.propertyName==="opacity"&&i&&e.remove(vn,fe)};ngOnDestroy(){this._cleanupTransitionEnd()}static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n,selectors:[["div","matFormFieldLineRipple",""]],hostAttrs:[1,"mdc-line-ripple"]})}return n})(),xn=(()=>{class n{_elementRef=r(O);_ngZone=r(H);open=!1;_notch;ngAfterViewInit(){let t=this._elementRef.nativeElement,e=t.querySelector(".mdc-floating-label");e?(t.classList.add("mdc-notched-outline--upgraded"),typeof requestAnimationFrame=="function"&&(e.style.transitionDuration="0s",this._ngZone.runOutsideAngular(()=>{requestAnimationFrame(()=>e.style.transitionDuration="")}))):t.classList.add("mdc-notched-outline--no-label")}_setNotchWidth(t){let e=this._notch.nativeElement;!this.open||!t?e.style.width="":e.style.width=`calc(${t}px * var(--mat-mdc-form-field-floating-label-scale, 0.75) + 9px)`}_setMaxWidth(t){this._notch.nativeElement.style.setProperty("--mat-form-field-notch-max-width",`calc(100% - ${t}px)`)}static \u0275fac=function(e){return new(e||n)};static \u0275cmp=C({type:n,selectors:[["div","matFormFieldNotchedOutline",""]],viewQuery:function(e,i){if(e&1&&V(Da,5),e&2){let a;m(a=h())&&(i._notch=a.first)}},hostAttrs:[1,"mdc-notched-outline"],hostVars:2,hostBindings:function(e,i){e&2&&_("mdc-notched-outline--notched",i.open)},inputs:{open:[0,"matFormFieldNotchedOutlineOpen","open"]},attrs:Ea,ngContentSelectors:Fa,decls:5,vars:0,consts:[["notch",""],[1,"mat-mdc-notch-piece","mdc-notched-outline__leading"],[1,"mat-mdc-notch-piece","mdc-notched-outline__notch"],[1,"mat-mdc-notch-piece","mdc-notched-outline__trailing"]],template:function(e,i){e&1&&(B(),ke(0,"div",1),kt(1,"div",2,0),y(3),Ot(),ke(4,"div",3))},encapsulation:2,changeDetection:0})}return n})(),Be=(()=>{class n{value=null;stateChanges;id;placeholder;ngControl=null;focused=!1;empty=!1;shouldLabelFloat=!1;required=!1;disabled=!1;errorState=!1;controlType;autofilled;userAriaDescribedBy;disableAutomaticLabeling;describedByIds;static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n})}return n})();var ze=new w("MatFormField"),io=new w("MAT_FORM_FIELD_DEFAULT_OPTIONS"),kn="fill",no="auto",wn="fixed",ao="translateY(-50%)",In=(()=>{class n{_elementRef=r(O);_changeDetectorRef=r(W);_platform=r(ot);_idGenerator=r(U);_ngZone=r(H);_defaults=r(io,{optional:!0});_currentDirection;_textField;_iconPrefixContainer;_textPrefixContainer;_iconSuffixContainer;_textSuffixContainer;_floatingLabel;_notchedOutline;_lineRipple;_iconPrefixContainerSignal=Qt("iconPrefixContainer");_textPrefixContainerSignal=Qt("textPrefixContainer");_iconSuffixContainerSignal=Qt("iconSuffixContainer");_textSuffixContainerSignal=Qt("textSuffixContainer");_prefixSuffixContainers=At(()=>[this._iconPrefixContainerSignal(),this._textPrefixContainerSignal(),this._iconSuffixContainerSignal(),this._textSuffixContainerSignal()].map(t=>t?.nativeElement).filter(t=>t!==void 0));_formFieldControl;_prefixChildren;_suffixChildren;_errorChildren;_hintChildren;_labelChild=bi(Le);get hideRequiredMarker(){return this._hideRequiredMarker}set hideRequiredMarker(t){this._hideRequiredMarker=K(t)}_hideRequiredMarker=!1;color="primary";get floatLabel(){return this._floatLabel||this._defaults?.floatLabel||no}set floatLabel(t){t!==this._floatLabel&&(this._floatLabel=t,this._changeDetectorRef.markForCheck())}_floatLabel;get appearance(){return this._appearanceSignal()}set appearance(t){let e=t||this._defaults?.appearance||kn;this._appearanceSignal.set(e)}_appearanceSignal=Z(kn);get subscriptSizing(){return this._subscriptSizing||this._defaults?.subscriptSizing||wn}set subscriptSizing(t){this._subscriptSizing=t||this._defaults?.subscriptSizing||wn}_subscriptSizing=null;get hintLabel(){return this._hintLabel}set hintLabel(t){this._hintLabel=t,this._processHints()}_hintLabel="";_hasIconPrefix=!1;_hasTextPrefix=!1;_hasIconSuffix=!1;_hasTextSuffix=!1;_labelId=this._idGenerator.getId("mat-mdc-form-field-label-");_hintLabelId=this._idGenerator.getId("mat-mdc-hint-");_describedByIds;get _control(){return this._explicitFormFieldControl||this._formFieldControl}set _control(t){this._explicitFormFieldControl=t}_destroyed=new k;_isFocused=null;_explicitFormFieldControl;_previousControl=null;_previousControlValidatorFn=null;_stateChanges;_valueChanges;_describedByChanges;_outlineLabelOffsetResizeObserver=null;_animationsDisabled=Q();constructor(){let t=this._defaults,e=r(ut);t&&(t.appearance&&(this.appearance=t.appearance),this._hideRequiredMarker=!!t?.hideRequiredMarker,t.color&&(this.color=t.color)),ii(()=>this._currentDirection=e.valueSignal()),this._syncOutlineLabelOffset()}ngAfterViewInit(){this._updateFocusState(),this._animationsDisabled||this._ngZone.runOutsideAngular(()=>{setTimeout(()=>{this._elementRef.nativeElement.classList.add("mat-form-field-animations-enabled")},300)}),this._changeDetectorRef.detectChanges()}ngAfterContentInit(){this._assertFormFieldControl(),this._initializeSubscript(),this._initializePrefixAndSuffix()}ngAfterContentChecked(){this._assertFormFieldControl(),this._control!==this._previousControl&&(this._initializeControl(this._previousControl),this._control.ngControl&&this._control.ngControl.control&&(this._previousControlValidatorFn=this._control.ngControl.control.validator),this._previousControl=this._control),this._control.ngControl&&this._control.ngControl.control&&this._control.ngControl.control.validator!==this._previousControlValidatorFn&&this._changeDetectorRef.markForCheck()}ngOnDestroy(){this._outlineLabelOffsetResizeObserver?.disconnect(),this._stateChanges?.unsubscribe(),this._valueChanges?.unsubscribe(),this._describedByChanges?.unsubscribe(),this._destroyed.next(),this._destroyed.complete()}getLabelId=At(()=>this._hasFloatingLabel()?this._labelId:null);getConnectedOverlayOrigin(){return this._textField||this._elementRef}_animateAndLockLabel(){this._hasFloatingLabel()&&(this.floatLabel="always")}_initializeControl(t){let e=this._control,i="mat-mdc-form-field-type-";t&&this._elementRef.nativeElement.classList.remove(i+t.controlType),e.controlType&&this._elementRef.nativeElement.classList.add(i+e.controlType),this._stateChanges?.unsubscribe(),this._stateChanges=e.stateChanges.subscribe(()=>{this._updateFocusState(),this._changeDetectorRef.markForCheck()}),this._describedByChanges?.unsubscribe(),this._describedByChanges=e.stateChanges.pipe(nt([void 0,void 0]),Ht(()=>[e.errorState,e.userAriaDescribedBy]),Je(),dt(([[a,o],[f,b]])=>a!==f||o!==b)).subscribe(()=>this._syncDescribedByIds()),this._valueChanges?.unsubscribe(),e.ngControl&&e.ngControl.valueChanges&&(this._valueChanges=e.ngControl.valueChanges.pipe(I(this._destroyed)).subscribe(()=>this._changeDetectorRef.markForCheck()))}_checkPrefixAndSuffixTypes(){this._hasIconPrefix=!!this._prefixChildren.find(t=>!t._isText),this._hasTextPrefix=!!this._prefixChildren.find(t=>t._isText),this._hasIconSuffix=!!this._suffixChildren.find(t=>!t._isText),this._hasTextSuffix=!!this._suffixChildren.find(t=>t._isText)}_initializePrefixAndSuffix(){this._checkPrefixAndSuffixTypes(),J(this._prefixChildren.changes,this._suffixChildren.changes).subscribe(()=>{this._checkPrefixAndSuffixTypes(),this._changeDetectorRef.markForCheck()})}_initializeSubscript(){this._hintChildren.changes.subscribe(()=>{this._processHints(),this._changeDetectorRef.markForCheck()}),this._errorChildren.changes.subscribe(()=>{this._syncDescribedByIds(),this._changeDetectorRef.markForCheck()}),this._validateHints(),this._syncDescribedByIds()}_assertFormFieldControl(){this._control}_updateFocusState(){let t=this._control.focused;t&&!this._isFocused?(this._isFocused=!0,this._lineRipple?.activate()):!t&&(this._isFocused||this._isFocused===null)&&(this._isFocused=!1,this._lineRipple?.deactivate()),this._elementRef.nativeElement.classList.toggle("mat-focused",t),this._textField?.nativeElement.classList.toggle("mdc-text-field--focused",t)}_syncOutlineLabelOffset(){gi({earlyRead:()=>{if(this._appearanceSignal()!=="outline")return this._outlineLabelOffsetResizeObserver?.disconnect(),null;if(globalThis.ResizeObserver){this._outlineLabelOffsetResizeObserver||=new globalThis.ResizeObserver(()=>{this._writeOutlinedLabelStyles(this._getOutlinedLabelOffset())});for(let t of this._prefixSuffixContainers())this._outlineLabelOffsetResizeObserver.observe(t,{box:"border-box"})}return this._getOutlinedLabelOffset()},write:t=>this._writeOutlinedLabelStyles(t())})}_shouldAlwaysFloat(){return this.floatLabel==="always"}_hasOutline(){return this.appearance==="outline"}_forceDisplayInfixLabel(){return!this._platform.isBrowser&&this._prefixChildren.length&&!this._shouldLabelFloat()}_hasFloatingLabel=At(()=>!!this._labelChild());_shouldLabelFloat(){return this._hasFloatingLabel()?this._control.shouldLabelFloat||this._shouldAlwaysFloat():!1}_shouldForward(t){let e=this._control?this._control.ngControl:null;return e&&e[t]}_getSubscriptMessageType(){return this._errorChildren&&this._errorChildren.length>0&&this._control.errorState?"error":"hint"}_handleLabelResized(){this._refreshOutlineNotchWidth()}_refreshOutlineNotchWidth(){!this._hasOutline()||!this._floatingLabel||!this._shouldLabelFloat()?this._notchedOutline?._setNotchWidth(0):this._notchedOutline?._setNotchWidth(this._floatingLabel.getWidth())}_processHints(){this._validateHints(),this._syncDescribedByIds()}_validateHints(){this._hintChildren}_syncDescribedByIds(){if(this._control){let t=[];if(this._control.userAriaDescribedBy&&typeof this._control.userAriaDescribedBy=="string"&&t.push(...this._control.userAriaDescribedBy.split(" ")),this._getSubscriptMessageType()==="hint"){let a=this._hintChildren?this._hintChildren.find(f=>f.align==="start"):null,o=this._hintChildren?this._hintChildren.find(f=>f.align==="end"):null;a?t.push(a.id):this._hintLabel&&t.push(this._hintLabelId),o&&t.push(o.id)}else this._errorChildren&&t.push(...this._errorChildren.map(a=>a.id));let e=this._control.describedByIds,i;if(e){let a=this._describedByIds||t;i=t.concat(e.filter(o=>o&&!a.includes(o)))}else i=t;this._control.setDescribedByIds(i),this._describedByIds=t}}_getOutlinedLabelOffset(){if(!this._hasOutline()||!this._floatingLabel)return null;if(!this._iconPrefixContainer&&!this._textPrefixContainer)return["",null];if(!this._isAttachedToDom())return null;let t=this._iconPrefixContainer?.nativeElement,e=this._textPrefixContainer?.nativeElement,i=this._iconSuffixContainer?.nativeElement,a=this._textSuffixContainer?.nativeElement,o=t?.getBoundingClientRect().width??0,f=e?.getBoundingClientRect().width??0,b=i?.getBoundingClientRect().width??0,xt=a?.getBoundingClientRect().width??0,Mt=this._currentDirection==="rtl"?"-1":"1",ia=`${o+f}px`,na=`calc(${Mt} * (${ia} + var(--mat-mdc-form-field-label-offset-x, 0px)))`,aa=`var(--mat-mdc-form-field-label-transform, ${ao} translateX(${na}))`,oa=o+f+b+xt;return[aa,oa]}_writeOutlinedLabelStyles(t){if(t!==null){let[e,i]=t;this._floatingLabel&&(this._floatingLabel.element.style.transform=e),i!==null&&this._notchedOutline?._setMaxWidth(i)}}_isAttachedToDom(){let t=this._elementRef.nativeElement;if(t.getRootNode){let e=t.getRootNode();return e&&e!==t}return document.documentElement.contains(t)}static \u0275fac=function(e){return new(e||n)};static \u0275cmp=C({type:n,selectors:[["mat-form-field"]],contentQueries:function(e,i,a){if(e&1&&(li(a,i._labelChild,Le,5),et(a,Be,5)(a,Tn,5)(a,Mn,5)(a,Cn,5)(a,Pe,5)),e&2){we();let o;m(o=h())&&(i._formFieldControl=o.first),m(o=h())&&(i._prefixChildren=o),m(o=h())&&(i._suffixChildren=o),m(o=h())&&(i._errorChildren=o),m(o=h())&&(i._hintChildren=o)}},viewQuery:function(e,i){if(e&1&&(ci(i._iconPrefixContainerSignal,fn,5)(i._textPrefixContainerSignal,un,5)(i._iconSuffixContainerSignal,_n,5)(i._textSuffixContainerSignal,bn,5),V(Oa,5)(fn,5)(un,5)(_n,5)(bn,5)(gn,5)(xn,5)(yn,5)),e&2){we(4);let a;m(a=h())&&(i._textField=a.first),m(a=h())&&(i._iconPrefixContainer=a.first),m(a=h())&&(i._textPrefixContainer=a.first),m(a=h())&&(i._iconSuffixContainer=a.first),m(a=h())&&(i._textSuffixContainer=a.first),m(a=h())&&(i._floatingLabel=a.first),m(a=h())&&(i._notchedOutline=a.first),m(a=h())&&(i._lineRipple=a.first)}},hostAttrs:[1,"mat-mdc-form-field"],hostVars:38,hostBindings:function(e,i){e&2&&_("mat-mdc-form-field-label-always-float",i._shouldAlwaysFloat())("mat-mdc-form-field-has-icon-prefix",i._hasIconPrefix)("mat-mdc-form-field-has-icon-suffix",i._hasIconSuffix)("mat-form-field-invalid",i._control.errorState)("mat-form-field-disabled",i._control.disabled)("mat-form-field-autofilled",i._control.autofilled)("mat-form-field-appearance-fill",i.appearance=="fill")("mat-form-field-appearance-outline",i.appearance=="outline")("mat-form-field-hide-placeholder",i._hasFloatingLabel()&&!i._shouldLabelFloat())("mat-primary",i.color!=="accent"&&i.color!=="warn")("mat-accent",i.color==="accent")("mat-warn",i.color==="warn")("ng-untouched",i._shouldForward("untouched"))("ng-touched",i._shouldForward("touched"))("ng-pristine",i._shouldForward("pristine"))("ng-dirty",i._shouldForward("dirty"))("ng-valid",i._shouldForward("valid"))("ng-invalid",i._shouldForward("invalid"))("ng-pending",i._shouldForward("pending"))},inputs:{hideRequiredMarker:"hideRequiredMarker",color:"color",floatLabel:"floatLabel",appearance:"appearance",subscriptSizing:"subscriptSizing",hintLabel:"hintLabel"},exportAs:["matFormField"],features:[$([{provide:ze,useExisting:n},{provide:Sn,useExisting:n}])],ngContentSelectors:Aa,decls:18,vars:21,consts:[["labelTemplate",""],["textField",""],["iconPrefixContainer",""],["textPrefixContainer",""],["textSuffixContainer",""],["iconSuffixContainer",""],[1,"mat-mdc-text-field-wrapper","mdc-text-field",3,"click"],[1,"mat-mdc-form-field-focus-overlay"],[1,"mat-mdc-form-field-flex"],["matFormFieldNotchedOutline","",3,"matFormFieldNotchedOutlineOpen"],[1,"mat-mdc-form-field-icon-prefix"],[1,"mat-mdc-form-field-text-prefix"],[1,"mat-mdc-form-field-infix"],[3,"ngTemplateOutlet"],[1,"mat-mdc-form-field-text-suffix"],[1,"mat-mdc-form-field-icon-suffix"],["matFormFieldLineRipple",""],["aria-atomic","true","aria-live","polite",1,"mat-mdc-form-field-subscript-wrapper","mat-mdc-form-field-bottom-align"],[1,"mat-mdc-form-field-error-wrapper"],[1,"mat-mdc-form-field-hint-wrapper"],["matFormFieldFloatingLabel","",3,"floating","monitorResize","id"],["aria-hidden","true",1,"mat-mdc-form-field-required-marker","mdc-floating-label--required"],[3,"id"],[1,"mat-mdc-form-field-hint-spacer"]],template:function(e,i){if(e&1&&(B(Ra),tt(0,Ba,1,1,"ng-template",null,0,Te),l(2,"div",6,1),u("click",function(o){return i._control.onContainerClick(o)}),g(4,za,1,0,"div",7),l(5,"div",8),g(6,Va,2,2,"div",9),g(7,Ha,3,0,"div",10),g(8,$a,3,0,"div",11),l(9,"div",12),g(10,Qa,1,1,null,13),y(11),d(),g(12,Ga,3,0,"div",14),g(13,qa,3,0,"div",15),d(),g(14,Ua,1,0,"div",16),d(),l(15,"div",17),g(16,Ya,2,0,"div",18)(17,Za,5,1,"div",19),d()),e&2){let a;c(2),_("mdc-text-field--filled",!i._hasOutline())("mdc-text-field--outlined",i._hasOutline())("mdc-text-field--no-label",!i._hasFloatingLabel())("mdc-text-field--disabled",i._control.disabled)("mdc-text-field--invalid",i._control.errorState),c(2),v(!i._hasOutline()&&!i._control.disabled?4:-1),c(2),v(i._hasOutline()?6:-1),c(),v(i._hasIconPrefix?7:-1),c(),v(i._hasTextPrefix?8:-1),c(2),v(!i._hasOutline()||i._forceDisplayInfixLabel()?10:-1),c(2),v(i._hasTextSuffix?12:-1),c(),v(i._hasIconSuffix?13:-1),c(),v(i._hasOutline()?-1:14),c(),_("mat-mdc-form-field-subscript-dynamic-size",i.subscriptSizing==="dynamic");let o=i._getSubscriptMessageType();c(),v((a=o)==="error"?16:a==="hint"?17:-1)}},dependencies:[gn,xn,xi,yn,Pe],styles:[`.mdc-text-field { + display: inline-flex; + align-items: baseline; + padding: 0 16px; + position: relative; + box-sizing: border-box; + overflow: hidden; + will-change: opacity, transform, color; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.mdc-text-field__input { + width: 100%; + min-width: 0; + border: none; + border-radius: 0; + background: none; + padding: 0; + -moz-appearance: none; + -webkit-appearance: none; + height: 28px; +} +.mdc-text-field__input::-webkit-calendar-picker-indicator, .mdc-text-field__input::-webkit-search-cancel-button { + display: none; +} +.mdc-text-field__input::-ms-clear { + display: none; +} +.mdc-text-field__input:focus { + outline: none; +} +.mdc-text-field__input:invalid { + box-shadow: none; +} +.mdc-text-field__input::placeholder { + opacity: 0; +} +.mdc-text-field__input::-moz-placeholder { + opacity: 0; +} +.mdc-text-field__input::-webkit-input-placeholder { + opacity: 0; +} +.mdc-text-field__input:-ms-input-placeholder { + opacity: 0; +} +.mdc-text-field--no-label .mdc-text-field__input::placeholder, .mdc-text-field--focused .mdc-text-field__input::placeholder { + opacity: 1; +} +.mdc-text-field--no-label .mdc-text-field__input::-moz-placeholder, .mdc-text-field--focused .mdc-text-field__input::-moz-placeholder { + opacity: 1; +} +.mdc-text-field--no-label .mdc-text-field__input::-webkit-input-placeholder, .mdc-text-field--focused .mdc-text-field__input::-webkit-input-placeholder { + opacity: 1; +} +.mdc-text-field--no-label .mdc-text-field__input:-ms-input-placeholder, .mdc-text-field--focused .mdc-text-field__input:-ms-input-placeholder { + opacity: 1; +} +.mdc-text-field--disabled:not(.mdc-text-field--no-label) .mdc-text-field__input.mat-mdc-input-disabled-interactive::placeholder { + opacity: 0; +} +.mdc-text-field--disabled:not(.mdc-text-field--no-label) .mdc-text-field__input.mat-mdc-input-disabled-interactive::-moz-placeholder { + opacity: 0; +} +.mdc-text-field--disabled:not(.mdc-text-field--no-label) .mdc-text-field__input.mat-mdc-input-disabled-interactive::-webkit-input-placeholder { + opacity: 0; +} +.mdc-text-field--disabled:not(.mdc-text-field--no-label) .mdc-text-field__input.mat-mdc-input-disabled-interactive:-ms-input-placeholder { + opacity: 0; +} +.mdc-text-field--outlined .mdc-text-field__input, .mdc-text-field--filled.mdc-text-field--no-label .mdc-text-field__input { + height: 100%; +} +.mdc-text-field--outlined .mdc-text-field__input { + display: flex; + border: none !important; + background-color: transparent; +} +.mdc-text-field--disabled .mdc-text-field__input { + pointer-events: auto; +} +.mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-text-field__input { + color: var(--mat-form-field-filled-input-text-color, var(--mat-sys-on-surface)); + caret-color: var(--mat-form-field-filled-caret-color, var(--mat-sys-primary)); +} +.mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-text-field__input::placeholder { + color: var(--mat-form-field-filled-input-text-placeholder-color, var(--mat-sys-on-surface-variant)); +} +.mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-text-field__input::-moz-placeholder { + color: var(--mat-form-field-filled-input-text-placeholder-color, var(--mat-sys-on-surface-variant)); +} +.mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-text-field__input::-webkit-input-placeholder { + color: var(--mat-form-field-filled-input-text-placeholder-color, var(--mat-sys-on-surface-variant)); +} +.mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-text-field__input:-ms-input-placeholder { + color: var(--mat-form-field-filled-input-text-placeholder-color, var(--mat-sys-on-surface-variant)); +} +.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-text-field__input { + color: var(--mat-form-field-outlined-input-text-color, var(--mat-sys-on-surface)); + caret-color: var(--mat-form-field-outlined-caret-color, var(--mat-sys-primary)); +} +.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-text-field__input::placeholder { + color: var(--mat-form-field-outlined-input-text-placeholder-color, var(--mat-sys-on-surface-variant)); +} +.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-text-field__input::-moz-placeholder { + color: var(--mat-form-field-outlined-input-text-placeholder-color, var(--mat-sys-on-surface-variant)); +} +.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-text-field__input::-webkit-input-placeholder { + color: var(--mat-form-field-outlined-input-text-placeholder-color, var(--mat-sys-on-surface-variant)); +} +.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-text-field__input:-ms-input-placeholder { + color: var(--mat-form-field-outlined-input-text-placeholder-color, var(--mat-sys-on-surface-variant)); +} +.mdc-text-field--filled.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-text-field__input { + caret-color: var(--mat-form-field-filled-error-caret-color, var(--mat-sys-error)); +} +.mdc-text-field--outlined.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-text-field__input { + caret-color: var(--mat-form-field-outlined-error-caret-color, var(--mat-sys-error)); +} +.mdc-text-field--filled.mdc-text-field--disabled .mdc-text-field__input { + color: var(--mat-form-field-filled-disabled-input-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); +} +.mdc-text-field--outlined.mdc-text-field--disabled .mdc-text-field__input { + color: var(--mat-form-field-outlined-disabled-input-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); +} +@media (forced-colors: active) { + .mdc-text-field--disabled .mdc-text-field__input { + background-color: Window; + } +} + +.mdc-text-field--filled { + height: 56px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + border-top-left-radius: var(--mat-form-field-filled-container-shape, var(--mat-sys-corner-extra-small)); + border-top-right-radius: var(--mat-form-field-filled-container-shape, var(--mat-sys-corner-extra-small)); +} +.mdc-text-field--filled:not(.mdc-text-field--disabled) { + background-color: var(--mat-form-field-filled-container-color, var(--mat-sys-surface-variant)); +} +.mdc-text-field--filled.mdc-text-field--disabled { + background-color: var(--mat-form-field-filled-disabled-container-color, color-mix(in srgb, var(--mat-sys-on-surface) 4%, transparent)); +} + +.mdc-text-field--outlined { + height: 56px; + overflow: visible; + padding-right: max(16px, var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small))); + padding-left: max(16px, var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small)) + 4px); +} +[dir=rtl] .mdc-text-field--outlined { + padding-right: max(16px, var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small)) + 4px); + padding-left: max(16px, var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small))); +} + +.mdc-floating-label { + position: absolute; + left: 0; + transform-origin: left top; + line-height: 1.15rem; + text-align: left; + text-overflow: ellipsis; + white-space: nowrap; + cursor: text; + overflow: hidden; + will-change: transform; +} +[dir=rtl] .mdc-floating-label { + right: 0; + left: auto; + transform-origin: right top; + text-align: right; +} +.mdc-text-field .mdc-floating-label { + top: 50%; + transform: translateY(-50%); + pointer-events: none; +} +.mdc-notched-outline .mdc-floating-label { + display: inline-block; + position: relative; + max-width: 100%; +} +.mdc-text-field--outlined .mdc-floating-label { + left: 4px; + right: auto; +} +[dir=rtl] .mdc-text-field--outlined .mdc-floating-label { + left: auto; + right: 4px; +} +.mdc-text-field--filled .mdc-floating-label { + left: 16px; + right: auto; +} +[dir=rtl] .mdc-text-field--filled .mdc-floating-label { + left: auto; + right: 16px; +} +.mdc-text-field--disabled .mdc-floating-label { + cursor: default; +} +@media (forced-colors: active) { + .mdc-text-field--disabled .mdc-floating-label { + z-index: 1; + } +} +.mdc-text-field--filled.mdc-text-field--no-label .mdc-floating-label { + display: none; +} +.mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-floating-label { + color: var(--mat-form-field-filled-label-text-color, var(--mat-sys-on-surface-variant)); +} +.mdc-text-field--filled:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-floating-label { + color: var(--mat-form-field-filled-focus-label-text-color, var(--mat-sys-primary)); +} +.mdc-text-field--filled:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-floating-label { + color: var(--mat-form-field-filled-hover-label-text-color, var(--mat-sys-on-surface-variant)); +} +.mdc-text-field--filled.mdc-text-field--disabled .mdc-floating-label { + color: var(--mat-form-field-filled-disabled-label-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); +} +.mdc-text-field--filled:not(.mdc-text-field--disabled).mdc-text-field--invalid .mdc-floating-label { + color: var(--mat-form-field-filled-error-label-text-color, var(--mat-sys-error)); +} +.mdc-text-field--filled:not(.mdc-text-field--disabled).mdc-text-field--invalid.mdc-text-field--focused .mdc-floating-label { + color: var(--mat-form-field-filled-error-focus-label-text-color, var(--mat-sys-error)); +} +.mdc-text-field--filled:not(.mdc-text-field--disabled).mdc-text-field--invalid:not(.mdc-text-field--disabled):hover .mdc-floating-label { + color: var(--mat-form-field-filled-error-hover-label-text-color, var(--mat-sys-on-error-container)); +} +.mdc-text-field--filled .mdc-floating-label { + font-family: var(--mat-form-field-filled-label-text-font, var(--mat-sys-body-large-font)); + font-size: var(--mat-form-field-filled-label-text-size, var(--mat-sys-body-large-size)); + font-weight: var(--mat-form-field-filled-label-text-weight, var(--mat-sys-body-large-weight)); + letter-spacing: var(--mat-form-field-filled-label-text-tracking, var(--mat-sys-body-large-tracking)); +} +.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-floating-label { + color: var(--mat-form-field-outlined-label-text-color, var(--mat-sys-on-surface-variant)); +} +.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-floating-label { + color: var(--mat-form-field-outlined-focus-label-text-color, var(--mat-sys-primary)); +} +.mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-floating-label { + color: var(--mat-form-field-outlined-hover-label-text-color, var(--mat-sys-on-surface)); +} +.mdc-text-field--outlined.mdc-text-field--disabled .mdc-floating-label { + color: var(--mat-form-field-outlined-disabled-label-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); +} +.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--invalid .mdc-floating-label { + color: var(--mat-form-field-outlined-error-label-text-color, var(--mat-sys-error)); +} +.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--invalid.mdc-text-field--focused .mdc-floating-label { + color: var(--mat-form-field-outlined-error-focus-label-text-color, var(--mat-sys-error)); +} +.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--invalid:not(.mdc-text-field--disabled):hover .mdc-floating-label { + color: var(--mat-form-field-outlined-error-hover-label-text-color, var(--mat-sys-on-error-container)); +} +.mdc-text-field--outlined .mdc-floating-label { + font-family: var(--mat-form-field-outlined-label-text-font, var(--mat-sys-body-large-font)); + font-size: var(--mat-form-field-outlined-label-text-size, var(--mat-sys-body-large-size)); + font-weight: var(--mat-form-field-outlined-label-text-weight, var(--mat-sys-body-large-weight)); + letter-spacing: var(--mat-form-field-outlined-label-text-tracking, var(--mat-sys-body-large-tracking)); +} + +.mdc-floating-label--float-above { + cursor: auto; + transform: translateY(-106%) scale(0.75); +} +.mdc-text-field--filled .mdc-floating-label--float-above { + transform: translateY(-106%) scale(0.75); +} +.mdc-text-field--outlined .mdc-floating-label--float-above { + transform: translateY(-37.25px) scale(1); + font-size: 0.75rem; +} +.mdc-notched-outline .mdc-floating-label--float-above { + text-overflow: clip; +} +.mdc-notched-outline--upgraded .mdc-floating-label--float-above { + max-width: 133.3333333333%; +} +.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above, .mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above { + transform: translateY(-34.75px) scale(0.75); +} +.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above, .mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above { + font-size: 1rem; +} + +.mdc-floating-label--required:not(.mdc-floating-label--hide-required-marker)::after { + margin-left: 1px; + margin-right: 0; + content: "*"; +} +[dir=rtl] .mdc-floating-label--required:not(.mdc-floating-label--hide-required-marker)::after { + margin-left: 0; + margin-right: 1px; +} + +.mdc-notched-outline { + display: flex; + position: absolute; + top: 0; + right: 0; + left: 0; + box-sizing: border-box; + width: 100%; + max-width: 100%; + height: 100%; + text-align: left; + pointer-events: none; +} +[dir=rtl] .mdc-notched-outline { + text-align: right; +} +.mdc-text-field--outlined .mdc-notched-outline { + z-index: 1; +} + +.mat-mdc-notch-piece { + box-sizing: border-box; + height: 100%; + pointer-events: none; + border: none; + border-top: 1px solid; + border-bottom: 1px solid; +} +.mdc-text-field--focused .mat-mdc-notch-piece { + border-width: 2px; +} +.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mat-mdc-notch-piece { + border-color: var(--mat-form-field-outlined-outline-color, var(--mat-sys-outline)); + border-width: var(--mat-form-field-outlined-outline-width, 1px); +} +.mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mat-mdc-notch-piece { + border-color: var(--mat-form-field-outlined-hover-outline-color, var(--mat-sys-on-surface)); +} +.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--focused .mat-mdc-notch-piece { + border-color: var(--mat-form-field-outlined-focus-outline-color, var(--mat-sys-primary)); +} +.mdc-text-field--outlined.mdc-text-field--disabled .mat-mdc-notch-piece { + border-color: var(--mat-form-field-outlined-disabled-outline-color, color-mix(in srgb, var(--mat-sys-on-surface) 12%, transparent)); +} +.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--invalid .mat-mdc-notch-piece { + border-color: var(--mat-form-field-outlined-error-outline-color, var(--mat-sys-error)); +} +.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--invalid:not(.mdc-text-field--focused):hover .mdc-notched-outline .mat-mdc-notch-piece { + border-color: var(--mat-form-field-outlined-error-hover-outline-color, var(--mat-sys-on-error-container)); +} +.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--invalid.mdc-text-field--focused .mat-mdc-notch-piece { + border-color: var(--mat-form-field-outlined-error-focus-outline-color, var(--mat-sys-error)); +} +.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline .mat-mdc-notch-piece { + border-width: var(--mat-form-field-outlined-focus-outline-width, 2px); +} + +.mdc-notched-outline__leading { + border-left: 1px solid; + border-right: none; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-top-left-radius: var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small)); + border-bottom-left-radius: var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small)); +} +.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading { + width: max(12px, var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small))); +} +[dir=rtl] .mdc-notched-outline__leading { + border-left: none; + border-right: 1px solid; + border-bottom-left-radius: 0; + border-top-left-radius: 0; + border-top-right-radius: var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small)); + border-bottom-right-radius: var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small)); +} + +.mdc-notched-outline__trailing { + flex-grow: 1; + border-left: none; + border-right: 1px solid; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + border-top-right-radius: var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small)); + border-bottom-right-radius: var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small)); +} +[dir=rtl] .mdc-notched-outline__trailing { + border-left: 1px solid; + border-right: none; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-top-left-radius: var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small)); + border-bottom-left-radius: var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small)); +} + +.mdc-notched-outline__notch { + flex: 0 0 auto; + width: auto; +} +.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__notch { + max-width: min(var(--mat-form-field-notch-max-width, 100%), calc(100% - max(12px, var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small))) * 2)); +} +.mdc-text-field--outlined .mdc-notched-outline--notched .mdc-notched-outline__notch { + max-width: min(100%, calc(100% - max(12px, var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small))) * 2)); +} +.mdc-text-field--outlined .mdc-notched-outline--notched .mdc-notched-outline__notch { + padding-top: 1px; +} +.mdc-text-field--focused.mdc-text-field--outlined .mdc-notched-outline--notched .mdc-notched-outline__notch { + padding-top: 2px; +} +.mdc-notched-outline--notched .mdc-notched-outline__notch { + padding-left: 0; + padding-right: 8px; + border-top: none; +} +[dir=rtl] .mdc-notched-outline--notched .mdc-notched-outline__notch { + padding-left: 8px; + padding-right: 0; +} +.mdc-notched-outline--no-label .mdc-notched-outline__notch { + display: none; +} + +.mdc-line-ripple::before, .mdc-line-ripple::after { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + border-bottom-style: solid; + content: ""; +} +.mdc-line-ripple::before { + z-index: 1; + border-bottom-width: var(--mat-form-field-filled-active-indicator-height, 1px); +} +.mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-line-ripple::before { + border-bottom-color: var(--mat-form-field-filled-active-indicator-color, var(--mat-sys-on-surface-variant)); +} +.mdc-text-field--filled:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-line-ripple::before { + border-bottom-color: var(--mat-form-field-filled-hover-active-indicator-color, var(--mat-sys-on-surface)); +} +.mdc-text-field--filled.mdc-text-field--disabled .mdc-line-ripple::before { + border-bottom-color: var(--mat-form-field-filled-disabled-active-indicator-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); +} +.mdc-text-field--filled:not(.mdc-text-field--disabled).mdc-text-field--invalid .mdc-line-ripple::before { + border-bottom-color: var(--mat-form-field-filled-error-active-indicator-color, var(--mat-sys-error)); +} +.mdc-text-field--filled:not(.mdc-text-field--disabled).mdc-text-field--invalid:not(.mdc-text-field--focused):hover .mdc-line-ripple::before { + border-bottom-color: var(--mat-form-field-filled-error-hover-active-indicator-color, var(--mat-sys-on-error-container)); +} +.mdc-line-ripple::after { + transform: scaleX(0); + opacity: 0; + z-index: 2; +} +.mdc-text-field--filled .mdc-line-ripple::after { + border-bottom-width: var(--mat-form-field-filled-focus-active-indicator-height, 2px); +} +.mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-line-ripple::after { + border-bottom-color: var(--mat-form-field-filled-focus-active-indicator-color, var(--mat-sys-primary)); +} +.mdc-text-field--filled.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-line-ripple::after { + border-bottom-color: var(--mat-form-field-filled-error-focus-active-indicator-color, var(--mat-sys-error)); +} + +.mdc-line-ripple--active::after { + transform: scaleX(1); + opacity: 1; +} + +.mdc-line-ripple--deactivating::after { + opacity: 0; +} + +.mdc-text-field--disabled { + pointer-events: none; +} + +.mat-mdc-form-field-textarea-control { + vertical-align: middle; + resize: vertical; + box-sizing: border-box; + height: auto; + margin: 0; + padding: 0; + border: none; + overflow: auto; +} + +.mat-mdc-form-field-input-control.mat-mdc-form-field-input-control { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + font: inherit; + letter-spacing: inherit; + text-decoration: inherit; + text-transform: inherit; + border: none; +} + +.mat-mdc-form-field .mat-mdc-floating-label.mdc-floating-label { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + line-height: normal; + pointer-events: all; + will-change: auto; +} + +.mat-mdc-form-field:not(.mat-form-field-disabled) .mat-mdc-floating-label.mdc-floating-label { + cursor: inherit; +} + +.mdc-text-field--no-label:not(.mdc-text-field--textarea) .mat-mdc-form-field-input-control.mdc-text-field__input, +.mat-mdc-text-field-wrapper .mat-mdc-form-field-input-control { + height: auto; +} + +.mat-mdc-text-field-wrapper .mat-mdc-form-field-input-control.mdc-text-field__input[type=color] { + height: 23px; +} + +.mat-mdc-text-field-wrapper { + height: auto; + flex: auto; + will-change: auto; +} + +.mat-mdc-form-field-has-icon-prefix .mat-mdc-text-field-wrapper { + padding-left: 0; + --mat-mdc-form-field-label-offset-x: -16px; +} + +.mat-mdc-form-field-has-icon-suffix .mat-mdc-text-field-wrapper { + padding-right: 0; +} + +[dir=rtl] .mat-mdc-text-field-wrapper { + padding-left: 16px; + padding-right: 16px; +} +[dir=rtl] .mat-mdc-form-field-has-icon-suffix .mat-mdc-text-field-wrapper { + padding-left: 0; +} +[dir=rtl] .mat-mdc-form-field-has-icon-prefix .mat-mdc-text-field-wrapper { + padding-right: 0; +} + +.mat-form-field-disabled .mdc-text-field__input::placeholder { + color: var(--mat-form-field-disabled-input-text-placeholder-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); +} +.mat-form-field-disabled .mdc-text-field__input::-moz-placeholder { + color: var(--mat-form-field-disabled-input-text-placeholder-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); +} +.mat-form-field-disabled .mdc-text-field__input::-webkit-input-placeholder { + color: var(--mat-form-field-disabled-input-text-placeholder-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); +} +.mat-form-field-disabled .mdc-text-field__input:-ms-input-placeholder { + color: var(--mat-form-field-disabled-input-text-placeholder-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); +} + +.mat-mdc-form-field-label-always-float .mdc-text-field__input::placeholder { + transition-delay: 40ms; + transition-duration: 110ms; + opacity: 1; +} + +.mat-mdc-text-field-wrapper .mat-mdc-form-field-infix .mat-mdc-floating-label { + left: auto; + right: auto; +} + +.mat-mdc-text-field-wrapper.mdc-text-field--outlined .mdc-text-field__input { + display: inline-block; +} + +.mat-mdc-form-field .mat-mdc-text-field-wrapper.mdc-text-field .mdc-notched-outline__notch { + padding-top: 0; +} + +.mat-mdc-form-field.mat-mdc-form-field.mat-mdc-form-field.mat-mdc-form-field.mat-mdc-form-field.mat-mdc-form-field .mdc-notched-outline__notch { + border-left: 1px solid transparent; +} + +[dir=rtl] .mat-mdc-form-field.mat-mdc-form-field.mat-mdc-form-field.mat-mdc-form-field.mat-mdc-form-field.mat-mdc-form-field .mdc-notched-outline__notch { + border-left: none; + border-right: 1px solid transparent; +} + +.mat-mdc-form-field-infix { + min-height: var(--mat-form-field-container-height, 56px); + padding-top: var(--mat-form-field-filled-with-label-container-padding-top, 24px); + padding-bottom: var(--mat-form-field-filled-with-label-container-padding-bottom, 8px); +} +.mdc-text-field--outlined .mat-mdc-form-field-infix, .mdc-text-field--no-label .mat-mdc-form-field-infix { + padding-top: var(--mat-form-field-container-vertical-padding, 16px); + padding-bottom: var(--mat-form-field-container-vertical-padding, 16px); +} + +.mat-mdc-text-field-wrapper .mat-mdc-form-field-flex .mat-mdc-floating-label { + top: calc(var(--mat-form-field-container-height, 56px) / 2); +} + +.mdc-text-field--filled .mat-mdc-floating-label { + display: var(--mat-form-field-filled-label-display, block); +} + +.mat-mdc-text-field-wrapper.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above { + --mat-mdc-form-field-label-transform: translateY(calc(calc(6.75px + var(--mat-form-field-container-height, 56px) / 2) * -1)) + scale(var(--mat-mdc-form-field-floating-label-scale, 0.75)); + transform: var(--mat-mdc-form-field-label-transform); +} + +@keyframes _mat-form-field-subscript-animation { + from { + opacity: 0; + transform: translateY(-5px); + } + to { + opacity: 1; + transform: translateY(0); + } +} +.mat-mdc-form-field-subscript-wrapper { + box-sizing: border-box; + width: 100%; + position: relative; +} + +.mat-mdc-form-field-hint-wrapper, +.mat-mdc-form-field-error-wrapper { + position: absolute; + top: 0; + left: 0; + right: 0; + padding: 0 16px; + opacity: 1; + transform: translateY(0); + animation: _mat-form-field-subscript-animation 0ms cubic-bezier(0.55, 0, 0.55, 0.2); +} + +.mat-mdc-form-field-subscript-dynamic-size .mat-mdc-form-field-hint-wrapper, +.mat-mdc-form-field-subscript-dynamic-size .mat-mdc-form-field-error-wrapper { + position: static; +} + +.mat-mdc-form-field-bottom-align::before { + content: ""; + display: inline-block; + height: 16px; +} + +.mat-mdc-form-field-bottom-align.mat-mdc-form-field-subscript-dynamic-size::before { + content: unset; +} + +.mat-mdc-form-field-hint-end { + order: 1; +} + +.mat-mdc-form-field-hint-wrapper { + display: flex; +} + +.mat-mdc-form-field-hint-spacer { + flex: 1 0 1em; +} + +.mat-mdc-form-field-error { + display: block; + color: var(--mat-form-field-error-text-color, var(--mat-sys-error)); +} + +.mat-mdc-form-field-subscript-wrapper, +.mat-mdc-form-field-bottom-align::before { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + font-family: var(--mat-form-field-subscript-text-font, var(--mat-sys-body-small-font)); + line-height: var(--mat-form-field-subscript-text-line-height, var(--mat-sys-body-small-line-height)); + font-size: var(--mat-form-field-subscript-text-size, var(--mat-sys-body-small-size)); + letter-spacing: var(--mat-form-field-subscript-text-tracking, var(--mat-sys-body-small-tracking)); + font-weight: var(--mat-form-field-subscript-text-weight, var(--mat-sys-body-small-weight)); +} + +.mat-mdc-form-field-focus-overlay { + top: 0; + left: 0; + right: 0; + bottom: 0; + position: absolute; + opacity: 0; + pointer-events: none; + background-color: var(--mat-form-field-state-layer-color, var(--mat-sys-on-surface)); +} +.mat-mdc-text-field-wrapper:hover .mat-mdc-form-field-focus-overlay { + opacity: var(--mat-form-field-hover-state-layer-opacity, var(--mat-sys-hover-state-layer-opacity)); +} +.mat-mdc-form-field.mat-focused .mat-mdc-form-field-focus-overlay { + opacity: var(--mat-form-field-focus-state-layer-opacity, 0); +} + +select.mat-mdc-form-field-input-control { + -moz-appearance: none; + -webkit-appearance: none; + background-color: transparent; + display: inline-flex; + box-sizing: border-box; +} +select.mat-mdc-form-field-input-control:not(:disabled) { + cursor: pointer; +} +select.mat-mdc-form-field-input-control:not(.mat-mdc-native-select-inline) option { + color: var(--mat-form-field-select-option-text-color, var(--mat-sys-neutral10)); +} +select.mat-mdc-form-field-input-control:not(.mat-mdc-native-select-inline) option:disabled { + color: var(--mat-form-field-select-disabled-option-text-color, color-mix(in srgb, var(--mat-sys-neutral10) 38%, transparent)); +} + +.mat-mdc-form-field-type-mat-native-select .mat-mdc-form-field-infix::after { + content: ""; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 5px solid; + position: absolute; + right: 0; + top: 50%; + margin-top: -2.5px; + pointer-events: none; + color: var(--mat-form-field-enabled-select-arrow-color, var(--mat-sys-on-surface-variant)); +} +[dir=rtl] .mat-mdc-form-field-type-mat-native-select .mat-mdc-form-field-infix::after { + right: auto; + left: 0; +} +.mat-mdc-form-field-type-mat-native-select.mat-focused .mat-mdc-form-field-infix::after { + color: var(--mat-form-field-focus-select-arrow-color, var(--mat-sys-primary)); +} +.mat-mdc-form-field-type-mat-native-select.mat-form-field-disabled .mat-mdc-form-field-infix::after { + color: var(--mat-form-field-disabled-select-arrow-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); +} +.mat-mdc-form-field-type-mat-native-select .mat-mdc-form-field-input-control { + padding-right: 15px; +} +[dir=rtl] .mat-mdc-form-field-type-mat-native-select .mat-mdc-form-field-input-control { + padding-right: 0; + padding-left: 15px; +} + +@media (forced-colors: active) { + .mat-form-field-appearance-fill .mat-mdc-text-field-wrapper { + outline: solid 1px; + } +} +@media (forced-colors: active) { + .mat-form-field-appearance-fill.mat-form-field-disabled .mat-mdc-text-field-wrapper { + outline-color: GrayText; + } +} + +@media (forced-colors: active) { + .mat-form-field-appearance-fill.mat-focused .mat-mdc-text-field-wrapper { + outline: dashed 3px; + } +} + +@media (forced-colors: active) { + .mat-mdc-form-field.mat-focused .mdc-notched-outline { + border: dashed 3px; + } +} + +.mat-mdc-form-field-input-control[type=date], .mat-mdc-form-field-input-control[type=datetime], .mat-mdc-form-field-input-control[type=datetime-local], .mat-mdc-form-field-input-control[type=month], .mat-mdc-form-field-input-control[type=week], .mat-mdc-form-field-input-control[type=time] { + line-height: 1; +} +.mat-mdc-form-field-input-control::-webkit-datetime-edit { + line-height: 1; + padding: 0; + margin-bottom: -2px; +} + +.mat-mdc-form-field { + --mat-mdc-form-field-floating-label-scale: 0.75; + display: inline-flex; + flex-direction: column; + min-width: 0; + text-align: left; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + font-family: var(--mat-form-field-container-text-font, var(--mat-sys-body-large-font)); + line-height: var(--mat-form-field-container-text-line-height, var(--mat-sys-body-large-line-height)); + font-size: var(--mat-form-field-container-text-size, var(--mat-sys-body-large-size)); + letter-spacing: var(--mat-form-field-container-text-tracking, var(--mat-sys-body-large-tracking)); + font-weight: var(--mat-form-field-container-text-weight, var(--mat-sys-body-large-weight)); +} +.mat-mdc-form-field .mdc-text-field--outlined .mdc-floating-label--float-above { + font-size: calc(var(--mat-form-field-outlined-label-text-populated-size) * var(--mat-mdc-form-field-floating-label-scale)); +} +.mat-mdc-form-field .mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above { + font-size: var(--mat-form-field-outlined-label-text-populated-size); +} +[dir=rtl] .mat-mdc-form-field { + text-align: right; +} + +.mat-mdc-form-field-flex { + display: inline-flex; + align-items: baseline; + box-sizing: border-box; + width: 100%; +} + +.mat-mdc-text-field-wrapper { + width: 100%; + z-index: 0; +} + +.mat-mdc-form-field-icon-prefix, +.mat-mdc-form-field-icon-suffix { + align-self: center; + line-height: 0; + pointer-events: auto; + position: relative; + z-index: 1; +} +.mat-mdc-form-field-icon-prefix > .mat-icon, +.mat-mdc-form-field-icon-suffix > .mat-icon { + padding: 0 12px; + box-sizing: content-box; +} + +.mat-mdc-form-field-icon-prefix { + color: var(--mat-form-field-leading-icon-color, var(--mat-sys-on-surface-variant)); +} +.mat-form-field-disabled .mat-mdc-form-field-icon-prefix { + color: var(--mat-form-field-disabled-leading-icon-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); +} + +.mat-mdc-form-field-icon-suffix { + color: var(--mat-form-field-trailing-icon-color, var(--mat-sys-on-surface-variant)); +} +.mat-form-field-disabled .mat-mdc-form-field-icon-suffix { + color: var(--mat-form-field-disabled-trailing-icon-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); +} +.mat-form-field-invalid .mat-mdc-form-field-icon-suffix { + color: var(--mat-form-field-error-trailing-icon-color, var(--mat-sys-error)); +} +.mat-form-field-invalid:not(.mat-focused):not(.mat-form-field-disabled) .mat-mdc-text-field-wrapper:hover .mat-mdc-form-field-icon-suffix { + color: var(--mat-form-field-error-hover-trailing-icon-color, var(--mat-sys-on-error-container)); +} +.mat-form-field-invalid.mat-focused .mat-mdc-text-field-wrapper .mat-mdc-form-field-icon-suffix { + color: var(--mat-form-field-error-focus-trailing-icon-color, var(--mat-sys-error)); +} + +.mat-mdc-form-field-icon-prefix, +[dir=rtl] .mat-mdc-form-field-icon-suffix { + padding: 0 4px 0 0; +} + +.mat-mdc-form-field-icon-suffix, +[dir=rtl] .mat-mdc-form-field-icon-prefix { + padding: 0 0 0 4px; +} + +.mat-mdc-form-field-subscript-wrapper .mat-icon, +.mat-mdc-form-field label .mat-icon { + width: 1em; + height: 1em; + font-size: inherit; +} + +.mat-mdc-form-field-infix { + flex: auto; + min-width: 0; + width: 180px; + position: relative; + box-sizing: border-box; +} +.mat-mdc-form-field-infix:has(textarea[cols]) { + width: auto; +} + +.mat-mdc-form-field .mdc-notched-outline__notch { + margin-left: -1px; + -webkit-clip-path: inset(-9em -999em -9em 1px); + clip-path: inset(-9em -999em -9em 1px); +} +[dir=rtl] .mat-mdc-form-field .mdc-notched-outline__notch { + margin-left: 0; + margin-right: -1px; + -webkit-clip-path: inset(-9em 1px -9em -999em); + clip-path: inset(-9em 1px -9em -999em); +} + +.mat-mdc-form-field.mat-form-field-animations-enabled .mdc-floating-label { + transition: transform 150ms cubic-bezier(0.4, 0, 0.2, 1), color 150ms cubic-bezier(0.4, 0, 0.2, 1); +} +.mat-mdc-form-field.mat-form-field-animations-enabled .mdc-text-field__input { + transition: opacity 150ms cubic-bezier(0.4, 0, 0.2, 1); +} +.mat-mdc-form-field.mat-form-field-animations-enabled .mdc-text-field__input::placeholder { + transition: opacity 67ms cubic-bezier(0.4, 0, 0.2, 1); +} +.mat-mdc-form-field.mat-form-field-animations-enabled .mdc-text-field__input::-moz-placeholder { + transition: opacity 67ms cubic-bezier(0.4, 0, 0.2, 1); +} +.mat-mdc-form-field.mat-form-field-animations-enabled .mdc-text-field__input::-webkit-input-placeholder { + transition: opacity 67ms cubic-bezier(0.4, 0, 0.2, 1); +} +.mat-mdc-form-field.mat-form-field-animations-enabled .mdc-text-field__input:-ms-input-placeholder { + transition: opacity 67ms cubic-bezier(0.4, 0, 0.2, 1); +} +.mat-mdc-form-field.mat-form-field-animations-enabled.mdc-text-field--no-label .mdc-text-field__input::placeholder, .mat-mdc-form-field.mat-form-field-animations-enabled.mdc-text-field--focused .mdc-text-field__input::placeholder { + transition-delay: 40ms; + transition-duration: 110ms; +} +.mat-mdc-form-field.mat-form-field-animations-enabled.mdc-text-field--no-label .mdc-text-field__input::-moz-placeholder, .mat-mdc-form-field.mat-form-field-animations-enabled.mdc-text-field--focused .mdc-text-field__input::-moz-placeholder { + transition-delay: 40ms; + transition-duration: 110ms; +} +.mat-mdc-form-field.mat-form-field-animations-enabled.mdc-text-field--no-label .mdc-text-field__input::-webkit-input-placeholder, .mat-mdc-form-field.mat-form-field-animations-enabled.mdc-text-field--focused .mdc-text-field__input::-webkit-input-placeholder { + transition-delay: 40ms; + transition-duration: 110ms; +} +.mat-mdc-form-field.mat-form-field-animations-enabled.mdc-text-field--no-label .mdc-text-field__input:-ms-input-placeholder, .mat-mdc-form-field.mat-form-field-animations-enabled.mdc-text-field--focused .mdc-text-field__input:-ms-input-placeholder { + transition-delay: 40ms; + transition-duration: 110ms; +} +.mat-mdc-form-field.mat-form-field-animations-enabled .mdc-text-field--filled:not(.mdc-ripple-upgraded):focus .mdc-text-field__ripple::before { + transition-duration: 75ms; +} +.mat-mdc-form-field.mat-form-field-animations-enabled .mdc-line-ripple::after { + transition: transform 180ms cubic-bezier(0.4, 0, 0.2, 1), opacity 180ms cubic-bezier(0.4, 0, 0.2, 1); +} +.mat-mdc-form-field.mat-form-field-animations-enabled .mat-mdc-form-field-hint-wrapper, +.mat-mdc-form-field.mat-form-field-animations-enabled .mat-mdc-form-field-error-wrapper { + animation-duration: 300ms; +} + +.mdc-notched-outline .mdc-floating-label { + max-width: calc(100% + 1px); +} + +.mdc-notched-outline--upgraded .mdc-floating-label--float-above { + max-width: calc(133.3333333333% + 1px); +} +`],encapsulation:2,changeDetection:0})}return n})();var oo=["*",[["mat-option"],["ng-container"]]],ro=["*","mat-option, ng-container"],so=["text"],lo=[[["mat-icon"]],"*"],co=["mat-icon","*"];function mo(n,s){if(n&1&&M(0,"mat-pseudo-checkbox",1),n&2){let t=p();D("disabled",t.disabled)("state",t.selected?"checked":"unchecked")}}function ho(n,s){if(n&1&&M(0,"mat-pseudo-checkbox",3),n&2){let t=p();D("disabled",t.disabled)}}function po(n,s){if(n&1&&(l(0,"span",4),E(1),d()),n&2){let t=p();c(),ft("(",t.group.label,")")}}var ue=new w("MAT_OPTION_PARENT_COMPONENT"),_e=new w("MatOptgroup"),je=(()=>{class n{label;disabled=!1;_labelId=r(U).getId("mat-optgroup-label-");_inert;constructor(){let t=r(ue,{optional:!0});this._inert=t?.inertGroups??!1}static \u0275fac=function(e){return new(e||n)};static \u0275cmp=C({type:n,selectors:[["mat-optgroup"]],hostAttrs:[1,"mat-mdc-optgroup"],hostVars:3,hostBindings:function(e,i){e&2&&T("role",i._inert?null:"group")("aria-disabled",i._inert?null:i.disabled.toString())("aria-labelledby",i._inert?null:i._labelId)},inputs:{label:"label",disabled:[2,"disabled","disabled",S]},exportAs:["matOptgroup"],features:[$([{provide:_e,useExisting:n}])],ngContentSelectors:ro,decls:5,vars:4,consts:[["role","presentation",1,"mat-mdc-optgroup-label",3,"id"],[1,"mdc-list-item__primary-text"]],template:function(e,i){e&1&&(B(oo),kt(0,"span",0)(1,"span",1),E(2),y(3),Ot()(),y(4,1)),e&2&&(_("mdc-list-item--disabled",i.disabled),Rt("id",i._labelId),c(2),ft("",i.label," "))},styles:[`.mat-mdc-optgroup { + color: var(--mat-optgroup-label-text-color, var(--mat-sys-on-surface-variant)); + font-family: var(--mat-optgroup-label-text-font, var(--mat-sys-title-small-font)); + line-height: var(--mat-optgroup-label-text-line-height, var(--mat-sys-title-small-line-height)); + font-size: var(--mat-optgroup-label-text-size, var(--mat-sys-title-small-size)); + letter-spacing: var(--mat-optgroup-label-text-tracking, var(--mat-sys-title-small-tracking)); + font-weight: var(--mat-optgroup-label-text-weight, var(--mat-sys-title-small-weight)); +} + +.mat-mdc-optgroup-label { + display: flex; + position: relative; + align-items: center; + justify-content: flex-start; + overflow: hidden; + min-height: 48px; + padding: 0 16px; + outline: none; +} +.mat-mdc-optgroup-label.mdc-list-item--disabled { + opacity: 0.38; +} +.mat-mdc-optgroup-label .mdc-list-item__primary-text { + font-size: inherit; + font-weight: inherit; + letter-spacing: inherit; + line-height: inherit; + font-family: inherit; + text-decoration: inherit; + text-transform: inherit; + white-space: normal; + color: inherit; +} +`],encapsulation:2,changeDetection:0})}return n})(),Ne=class{source;isUserInput;constructor(s,t=!1){this.source=s,this.isUserInput=t}},zt=(()=>{class n{_element=r(O);_changeDetectorRef=r(W);_parent=r(ue,{optional:!0});group=r(_e,{optional:!0});_signalDisableRipple=!1;_selected=!1;_active=!1;_mostRecentViewValue="";get multiple(){return this._parent&&this._parent.multiple}get selected(){return this._selected}value;id=r(U).getId("mat-option-");get disabled(){return this.group&&this.group.disabled||this._disabled()}set disabled(t){this._disabled.set(t)}_disabled=Z(!1);get disableRipple(){return this._signalDisableRipple?this._parent.disableRipple():!!this._parent?.disableRipple}get hideSingleSelectionIndicator(){return!!(this._parent&&this._parent.hideSingleSelectionIndicator)}onSelectionChange=new F;_text;_stateChanges=new k;constructor(){let t=r(wt);t.load(Ct),t.load(zi),this._signalDisableRipple=!!this._parent&&oi(this._parent.disableRipple)}get active(){return this._active}get viewValue(){return(this._text?.nativeElement.textContent||"").trim()}select(t=!0){this._selected||(this._selected=!0,this._changeDetectorRef.markForCheck(),t&&this._emitSelectionChangeEvent())}deselect(t=!0){this._selected&&(this._selected=!1,this._changeDetectorRef.markForCheck(),t&&this._emitSelectionChangeEvent())}focus(t,e){let i=this._getHostElement();typeof i.focus=="function"&&i.focus(e)}setActiveStyles(){this._active||(this._active=!0,this._changeDetectorRef.markForCheck())}setInactiveStyles(){this._active&&(this._active=!1,this._changeDetectorRef.markForCheck())}getLabel(){return this.viewValue}_handleKeydown(t){(t.keyCode===13||t.keyCode===32)&&!ct(t)&&(this._selectViaInteraction(),t.preventDefault())}_selectViaInteraction(){this.disabled||(this._selected=this.multiple?!this._selected:!0,this._changeDetectorRef.markForCheck(),this._emitSelectionChangeEvent(!0))}_getTabIndex(){return this.disabled?"-1":"0"}_getHostElement(){return this._element.nativeElement}ngAfterViewChecked(){if(this._selected){let t=this.viewValue;t!==this._mostRecentViewValue&&(this._mostRecentViewValue&&this._stateChanges.next(),this._mostRecentViewValue=t)}}ngOnDestroy(){this._stateChanges.complete()}_emitSelectionChangeEvent(t=!1){this.onSelectionChange.emit(new Ne(this,t))}static \u0275fac=function(e){return new(e||n)};static \u0275cmp=C({type:n,selectors:[["mat-option"]],viewQuery:function(e,i){if(e&1&&V(so,7),e&2){let a;m(a=h())&&(i._text=a.first)}},hostAttrs:["role","option",1,"mat-mdc-option","mdc-list-item"],hostVars:11,hostBindings:function(e,i){e&1&&u("click",function(){return i._selectViaInteraction()})("keydown",function(o){return i._handleKeydown(o)}),e&2&&(Rt("id",i.id),T("aria-selected",i.selected)("aria-disabled",i.disabled.toString()),_("mdc-list-item--selected",i.selected)("mat-mdc-option-multiple",i.multiple)("mat-mdc-option-active",i.active)("mdc-list-item--disabled",i.disabled))},inputs:{value:"value",id:"id",disabled:[2,"disabled","disabled",S]},outputs:{onSelectionChange:"onSelectionChange"},exportAs:["matOption"],ngContentSelectors:co,decls:8,vars:5,consts:[["text",""],["aria-hidden","true",1,"mat-mdc-option-pseudo-checkbox",3,"disabled","state"],[1,"mdc-list-item__primary-text"],["state","checked","aria-hidden","true","appearance","minimal",1,"mat-mdc-option-pseudo-checkbox",3,"disabled"],[1,"cdk-visually-hidden"],["aria-hidden","true","mat-ripple","",1,"mat-mdc-option-ripple","mat-focus-indicator",3,"matRippleTrigger","matRippleDisabled"]],template:function(e,i){e&1&&(B(lo),g(0,mo,1,2,"mat-pseudo-checkbox",1),y(1),l(2,"span",2,0),y(4,1),d(),g(5,ho,1,1,"mat-pseudo-checkbox",3),g(6,po,2,1,"span",4),M(7,"div",5)),e&2&&(v(i.multiple?0:-1),c(5),v(!i.multiple&&i.selected&&!i.hideSingleSelectionIndicator?5:-1),c(),v(i.group&&i.group._inert?6:-1),c(),D("matRippleTrigger",i._getHostElement())("matRippleDisabled",i.disabled||i.disableRipple))},dependencies:[Ki,vt],styles:[`.mat-mdc-option { + -webkit-user-select: none; + user-select: none; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + display: flex; + position: relative; + align-items: center; + justify-content: flex-start; + overflow: hidden; + min-height: 48px; + padding: 0 16px; + cursor: pointer; + -webkit-tap-highlight-color: transparent; + color: var(--mat-option-label-text-color, var(--mat-sys-on-surface)); + font-family: var(--mat-option-label-text-font, var(--mat-sys-label-large-font)); + line-height: var(--mat-option-label-text-line-height, var(--mat-sys-label-large-line-height)); + font-size: var(--mat-option-label-text-size, var(--mat-sys-body-large-size)); + letter-spacing: var(--mat-option-label-text-tracking, var(--mat-sys-label-large-tracking)); + font-weight: var(--mat-option-label-text-weight, var(--mat-sys-body-large-weight)); +} +.mat-mdc-option:hover:not(.mdc-list-item--disabled) { + background-color: var(--mat-option-hover-state-layer-color, color-mix(in srgb, var(--mat-sys-on-surface) calc(var(--mat-sys-hover-state-layer-opacity) * 100%), transparent)); +} +.mat-mdc-option:focus.mdc-list-item, .mat-mdc-option.mat-mdc-option-active.mdc-list-item { + background-color: var(--mat-option-focus-state-layer-color, color-mix(in srgb, var(--mat-sys-on-surface) calc(var(--mat-sys-focus-state-layer-opacity) * 100%), transparent)); + outline: 0; +} +.mat-mdc-option.mdc-list-item--selected:not(.mdc-list-item--disabled):not(.mat-mdc-option-active, .mat-mdc-option-multiple, :focus, :hover) { + background-color: var(--mat-option-selected-state-layer-color, var(--mat-sys-secondary-container)); +} +.mat-mdc-option.mdc-list-item--selected:not(.mdc-list-item--disabled):not(.mat-mdc-option-active, .mat-mdc-option-multiple, :focus, :hover) .mdc-list-item__primary-text { + color: var(--mat-option-selected-state-label-text-color, var(--mat-sys-on-secondary-container)); +} +.mat-mdc-option .mat-pseudo-checkbox { + --mat-pseudo-checkbox-minimal-selected-checkmark-color: var(--mat-option-selected-state-label-text-color, var(--mat-sys-on-secondary-container)); +} +.mat-mdc-option.mdc-list-item { + align-items: center; + background: transparent; +} +.mat-mdc-option.mdc-list-item--disabled { + cursor: default; + pointer-events: none; +} +.mat-mdc-option.mdc-list-item--disabled .mat-mdc-option-pseudo-checkbox, .mat-mdc-option.mdc-list-item--disabled .mdc-list-item__primary-text, .mat-mdc-option.mdc-list-item--disabled > mat-icon { + opacity: 0.38; +} +.mat-mdc-optgroup .mat-mdc-option:not(.mat-mdc-option-multiple) { + padding-left: 32px; +} +[dir=rtl] .mat-mdc-optgroup .mat-mdc-option:not(.mat-mdc-option-multiple) { + padding-left: 16px; + padding-right: 32px; +} +.mat-mdc-option .mat-icon, +.mat-mdc-option .mat-pseudo-checkbox-full { + margin-right: 16px; + flex-shrink: 0; +} +[dir=rtl] .mat-mdc-option .mat-icon, +[dir=rtl] .mat-mdc-option .mat-pseudo-checkbox-full { + margin-right: 0; + margin-left: 16px; +} +.mat-mdc-option .mat-pseudo-checkbox-minimal { + margin-left: 16px; + flex-shrink: 0; +} +[dir=rtl] .mat-mdc-option .mat-pseudo-checkbox-minimal { + margin-right: 16px; + margin-left: 0; +} +.mat-mdc-option .mat-mdc-option-ripple { + top: 0; + left: 0; + right: 0; + bottom: 0; + position: absolute; + pointer-events: none; +} +.mat-mdc-option .mdc-list-item__primary-text { + white-space: normal; + font-size: inherit; + font-weight: inherit; + letter-spacing: inherit; + line-height: inherit; + font-family: inherit; + text-decoration: inherit; + text-transform: inherit; + margin-right: auto; +} +[dir=rtl] .mat-mdc-option .mdc-list-item__primary-text { + margin-right: 0; + margin-left: auto; +} +@media (forced-colors: active) { + .mat-mdc-option.mdc-list-item--selected:not(:has(.mat-mdc-option-pseudo-checkbox))::after { + content: ""; + position: absolute; + top: 50%; + right: 16px; + transform: translateY(-50%); + width: 10px; + height: 0; + border-bottom: solid 10px; + border-radius: 10px; + } + [dir=rtl] .mat-mdc-option.mdc-list-item--selected:not(:has(.mat-mdc-option-pseudo-checkbox))::after { + right: auto; + left: 16px; + } +} + +.mat-mdc-option-multiple { + --mat-list-list-item-selected-container-color: var(--mat-list-list-item-container-color, transparent); +} + +.mat-mdc-option-active .mat-focus-indicator::before { + content: ""; +} +`],encapsulation:2,changeDetection:0})}return n})();function Dn(n,s,t){if(t.length){let e=s.toArray(),i=t.toArray(),a=0;for(let o=0;ot+e?Math.max(0,n-e+s):t}var Fn=(()=>{class n{isErrorState(t,e){return!!(t&&t.invalid&&(t.touched||e&&e.submitted))}static \u0275fac=function(e){return new(e||n)};static \u0275prov=P({token:n,factory:n.\u0275fac,providedIn:"root"})}return n})();var be=class{_defaultMatcher;ngControl;_parentFormGroup;_parentForm;_stateChanges;errorState=!1;matcher;constructor(s,t,e,i,a){this._defaultMatcher=s,this.ngControl=t,this._parentFormGroup=e,this._parentForm=i,this._stateChanges=a}updateErrorState(){let s=this.errorState,t=this._parentFormGroup||this._parentForm,e=this.matcher||this._defaultMatcher,i=this.ngControl?this.ngControl.control:null,a=e?.isErrorState(i,t)??!1;a!==s&&(this.errorState=a,this._stateChanges.next())}};var On=(()=>{class n{static \u0275fac=function(e){return new(e||n)};static \u0275mod=N({type:n});static \u0275inj=z({imports:[ce,In,j]})}return n})();var ge=(()=>{class n{static \u0275fac=function(e){return new(e||n)};static \u0275mod=N({type:n});static \u0275inj=z({imports:[j]})}return n})();var Nt=(()=>{class n{static \u0275fac=function(e){return new(e||n)};static \u0275mod=N({type:n});static \u0275inj=z({imports:[Kt,ge,zt,j]})}return n})();var bo=["trigger"],go=["panel"],vo=[[["mat-select-trigger"]],"*"],yo=["mat-select-trigger","*"];function xo(n,s){if(n&1&&(l(0,"span",4),E(1),d()),n&2){let t=p();c(),pt(t.placeholder)}}function ko(n,s){n&1&&y(0)}function wo(n,s){if(n&1&&(l(0,"span",11),E(1),d()),n&2){let t=p(2);c(),pt(t.triggerValue)}}function Co(n,s){if(n&1&&(l(0,"span",5),g(1,ko,1,0)(2,wo,2,1,"span",11),d()),n&2){let t=p();c(),v(t.customTrigger?1:2)}}function To(n,s){if(n&1){let t=lt();l(0,"div",12,1),u("keydown",function(i){R(t);let a=p();return A(a._handleKeydown(i))}),y(2,1),d()}if(n&2){let t=p();bt(t.panelClass),_("mat-select-panel-animations-enabled",!t._animationsDisabled)("mat-primary",(t._parentFormField==null?null:t._parentFormField.color)==="primary")("mat-accent",(t._parentFormField==null?null:t._parentFormField.color)==="accent")("mat-warn",(t._parentFormField==null?null:t._parentFormField.color)==="warn")("mat-undefined",!(t._parentFormField!=null&&t._parentFormField.color)),T("id",t.id+"-panel")("aria-multiselectable",t.multiple)("aria-label",t.ariaLabel||null)("aria-labelledby",t._getPanelAriaLabelledby())}}var Mo=new w("mat-select-scroll-strategy",{providedIn:"root",factory:()=>{let n=r(G);return()=>re(n)}}),So=new w("MAT_SELECT_CONFIG"),Ln=new w("MatSelectTrigger"),Ve=class{source;value;constructor(s,t){this.source=s,this.value=t}},Pn=(()=>{class n{_viewportRuler=r(ne);_changeDetectorRef=r(W);_elementRef=r(O);_dir=r(ut,{optional:!0});_idGenerator=r(U);_renderer=r(st);_parentFormField=r(ze,{optional:!0});ngControl=r(Ai,{self:!0,optional:!0});_liveAnnouncer=r(de);_defaultOptions=r(So,{optional:!0});_animationsDisabled=Q();_popoverLocation;_initialized=new k;_cleanupDetach;options;optionGroups;customTrigger;_positions=[{originX:"start",originY:"bottom",overlayX:"start",overlayY:"top"},{originX:"end",originY:"bottom",overlayX:"end",overlayY:"top"},{originX:"start",originY:"top",overlayX:"start",overlayY:"bottom",panelClass:"mat-mdc-select-panel-above"},{originX:"end",originY:"top",overlayX:"end",overlayY:"bottom",panelClass:"mat-mdc-select-panel-above"}];_scrollOptionIntoView(t){let e=this.options.toArray()[t];if(e){let i=this.panel.nativeElement,a=Dn(t,this.options,this.optionGroups),o=e._getHostElement();t===0&&a===1?i.scrollTop=0:i.scrollTop=En(o.offsetTop,o.offsetHeight,i.scrollTop,i.offsetHeight)}}_positioningSettled(){this._scrollOptionIntoView(this._keyManager.activeItemIndex||0)}_getChangeEvent(t){return new Ve(this,t)}_scrollStrategyFactory=r(Mo);_panelOpen=!1;_compareWith=(t,e)=>t===e;_uid=this._idGenerator.getId("mat-select-");_triggerAriaLabelledBy=null;_previousControl;_destroy=new k;_errorStateTracker;stateChanges=new k;disableAutomaticLabeling=!0;userAriaDescribedBy;_selectionModel;_keyManager;_preferredOverlayOrigin;_overlayWidth;_onChange=()=>{};_onTouched=()=>{};_valueId=this._idGenerator.getId("mat-select-value-");_scrollStrategy;_overlayPanelClass=this._defaultOptions?.overlayPanelClass||"";get focused(){return this._focused||this._panelOpen}_focused=!1;controlType="mat-select";trigger;panel;_overlayDir;panelClass;disabled=!1;get disableRipple(){return this._disableRipple()}set disableRipple(t){this._disableRipple.set(t)}_disableRipple=Z(!1);tabIndex=0;get hideSingleSelectionIndicator(){return this._hideSingleSelectionIndicator}set hideSingleSelectionIndicator(t){this._hideSingleSelectionIndicator=t,this._syncParentProperties()}_hideSingleSelectionIndicator=this._defaultOptions?.hideSingleSelectionIndicator??!1;get placeholder(){return this._placeholder}set placeholder(t){this._placeholder=t,this.stateChanges.next()}_placeholder;get required(){return this._required??this.ngControl?.control?.hasValidator(Ri.required)??!1}set required(t){this._required=t,this.stateChanges.next()}_required;get multiple(){return this._multiple}set multiple(t){this._selectionModel,this._multiple=t}_multiple=!1;disableOptionCentering=this._defaultOptions?.disableOptionCentering??!1;get compareWith(){return this._compareWith}set compareWith(t){this._compareWith=t,this._selectionModel&&this._initializeSelection()}get value(){return this._value}set value(t){this._assignValue(t)&&this._onChange(t)}_value;ariaLabel="";ariaLabelledby;get errorStateMatcher(){return this._errorStateTracker.matcher}set errorStateMatcher(t){this._errorStateTracker.matcher=t}typeaheadDebounceInterval;sortComparator;get id(){return this._id}set id(t){this._id=t||this._uid,this.stateChanges.next()}_id;get errorState(){return this._errorStateTracker.errorState}set errorState(t){this._errorStateTracker.errorState=t}panelWidth=this._defaultOptions&&typeof this._defaultOptions.panelWidth<"u"?this._defaultOptions.panelWidth:"auto";canSelectNullableOptions=this._defaultOptions?.canSelectNullableOptions??!1;optionSelectionChanges=Ye(()=>{let t=this.options;return t?t.changes.pipe(nt(t),$t(()=>J(...t.map(e=>e.onSelectionChange)))):this._initialized.pipe($t(()=>this.optionSelectionChanges))});openedChange=new F;_openedStream=this.openedChange.pipe(dt(t=>t),Ht(()=>{}));_closedStream=this.openedChange.pipe(dt(t=>!t),Ht(()=>{}));selectionChange=new F;valueChange=new F;constructor(){let t=r(Fn),e=r(Li,{optional:!0}),i=r(Pi,{optional:!0}),a=r(new ie("tabindex"),{optional:!0}),o=r(Oi,{optional:!0});this.ngControl&&(this.ngControl.valueAccessor=this),this._defaultOptions?.typeaheadDebounceInterval!=null&&(this.typeaheadDebounceInterval=this._defaultOptions.typeaheadDebounceInterval),this._errorStateTracker=new be(t,this.ngControl,i,e,this.stateChanges),this._scrollStrategy=this._scrollStrategyFactory(),this.tabIndex=a==null?0:parseInt(a)||0,this._popoverLocation=o?.usePopover===!1?null:"inline",this.id=this.id}ngOnInit(){this._selectionModel=new Yi(this.multiple),this.stateChanges.next(),this._viewportRuler.change().pipe(I(this._destroy)).subscribe(()=>{this.panelOpen&&(this._overlayWidth=this._getOverlayWidth(this._preferredOverlayOrigin),this._changeDetectorRef.detectChanges())})}ngAfterContentInit(){this._initialized.next(),this._initialized.complete(),this._initKeyManager(),this._selectionModel.changed.pipe(I(this._destroy)).subscribe(t=>{t.added.forEach(e=>e.select()),t.removed.forEach(e=>e.deselect())}),this.options.changes.pipe(nt(null),I(this._destroy)).subscribe(()=>{this._resetOptions(),this._initializeSelection()})}ngDoCheck(){let t=this._getTriggerAriaLabelledby(),e=this.ngControl;if(t!==this._triggerAriaLabelledBy){let i=this._elementRef.nativeElement;this._triggerAriaLabelledBy=t,t?i.setAttribute("aria-labelledby",t):i.removeAttribute("aria-labelledby")}e&&(this._previousControl!==e.control&&(this._previousControl!==void 0&&e.disabled!==null&&e.disabled!==this.disabled&&(this.disabled=e.disabled),this._previousControl=e.control),this.updateErrorState())}ngOnChanges(t){(t.disabled||t.userAriaDescribedBy)&&this.stateChanges.next(),t.typeaheadDebounceInterval&&this._keyManager&&this._keyManager.withTypeAhead(this.typeaheadDebounceInterval),t.panelClass&&this.panelClass instanceof Set&&(this.panelClass=Array.from(this.panelClass))}ngOnDestroy(){this._cleanupDetach?.(),this._keyManager?.destroy(),this._destroy.next(),this._destroy.complete(),this.stateChanges.complete(),this._clearFromModal()}toggle(){this.panelOpen?this.close():this.open()}open(){this._canOpen()&&(this._parentFormField&&(this._preferredOverlayOrigin=this._parentFormField.getConnectedOverlayOrigin()),this._cleanupDetach?.(),this._overlayWidth=this._getOverlayWidth(this._preferredOverlayOrigin),this._applyModalPanelOwnership(),this._panelOpen=!0,this._overlayDir.positionChange.pipe(Xe(1)).subscribe(()=>{this._changeDetectorRef.detectChanges(),this._positioningSettled()}),this._overlayDir.attachOverlay(),this._keyManager.withHorizontalOrientation(null),this._highlightCorrectOption(),this._changeDetectorRef.markForCheck(),this.stateChanges.next(),Promise.resolve().then(()=>this.openedChange.emit(!0)))}_trackedModal=null;_applyModalPanelOwnership(){let t=this._elementRef.nativeElement.closest('body > .cdk-overlay-container [aria-modal="true"]');if(!t)return;let e=`${this.id}-panel`;this._trackedModal&&Ie(this._trackedModal,"aria-owns",e),Wi(t,"aria-owns",e),this._trackedModal=t}_clearFromModal(){if(!this._trackedModal)return;let t=`${this.id}-panel`;Ie(this._trackedModal,"aria-owns",t),this._trackedModal=null}close(){this._panelOpen&&(this._panelOpen=!1,this._exitAndDetach(),this._keyManager.withHorizontalOrientation(this._isRtl()?"rtl":"ltr"),this._changeDetectorRef.markForCheck(),this._onTouched(),this.stateChanges.next(),Promise.resolve().then(()=>this.openedChange.emit(!1)))}_exitAndDetach(){if(this._animationsDisabled||!this.panel){this._detachOverlay();return}this._cleanupDetach?.(),this._cleanupDetach=()=>{e(),clearTimeout(i),this._cleanupDetach=void 0};let t=this.panel.nativeElement,e=this._renderer.listen(t,"animationend",a=>{a.animationName==="_mat-select-exit"&&(this._cleanupDetach?.(),this._detachOverlay())}),i=setTimeout(()=>{this._cleanupDetach?.(),this._detachOverlay()},200);t.classList.add("mat-select-panel-exit")}_detachOverlay(){this._overlayDir.detachOverlay(),this._changeDetectorRef.markForCheck()}writeValue(t){this._assignValue(t)}registerOnChange(t){this._onChange=t}registerOnTouched(t){this._onTouched=t}setDisabledState(t){this.disabled=t,this._changeDetectorRef.markForCheck(),this.stateChanges.next()}get panelOpen(){return this._panelOpen}get selected(){return this.multiple?this._selectionModel?.selected||[]:this._selectionModel?.selected[0]}get triggerValue(){if(this.empty)return"";if(this._multiple){let t=this._selectionModel.selected.map(e=>e.viewValue);return this._isRtl()&&t.reverse(),t.join(", ")}return this._selectionModel.selected[0].viewValue}updateErrorState(){this._errorStateTracker.updateErrorState()}_isRtl(){return this._dir?this._dir.value==="rtl":!1}_handleKeydown(t){this.disabled||(this.panelOpen?this._handleOpenKeydown(t):this._handleClosedKeydown(t))}_handleClosedKeydown(t){let e=t.keyCode,i=e===40||e===38||e===37||e===39,a=e===13||e===32,o=this._keyManager;if(!o.isTyping()&&a&&!ct(t)||(this.multiple||t.altKey)&&i)t.preventDefault(),this.open();else if(!this.multiple){let f=this.selected;o.onKeydown(t);let b=this.selected;b&&f!==b&&this._liveAnnouncer.announce(b.viewValue,1e4)}}_handleOpenKeydown(t){let e=this._keyManager,i=t.keyCode,a=i===40||i===38,o=e.isTyping();if(a&&t.altKey)t.preventDefault(),this.close();else if(!o&&(i===13||i===32)&&e.activeItem&&!ct(t))t.preventDefault(),e.activeItem._selectViaInteraction();else if(!o&&this._multiple&&i===65&&t.ctrlKey){t.preventDefault();let f=this.options.some(b=>!b.disabled&&!b.selected);this.options.forEach(b=>{b.disabled||(f?b.select():b.deselect())})}else{let f=e.activeItemIndex;e.onKeydown(t),this._multiple&&a&&t.shiftKey&&e.activeItem&&e.activeItemIndex!==f&&e.activeItem._selectViaInteraction()}}_handleOverlayKeydown(t){t.keyCode===27&&!ct(t)&&(t.preventDefault(),this.close())}_onFocus(){this.disabled||(this._focused=!0,this.stateChanges.next())}_onBlur(){this._focused=!1,this._keyManager?.cancelTypeahead(),!this.disabled&&!this.panelOpen&&(this._onTouched(),this._changeDetectorRef.markForCheck(),this.stateChanges.next())}get empty(){return!this._selectionModel||this._selectionModel.isEmpty()}_initializeSelection(){Promise.resolve().then(()=>{this.ngControl&&(this._value=this.ngControl.value),this._setSelectionByValue(this._value),this.stateChanges.next()})}_setSelectionByValue(t){if(this.options.forEach(e=>e.setInactiveStyles()),this._selectionModel.clear(),this.multiple&&t)Array.isArray(t),t.forEach(e=>this._selectOptionByValue(e)),this._sortValues();else{let e=this._selectOptionByValue(t);e?this._keyManager.updateActiveItem(e):this.panelOpen||this._keyManager.updateActiveItem(-1)}this._changeDetectorRef.markForCheck()}_selectOptionByValue(t){let e=this.options.find(i=>{if(this._selectionModel.isSelected(i))return!1;try{return(i.value!=null||this.canSelectNullableOptions)&&this._compareWith(i.value,t)}catch{return!1}});return e&&this._selectionModel.select(e),e}_assignValue(t){return t!==this._value||this._multiple&&Array.isArray(t)?(this.options&&this._setSelectionByValue(t),this._value=t,!0):!1}_skipPredicate=t=>this.panelOpen?!1:t.disabled;_getOverlayWidth(t){return this.panelWidth==="auto"?(t instanceof Me?t.elementRef:t||this._elementRef).nativeElement.getBoundingClientRect().width:this.panelWidth===null?"":this.panelWidth}_syncParentProperties(){if(this.options)for(let t of this.options)t._changeDetectorRef.markForCheck()}_initKeyManager(){this._keyManager=new Hi(this.options).withTypeAhead(this.typeaheadDebounceInterval).withVerticalOrientation().withHorizontalOrientation(this._isRtl()?"rtl":"ltr").withHomeAndEnd().withPageUpDown().withAllowedModifierKeys(["shiftKey"]).skipPredicate(this._skipPredicate),this._keyManager.tabOut.subscribe(()=>{this.panelOpen&&(!this.multiple&&this._keyManager.activeItem&&this._keyManager.activeItem._selectViaInteraction(),this.focus(),this.close())}),this._keyManager.change.subscribe(()=>{this._panelOpen&&this.panel?this._scrollOptionIntoView(this._keyManager.activeItemIndex||0):!this._panelOpen&&!this.multiple&&this._keyManager.activeItem&&this._keyManager.activeItem._selectViaInteraction()})}_resetOptions(){let t=J(this.options.changes,this._destroy);this.optionSelectionChanges.pipe(I(t)).subscribe(e=>{this._onSelect(e.source,e.isUserInput),e.isUserInput&&!this.multiple&&this._panelOpen&&(this.close(),this.focus())}),J(...this.options.map(e=>e._stateChanges)).pipe(I(t)).subscribe(()=>{this._changeDetectorRef.detectChanges(),this.stateChanges.next()})}_onSelect(t,e){let i=this._selectionModel.isSelected(t);!this.canSelectNullableOptions&&t.value==null&&!this._multiple?(t.deselect(),this._selectionModel.clear(),this.value!=null&&this._propagateChanges(t.value)):(i!==t.selected&&(t.selected?this._selectionModel.select(t):this._selectionModel.deselect(t)),e&&this._keyManager.setActiveItem(t),this.multiple&&(this._sortValues(),e&&this.focus())),i!==this._selectionModel.isSelected(t)&&this._propagateChanges(),this.stateChanges.next()}_sortValues(){if(this.multiple){let t=this.options.toArray();this._selectionModel.sort((e,i)=>this.sortComparator?this.sortComparator(e,i,t):t.indexOf(e)-t.indexOf(i)),this.stateChanges.next()}}_propagateChanges(t){let e;this.multiple?e=this.selected.map(i=>i.value):e=this.selected?this.selected.value:t,this._value=e,this.valueChange.emit(e),this._onChange(e),this.selectionChange.emit(this._getChangeEvent(e)),this._changeDetectorRef.markForCheck()}_highlightCorrectOption(){if(this._keyManager)if(this.empty){let t=-1;for(let e=0;e0&&!!this._overlayDir}focus(t){this._elementRef.nativeElement.focus(t)}_getPanelAriaLabelledby(){if(this.ariaLabel)return null;let t=this._parentFormField?.getLabelId()||null,e=t?t+" ":"";return this.ariaLabelledby?e+this.ariaLabelledby:t}_getAriaActiveDescendant(){return this.panelOpen&&this._keyManager&&this._keyManager.activeItem?this._keyManager.activeItem.id:null}_getTriggerAriaLabelledby(){if(this.ariaLabel)return null;let t=this._parentFormField?.getLabelId()||"";return this.ariaLabelledby&&(t+=" "+this.ariaLabelledby),t||(t=this._valueId),t}get describedByIds(){return this._elementRef.nativeElement.getAttribute("aria-describedby")?.split(" ")||[]}setDescribedByIds(t){let e=this._elementRef.nativeElement;t.length?e.setAttribute("aria-describedby",t.join(" ")):e.removeAttribute("aria-describedby")}onContainerClick(t){let e=wi(t);e&&(e.tagName==="MAT-OPTION"||e.classList.contains("cdk-overlay-backdrop")||e.closest(".mat-mdc-select-panel"))||(this.focus(),this.open())}get shouldLabelFloat(){return this.panelOpen||!this.empty||this.focused&&!!this.placeholder}static \u0275fac=function(e){return new(e||n)};static \u0275cmp=C({type:n,selectors:[["mat-select"]],contentQueries:function(e,i,a){if(e&1&&et(a,Ln,5)(a,zt,5)(a,_e,5),e&2){let o;m(o=h())&&(i.customTrigger=o.first),m(o=h())&&(i.options=o),m(o=h())&&(i.optionGroups=o)}},viewQuery:function(e,i){if(e&1&&V(bo,5)(go,5)(Se,5),e&2){let a;m(a=h())&&(i.trigger=a.first),m(a=h())&&(i.panel=a.first),m(a=h())&&(i._overlayDir=a.first)}},hostAttrs:["role","combobox","aria-haspopup","listbox",1,"mat-mdc-select"],hostVars:21,hostBindings:function(e,i){e&1&&u("keydown",function(o){return i._handleKeydown(o)})("focus",function(){return i._onFocus()})("blur",function(){return i._onBlur()}),e&2&&(T("id",i.id)("tabindex",i.disabled?-1:i.tabIndex)("aria-controls",i.panelOpen?i.id+"-panel":null)("aria-expanded",i.panelOpen)("aria-label",i.ariaLabel||null)("aria-required",i.required.toString())("aria-disabled",i.disabled.toString())("aria-invalid",i.errorState)("aria-activedescendant",i._getAriaActiveDescendant()),_("mat-mdc-select-disabled",i.disabled)("mat-mdc-select-invalid",i.errorState)("mat-mdc-select-required",i.required)("mat-mdc-select-empty",i.empty)("mat-mdc-select-multiple",i.multiple)("mat-select-open",i.panelOpen))},inputs:{userAriaDescribedBy:[0,"aria-describedby","userAriaDescribedBy"],panelClass:"panelClass",disabled:[2,"disabled","disabled",S],disableRipple:[2,"disableRipple","disableRipple",S],tabIndex:[2,"tabIndex","tabIndex",t=>t==null?0:gt(t)],hideSingleSelectionIndicator:[2,"hideSingleSelectionIndicator","hideSingleSelectionIndicator",S],placeholder:"placeholder",required:[2,"required","required",S],multiple:[2,"multiple","multiple",S],disableOptionCentering:[2,"disableOptionCentering","disableOptionCentering",S],compareWith:"compareWith",value:"value",ariaLabel:[0,"aria-label","ariaLabel"],ariaLabelledby:[0,"aria-labelledby","ariaLabelledby"],errorStateMatcher:"errorStateMatcher",typeaheadDebounceInterval:[2,"typeaheadDebounceInterval","typeaheadDebounceInterval",gt],sortComparator:"sortComparator",id:"id",panelWidth:"panelWidth",canSelectNullableOptions:[2,"canSelectNullableOptions","canSelectNullableOptions",S]},outputs:{openedChange:"openedChange",_openedStream:"opened",_closedStream:"closed",selectionChange:"selectionChange",valueChange:"valueChange"},exportAs:["matSelect"],features:[$([{provide:Be,useExisting:n},{provide:ue,useExisting:n}]),Wt],ngContentSelectors:yo,decls:11,vars:10,consts:[["fallbackOverlayOrigin","cdkOverlayOrigin","trigger",""],["panel",""],["cdk-overlay-origin","",1,"mat-mdc-select-trigger",3,"click"],[1,"mat-mdc-select-value"],[1,"mat-mdc-select-placeholder","mat-mdc-select-min-line"],[1,"mat-mdc-select-value-text"],[1,"mat-mdc-select-arrow-wrapper"],[1,"mat-mdc-select-arrow"],["viewBox","0 0 24 24","width","24px","height","24px","focusable","false","aria-hidden","true"],["d","M7 10l5 5 5-5z"],["cdk-connected-overlay","","cdkConnectedOverlayHasBackdrop","","cdkConnectedOverlayBackdropClass","cdk-overlay-transparent-backdrop",3,"detach","backdropClick","overlayKeydown","cdkConnectedOverlayDisableClose","cdkConnectedOverlayPanelClass","cdkConnectedOverlayScrollStrategy","cdkConnectedOverlayOrigin","cdkConnectedOverlayPositions","cdkConnectedOverlayWidth","cdkConnectedOverlayFlexibleDimensions","cdkConnectedOverlayUsePopover"],[1,"mat-mdc-select-min-line"],["role","listbox","tabindex","-1",1,"mat-mdc-select-panel","mdc-menu-surface","mdc-menu-surface--open",3,"keydown"]],template:function(e,i){if(e&1&&(B(vo),l(0,"div",2,0),u("click",function(){return i.open()}),l(3,"div",3),g(4,xo,2,1,"span",4)(5,Co,3,1,"span",5),d(),l(6,"div",6)(7,"div",7),mt(),l(8,"svg",8),M(9,"path",9),d()()()(),tt(10,To,3,16,"ng-template",10),u("detach",function(){return i.close()})("backdropClick",function(){return i.close()})("overlayKeydown",function(o){return i._handleOverlayKeydown(o)})),e&2){let a=ht(1);c(3),T("id",i._valueId),c(),v(i.empty?4:5),c(6),D("cdkConnectedOverlayDisableClose",!0)("cdkConnectedOverlayPanelClass",i._overlayPanelClass)("cdkConnectedOverlayScrollStrategy",i._scrollStrategy)("cdkConnectedOverlayOrigin",i._preferredOverlayOrigin||a)("cdkConnectedOverlayPositions",i._positions)("cdkConnectedOverlayWidth",i._overlayWidth)("cdkConnectedOverlayFlexibleDimensions",!0)("cdkConnectedOverlayUsePopover",i._popoverLocation)}},dependencies:[Me,Se],styles:[`@keyframes _mat-select-enter { + from { + opacity: 0; + transform: scaleY(0.8); + } + to { + opacity: 1; + transform: none; + } +} +@keyframes _mat-select-exit { + from { + opacity: 1; + } + to { + opacity: 0; + } +} +.mat-mdc-select { + display: inline-block; + width: 100%; + outline: none; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + color: var(--mat-select-enabled-trigger-text-color, var(--mat-sys-on-surface)); + font-family: var(--mat-select-trigger-text-font, var(--mat-sys-body-large-font)); + line-height: var(--mat-select-trigger-text-line-height, var(--mat-sys-body-large-line-height)); + font-size: var(--mat-select-trigger-text-size, var(--mat-sys-body-large-size)); + font-weight: var(--mat-select-trigger-text-weight, var(--mat-sys-body-large-weight)); + letter-spacing: var(--mat-select-trigger-text-tracking, var(--mat-sys-body-large-tracking)); +} + +div.mat-mdc-select-panel { + box-shadow: var(--mat-select-container-elevation-shadow, 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12)); +} + +.mat-mdc-select-disabled { + color: var(--mat-select-disabled-trigger-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); +} +.mat-mdc-select-disabled .mat-mdc-select-placeholder { + color: var(--mat-select-disabled-trigger-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); +} + +.mat-mdc-select-trigger { + display: inline-flex; + align-items: center; + cursor: pointer; + position: relative; + box-sizing: border-box; + width: 100%; +} +.mat-mdc-select-disabled .mat-mdc-select-trigger { + -webkit-user-select: none; + user-select: none; + cursor: default; +} + +.mat-mdc-select-value { + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.mat-mdc-select-value-text { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.mat-mdc-select-arrow-wrapper { + height: 24px; + flex-shrink: 0; + display: inline-flex; + align-items: center; +} +.mat-form-field-appearance-fill .mdc-text-field--no-label .mat-mdc-select-arrow-wrapper { + transform: none; +} + +.mat-mdc-form-field .mat-mdc-select.mat-mdc-select-invalid .mat-mdc-select-arrow, +.mat-form-field-invalid:not(.mat-form-field-disabled) .mat-mdc-form-field-infix::after { + color: var(--mat-select-invalid-arrow-color, var(--mat-sys-error)); +} + +.mat-mdc-select-arrow { + width: 10px; + height: 5px; + position: relative; + color: var(--mat-select-enabled-arrow-color, var(--mat-sys-on-surface-variant)); +} +.mat-mdc-form-field.mat-focused .mat-mdc-select-arrow { + color: var(--mat-select-focused-arrow-color, var(--mat-sys-primary)); +} +.mat-mdc-form-field .mat-mdc-select.mat-mdc-select-disabled .mat-mdc-select-arrow { + color: var(--mat-select-disabled-arrow-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); +} +.mat-select-open .mat-mdc-select-arrow { + transform: rotate(180deg); +} +.mat-form-field-animations-enabled .mat-mdc-select-arrow { + transition: transform 80ms linear; +} +.mat-mdc-select-arrow svg { + fill: currentColor; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} +@media (forced-colors: active) { + .mat-mdc-select-arrow svg { + fill: CanvasText; + } + .mat-mdc-select-disabled .mat-mdc-select-arrow svg { + fill: GrayText; + } +} + +div.mat-mdc-select-panel { + width: 100%; + max-height: 275px; + outline: 0; + overflow: auto; + padding: 8px 0; + box-sizing: border-box; + transform-origin: top center; + border-radius: 0 0 4px 4px; + position: relative; + background-color: var(--mat-select-panel-background-color, var(--mat-sys-surface-container)); +} +.mat-mdc-select-panel-above div.mat-mdc-select-panel { + border-radius: 4px 4px 0 0; + transform-origin: bottom center; +} +@media (forced-colors: active) { + div.mat-mdc-select-panel { + outline: solid 1px; + } +} + +.mat-select-panel-animations-enabled { + animation: _mat-select-enter 120ms cubic-bezier(0, 0, 0.2, 1); +} +.mat-select-panel-animations-enabled.mat-select-panel-exit { + animation: _mat-select-exit 100ms linear; +} + +.mat-mdc-select-placeholder { + transition: color 400ms 133.3333333333ms cubic-bezier(0.25, 0.8, 0.25, 1); + color: var(--mat-select-placeholder-text-color, var(--mat-sys-on-surface-variant)); +} +.mat-mdc-form-field:not(.mat-form-field-animations-enabled) .mat-mdc-select-placeholder, ._mat-animation-noopable .mat-mdc-select-placeholder { + transition: none; +} +.mat-form-field-hide-placeholder .mat-mdc-select-placeholder { + color: transparent; + -webkit-text-fill-color: transparent; + transition: none; + display: block; +} + +.mat-mdc-form-field-type-mat-select:not(.mat-form-field-disabled) .mat-mdc-text-field-wrapper { + cursor: pointer; +} +.mat-mdc-form-field-type-mat-select.mat-form-field-appearance-fill .mat-mdc-floating-label { + max-width: calc(100% - 18px); +} +.mat-mdc-form-field-type-mat-select.mat-form-field-appearance-fill .mdc-floating-label--float-above { + max-width: calc(100% / 0.75 - 24px); +} +.mat-mdc-form-field-type-mat-select.mat-form-field-appearance-outline .mdc-notched-outline__notch { + max-width: calc(100% - 60px); +} +.mat-mdc-form-field-type-mat-select.mat-form-field-appearance-outline .mdc-text-field--label-floating .mdc-notched-outline__notch { + max-width: calc(100% - 24px); +} + +.mat-mdc-select-min-line:empty::before { + content: " "; + white-space: pre; + width: 1px; + display: inline-block; + visibility: hidden; +} + +.mat-form-field-appearance-fill .mat-mdc-select-arrow-wrapper { + transform: var(--mat-select-arrow-transform, translateY(-8px)); +} +`],encapsulation:2,changeDetection:0})}return n})(),El=(()=>{class n{static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n,selectors:[["mat-select-trigger"]],features:[$([{provide:Ln,useExisting:n}])]})}return n})(),Bn=(()=>{class n{static \u0275fac=function(e){return new(e||n)};static \u0275mod=N({type:n});static \u0275inj=z({imports:[Pt,Nt,j,ae,On,Nt]})}return n})();var Rl=(()=>{class n{static \u0275fac=function(e){return new(e||n)};static \u0275mod=N({type:n});static \u0275inj=z({imports:[j]})}return n})();var Do=["mat-internal-form-field",""],Eo=["*"],Ll=(()=>{class n{labelPosition="after";static \u0275fac=function(e){return new(e||n)};static \u0275cmp=C({type:n,selectors:[["div","mat-internal-form-field",""]],hostAttrs:[1,"mdc-form-field","mat-internal-form-field"],hostVars:2,hostBindings:function(e,i){e&2&&_("mdc-form-field--align-end",i.labelPosition==="before")},inputs:{labelPosition:"labelPosition"},attrs:Do,ngContentSelectors:Eo,decls:1,vars:0,template:function(e,i){e&1&&(B(),y(0))},styles:[`.mat-internal-form-field { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + display: inline-flex; + align-items: center; + vertical-align: middle; +} +.mat-internal-form-field > label { + margin-left: 0; + margin-right: auto; + padding-left: 4px; + padding-right: 0; + order: 0; +} +[dir=rtl] .mat-internal-form-field > label { + margin-left: auto; + margin-right: 0; + padding-left: 0; + padding-right: 4px; +} + +.mdc-form-field--align-end > label { + margin-left: auto; + margin-right: 0; + padding-left: 0; + padding-right: 4px; + order: -1; +} +[dir=rtl] .mdc-form-field--align-end .mdc-form-field--align-end label { + margin-left: 0; + margin-right: auto; + padding-left: 4px; + padding-right: 0; +} +`],encapsulation:2,changeDetection:0})}return n})();var $e=(()=>{class n{get vertical(){return this._vertical}set vertical(t){this._vertical=K(t)}_vertical=!1;get inset(){return this._inset}set inset(t){this._inset=K(t)}_inset=!1;static \u0275fac=function(e){return new(e||n)};static \u0275cmp=C({type:n,selectors:[["mat-divider"]],hostAttrs:["role","separator",1,"mat-divider"],hostVars:7,hostBindings:function(e,i){e&2&&(T("aria-orientation",i.vertical?"vertical":"horizontal"),_("mat-divider-vertical",i.vertical)("mat-divider-horizontal",!i.vertical)("mat-divider-inset",i.inset))},inputs:{vertical:"vertical",inset:"inset"},decls:0,vars:0,template:function(e,i){},styles:[`.mat-divider { + display: block; + margin: 0; + border-top-style: solid; + border-top-color: var(--mat-divider-color, var(--mat-sys-outline-variant)); + border-top-width: var(--mat-divider-width, 1px); +} +.mat-divider.mat-divider-vertical { + border-top: 0; + border-right-style: solid; + border-right-color: var(--mat-divider-color, var(--mat-sys-outline-variant)); + border-right-width: var(--mat-divider-width, 1px); +} +.mat-divider.mat-divider-inset { + margin-left: 80px; +} +[dir=rtl] .mat-divider.mat-divider-inset { + margin-left: auto; + margin-right: 80px; +} +`],encapsulation:2,changeDetection:0})}return n})(),ve=(()=>{class n{static \u0275fac=function(e){return new(e||n)};static \u0275mod=N({type:n});static \u0275inj=z({imports:[j]})}return n})();var Fo=["tooltip"],Oo=20;var Ro=new w("mat-tooltip-scroll-strategy",{providedIn:"root",factory:()=>{let n=r(G);return()=>re(n,{scrollThrottle:Oo})}}),Ao=new w("mat-tooltip-default-options",{providedIn:"root",factory:()=>({showDelay:0,hideDelay:0,touchendHideDelay:1500})});var zn="tooltip-panel",Lo={passive:!0},Po=8,Bo=8,zo=24,No=200,We=(()=>{class n{_elementRef=r(O);_ngZone=r(H);_platform=r(ot);_ariaDescriber=r(Qi);_focusMonitor=r(le);_dir=r(ut);_injector=r(G);_viewContainerRef=r(ee);_mediaMatcher=r(Ni);_document=r(It);_renderer=r(st);_animationsDisabled=Q();_defaultOptions=r(Ao,{optional:!0});_overlayRef=null;_tooltipInstance=null;_overlayPanelClass;_portal;_position="below";_positionAtOrigin=!1;_disabled=!1;_tooltipClass;_viewInitialized=!1;_pointerExitEventsInitialized=!1;_tooltipComponent=Nn;_viewportMargin=8;_currentPosition;_cssClassPrefix="mat-mdc";_ariaDescriptionPending=!1;_dirSubscribed=!1;get position(){return this._position}set position(t){t!==this._position&&(this._position=t,this._overlayRef&&(this._updatePosition(this._overlayRef),this._tooltipInstance?.show(0),this._overlayRef.updatePosition()))}get positionAtOrigin(){return this._positionAtOrigin}set positionAtOrigin(t){this._positionAtOrigin=K(t),this._detach(),this._overlayRef=null}get disabled(){return this._disabled}set disabled(t){let e=K(t);this._disabled!==e&&(this._disabled=e,e?this.hide(0):this._setupPointerEnterEventsIfNeeded(),this._syncAriaDescription(this.message))}get showDelay(){return this._showDelay}set showDelay(t){this._showDelay=Gt(t)}_showDelay;get hideDelay(){return this._hideDelay}set hideDelay(t){this._hideDelay=Gt(t),this._tooltipInstance&&(this._tooltipInstance._mouseLeaveHideDelay=this._hideDelay)}_hideDelay;touchGestures="auto";get message(){return this._message}set message(t){let e=this._message;this._message=t!=null?String(t).trim():"",!this._message&&this._isTooltipVisible()?this.hide(0):(this._setupPointerEnterEventsIfNeeded(),this._updateTooltipMessage()),this._syncAriaDescription(e)}_message="";get tooltipClass(){return this._tooltipClass}set tooltipClass(t){this._tooltipClass=t,this._tooltipInstance&&this._setTooltipClass(this._tooltipClass)}_eventCleanups=[];_touchstartTimeout=null;_destroyed=new k;_isDestroyed=!1;constructor(){let t=this._defaultOptions;t&&(this._showDelay=t.showDelay,this._hideDelay=t.hideDelay,t.position&&(this.position=t.position),t.positionAtOrigin&&(this.positionAtOrigin=t.positionAtOrigin),t.touchGestures&&(this.touchGestures=t.touchGestures),t.tooltipClass&&(this.tooltipClass=t.tooltipClass)),this._viewportMargin=Po}ngAfterViewInit(){this._viewInitialized=!0,this._setupPointerEnterEventsIfNeeded(),this._focusMonitor.monitor(this._elementRef).pipe(I(this._destroyed)).subscribe(t=>{t?t==="keyboard"&&this._ngZone.run(()=>this.show()):this._ngZone.run(()=>this.hide(0))})}ngOnDestroy(){let t=this._elementRef.nativeElement;this._touchstartTimeout&&clearTimeout(this._touchstartTimeout),this._overlayRef&&(this._overlayRef.dispose(),this._tooltipInstance=null),this._eventCleanups.forEach(e=>e()),this._eventCleanups.length=0,this._destroyed.next(),this._destroyed.complete(),this._isDestroyed=!0,this._ariaDescriber.removeDescription(t,this.message,"tooltip"),this._focusMonitor.stopMonitoring(t)}show(t=this.showDelay,e){if(this.disabled||!this.message||this._isTooltipVisible()){this._tooltipInstance?._cancelPendingAnimations();return}let i=this._createOverlay(e);this._detach(),this._portal=this._portal||new qt(this._tooltipComponent,this._viewContainerRef);let a=this._tooltipInstance=i.attach(this._portal).instance;a._triggerElement=this._elementRef.nativeElement,a._mouseLeaveHideDelay=this._hideDelay,a.afterHidden().pipe(I(this._destroyed)).subscribe(()=>this._detach()),this._setTooltipClass(this._tooltipClass),this._updateTooltipMessage(),a.show(t)}hide(t=this.hideDelay){let e=this._tooltipInstance;e&&(e.isVisible()?e.hide(t):(e._cancelPendingAnimations(),this._detach()))}toggle(t){this._isTooltipVisible()?this.hide():this.show(void 0,t)}_isTooltipVisible(){return!!this._tooltipInstance&&this._tooltipInstance.isVisible()}_createOverlay(t){if(this._overlayRef){let o=this._overlayRef.getConfig().positionStrategy;if((!this.positionAtOrigin||!t)&&o._origin instanceof O)return this._overlayRef;this._detach()}let e=this._injector.get(Ci).getAncestorScrollContainers(this._elementRef),i=`${this._cssClassPrefix}-${zn}`,a=Ei(this._injector,this.positionAtOrigin?t||this._elementRef:this._elementRef).withTransformOriginOn(`.${this._cssClassPrefix}-tooltip`).withFlexibleDimensions(!1).withViewportMargin(this._viewportMargin).withScrollableContainers(e).withPopoverLocation("global");return a.positionChanges.pipe(I(this._destroyed)).subscribe(o=>{this._updateCurrentPositionClass(o.connectionPair),this._tooltipInstance&&o.scrollableViewProperties.isOverlayClipped&&this._tooltipInstance.isVisible()&&this._ngZone.run(()=>this.hide(0))}),this._overlayRef=se(this._injector,{direction:this._dir,positionStrategy:a,panelClass:this._overlayPanelClass?[...this._overlayPanelClass,i]:i,scrollStrategy:this._injector.get(Ro)(),disableAnimations:this._animationsDisabled,eventPredicate:this._overlayEventPredicate}),this._updatePosition(this._overlayRef),this._overlayRef.detachments().pipe(I(this._destroyed)).subscribe(()=>this._detach()),this._overlayRef.outsidePointerEvents().pipe(I(this._destroyed)).subscribe(()=>this._tooltipInstance?._handleBodyInteraction()),this._overlayRef.keydownEvents().pipe(I(this._destroyed)).subscribe(o=>{o.preventDefault(),o.stopPropagation(),this._ngZone.run(()=>this.hide(0))}),this._defaultOptions?.disableTooltipInteractivity&&this._overlayRef.addPanelClass(`${this._cssClassPrefix}-tooltip-panel-non-interactive`),this._dirSubscribed||(this._dirSubscribed=!0,this._dir.change.pipe(I(this._destroyed)).subscribe(()=>{this._overlayRef&&this._updatePosition(this._overlayRef)})),this._overlayRef}_detach(){this._overlayRef&&this._overlayRef.hasAttached()&&this._overlayRef.detach(),this._tooltipInstance=null}_updatePosition(t){let e=t.getConfig().positionStrategy,i=this._getOrigin(),a=this._getOverlayPosition();e.withPositions([this._addOffset(X(X({},i.main),a.main)),this._addOffset(X(X({},i.fallback),a.fallback))])}_addOffset(t){let e=Bo,i=!this._dir||this._dir.value=="ltr";return t.originY==="top"?t.offsetY=-e:t.originY==="bottom"?t.offsetY=e:t.originX==="start"?t.offsetX=i?-e:e:t.originX==="end"&&(t.offsetX=i?e:-e),t}_getOrigin(){let t=!this._dir||this._dir.value=="ltr",e=this.position,i;e=="above"||e=="below"?i={originX:"center",originY:e=="above"?"top":"bottom"}:e=="before"||e=="left"&&t||e=="right"&&!t?i={originX:"start",originY:"center"}:(e=="after"||e=="right"&&t||e=="left"&&!t)&&(i={originX:"end",originY:"center"});let{x:a,y:o}=this._invertPosition(i.originX,i.originY);return{main:i,fallback:{originX:a,originY:o}}}_getOverlayPosition(){let t=!this._dir||this._dir.value=="ltr",e=this.position,i;e=="above"?i={overlayX:"center",overlayY:"bottom"}:e=="below"?i={overlayX:"center",overlayY:"top"}:e=="before"||e=="left"&&t||e=="right"&&!t?i={overlayX:"end",overlayY:"center"}:(e=="after"||e=="right"&&t||e=="left"&&!t)&&(i={overlayX:"start",overlayY:"center"});let{x:a,y:o}=this._invertPosition(i.overlayX,i.overlayY);return{main:i,fallback:{overlayX:a,overlayY:o}}}_updateTooltipMessage(){this._tooltipInstance&&(this._tooltipInstance.message=this.message,this._tooltipInstance._markForCheck(),rt(()=>{this._tooltipInstance&&this._overlayRef.updatePosition()},{injector:this._injector}))}_setTooltipClass(t){this._tooltipInstance&&(this._tooltipInstance.tooltipClass=t instanceof Set?Array.from(t):t,this._tooltipInstance._markForCheck())}_invertPosition(t,e){return this.position==="above"||this.position==="below"?e==="top"?e="bottom":e==="bottom"&&(e="top"):t==="end"?t="start":t==="start"&&(t="end"),{x:t,y:e}}_updateCurrentPositionClass(t){let{overlayY:e,originX:i,originY:a}=t,o;if(e==="center"?this._dir&&this._dir.value==="rtl"?o=i==="end"?"left":"right":o=i==="start"?"left":"right":o=e==="bottom"&&a==="top"?"above":"below",o!==this._currentPosition){let f=this._overlayRef;if(f){let b=`${this._cssClassPrefix}-${zn}-`;f.removePanelClass(b+this._currentPosition),f.addPanelClass(b+o)}this._currentPosition=o}}_setupPointerEnterEventsIfNeeded(){this._disabled||!this.message||!this._viewInitialized||this._eventCleanups.length||(this._isTouchPlatform()?this.touchGestures!=="off"&&(this._disableNativeGesturesIfNecessary(),this._addListener("touchstart",t=>{let e=t.targetTouches?.[0],i=e?{x:e.clientX,y:e.clientY}:void 0;this._setupPointerExitEventsIfNeeded(),this._touchstartTimeout&&clearTimeout(this._touchstartTimeout);let a=500;this._touchstartTimeout=setTimeout(()=>{this._touchstartTimeout=null,this.show(void 0,i)},this._defaultOptions?.touchLongPressShowDelay??a)})):this._addListener("mouseenter",t=>{this._setupPointerExitEventsIfNeeded();let e;t.x!==void 0&&t.y!==void 0&&(e=t),this.show(void 0,e)}))}_setupPointerExitEventsIfNeeded(){if(!this._pointerExitEventsInitialized){if(this._pointerExitEventsInitialized=!0,!this._isTouchPlatform())this._addListener("mouseleave",t=>{let e=t.relatedTarget;(!e||!this._overlayRef?.overlayElement.contains(e))&&this.hide()}),this._addListener("wheel",t=>{if(this._isTooltipVisible()){let e=this._document.elementFromPoint(t.clientX,t.clientY),i=this._elementRef.nativeElement;e!==i&&!i.contains(e)&&this.hide()}});else if(this.touchGestures!=="off"){this._disableNativeGesturesIfNecessary();let t=()=>{this._touchstartTimeout&&clearTimeout(this._touchstartTimeout),this.hide(this._defaultOptions?.touchendHideDelay)};this._addListener("touchend",t),this._addListener("touchcancel",t)}}}_addListener(t,e){this._eventCleanups.push(this._renderer.listen(this._elementRef.nativeElement,t,e,Lo))}_isTouchPlatform(){let t=this._defaultOptions?.detectHoverCapability;return typeof t=="function"?!t():this._platform.IOS||this._platform.ANDROID?!0:this._platform.isBrowser?!!t&&this._mediaMatcher.matchMedia("(any-hover: none)").matches:!1}_disableNativeGesturesIfNecessary(){let t=this.touchGestures;if(t!=="off"){let e=this._elementRef.nativeElement,i=e.style;(t==="on"||e.nodeName!=="INPUT"&&e.nodeName!=="TEXTAREA")&&(i.userSelect=i.msUserSelect=i.webkitUserSelect=i.MozUserSelect="none"),(t==="on"||!e.draggable)&&(i.webkitUserDrag="none"),i.touchAction="none",i.webkitTapHighlightColor="transparent"}}_syncAriaDescription(t){this._ariaDescriptionPending||(this._ariaDescriptionPending=!0,this._ariaDescriber.removeDescription(this._elementRef.nativeElement,t,"tooltip"),this._isDestroyed||rt({write:()=>{this._ariaDescriptionPending=!1,this.message&&!this.disabled&&this._ariaDescriber.describe(this._elementRef.nativeElement,this.message,"tooltip")}},{injector:this._injector}))}_overlayEventPredicate=t=>t.type==="keydown"?this._isTooltipVisible()&&t.keyCode===27&&!ct(t):!0;static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n,selectors:[["","matTooltip",""]],hostAttrs:[1,"mat-mdc-tooltip-trigger"],hostVars:2,hostBindings:function(e,i){e&2&&_("mat-mdc-tooltip-disabled",i.disabled)},inputs:{position:[0,"matTooltipPosition","position"],positionAtOrigin:[0,"matTooltipPositionAtOrigin","positionAtOrigin"],disabled:[0,"matTooltipDisabled","disabled"],showDelay:[0,"matTooltipShowDelay","showDelay"],hideDelay:[0,"matTooltipHideDelay","hideDelay"],touchGestures:[0,"matTooltipTouchGestures","touchGestures"],message:[0,"matTooltip","message"],tooltipClass:[0,"matTooltipClass","tooltipClass"]},exportAs:["matTooltip"]})}return n})(),Nn=(()=>{class n{_changeDetectorRef=r(W);_elementRef=r(O);_isMultiline=!1;message;tooltipClass;_showTimeoutId;_hideTimeoutId;_triggerElement;_mouseLeaveHideDelay;_animationsDisabled=Q();_tooltip;_closeOnInteraction=!1;_isVisible=!1;_onHide=new k;_showAnimation="mat-mdc-tooltip-show";_hideAnimation="mat-mdc-tooltip-hide";constructor(){}show(t){this._hideTimeoutId!=null&&clearTimeout(this._hideTimeoutId),this._showTimeoutId=setTimeout(()=>{this._toggleVisibility(!0),this._showTimeoutId=void 0},t)}hide(t){this._showTimeoutId!=null&&clearTimeout(this._showTimeoutId),this._hideTimeoutId=setTimeout(()=>{this._toggleVisibility(!1),this._hideTimeoutId=void 0},t)}afterHidden(){return this._onHide}isVisible(){return this._isVisible}ngOnDestroy(){this._cancelPendingAnimations(),this._onHide.complete(),this._triggerElement=null}_handleBodyInteraction(){this._closeOnInteraction&&this.hide(0)}_markForCheck(){this._changeDetectorRef.markForCheck()}_handleMouseLeave({relatedTarget:t}){(!t||!this._triggerElement.contains(t))&&(this.isVisible()?this.hide(this._mouseLeaveHideDelay):this._finalizeAnimation(!1))}_onShow(){this._isMultiline=this._isTooltipMultiline(),this._markForCheck()}_isTooltipMultiline(){let t=this._elementRef.nativeElement.getBoundingClientRect();return t.height>zo&&t.width>=No}_handleAnimationEnd({animationName:t}){(t===this._showAnimation||t===this._hideAnimation)&&this._finalizeAnimation(t===this._showAnimation)}_cancelPendingAnimations(){this._showTimeoutId!=null&&clearTimeout(this._showTimeoutId),this._hideTimeoutId!=null&&clearTimeout(this._hideTimeoutId),this._showTimeoutId=this._hideTimeoutId=void 0}_finalizeAnimation(t){t?this._closeOnInteraction=!0:this.isVisible()||this._onHide.next()}_toggleVisibility(t){let e=this._tooltip.nativeElement,i=this._showAnimation,a=this._hideAnimation;if(e.classList.remove(t?a:i),e.classList.add(t?i:a),this._isVisible!==t&&(this._isVisible=t,this._changeDetectorRef.markForCheck()),t&&!this._animationsDisabled&&typeof getComputedStyle=="function"){let o=getComputedStyle(e);(o.getPropertyValue("animation-duration")==="0s"||o.getPropertyValue("animation-name")==="none")&&(this._animationsDisabled=!0)}t&&this._onShow(),this._animationsDisabled&&(e.classList.add("_mat-animation-noopable"),this._finalizeAnimation(t))}static \u0275fac=function(e){return new(e||n)};static \u0275cmp=C({type:n,selectors:[["mat-tooltip-component"]],viewQuery:function(e,i){if(e&1&&V(Fo,7),e&2){let a;m(a=h())&&(i._tooltip=a.first)}},hostAttrs:["aria-hidden","true"],hostBindings:function(e,i){e&1&&u("mouseleave",function(o){return i._handleMouseLeave(o)})},decls:4,vars:5,consts:[["tooltip",""],[1,"mdc-tooltip","mat-mdc-tooltip",3,"animationend"],[1,"mat-mdc-tooltip-surface","mdc-tooltip__surface"]],template:function(e,i){e&1&&(kt(0,"div",1,0),si("animationend",function(o){return i._handleAnimationEnd(o)}),kt(2,"div",2),E(3),Ot()()),e&2&&(bt(i.tooltipClass),_("mdc-tooltip--multiline",i._isMultiline),c(3),pt(i.message))},styles:[`.mat-mdc-tooltip { + position: relative; + transform: scale(0); + display: inline-flex; +} +.mat-mdc-tooltip::before { + content: ""; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: -1; + position: absolute; +} +.mat-mdc-tooltip-panel-below .mat-mdc-tooltip::before { + top: -8px; +} +.mat-mdc-tooltip-panel-above .mat-mdc-tooltip::before { + bottom: -8px; +} +.mat-mdc-tooltip-panel-right .mat-mdc-tooltip::before { + left: -8px; +} +.mat-mdc-tooltip-panel-left .mat-mdc-tooltip::before { + right: -8px; +} +.mat-mdc-tooltip._mat-animation-noopable { + animation: none; + transform: scale(1); +} + +.mat-mdc-tooltip-surface { + word-break: normal; + overflow-wrap: anywhere; + padding: 4px 8px; + min-width: 40px; + max-width: 200px; + min-height: 24px; + max-height: 40vh; + box-sizing: border-box; + overflow: hidden; + text-align: center; + will-change: transform, opacity; + background-color: var(--mat-tooltip-container-color, var(--mat-sys-inverse-surface)); + color: var(--mat-tooltip-supporting-text-color, var(--mat-sys-inverse-on-surface)); + border-radius: var(--mat-tooltip-container-shape, var(--mat-sys-corner-extra-small)); + font-family: var(--mat-tooltip-supporting-text-font, var(--mat-sys-body-small-font)); + font-size: var(--mat-tooltip-supporting-text-size, var(--mat-sys-body-small-size)); + font-weight: var(--mat-tooltip-supporting-text-weight, var(--mat-sys-body-small-weight)); + line-height: var(--mat-tooltip-supporting-text-line-height, var(--mat-sys-body-small-line-height)); + letter-spacing: var(--mat-tooltip-supporting-text-tracking, var(--mat-sys-body-small-tracking)); +} +.mat-mdc-tooltip-surface::before { + position: absolute; + box-sizing: border-box; + width: 100%; + height: 100%; + top: 0; + left: 0; + border: 1px solid transparent; + border-radius: inherit; + content: ""; + pointer-events: none; +} +.mdc-tooltip--multiline .mat-mdc-tooltip-surface { + text-align: left; +} +[dir=rtl] .mdc-tooltip--multiline .mat-mdc-tooltip-surface { + text-align: right; +} + +.mat-mdc-tooltip-panel { + line-height: normal; +} +.mat-mdc-tooltip-panel.mat-mdc-tooltip-panel-non-interactive { + pointer-events: none; +} + +@keyframes mat-mdc-tooltip-show { + 0% { + opacity: 0; + transform: scale(0.8); + } + 100% { + opacity: 1; + transform: scale(1); + } +} +@keyframes mat-mdc-tooltip-hide { + 0% { + opacity: 1; + transform: scale(1); + } + 100% { + opacity: 0; + transform: scale(0.8); + } +} +.mat-mdc-tooltip-show { + animation: mat-mdc-tooltip-show 150ms cubic-bezier(0, 0, 0.2, 1) forwards; +} + +.mat-mdc-tooltip-hide { + animation: mat-mdc-tooltip-hide 75ms cubic-bezier(0.4, 0, 1, 1) forwards; +} +`],encapsulation:2,changeDetection:0})}return n})();var jn=(()=>{class n{static \u0275fac=function(e){return new(e||n)};static \u0275mod=N({type:n});static \u0275inj=z({imports:[Vi,Pt,j,ae]})}return n})();var jt=class{data=[];keyIndex=new Map;dataChange=new St([]);itemUpdated=new k;getItems(){return this.data}add(s){if(this.findIndex(s)>=0){this.update(s);return}this.data=[...this.data,s],this.keyIndex.set(this.getItemKey(s),this.data.length-1),this.dataChange.next(this.data)}set(s){let t=[];for(let e of s||[]){let i=this.getItemKey(e),a=this.keyIndex.get(i);t.push(a!==void 0?Object.assign(this.data[a],e):e)}this.data=t,this.reindex(),this.dataChange.next(this.data)}get(s){let t=this.keyIndex.get(s);if(t!==void 0)return this.data[t]}update(s){let t=this.findIndex(s);if(t>=0){let e=Object.assign(this.data[t],s);this.data=[...this.data],this.data[t]=e,this.dataChange.next(this.data),this.itemUpdated.next(e)}}remove(s){let t=this.findIndex(s);t>=0&&(this.data=this.data.filter((e,i)=>i!==t),this.reindex(),this.dataChange.next(this.data))}applyBatch(s,t){if(s.length>0){let e=this.data.length;this.data=[...this.data,...s];for(let i=0;i0){let e=new Set(t.map(i=>this.getItemKey(i)));this.data=this.data.filter(i=>!e.has(this.getItemKey(i))),this.reindex()}else s.length===0&&(this.data=[...this.data]);this.dataChange.next(this.data)}get changes(){return this.dataChange}get itemChanged(){return this.itemUpdated}clear(){this.data=[],this.keyIndex.clear(),this.dataChange.next(this.data)}reindex(){this.keyIndex.clear();for(let s=0;s{class n extends jt{getItemKey(t){return t.link_id}static \u0275fac=(()=>{let t;return function(i){return(t||(t=Y(n)))(i||n)}})();static \u0275prov=P({token:n,factory:n.\u0275fac})}return n})();var Hn=(()=>{class n extends jt{getItemKey(t){return t.node_id}static \u0275fac=(()=>{let t;return function(i){return(t||(t=Y(n)))(i||n)}})();static \u0275prov=P({token:n,factory:n.\u0275fac})}return n})();var $n=(()=>{class n{httpController;constructor(t){this.httpController=t}getComputes(t){return this.httpController.get(t,"/computes")}getCompute(t,e){return this.httpController.get(t,`/computes/${e}`)}createCompute(t,e){return this.httpController.post(t,"/computes",e)}updateCompute(t,e,i){return this.httpController.put(t,`/computes/${e}`,i)}deleteCompute(t,e){return this.httpController.delete(t,`/computes/${e}`)}connectCompute(t,e){return this.httpController.post(t,`/computes/${e}/connect`,null)}getStatistics(t){return this.httpController.get(t,"/statistics")}static \u0275fac=function(e){return new(e||n)(at(me))};static \u0275prov=P({token:n,factory:n.\u0275fac})}return n})();var Wn=(()=>{class n{settings={crash_reports:!0,console_command:void 0,anonymous_statistics:!0};reportsSettings="crash_reports";consoleSettings="console_command";statisticsSettings="statistics_command";constructor(){this.getItem(this.reportsSettings)&&(this.settings.crash_reports=this.getItem(this.reportsSettings)==="true"),this.getItem(this.consoleSettings)&&(this.settings.console_command=this.getItem(this.consoleSettings)),this.getItem(this.statisticsSettings)&&(this.settings.anonymous_statistics=this.getItem(this.statisticsSettings)==="true")}setReportsSettings(t){this.settings.crash_reports=t,this.removeItem(this.reportsSettings),t?this.setItem(this.reportsSettings,"true"):this.setItem(this.reportsSettings,"false")}setStatisticsSettings(t){this.settings.anonymous_statistics=t,this.removeItem(this.statisticsSettings),t?this.setItem(this.statisticsSettings,"true"):this.setItem(this.statisticsSettings,"false")}getReportsSettings(){return this.getItem(this.reportsSettings)==="true"}getStatisticsSettings(){return this.getItem(this.statisticsSettings)==="true"}setConsoleSettings(t){this.settings.console_command=t,this.removeItem(this.consoleSettings),this.setItem(this.consoleSettings,t)}getConsoleSettings(){return this.getItem(this.consoleSettings)}removeItem(t){localStorage.removeItem(t)}setItem(t,e){localStorage.setItem(t,e)}getItem(t){return localStorage.getItem(t)}getAll(){return this.settings}setAll(t){this.settings=t,this.setConsoleSettings(t.console_command),this.setReportsSettings(t.crash_reports),this.setStatisticsSettings(t.anonymous_statistics)}static \u0275fac=function(e){return new(e||n)};static \u0275prov=P({token:n,factory:n.\u0275fac,providedIn:"root"})}return n})();var Qn=(()=>{class n{controllerId;projectId;controllerIdProjectList;setcontrollerId(t){this.controllerId=t}setProjectId(t){this.projectId=t}setcontrollerIdProjectList(t){this.controllerIdProjectList=t}getcontrollerId(){return this.controllerId}getProjectId(){return this.projectId}getcontrollerIdProjectList(){return this.controllerIdProjectList}removeData(){this.controllerId="",this.projectId=""}static \u0275fac=function(e){return new(e||n)};static \u0275prov=P({token:n,factory:n.\u0275fac})}return n})();var Gn=(()=>{class n{httpController;settingsService;recentlyOpenedProjectService;compression_methods=[{id:1,value:"none",name:"None"},{id:2,value:"zip",name:"Zip compression (deflate)"},{id:3,value:"bzip2",name:"Bzip2 compression"},{id:4,value:"lzma",name:"Lzma compression"},{id:5,value:"zstd",name:"Zstandard compression"}];compression_level_default_value=[{id:1,name:"none",value:"",selectionValues:[]},{id:2,name:"zip",value:6,selectionValues:[0,1,2,3,4,5,6,7,8,9]},{id:3,name:"bzip2",value:9,selectionValues:[1,2,3,4,5,6,7,8,9]},{id:4,name:"lzma",value:" ",selectionValues:[]},{id:5,name:"zstd",value:3,selectionValues:[1,2,3,4,5,6,7,8,9.1,11,12,13,14,15,16,17,18,19,20,21,22]}];projectListSubject=new k;projectLockIconSubject=new k;constructor(t,e,i){this.httpController=t,this.settingsService=e,this.recentlyOpenedProjectService=i}projectListUpdated(){this.projectListSubject.next(!0)}getReadmeFile(t,e){return this.httpController.getText(t,`/projects/${e}/files/README.txt`)}postReadmeFile(t,e,i){return this.httpController.post(t,`/projects/${e}/files/README.txt`,i)}get(t,e){return this.httpController.get(t,`/projects/${e}`)}open(t,e){return this.httpController.post(t,`/projects/${e}/open`,{})}close(t,e){return this.recentlyOpenedProjectService.removeData(),this.httpController.post(t,`/projects/${e}/close`,{})}list(t){return this.httpController.get(t,"/projects")}nodes(t,e){return this.httpController.get(t,`/projects/${e}/nodes`)}links(t,e){return this.httpController.get(t,`/projects/${e}/links`)}drawings(t,e){return this.httpController.get(t,`/projects/${e}/drawings`)}add(t,e,i){return this.httpController.post(t,"/projects",{name:e,project_id:i})}update(t,e){return this.httpController.put(t,`/projects/${e.project_id}`,{auto_close:e.auto_close,auto_open:e.auto_open,auto_start:e.auto_start,drawing_grid_size:e.drawing_grid_size,grid_size:e.grid_size,name:e.name,scene_width:e.scene_width,scene_height:e.scene_height,snap_to_grid:e.snap_to_grid,show_grid:e.show_grid,show_interface_labels:e.show_interface_labels,show_layers:e.show_layers,variables:e.variables,zoom:e.zoom})}delete(t,e){return this.httpController.delete(t,`/projects/${e}`)}getUploadPath(t,e,i){return`${t.protocol}//${t.host}:${t.port}/${_t.current_version}/projects/${e}/import?name=${i}`}getExportPath(t,e){return`${t.protocol}//${t.host}:${t.port}/${_t.current_version}/projects/${e.project_id}/export`}export(t,e){return this.httpController.get(t,`/projects/${e}/export`)}getStatistics(t,e){return this.httpController.get(t,`/projects/${e}/stats`)}duplicate(t,e,i){return this.httpController.post(t,`/projects/${e}/duplicate`,{name:i})}isReadOnly(t){return t.readonly?t.readonly:!1}getCompression(){return this.compression_methods}getCompressionLevel(){return this.compression_level_default_value}getexportPortableProjectPath(t,e,i={}){return i.compression_level!=null&&i.compression_level!=""?`${t.protocol}//${t.host}:${t.port}/${_t.current_version}/projects/${e}/export?include_snapshots=${i.include_snapshots}&include_images=${i.include_base_image}&reset_mac_addresses=${i.reset_mac_address}&compression=${i.compression}&compression_level=${i.compression_level}&token=${t.authToken}`:`${t.protocol}//${t.host}:${t.port}/${_t.current_version}/projects/${e}/export?include_snapshots=${i.include_snapshots}&include_images=${i.include_base_image}&reset_mac_addresses=${i.reset_mac_address}&compression=${i.compression}&token=${t.authToken}`}getProjectStatus(t,e){return this.get(t,`${e}/locked`)}projectUpdateLockIcon(){this.projectLockIconSubject.next(!0)}static \u0275fac=function(e){return new(e||n)(at(me),at(Wn),at(Qn))};static \u0275prov=P({token:n,factory:n.\u0275fac})}return n})();var Wo=new w("DEFAULT_THEME_TOKEN",{providedIn:"root",factory:()=>"indigo-pink"}),qn=(()=>{class n{document;_darkMode$=new St(!1);darkMode$=this._darkMode$.asObservable();themeChanged=new F;mapThemeChanged=new F;currentTheme="indigo-pink";currentMapTheme="auto";savedTheme="indigo-pink";savedMapTheme="auto";availableThemes=[{key:"deeppurple-amber",label:"Deep Purple & Amber",type:"light",primaryColor:"#6750A4"},{key:"indigo-pink",label:"Indigo & Pink",type:"light",primaryColor:"#3F51B5"},{key:"magenta-violet",label:"Magenta & Violet",type:"light",primaryColor:"#D81B60"},{key:"rose-red",label:"Rose & Red",type:"light",primaryColor:"#E91E63"},{key:"pink-bluegrey",label:"Pink & Bluegrey",type:"dark",primaryColor:"#E91E63"},{key:"purple-green",label:"Purple & Green",type:"dark",primaryColor:"#7E57C2"},{key:"azure-blue",label:"Azure & Blue",type:"dark",primaryColor:"#0078D4"},{key:"cyan-orange",label:"Cyan & Orange",type:"dark",primaryColor:"#00B7C3"}];availableMapBackgrounds=[{key:"auto",label:"Follow global theme",background:"",textColor:"",type:"light"},{key:"light-1",label:"Cyan Sky",background:"radial-gradient(ellipse at 20% 20%, #B2EBF2 0%, #E0F7FA 70%)",textColor:"#006064",type:"light"},{key:"light-2",label:"Blue Sky",background:"radial-gradient(ellipse at 20% 20%, #BBDEFB 0%, #E3F2FD 70%)",textColor:"#1565C0",type:"light"},{key:"light-3",label:"Cloud Gray",background:"radial-gradient(ellipse at 20% 20%, #F5F5F5 0%, #FAFAFA 70%)",textColor:"#424242",type:"light"},{key:"light-4",label:"Lavender",background:"radial-gradient(ellipse at 20% 20%, #E1BEE7 0%, #F3E5F5 70%)",textColor:"#4A148C",type:"light"},{key:"dark-1",label:"Deep Cyan",background:"linear-gradient(135deg, #006064 0%, #00838F 50%, #006064 100%)",textColor:"#FFFFFF",type:"dark"},{key:"dark-2",label:"Deep Blue",background:"linear-gradient(135deg, #1565C0 0%, #1976D2 50%, #1565C0 100%)",textColor:"#FFFFFF",type:"dark"},{key:"dark-3",label:"Charcoal",background:"linear-gradient(135deg, #424242 0%, #616161 50%, #424242 100%)",textColor:"#FFFFFF",type:"dark"},{key:"dark-4",label:"Deep Purple",background:"linear-gradient(135deg, #4A148C 0%, #6A1B9A 50%, #4A148C 100%)",textColor:"#FFFFFF",type:"dark"}];constructor(t,e){this.document=t;let i=localStorage.getItem("theme");this.currentTheme=i||e,this.savedTheme=this.currentTheme;let a=localStorage.getItem("mapTheme");this.currentMapTheme=a||"auto",this.savedMapTheme=this.currentMapTheme,this.applyTheme(this.currentTheme)}getCurrentTheme(){return this.currentTheme}getThemeType(){return this.isDarkTheme(this.currentTheme)?"dark":"light"}getActualMapTheme(){if(this.savedMapTheme==="auto")return this.getThemeType();let t=this.availableMapBackgrounds.find(e=>e.key===this.savedMapTheme);return t?t.type:"light"}getActualTheme(){return this.getThemeType()}isDarkTheme(t){return t==="pink-bluegrey"||t==="purple-green"||t==="azure-blue"||t==="cyan-orange"}setTheme(t){this.currentTheme!==t&&(this.currentTheme=t,this.savedTheme=t,this.applyTheme(t),this.saveThemePreference(t),this.themeChanged.emit(t),this.currentMapTheme==="auto"&&this.mapThemeChanged.emit(t),this._darkMode$.next(this.isDarkTheme(t)))}toggleTheme(){let t=this.getThemeType(),e;t==="dark"?e=this.availableThemes.find(i=>i.type==="light")?.key||"deeppurple-amber":e=this.availableThemes.find(i=>i.type==="dark")?.key||"pink-bluegrey",this.setTheme(e)}setDarkMode(t){let e=t?"pink-bluegrey":"indigo-pink";this.setTheme(e)}setMapTheme(t){this.currentMapTheme=t,this.savedMapTheme=t,localStorage.setItem("mapTheme",t),this.mapThemeChanged.emit(this.getActualMapTheme())}restoreTheme(){let t=localStorage.getItem("theme");t&&this.availableThemes.some(e=>e.key===t)&&this.setTheme(t)}applyTheme(t){let e=this.document.documentElement;e.classList.remove("theme-deeppurple-amber","theme-indigo-pink","theme-magenta-violet","theme-rose-red","theme-pink-bluegrey","theme-purple-green","theme-azure-blue","theme-cyan-orange"),e.classList.add(`theme-${t}`)}saveThemePreference(t){localStorage.setItem("theme",t)}isDarkMode(){return this.isDarkTheme(this.currentTheme)}isLightMode(){return!this.isDarkTheme(this.currentTheme)}getCanvasLabelColor(){return this.getActualMapTheme()==="dark"?"#FFFFFF":"#000000"}getCanvasLinkColor(){return this.getActualMapTheme()==="dark"?"#FFFFFF":"#000000"}static \u0275fac=function(e){return new(e||n)(at(It),at(Wo))};static \u0275prov=P({token:n,factory:n.\u0275fac,providedIn:"root"})}return n})();var Un=(()=>{class n{ws;currentController;computeNotificationEmitter=new F;computeCache=new Map;projectNotificationEmitter=new F;computeCacheUpdated=new F;notificationsPath(t){let e="ws";return t.protocol==="https:"&&(e="wss"),`${e}://${t.host}:${t.port}/${_t.current_version}/notifications/ws?token=${t.authToken}`}projectNotificationsPath(t,e){let i="ws";return t.protocol==="https:"&&(i="wss"),`${i}://${t.host}:${t.port}/${_t.current_version}/projects/${e}/notifications/ws?token=${t.authToken}`}markerNotificationsPath(t,e){let i="ws";return t.protocol==="https:"&&(i="wss"),`${i}://${t.host}:${t.port}/${_t.current_version}/projects/${e}/notifications/markers/ws?token=${t.authToken}`}connectToComputeNotifications(t){this.ws&&this.currentController===t||(this.disconnect(),this.currentController=t,this.ws=new WebSocket(this.notificationsPath(t)),this.ws.onmessage=e=>{let i=JSON.parse(e.data);this.handleMessage(i)},this.ws.onerror=()=>{console.error("Compute notifications WebSocket error")},this.ws.onclose=()=>{this.ws=null})}disconnect(){this.ws&&(this.ws.close(),this.ws=null,this.currentController=null,this.computeCache.clear())}getCachedComputes(){return Array.from(this.computeCache.values())}hasCachedData(){return this.computeCache.size>0}setInitialComputes(t){this.computeCache.clear(),t.forEach(e=>{this.computeCache.set(e.compute_id,e)}),this.computeCacheUpdated.emit(this.getCachedComputes())}handleMessage(t){switch(t.action){case"compute.created":this.computeCache.set(t.event.compute_id,t.event),this.computeNotificationEmitter.emit(t),this.computeCacheUpdated.emit(this.getCachedComputes());break;case"compute.updated":this.computeCache.set(t.event.compute_id,t.event),this.computeNotificationEmitter.emit(t),this.computeCacheUpdated.emit(this.getCachedComputes());break;case"compute.deleted":this.computeCache.delete(t.event.compute_id),this.computeNotificationEmitter.emit(t),this.computeCacheUpdated.emit(this.getCachedComputes());break;case"project.created":case"project.opened":case"project.closed":case"project.updated":case"project.deleted":this.projectNotificationEmitter.emit(t);break}}static \u0275fac=function(e){return new(e||n)};static \u0275prov=P({token:n,factory:n.\u0275fac})}return n})();function Qo(n,s){if(n&1){let t=lt();l(0,"div",1)(1,"button",2),u("click",function(){R(t);let i=p();return A(i.action())}),E(2),d()()}if(n&2){let t=p();c(2),ft(" ",t.data.action," ")}}var Go=["label"];function qo(n,s){}var Uo=Math.pow(2,31)-1,Zt=class{_overlayRef;instance;containerInstance;_afterDismissed=new k;_afterOpened=new k;_onAction=new k;_durationTimeoutId;_dismissedByAction=!1;constructor(s,t){this._overlayRef=t,this.containerInstance=s,s._onExit.subscribe(()=>this._finishDismiss())}dismiss(){this._afterDismissed.closed||this.containerInstance.exit(),clearTimeout(this._durationTimeoutId)}dismissWithAction(){this._onAction.closed||(this._dismissedByAction=!0,this._onAction.next(),this._onAction.complete(),this.dismiss()),clearTimeout(this._durationTimeoutId)}closeWithAction(){this.dismissWithAction()}_dismissAfter(s){this._durationTimeoutId=setTimeout(()=>this.dismiss(),Math.min(s,Uo))}_open(){this._afterOpened.closed||(this._afterOpened.next(),this._afterOpened.complete())}_finishDismiss(){this._overlayRef.dispose(),this._onAction.closed||this._onAction.complete(),this._afterDismissed.next({dismissedByAction:this._dismissedByAction}),this._afterDismissed.complete(),this._dismissedByAction=!1}afterDismissed(){return this._afterDismissed}afterOpened(){return this.containerInstance._onEnter}onAction(){return this._onAction}},Yn=new w("MatSnackBarData"),Vt=class{politeness="polite";announcementMessage="";viewContainerRef;duration=0;panelClass;direction;data=null;horizontalPosition="center";verticalPosition="bottom"},Yo=(()=>{class n{static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n,selectors:[["","matSnackBarLabel",""]],hostAttrs:[1,"mat-mdc-snack-bar-label","mdc-snackbar__label"]})}return n})(),Ko=(()=>{class n{static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n,selectors:[["","matSnackBarActions",""]],hostAttrs:[1,"mat-mdc-snack-bar-actions","mdc-snackbar__actions"]})}return n})(),Zo=(()=>{class n{static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n,selectors:[["","matSnackBarAction",""]],hostAttrs:[1,"mat-mdc-snack-bar-action","mdc-snackbar__action"]})}return n})(),Kn=(()=>{class n{snackBarRef=r(Zt);data=r(Yn);constructor(){}action(){this.snackBarRef.dismissWithAction()}get hasAction(){return!!this.data.action}static \u0275fac=function(e){return new(e||n)};static \u0275cmp=C({type:n,selectors:[["simple-snack-bar"]],hostAttrs:[1,"mat-mdc-simple-snack-bar"],exportAs:["matSnackBar"],decls:3,vars:2,consts:[["matSnackBarLabel",""],["matSnackBarActions",""],["matButton","","matSnackBarAction","",3,"click"]],template:function(e,i){e&1&&(l(0,"div",0),E(1),d(),g(2,Qo,3,1,"div",1)),e&2&&(c(),ft(" ",i.data.message,` +`),c(),v(i.hasAction?2:-1))},dependencies:[qi,Yo,Ko,Zo],styles:[`.mat-mdc-simple-snack-bar { + display: flex; +} +.mat-mdc-simple-snack-bar .mat-mdc-snack-bar-label { + max-height: 50vh; + overflow: auto; +} +`],encapsulation:2,changeDetection:0})}return n})(),Qe="_mat-snack-bar-enter",Ge="_mat-snack-bar-exit",Xo=(()=>{class n extends Mi{_ngZone=r(H);_elementRef=r(O);_changeDetectorRef=r(W);_platform=r(ot);_animationsDisabled=Q();snackBarConfig=r(Vt);_document=r(It);_trackedModals=new Set;_enterFallback;_exitFallback;_injector=r(G);_announceDelay=150;_announceTimeoutId;_destroyed=!1;_portalOutlet;_onAnnounce=new k;_onExit=new k;_onEnter=new k;_animationState="void";_live;_label;_role;_liveElementId=r(U).getId("mat-snack-bar-container-live-");constructor(){super();let t=this.snackBarConfig;t.politeness==="assertive"&&!t.announcementMessage?this._live="assertive":t.politeness==="off"?this._live="off":this._live="polite",this._platform.FIREFOX&&(this._live==="polite"&&(this._role="status"),this._live==="assertive"&&(this._role="alert"))}attachComponentPortal(t){this._assertNotAttached();let e=this._portalOutlet.attachComponentPortal(t);return this._afterPortalAttached(),e}attachTemplatePortal(t){this._assertNotAttached();let e=this._portalOutlet.attachTemplatePortal(t);return this._afterPortalAttached(),e}attachDomPortal=t=>{this._assertNotAttached();let e=this._portalOutlet.attachDomPortal(t);return this._afterPortalAttached(),e};onAnimationEnd(t){t===Ge?this._completeExit():t===Qe&&(clearTimeout(this._enterFallback),this._ngZone.run(()=>{this._onEnter.next(),this._onEnter.complete()}))}enter(){this._destroyed||(this._animationState="visible",this._changeDetectorRef.markForCheck(),this._changeDetectorRef.detectChanges(),this._screenReaderAnnounce(),this._animationsDisabled?rt(()=>{this._ngZone.run(()=>queueMicrotask(()=>this.onAnimationEnd(Qe)))},{injector:this._injector}):(clearTimeout(this._enterFallback),this._enterFallback=setTimeout(()=>{this._elementRef.nativeElement.classList.add("mat-snack-bar-fallback-visible"),this.onAnimationEnd(Qe)},200)))}exit(){return this._destroyed?te(void 0):(this._ngZone.run(()=>{this._animationState="hidden",this._changeDetectorRef.markForCheck(),this._elementRef.nativeElement.setAttribute("mat-exit",""),clearTimeout(this._announceTimeoutId),this._animationsDisabled?rt(()=>{this._ngZone.run(()=>queueMicrotask(()=>this.onAnimationEnd(Ge)))},{injector:this._injector}):(clearTimeout(this._exitFallback),this._exitFallback=setTimeout(()=>this.onAnimationEnd(Ge),200))}),this._onExit)}ngOnDestroy(){this._destroyed=!0,this._clearFromModals(),this._completeExit()}_completeExit(){clearTimeout(this._exitFallback),queueMicrotask(()=>{this._onExit.next(),this._onExit.complete()})}_afterPortalAttached(){let t=this._elementRef.nativeElement,e=this.snackBarConfig.panelClass;e&&(Array.isArray(e)?e.forEach(o=>t.classList.add(o)):t.classList.add(e)),this._exposeToModals();let i=this._label.nativeElement,a="mdc-snackbar__label";i.classList.toggle(a,!i.querySelector(`.${a}`))}_exposeToModals(){let t=this._liveElementId,e=this._document.querySelectorAll('body > .cdk-overlay-container [aria-modal="true"]');for(let i=0;i{let e=t.getAttribute("aria-owns");if(e){let i=e.replace(this._liveElementId,"").trim();i.length>0?t.setAttribute("aria-owns",i):t.removeAttribute("aria-owns")}}),this._trackedModals.clear()}_assertNotAttached(){this._portalOutlet.hasAttached()}_screenReaderAnnounce(){this._announceTimeoutId||this._ngZone.runOutsideAngular(()=>{this._announceTimeoutId=setTimeout(()=>{if(this._destroyed)return;let t=this._elementRef.nativeElement,e=t.querySelector("[aria-hidden]"),i=t.querySelector("[aria-live]");if(e&&i){let a=null;this._platform.isBrowser&&document.activeElement instanceof HTMLElement&&e.contains(document.activeElement)&&(a=document.activeElement),e.removeAttribute("aria-hidden"),i.appendChild(e),a?.focus(),this._onAnnounce.next(),this._onAnnounce.complete()}},this._announceDelay)})}static \u0275fac=function(e){return new(e||n)};static \u0275cmp=C({type:n,selectors:[["mat-snack-bar-container"]],viewQuery:function(e,i){if(e&1&&V(Lt,7)(Go,7),e&2){let a;m(a=h())&&(i._portalOutlet=a.first),m(a=h())&&(i._label=a.first)}},hostAttrs:[1,"mdc-snackbar","mat-mdc-snack-bar-container"],hostVars:6,hostBindings:function(e,i){e&1&&u("animationend",function(o){return i.onAnimationEnd(o.animationName)})("animationcancel",function(o){return i.onAnimationEnd(o.animationName)}),e&2&&_("mat-snack-bar-container-enter",i._animationState==="visible")("mat-snack-bar-container-exit",i._animationState==="hidden")("mat-snack-bar-container-animations-enabled",!i._animationsDisabled)},features:[q],decls:6,vars:3,consts:[["label",""],[1,"mdc-snackbar__surface","mat-mdc-snackbar-surface"],[1,"mat-mdc-snack-bar-label"],["aria-hidden","true"],["cdkPortalOutlet",""]],template:function(e,i){e&1&&(l(0,"div",1)(1,"div",2,0)(3,"div",3),tt(4,qo,0,0,"ng-template",4),d(),M(5,"div"),d()()),e&2&&(c(5),T("aria-live",i._live)("role",i._role)("id",i._liveElementId))},dependencies:[Lt],styles:[`@keyframes _mat-snack-bar-enter { + from { + transform: scale(0.8); + opacity: 0; + } + to { + transform: scale(1); + opacity: 1; + } +} +@keyframes _mat-snack-bar-exit { + from { + opacity: 1; + } + to { + opacity: 0; + } +} +.mat-mdc-snack-bar-container { + display: flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + margin: 8px; +} +.mat-mdc-snack-bar-handset .mat-mdc-snack-bar-container { + width: 100vw; +} + +.mat-snack-bar-container-animations-enabled { + opacity: 0; +} +.mat-snack-bar-container-animations-enabled.mat-snack-bar-fallback-visible { + opacity: 1; +} +.mat-snack-bar-container-animations-enabled.mat-snack-bar-container-enter { + animation: _mat-snack-bar-enter 150ms cubic-bezier(0, 0, 0.2, 1) forwards; +} +.mat-snack-bar-container-animations-enabled.mat-snack-bar-container-exit { + animation: _mat-snack-bar-exit 75ms cubic-bezier(0.4, 0, 1, 1) forwards; +} + +.mat-mdc-snackbar-surface { + box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0, 0, 0, 0.12); + display: flex; + align-items: center; + justify-content: flex-start; + box-sizing: border-box; + padding-left: 0; + padding-right: 8px; +} +[dir=rtl] .mat-mdc-snackbar-surface { + padding-right: 0; + padding-left: 8px; +} +.mat-mdc-snack-bar-container .mat-mdc-snackbar-surface { + min-width: 344px; + max-width: 672px; +} +.mat-mdc-snack-bar-handset .mat-mdc-snackbar-surface { + width: 100%; + min-width: 0; +} +@media (forced-colors: active) { + .mat-mdc-snackbar-surface { + outline: solid 1px; + } +} +.mat-mdc-snack-bar-container .mat-mdc-snackbar-surface { + color: var(--mat-snack-bar-supporting-text-color, var(--mat-sys-inverse-on-surface)); + border-radius: var(--mat-snack-bar-container-shape, var(--mat-sys-corner-extra-small)); + background-color: var(--mat-snack-bar-container-color, var(--mat-sys-inverse-surface)); +} + +.mdc-snackbar__label { + width: 100%; + flex-grow: 1; + box-sizing: border-box; + margin: 0; + padding: 14px 8px 14px 16px; +} +[dir=rtl] .mdc-snackbar__label { + padding-left: 8px; + padding-right: 16px; +} +.mat-mdc-snack-bar-container .mdc-snackbar__label { + font-family: var(--mat-snack-bar-supporting-text-font, var(--mat-sys-body-medium-font)); + font-size: var(--mat-snack-bar-supporting-text-size, var(--mat-sys-body-medium-size)); + font-weight: var(--mat-snack-bar-supporting-text-weight, var(--mat-sys-body-medium-weight)); + line-height: var(--mat-snack-bar-supporting-text-line-height, var(--mat-sys-body-medium-line-height)); +} + +.mat-mdc-snack-bar-actions { + display: flex; + flex-shrink: 0; + align-items: center; + box-sizing: border-box; +} + +.mat-mdc-snack-bar-handset, +.mat-mdc-snack-bar-container, +.mat-mdc-snack-bar-label { + flex: 1 1 auto; +} + +.mat-mdc-snack-bar-container .mat-mdc-button.mat-mdc-snack-bar-action:not(:disabled).mat-unthemed { + color: var(--mat-snack-bar-button-color, var(--mat-sys-inverse-primary)); +} +.mat-mdc-snack-bar-container .mat-mdc-button.mat-mdc-snack-bar-action:not(:disabled) { + --mat-button-text-state-layer-color: currentColor; + --mat-button-text-ripple-color: currentColor; +} +.mat-mdc-snack-bar-container .mat-mdc-button.mat-mdc-snack-bar-action:not(:disabled) .mat-ripple-element { + opacity: 0.1; +} +`],encapsulation:2})}return n})(),Jo=new w("mat-snack-bar-default-options",{providedIn:"root",factory:()=>new Vt}),qe=(()=>{class n{_live=r(de);_injector=r(G);_breakpointObserver=r(ji);_parentSnackBar=r(n,{optional:!0,skipSelf:!0});_defaultConfig=r(Jo);_animationsDisabled=Q();_snackBarRefAtThisLevel=null;simpleSnackBarComponent=Kn;snackBarContainerComponent=Xo;handsetCssClass="mat-mdc-snack-bar-handset";get _openedSnackBarRef(){let t=this._parentSnackBar;return t?t._openedSnackBarRef:this._snackBarRefAtThisLevel}set _openedSnackBarRef(t){this._parentSnackBar?this._parentSnackBar._openedSnackBarRef=t:this._snackBarRefAtThisLevel=t}constructor(){}openFromComponent(t,e){return this._attach(t,e)}openFromTemplate(t,e){return this._attach(t,e)}open(t,e="",i){let a=X(X({},this._defaultConfig),i);return a.data={message:t,action:e},a.announcementMessage===t&&(a.announcementMessage=void 0),this.openFromComponent(this.simpleSnackBarComponent,a)}dismiss(){this._openedSnackBarRef&&this._openedSnackBarRef.dismiss()}ngOnDestroy(){this._snackBarRefAtThisLevel&&this._snackBarRefAtThisLevel.dismiss()}_attachSnackBarContainer(t,e){let i=e&&e.viewContainerRef&&e.viewContainerRef.injector,a=G.create({parent:i||this._injector,providers:[{provide:Vt,useValue:e}]}),o=new qt(this.snackBarContainerComponent,e.viewContainerRef,a),f=t.attach(o);return f.instance.snackBarConfig=e,f.instance}_attach(t,e){let i=X(X(X({},new Vt),this._defaultConfig),e),a=this._createOverlay(i),o=this._attachSnackBarContainer(a,i),f=new Zt(o,a);if(t instanceof Dt){let b=new oe(t,null,{$implicit:i.data,snackBarRef:f});f.instance=o.attachTemplatePortal(b)}else{let b=this._createInjector(i,f),xt=new qt(t,void 0,b),Mt=o.attachComponentPortal(xt);f.instance=Mt.instance}return this._breakpointObserver.observe(Gi.HandsetPortrait).pipe(I(a.detachments())).subscribe(b=>{a.overlayElement.classList.toggle(this.handsetCssClass,b.matches)}),i.announcementMessage&&o._onAnnounce.subscribe(()=>{this._live.announce(i.announcementMessage,i.politeness)}),this._animateSnackBar(f,i),this._openedSnackBarRef=f,this._openedSnackBarRef}_animateSnackBar(t,e){t.afterDismissed().subscribe(()=>{this._openedSnackBarRef==t&&(this._openedSnackBarRef=null),e.announcementMessage&&this._live.clear()}),e.duration&&e.duration>0&&t.afterOpened().subscribe(()=>t._dismissAfter(e.duration)),this._openedSnackBarRef?(this._openedSnackBarRef.afterDismissed().subscribe(()=>{t.containerInstance.enter()}),this._openedSnackBarRef.dismiss()):t.containerInstance.enter()}_createOverlay(t){let e=new Di;e.direction=t.direction;let i=Fi(this._injector),a=t.direction==="rtl",o=t.horizontalPosition==="left"||t.horizontalPosition==="start"&&!a||t.horizontalPosition==="end"&&a,f=!o&&t.horizontalPosition!=="center";return o?i.left("0"):f?i.right("0"):i.centerHorizontally(),t.verticalPosition==="top"?i.top("0"):i.bottom("0"),e.positionStrategy=i,e.disableAnimations=this._animationsDisabled,se(this._injector,e)}_createInjector(t,e){let i=t&&t.viewContainerRef&&t.viewContainerRef.injector;return G.create({parent:i||this._injector,providers:[{provide:Zt,useValue:e},{provide:Yn,useValue:t.data}]})}static \u0275fac=function(e){return new(e||n)};static \u0275prov=P({token:n,factory:n.\u0275fac,providedIn:"root"})}return n})();var ad=(()=>{class n{static \u0275fac=function(e){return new(e||n)};static \u0275mod=N({type:n});static \u0275inj=z({providers:[qe],imports:[Pt,Ii,Ui,Kn,j]})}return n})();var Zn=(()=>{class n{snackbar;snackBarConfigForSuccess={duration:4e3,panelClass:["snackabar-success"],MatSnackBarHorizontalPosition:"center",MatSnackBarVerticalPosition:"bottom"};snackBarConfigForWarning={duration:4e3,panelClass:["snackabar-warning"],MatSnackBarHorizontalPosition:"center",MatSnackBarVerticalPosition:"bottom"};snackBarConfigForError={duration:1e4,panelClass:["snackabar-error"],MatSnackBarHorizontalPosition:"center",MatSnackBarVerticalPosition:"bottom"};constructor(t){this.snackbar=t}error(t){console.error(t),this.snackbar.open(t,"Close",this.snackBarConfigForError)}warning(t){this.snackbar.open(t,"Close",this.snackBarConfigForWarning)}success(t){this.snackbar.open(t,"Close",this.snackBarConfigForSuccess)}static \u0275fac=function(e){return new(e||n)(at(qe))};static \u0275prov=P({token:n,factory:n.\u0275fac})}return n})();var Xn=["*"],Jn=`.mdc-list { + margin: 0; + padding: 8px 0; + list-style-type: none; +} +.mdc-list:focus { + outline: none; +} + +.mdc-list-item { + display: flex; + position: relative; + justify-content: flex-start; + overflow: hidden; + padding: 0; + align-items: stretch; + cursor: pointer; + padding-left: 16px; + padding-right: 16px; + background-color: var(--mat-list-list-item-container-color, transparent); + border-radius: var(--mat-list-list-item-container-shape, var(--mat-sys-corner-none)); +} +.mdc-list-item.mdc-list-item--selected { + background-color: var(--mat-list-list-item-selected-container-color); +} +.mdc-list-item:focus { + outline: 0; +} +.mdc-list-item.mdc-list-item--disabled { + cursor: auto; +} +.mdc-list-item.mdc-list-item--with-one-line { + height: var(--mat-list-list-item-one-line-container-height, 48px); +} +.mdc-list-item.mdc-list-item--with-one-line .mdc-list-item__start { + align-self: center; + margin-top: 0; +} +.mdc-list-item.mdc-list-item--with-one-line .mdc-list-item__end { + align-self: center; + margin-top: 0; +} +.mdc-list-item.mdc-list-item--with-two-lines { + height: var(--mat-list-list-item-two-line-container-height, 64px); +} +.mdc-list-item.mdc-list-item--with-two-lines .mdc-list-item__start { + align-self: flex-start; + margin-top: 16px; +} +.mdc-list-item.mdc-list-item--with-two-lines .mdc-list-item__end { + align-self: center; + margin-top: 0; +} +.mdc-list-item.mdc-list-item--with-three-lines { + height: var(--mat-list-list-item-three-line-container-height, 88px); +} +.mdc-list-item.mdc-list-item--with-three-lines .mdc-list-item__start { + align-self: flex-start; + margin-top: 16px; +} +.mdc-list-item.mdc-list-item--with-three-lines .mdc-list-item__end { + align-self: flex-start; + margin-top: 16px; +} +.mdc-list-item.mdc-list-item--selected::before, .mdc-list-item.mdc-list-item--selected:focus::before, .mdc-list-item:not(.mdc-list-item--selected):focus::before { + position: absolute; + box-sizing: border-box; + width: 100%; + height: 100%; + top: 0; + left: 0; + content: ""; + pointer-events: none; +} + +a.mdc-list-item { + color: inherit; + text-decoration: none; +} + +.mdc-list-item__start { + fill: currentColor; + flex-shrink: 0; + pointer-events: none; +} +.mdc-list-item--with-leading-icon .mdc-list-item__start { + color: var(--mat-list-list-item-leading-icon-color, var(--mat-sys-on-surface-variant)); + width: var(--mat-list-list-item-leading-icon-size, 24px); + height: var(--mat-list-list-item-leading-icon-size, 24px); + margin-left: 16px; + margin-right: 32px; +} +[dir=rtl] .mdc-list-item--with-leading-icon .mdc-list-item__start { + margin-left: 32px; + margin-right: 16px; +} +.mdc-list-item--with-leading-icon:hover .mdc-list-item__start { + color: var(--mat-list-list-item-hover-leading-icon-color); +} +.mdc-list-item--with-leading-avatar .mdc-list-item__start { + width: var(--mat-list-list-item-leading-avatar-size, 40px); + height: var(--mat-list-list-item-leading-avatar-size, 40px); + margin-left: 16px; + margin-right: 16px; + border-radius: 50%; +} +.mdc-list-item--with-leading-avatar .mdc-list-item__start, [dir=rtl] .mdc-list-item--with-leading-avatar .mdc-list-item__start { + margin-left: 16px; + margin-right: 16px; + border-radius: 50%; +} + +.mdc-list-item__end { + flex-shrink: 0; + pointer-events: none; +} +.mdc-list-item--with-trailing-meta .mdc-list-item__end { + font-family: var(--mat-list-list-item-trailing-supporting-text-font, var(--mat-sys-label-small-font)); + line-height: var(--mat-list-list-item-trailing-supporting-text-line-height, var(--mat-sys-label-small-line-height)); + font-size: var(--mat-list-list-item-trailing-supporting-text-size, var(--mat-sys-label-small-size)); + font-weight: var(--mat-list-list-item-trailing-supporting-text-weight, var(--mat-sys-label-small-weight)); + letter-spacing: var(--mat-list-list-item-trailing-supporting-text-tracking, var(--mat-sys-label-small-tracking)); +} +.mdc-list-item--with-trailing-icon .mdc-list-item__end { + color: var(--mat-list-list-item-trailing-icon-color, var(--mat-sys-on-surface-variant)); + width: var(--mat-list-list-item-trailing-icon-size, 24px); + height: var(--mat-list-list-item-trailing-icon-size, 24px); +} +.mdc-list-item--with-trailing-icon:hover .mdc-list-item__end { + color: var(--mat-list-list-item-hover-trailing-icon-color); +} +.mdc-list-item.mdc-list-item--with-trailing-meta .mdc-list-item__end { + color: var(--mat-list-list-item-trailing-supporting-text-color, var(--mat-sys-on-surface-variant)); +} +.mdc-list-item--selected.mdc-list-item--with-trailing-icon .mdc-list-item__end { + color: var(--mat-list-list-item-selected-trailing-icon-color, var(--mat-sys-primary)); +} + +.mdc-list-item__content { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + align-self: center; + flex: 1; + pointer-events: none; +} +.mdc-list-item--with-two-lines .mdc-list-item__content, .mdc-list-item--with-three-lines .mdc-list-item__content { + align-self: stretch; +} + +.mdc-list-item__primary-text { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + color: var(--mat-list-list-item-label-text-color, var(--mat-sys-on-surface)); + font-family: var(--mat-list-list-item-label-text-font, var(--mat-sys-body-large-font)); + line-height: var(--mat-list-list-item-label-text-line-height, var(--mat-sys-body-large-line-height)); + font-size: var(--mat-list-list-item-label-text-size, var(--mat-sys-body-large-size)); + font-weight: var(--mat-list-list-item-label-text-weight, var(--mat-sys-body-large-weight)); + letter-spacing: var(--mat-list-list-item-label-text-tracking, var(--mat-sys-body-large-tracking)); +} +.mdc-list-item:hover .mdc-list-item__primary-text { + color: var(--mat-list-list-item-hover-label-text-color, var(--mat-sys-on-surface)); +} +.mdc-list-item:focus .mdc-list-item__primary-text { + color: var(--mat-list-list-item-focus-label-text-color, var(--mat-sys-on-surface)); +} +.mdc-list-item--with-two-lines .mdc-list-item__primary-text, .mdc-list-item--with-three-lines .mdc-list-item__primary-text { + display: block; + margin-top: 0; + line-height: normal; + margin-bottom: -20px; +} +.mdc-list-item--with-two-lines .mdc-list-item__primary-text::before, .mdc-list-item--with-three-lines .mdc-list-item__primary-text::before { + display: inline-block; + width: 0; + height: 28px; + content: ""; + vertical-align: 0; +} +.mdc-list-item--with-two-lines .mdc-list-item__primary-text::after, .mdc-list-item--with-three-lines .mdc-list-item__primary-text::after { + display: inline-block; + width: 0; + height: 20px; + content: ""; + vertical-align: -20px; +} + +.mdc-list-item__secondary-text { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + display: block; + margin-top: 0; + color: var(--mat-list-list-item-supporting-text-color, var(--mat-sys-on-surface-variant)); + font-family: var(--mat-list-list-item-supporting-text-font, var(--mat-sys-body-medium-font)); + line-height: var(--mat-list-list-item-supporting-text-line-height, var(--mat-sys-body-medium-line-height)); + font-size: var(--mat-list-list-item-supporting-text-size, var(--mat-sys-body-medium-size)); + font-weight: var(--mat-list-list-item-supporting-text-weight, var(--mat-sys-body-medium-weight)); + letter-spacing: var(--mat-list-list-item-supporting-text-tracking, var(--mat-sys-body-medium-tracking)); +} +.mdc-list-item__secondary-text::before { + display: inline-block; + width: 0; + height: 20px; + content: ""; + vertical-align: 0; +} +.mdc-list-item--with-three-lines .mdc-list-item__secondary-text { + white-space: normal; + line-height: 20px; +} +.mdc-list-item--with-overline .mdc-list-item__secondary-text { + white-space: nowrap; + line-height: auto; +} + +.mdc-list-item--with-leading-radio.mdc-list-item, +.mdc-list-item--with-leading-checkbox.mdc-list-item, +.mdc-list-item--with-leading-icon.mdc-list-item, +.mdc-list-item--with-leading-avatar.mdc-list-item { + padding-left: 0; + padding-right: 16px; +} +[dir=rtl] .mdc-list-item--with-leading-radio.mdc-list-item, +[dir=rtl] .mdc-list-item--with-leading-checkbox.mdc-list-item, +[dir=rtl] .mdc-list-item--with-leading-icon.mdc-list-item, +[dir=rtl] .mdc-list-item--with-leading-avatar.mdc-list-item { + padding-left: 16px; + padding-right: 0; +} +.mdc-list-item--with-leading-radio.mdc-list-item--with-two-lines .mdc-list-item__primary-text, +.mdc-list-item--with-leading-checkbox.mdc-list-item--with-two-lines .mdc-list-item__primary-text, +.mdc-list-item--with-leading-icon.mdc-list-item--with-two-lines .mdc-list-item__primary-text, +.mdc-list-item--with-leading-avatar.mdc-list-item--with-two-lines .mdc-list-item__primary-text { + display: block; + margin-top: 0; + line-height: normal; + margin-bottom: -20px; +} +.mdc-list-item--with-leading-radio.mdc-list-item--with-two-lines .mdc-list-item__primary-text::before, +.mdc-list-item--with-leading-checkbox.mdc-list-item--with-two-lines .mdc-list-item__primary-text::before, +.mdc-list-item--with-leading-icon.mdc-list-item--with-two-lines .mdc-list-item__primary-text::before, +.mdc-list-item--with-leading-avatar.mdc-list-item--with-two-lines .mdc-list-item__primary-text::before { + display: inline-block; + width: 0; + height: 32px; + content: ""; + vertical-align: 0; +} +.mdc-list-item--with-leading-radio.mdc-list-item--with-two-lines .mdc-list-item__primary-text::after, +.mdc-list-item--with-leading-checkbox.mdc-list-item--with-two-lines .mdc-list-item__primary-text::after, +.mdc-list-item--with-leading-icon.mdc-list-item--with-two-lines .mdc-list-item__primary-text::after, +.mdc-list-item--with-leading-avatar.mdc-list-item--with-two-lines .mdc-list-item__primary-text::after { + display: inline-block; + width: 0; + height: 20px; + content: ""; + vertical-align: -20px; +} +.mdc-list-item--with-leading-radio.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end, +.mdc-list-item--with-leading-checkbox.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end, +.mdc-list-item--with-leading-icon.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end, +.mdc-list-item--with-leading-avatar.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end { + display: block; + margin-top: 0; + line-height: normal; +} +.mdc-list-item--with-leading-radio.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end::before, +.mdc-list-item--with-leading-checkbox.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end::before, +.mdc-list-item--with-leading-icon.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end::before, +.mdc-list-item--with-leading-avatar.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end::before { + display: inline-block; + width: 0; + height: 32px; + content: ""; + vertical-align: 0; +} + +.mdc-list-item--with-trailing-icon.mdc-list-item, [dir=rtl] .mdc-list-item--with-trailing-icon.mdc-list-item { + padding-left: 0; + padding-right: 0; +} +.mdc-list-item--with-trailing-icon .mdc-list-item__end { + margin-left: 16px; + margin-right: 16px; +} + +.mdc-list-item--with-trailing-meta.mdc-list-item { + padding-left: 16px; + padding-right: 0; +} +[dir=rtl] .mdc-list-item--with-trailing-meta.mdc-list-item { + padding-left: 0; + padding-right: 16px; +} +.mdc-list-item--with-trailing-meta .mdc-list-item__end { + -webkit-user-select: none; + user-select: none; + margin-left: 28px; + margin-right: 16px; +} +[dir=rtl] .mdc-list-item--with-trailing-meta .mdc-list-item__end { + margin-left: 16px; + margin-right: 28px; +} +.mdc-list-item--with-trailing-meta.mdc-list-item--with-three-lines .mdc-list-item__end, .mdc-list-item--with-trailing-meta.mdc-list-item--with-two-lines .mdc-list-item__end { + display: block; + line-height: normal; + align-self: flex-start; + margin-top: 0; +} +.mdc-list-item--with-trailing-meta.mdc-list-item--with-three-lines .mdc-list-item__end::before, .mdc-list-item--with-trailing-meta.mdc-list-item--with-two-lines .mdc-list-item__end::before { + display: inline-block; + width: 0; + height: 28px; + content: ""; + vertical-align: 0; +} + +.mdc-list-item--with-leading-radio .mdc-list-item__start, +.mdc-list-item--with-leading-checkbox .mdc-list-item__start { + margin-left: 8px; + margin-right: 24px; +} +[dir=rtl] .mdc-list-item--with-leading-radio .mdc-list-item__start, +[dir=rtl] .mdc-list-item--with-leading-checkbox .mdc-list-item__start { + margin-left: 24px; + margin-right: 8px; +} +.mdc-list-item--with-leading-radio.mdc-list-item--with-two-lines .mdc-list-item__start, +.mdc-list-item--with-leading-checkbox.mdc-list-item--with-two-lines .mdc-list-item__start { + align-self: flex-start; + margin-top: 8px; +} + +.mdc-list-item--with-trailing-radio.mdc-list-item, +.mdc-list-item--with-trailing-checkbox.mdc-list-item { + padding-left: 16px; + padding-right: 0; +} +[dir=rtl] .mdc-list-item--with-trailing-radio.mdc-list-item, +[dir=rtl] .mdc-list-item--with-trailing-checkbox.mdc-list-item { + padding-left: 0; + padding-right: 16px; +} +.mdc-list-item--with-trailing-radio.mdc-list-item--with-leading-icon, .mdc-list-item--with-trailing-radio.mdc-list-item--with-leading-avatar, +.mdc-list-item--with-trailing-checkbox.mdc-list-item--with-leading-icon, +.mdc-list-item--with-trailing-checkbox.mdc-list-item--with-leading-avatar { + padding-left: 0; +} +[dir=rtl] .mdc-list-item--with-trailing-radio.mdc-list-item--with-leading-icon, [dir=rtl] .mdc-list-item--with-trailing-radio.mdc-list-item--with-leading-avatar, +[dir=rtl] .mdc-list-item--with-trailing-checkbox.mdc-list-item--with-leading-icon, +[dir=rtl] .mdc-list-item--with-trailing-checkbox.mdc-list-item--with-leading-avatar { + padding-right: 0; +} +.mdc-list-item--with-trailing-radio .mdc-list-item__end, +.mdc-list-item--with-trailing-checkbox .mdc-list-item__end { + margin-left: 24px; + margin-right: 8px; +} +[dir=rtl] .mdc-list-item--with-trailing-radio .mdc-list-item__end, +[dir=rtl] .mdc-list-item--with-trailing-checkbox .mdc-list-item__end { + margin-left: 8px; + margin-right: 24px; +} +.mdc-list-item--with-trailing-radio.mdc-list-item--with-three-lines .mdc-list-item__end, +.mdc-list-item--with-trailing-checkbox.mdc-list-item--with-three-lines .mdc-list-item__end { + align-self: flex-start; + margin-top: 8px; +} + +.mdc-list-group__subheader { + margin: 0.75rem 16px; +} + +.mdc-list-item--disabled .mdc-list-item__start, +.mdc-list-item--disabled .mdc-list-item__content, +.mdc-list-item--disabled .mdc-list-item__end { + opacity: 1; +} +.mdc-list-item--disabled .mdc-list-item__primary-text, +.mdc-list-item--disabled .mdc-list-item__secondary-text { + opacity: var(--mat-list-list-item-disabled-label-text-opacity, 0.3); +} +.mdc-list-item--disabled.mdc-list-item--with-leading-icon .mdc-list-item__start { + color: var(--mat-list-list-item-disabled-leading-icon-color, var(--mat-sys-on-surface)); + opacity: var(--mat-list-list-item-disabled-leading-icon-opacity, 0.38); +} +.mdc-list-item--disabled.mdc-list-item--with-trailing-icon .mdc-list-item__end { + color: var(--mat-list-list-item-disabled-trailing-icon-color, var(--mat-sys-on-surface)); + opacity: var(--mat-list-list-item-disabled-trailing-icon-opacity, 0.38); +} + +.mat-mdc-list-item.mat-mdc-list-item-both-leading-and-trailing, [dir=rtl] .mat-mdc-list-item.mat-mdc-list-item-both-leading-and-trailing { + padding-left: 0; + padding-right: 0; +} + +.mdc-list-item.mdc-list-item--disabled .mdc-list-item__primary-text { + color: var(--mat-list-list-item-disabled-label-text-color, var(--mat-sys-on-surface)); +} + +.mdc-list-item:hover::before { + background-color: var(--mat-list-list-item-hover-state-layer-color, var(--mat-sys-on-surface)); + opacity: var(--mat-list-list-item-hover-state-layer-opacity, var(--mat-sys-hover-state-layer-opacity)); +} + +.mdc-list-item.mdc-list-item--disabled::before { + background-color: var(--mat-list-list-item-disabled-state-layer-color, var(--mat-sys-on-surface)); + opacity: var(--mat-list-list-item-disabled-state-layer-opacity, var(--mat-sys-focus-state-layer-opacity)); +} + +.mdc-list-item:focus::before { + background-color: var(--mat-list-list-item-focus-state-layer-color, var(--mat-sys-on-surface)); + opacity: var(--mat-list-list-item-focus-state-layer-opacity, var(--mat-sys-focus-state-layer-opacity)); +} + +.mdc-list-item--disabled .mdc-radio, +.mdc-list-item--disabled .mdc-checkbox { + opacity: var(--mat-list-list-item-disabled-label-text-opacity, 0.3); +} + +.mdc-list-item--with-leading-avatar .mat-mdc-list-item-avatar { + border-radius: var(--mat-list-list-item-leading-avatar-shape, var(--mat-sys-corner-full)); + background-color: var(--mat-list-list-item-leading-avatar-color, var(--mat-sys-primary-container)); +} + +.mat-mdc-list-item-icon { + font-size: var(--mat-list-list-item-leading-icon-size, 24px); +} + +@media (forced-colors: active) { + a.mdc-list-item--activated::after { + content: ""; + position: absolute; + top: 50%; + right: 16px; + transform: translateY(-50%); + width: 10px; + height: 0; + border-bottom: solid 10px; + border-radius: 10px; + } + a.mdc-list-item--activated [dir=rtl]::after { + right: auto; + left: 16px; + } +} + +.mat-mdc-list-base { + display: block; +} +.mat-mdc-list-base .mdc-list-item__start, +.mat-mdc-list-base .mdc-list-item__end, +.mat-mdc-list-base .mdc-list-item__content { + pointer-events: auto; +} + +.mat-mdc-list-item, +.mat-mdc-list-option { + width: 100%; + box-sizing: border-box; + -webkit-tap-highlight-color: transparent; +} +.mat-mdc-list-item:not(.mat-mdc-list-item-interactive), +.mat-mdc-list-option:not(.mat-mdc-list-item-interactive) { + cursor: default; +} +.mat-mdc-list-item .mat-divider-inset, +.mat-mdc-list-option .mat-divider-inset { + position: absolute; + left: 0; + right: 0; + bottom: 0; +} +.mat-mdc-list-item .mat-mdc-list-item-avatar ~ .mat-divider-inset, +.mat-mdc-list-option .mat-mdc-list-item-avatar ~ .mat-divider-inset { + margin-left: 72px; +} +[dir=rtl] .mat-mdc-list-item .mat-mdc-list-item-avatar ~ .mat-divider-inset, +[dir=rtl] .mat-mdc-list-option .mat-mdc-list-item-avatar ~ .mat-divider-inset { + margin-right: 72px; +} + +.mat-mdc-list-item-interactive::before { + top: 0; + left: 0; + right: 0; + bottom: 0; + position: absolute; + content: ""; + opacity: 0; + pointer-events: none; + border-radius: inherit; +} + +.mat-mdc-list-item > .mat-focus-indicator { + top: 0; + left: 0; + right: 0; + bottom: 0; + position: absolute; + pointer-events: none; +} +.mat-mdc-list-item:focus-visible > .mat-focus-indicator::before { + content: ""; +} + +.mat-mdc-list-item.mdc-list-item--with-three-lines .mat-mdc-list-item-line.mdc-list-item__secondary-text { + white-space: nowrap; + line-height: normal; +} +.mat-mdc-list-item.mdc-list-item--with-three-lines .mat-mdc-list-item-unscoped-content.mdc-list-item__secondary-text { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} + +mat-action-list button { + background: none; + color: inherit; + border: none; + font: inherit; + outline: inherit; + -webkit-tap-highlight-color: transparent; + text-align: start; +} +mat-action-list button::-moz-focus-inner { + border: 0; +} + +.mdc-list-item--with-leading-icon .mdc-list-item__start { + margin-inline-start: var(--mat-list-list-item-leading-icon-start-space, 16px); + margin-inline-end: var(--mat-list-list-item-leading-icon-end-space, 16px); +} + +.mat-mdc-nav-list .mat-mdc-list-item { + border-radius: var(--mat-list-active-indicator-shape, var(--mat-sys-corner-full)); + --mat-focus-indicator-border-radius: var(--mat-list-active-indicator-shape, var(--mat-sys-corner-full)); +} +.mat-mdc-nav-list .mat-mdc-list-item.mdc-list-item--activated { + background-color: var(--mat-list-active-indicator-color, var(--mat-sys-secondary-container)); +} +`,er=["unscopedContent"],ir=["text"],nr=[[["","matListItemAvatar",""],["","matListItemIcon",""]],[["","matListItemTitle",""]],[["","matListItemLine",""]],"*",[["","matListItemMeta",""]],[["mat-divider"]]],ar=["[matListItemAvatar],[matListItemIcon]","[matListItemTitle]","[matListItemLine]","*","[matListItemMeta]","mat-divider"];var or=new w("ListOption"),rr=(()=>{class n{_elementRef=r(O);constructor(){}static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n,selectors:[["","matListItemTitle",""]],hostAttrs:[1,"mat-mdc-list-item-title","mdc-list-item__primary-text"]})}return n})(),sr=(()=>{class n{_elementRef=r(O);constructor(){}static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n,selectors:[["","matListItemLine",""]],hostAttrs:[1,"mat-mdc-list-item-line","mdc-list-item__secondary-text"]})}return n})(),lr=(()=>{class n{static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n,selectors:[["","matListItemMeta",""]],hostAttrs:[1,"mat-mdc-list-item-meta","mdc-list-item__end"]})}return n})(),ta=(()=>{class n{_listOption=r(or,{optional:!0});constructor(){}_isAlignedAtStart(){return!this._listOption||this._listOption?._getTogglePosition()==="after"}static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n,hostVars:4,hostBindings:function(e,i){e&2&&_("mdc-list-item__start",i._isAlignedAtStart())("mdc-list-item__end",!i._isAlignedAtStart())}})}return n})(),cr=(()=>{class n extends ta{static \u0275fac=(()=>{let t;return function(i){return(t||(t=Y(n)))(i||n)}})();static \u0275dir=x({type:n,selectors:[["","matListItemAvatar",""]],hostAttrs:[1,"mat-mdc-list-item-avatar"],features:[q]})}return n})(),dr=(()=>{class n extends ta{static \u0275fac=(()=>{let t;return function(i){return(t||(t=Y(n)))(i||n)}})();static \u0275dir=x({type:n,selectors:[["","matListItemIcon",""]],hostAttrs:[1,"mat-mdc-list-item-icon"],features:[q]})}return n})(),mr=new w("MAT_LIST_CONFIG"),Xt=(()=>{class n{_isNonInteractive=!0;get disableRipple(){return this._disableRipple}set disableRipple(t){this._disableRipple=K(t)}_disableRipple=!1;get disabled(){return this._disabled()}set disabled(t){this._disabled.set(K(t))}_disabled=Z(!1);_defaultOptions=r(mr,{optional:!0});static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n,hostVars:1,hostBindings:function(e,i){e&2&&T("aria-disabled",i.disabled)},inputs:{disableRipple:"disableRipple",disabled:"disabled"}})}return n})(),hr=(()=>{class n{_elementRef=r(O);_ngZone=r(H);_listBase=r(Xt,{optional:!0});_platform=r(ot);_hostElement;_isButtonElement;_noopAnimations=Q();_avatars;_icons;set lines(t){this._explicitLines=Gt(t,null),this._updateItemLines(!1)}_explicitLines=null;get disableRipple(){return this.disabled||this._disableRipple||this._noopAnimations||!!this._listBase?.disableRipple}set disableRipple(t){this._disableRipple=K(t)}_disableRipple=!1;get disabled(){return this._disabled()||!!this._listBase?.disabled}set disabled(t){this._disabled.set(K(t))}_disabled=Z(!1);_subscriptions=new it;_rippleRenderer=null;_hasUnscopedTextContent=!1;rippleConfig;get rippleDisabled(){return this.disableRipple||!!this.rippleConfig.disabled}constructor(){r(wt).load(Ct);let t=r(Yt,{optional:!0});this.rippleConfig=t||{},this._hostElement=this._elementRef.nativeElement,this._isButtonElement=this._hostElement.nodeName.toLowerCase()==="button",this._listBase&&!this._listBase._isNonInteractive&&this._initInteractiveListItem(),this._isButtonElement&&!this._hostElement.hasAttribute("type")&&this._hostElement.setAttribute("type","button")}ngAfterViewInit(){this._monitorProjectedLinesAndTitle(),this._updateItemLines(!0)}ngOnDestroy(){this._subscriptions.unsubscribe(),this._rippleRenderer!==null&&this._rippleRenderer._removeTriggerEvents()}_hasIconOrAvatar(){return!!(this._avatars.length||this._icons.length)}_initInteractiveListItem(){this._hostElement.classList.add("mat-mdc-list-item-interactive"),this._rippleRenderer=new De(this,this._ngZone,this._hostElement,this._platform,r(G)),this._rippleRenderer.setupTriggerEvents(this._hostElement)}_monitorProjectedLinesAndTitle(){this._ngZone.runOutsideAngular(()=>{this._subscriptions.add(J(this._lines.changes,this._titles.changes).subscribe(()=>this._updateItemLines(!1)))})}_updateItemLines(t){if(!this._lines||!this._titles||!this._unscopedContent)return;t&&this._checkDomForUnscopedTextContent();let e=this._explicitLines??this._inferLinesFromContent(),i=this._unscopedContent.nativeElement;if(this._hostElement.classList.toggle("mat-mdc-list-item-single-line",e<=1),this._hostElement.classList.toggle("mdc-list-item--with-one-line",e<=1),this._hostElement.classList.toggle("mdc-list-item--with-two-lines",e===2),this._hostElement.classList.toggle("mdc-list-item--with-three-lines",e===3),this._hasUnscopedTextContent){let a=this._titles.length===0&&e===1;i.classList.toggle("mdc-list-item__primary-text",a),i.classList.toggle("mdc-list-item__secondary-text",!a)}else i.classList.remove("mdc-list-item__primary-text"),i.classList.remove("mdc-list-item__secondary-text")}_inferLinesFromContent(){let t=this._titles.length+this._lines.length;return this._hasUnscopedTextContent&&(t+=1),t}_checkDomForUnscopedTextContent(){this._hasUnscopedTextContent=Array.from(this._unscopedContent.nativeElement.childNodes).filter(t=>t.nodeType!==t.COMMENT_NODE).some(t=>!!(t.textContent&&t.textContent.trim()))}static \u0275fac=function(e){return new(e||n)};static \u0275dir=x({type:n,contentQueries:function(e,i,a){if(e&1&&et(a,cr,4)(a,dr,4),e&2){let o;m(o=h())&&(i._avatars=o),m(o=h())&&(i._icons=o)}},hostVars:4,hostBindings:function(e,i){e&2&&(T("aria-disabled",i.disabled)("disabled",i._isButtonElement&&i.disabled||null),_("mdc-list-item--disabled",i.disabled))},inputs:{lines:"lines",disableRipple:"disableRipple",disabled:"disabled"}})}return n})();var Od=(()=>{class n extends Xt{static \u0275fac=(()=>{let t;return function(i){return(t||(t=Y(n)))(i||n)}})();static \u0275cmp=C({type:n,selectors:[["mat-list"]],hostAttrs:[1,"mat-mdc-list","mat-mdc-list-base","mdc-list"],exportAs:["matList"],features:[$([{provide:Xt,useExisting:n}]),q],ngContentSelectors:Xn,decls:1,vars:0,template:function(e,i){e&1&&(B(),y(0))},styles:[Jn],encapsulation:2,changeDetection:0})}return n})(),Rd=(()=>{class n extends hr{_lines;_titles;_meta;_unscopedContent;_itemText;get activated(){return this._activated}set activated(t){this._activated=K(t)}_activated=!1;_getAriaCurrent(){return this._hostElement.nodeName==="A"&&this._activated?"page":null}_hasBothLeadingAndTrailing(){return this._meta.length!==0&&(this._avatars.length!==0||this._icons.length!==0)}static \u0275fac=(()=>{let t;return function(i){return(t||(t=Y(n)))(i||n)}})();static \u0275cmp=C({type:n,selectors:[["mat-list-item"],["a","mat-list-item",""],["button","mat-list-item",""]],contentQueries:function(e,i,a){if(e&1&&et(a,sr,5)(a,rr,5)(a,lr,5),e&2){let o;m(o=h())&&(i._lines=o),m(o=h())&&(i._titles=o),m(o=h())&&(i._meta=o)}},viewQuery:function(e,i){if(e&1&&V(er,5)(ir,5),e&2){let a;m(a=h())&&(i._unscopedContent=a.first),m(a=h())&&(i._itemText=a.first)}},hostAttrs:[1,"mat-mdc-list-item","mdc-list-item"],hostVars:13,hostBindings:function(e,i){e&2&&(T("aria-current",i._getAriaCurrent()),_("mdc-list-item--activated",i.activated)("mdc-list-item--with-leading-avatar",i._avatars.length!==0)("mdc-list-item--with-leading-icon",i._icons.length!==0)("mdc-list-item--with-trailing-meta",i._meta.length!==0)("mat-mdc-list-item-both-leading-and-trailing",i._hasBothLeadingAndTrailing())("_mat-animation-noopable",i._noopAnimations))},inputs:{activated:"activated"},exportAs:["matListItem"],features:[q],ngContentSelectors:ar,decls:10,vars:0,consts:[["unscopedContent",""],[1,"mdc-list-item__content"],[1,"mat-mdc-list-item-unscoped-content",3,"cdkObserveContent"],[1,"mat-focus-indicator"]],template:function(e,i){e&1&&(B(nr),y(0),l(1,"span",1),y(2,1),y(3,2),l(4,"span",2,0),u("cdkObserveContent",function(){return i._updateItemLines(!0)}),y(6,3),d()(),y(7,4),y(8,5),M(9,"div",3))},dependencies:[Ut],encapsulation:2,changeDetection:0})}return n})();var Ad=(()=>{class n extends Xt{_isNonInteractive=!1;static \u0275fac=(()=>{let t;return function(i){return(t||(t=Y(n)))(i||n)}})();static \u0275cmp=C({type:n,selectors:[["mat-nav-list"]],hostAttrs:["role","navigation",1,"mat-mdc-nav-list","mat-mdc-list-base","mdc-list"],exportAs:["matNavList"],features:[$([{provide:Xt,useExisting:n}]),q],ngContentSelectors:Xn,decls:1,vars:0,template:function(e,i){e&1&&(B(),y(0))},styles:[Jn],encapsulation:2,changeDetection:0})}return n})();var Ld=(()=>{class n{static \u0275fac=function(e){return new(e||n)};static \u0275mod=N({type:n});static \u0275inj=z({imports:[ce,Kt,ge,j,ve]})}return n})();var fr=n=>({lightTheme:n}),ur=()=>({right:!0,left:!0,bottom:!0,top:!0}),ea=(n,s)=>s.name;function _r(n,s){if(n&1){let t=lt();l(0,"div",1),u("mousemove",function(i){R(t);let a=p();return A(a.dragWidget(i))},ye)("mouseup",function(){R(t);let i=p();return A(i.toggleDragging(!1))},ye),d()}}function br(n,s){n&1&&(mt(),l(0,"svg",25),M(1,"rect",26),d())}function gr(n,s){n&1&&(mt(),l(0,"svg",25),M(1,"rect",27),d())}function vr(n,s){n&1&&(mt(),l(0,"svg",25),M(1,"rect",28),d())}function yr(n,s){if(n&1&&(l(0,"div"),E(1),d()),n&2){let t=p().$implicit;c(),mi("",t.console_type,"://",t.console_host,":",t.console)}}function xr(n,s){n&1&&(l(0,"div"),E(1,"none"),d())}function kr(n,s){if(n&1&&(l(0,"div",21)(1,"div"),g(2,br,2,0,":svg:svg",25),g(3,gr,2,0,":svg:svg",25),g(4,vr,2,0,":svg:svg",25),E(5),d(),g(6,yr,2,3,"div"),g(7,xr,2,0,"div"),d()),n&2){let t=s.$implicit;c(2),v(t.status==="started"?2:-1),c(),v(t.status==="suspended"?3:-1),c(),v(t.status==="stopped"?4:-1),c(),ft(" ",t.name," "),c(),v(t.console!==null&&t.console!==void 0&&t.console_type!=="none"?6:-1),c(),v(t.console===null||t.console===void 0||t.console_type==="none"?7:-1)}}function wr(n,s){n&1&&(mt(),l(0,"svg",25),M(1,"rect",26),d())}function Cr(n,s){n&1&&(mt(),l(0,"svg",25),M(1,"rect",28),d())}function Tr(n,s){if(n&1&&(l(0,"div",24)(1,"div"),g(2,wr,2,0,":svg:svg",25),g(3,Cr,2,0,":svg:svg",25),E(4),d(),l(5,"div"),E(6),d()()),n&2){let t=s.$implicit,e=p(2);D("matTooltip",e.getComputeTooltip(t)),c(2),v(t.connected?2:-1),c(),v(t.connected?-1:3),c(),ft(" ",e.truncateComputeName(t.name)," "),c(2),di(" ",t.host,":",t.port," ")}}function Mr(n,s){if(n&1){let t=lt();l(0,"div",2),u("mousedown",function(){R(t);let i=p();return A(i.toggleDragging(!0))})("resizeStart",function(){R(t);let i=p();return A(i.toggleDragging(!1))})("resizeEnd",function(i){R(t);let a=p();return A(a.onResizeEnd(i))}),l(1,"div",3)(2,"mat-tab-group")(3,"mat-tab",4),u("click",function(){R(t);let i=p();return A(i.toggleTopologyVisibility(!0))}),l(4,"div",5)(5,"div",6)(6,"mat-select",7)(7,"mat-optgroup",8)(8,"mat-option",9),u("onSelectionChange",function(){R(t);let i=p();return A(i.applyStatusFilter("started"))}),E(9,"started"),d(),l(10,"mat-option",10),u("onSelectionChange",function(){R(t);let i=p();return A(i.applyStatusFilter("suspended"))}),E(11,"suspended"),d(),l(12,"mat-option",11),u("onSelectionChange",function(){R(t);let i=p();return A(i.applyStatusFilter("stopped"))}),E(13,"stopped"),d()(),l(14,"mat-optgroup",12)(15,"mat-option",13),u("onSelectionChange",function(){R(t);let i=p();return A(i.applyCaptureFilter("capture"))}),E(16,"active capture(s)"),d(),l(17,"mat-option",14),u("onSelectionChange",function(){R(t);let i=p();return A(i.applyCaptureFilter("packet"))}),E(18,"active packet captures"),d()()()(),l(19,"div",15)(20,"mat-select",16),u("selectionChange",function(){R(t);let i=p();return A(i.setSortingOrder())}),fi("valueChange",function(i){R(t);let a=p();return pi(a.sortingOrder,i)||(a.sortingOrder=i),A(i)}),l(21,"mat-option",17),E(22,"sort by name ascending"),d(),l(23,"mat-option",18),E(24,"sort by name descending"),d()()(),M(25,"mat-divider",19),l(26,"div",20),Et(27,kr,8,6,"div",21,ea),d()()(),l(29,"mat-tab",22),u("click",function(){R(t);let i=p();return A(i.toggleTopologyVisibility(!1))}),l(30,"div",5)(31,"div",23),Et(32,Tr,7,6,"div",24,ea),d()()()()()()}if(n&2){let t=p();D("ngStyle",t.style)("ngClass",_i(7,fr,t.isLightThemeEnabled))("validateResize",t.validate)("resizeEdges",ui(9,ur))("enableGhostResize",!0),c(20),hi("value",t.sortingOrder),c(6),D("ngStyle",t.styleInside),c(),Ft(t.filteredNodes),c(5),Ft(t.computes)}}var im=(()=>{class n{nodesDataSource=r(Hn);projectService=r(Gn);computeService=r($n);linksDataSource=r(Vn);themeService=r(qn);notificationService=r(Un);toasterService=r(Zn);cd=r(W);controller;project;computesInitialized=!1;closeTopologySummary=new F;style={};styleInside={height:"280px"};subscriptions=[];projectsStatistics;nodes=[];filteredNodes=[];sortingOrder="asc";startedStatusFilterEnabled=!1;suspendedStatusFilterEnabled=!1;stoppedStatusFilterEnabled=!1;captureFilterEnabled=!1;packetFilterEnabled=!1;computes=[];isTopologyVisible=!0;isDraggingEnabled=!1;isLightThemeEnabled=!1;constructor(){}ngOnInit(){this.themeService.getActualTheme()==="light"?this.isLightThemeEnabled=!0:this.isLightThemeEnabled=!1,this.subscriptions.push(this.nodesDataSource.changes.subscribe(t=>{let e=[...t];this.nodes=e,e.forEach(i=>{(i.console_host==="0.0.0.0"||i.console_host==="0:0:0:0:0:0:0:0"||i.console_host==="::")&&(i.console_host=this.controller?.host)}),this.filteredNodes=this.sortingOrder==="asc"?e.sort(this.compareAsc):e.sort(this.compareDesc),this.cd.markForCheck()})),setTimeout(()=>{this.initializeComputesAndNotifications()},0),this.revertPosition()}initializeComputesAndNotifications(){if(!(!this.controller||!this.project||this.computesInitialized)){if(this.computesInitialized=!0,this.projectService.getStatistics(this.controller,this.project.project_id).subscribe({next:t=>{this.projectsStatistics=t,this.cd.markForCheck()},error:t=>{let e=t.error?.message||t.message||"Failed to load project statistics";this.toasterService.error(e),this.cd.markForCheck()}}),this.notificationService.hasCachedData()){let t=this.notificationService.getCachedComputes();this.computes=t,this.cd.markForCheck()}else this.computeService.getComputes(this.controller).subscribe({next:t=>{this.notificationService.setInitialComputes(t),this.computes=t,this.cd.markForCheck()},error:t=>{let e=t.error?.message||t.message||"Failed to load computes";this.toasterService.error(e),this.cd.markForCheck()}});this.subscriptions.push(this.notificationService.computeNotificationEmitter.subscribe(t=>{this.handleComputeNotification(t)})),this.subscriptions.push(this.notificationService.computeCacheUpdated.subscribe(t=>{this.computes=t,this.cd.markForCheck()}))}}revertPosition(){let t=localStorage.getItem("leftPosition"),e=localStorage.getItem("rightPosition"),i=localStorage.getItem("topPosition"),a=localStorage.getItem("widthOfWidget"),o=localStorage.getItem("heightOfWidget");i?this.style={position:"fixed",left:`${+t}px`,right:`${+e}px`,top:`${+i}px`,width:`${+a}px`,height:`${+o}px`}:this.style={top:"60px",right:"0px",width:"320px",height:"400px"}}toggleDragging(t){this.isDraggingEnabled=t}dragWidget(t){let e=Number(t.movementX),i=Number(t.movementY),a=Number(this.style.width.split("px")[0]),o=Number(this.style.height.split("px")[0]),f=Number(this.style.top.split("px")[0])+i;if(this.style.left){let b=Number(this.style.left.split("px")[0])+e;this.style={position:"fixed",left:`${b}px`,top:`${f}px`,width:`${a}px`,height:`${o}px`},localStorage.setItem("leftPosition",b.toString()),localStorage.setItem("topPosition",f.toString()),localStorage.setItem("widthOfWidget",a.toString()),localStorage.setItem("heightOfWidget",o.toString())}else{let b=Number(this.style.right.split("px")[0])-e;this.style={position:"fixed",right:`${b}px`,top:`${f}px`,width:`${a}px`,height:`${o}px`},localStorage.setItem("rightPosition",b.toString()),localStorage.setItem("topPosition",f.toString()),localStorage.setItem("widthOfWidget",a.toString()),localStorage.setItem("heightOfWidget",o.toString())}}validate(t){return!(t.rectangle.width&&t.rectangle.height&&(t.rectangle.width<290||t.rectangle.height<260))}onResizeEnd(t){this.style={position:"fixed",left:`${t.rectangle.left}px`,top:`${t.rectangle.top}px`,width:`${t.rectangle.width}px`,height:`${t.rectangle.height}px`},this.styleInside={height:`${t.rectangle.height-120}px`}}toggleTopologyVisibility(t){this.isTopologyVisible=t,this.revertPosition()}compareAsc(t,e){return t.namei.compute_id===t.event.compute_id)!==-1?this.computes=this.computes.map(i=>i.compute_id===t.event.compute_id?t.event:i):this.computes=[...this.computes,t.event];break;case"compute.deleted":this.computes=this.computes.filter(i=>i.compute_id!==t.event.compute_id);break}this.cd.markForCheck()}ngOnDestroy(){this.subscriptions.forEach(t=>t.unsubscribe())}setSortingOrder(){this.sortingOrder==="asc"?this.filteredNodes=this.filteredNodes.sort(this.compareAsc):this.filteredNodes=this.filteredNodes.sort(this.compareDesc)}applyStatusFilter(t){t==="started"?this.startedStatusFilterEnabled=!this.startedStatusFilterEnabled:t==="stopped"?this.stoppedStatusFilterEnabled=!this.stoppedStatusFilterEnabled:t==="suspended"&&(this.suspendedStatusFilterEnabled=!this.suspendedStatusFilterEnabled),this.applyFilters()}applyCaptureFilter(t){t==="capture"?this.captureFilterEnabled=!this.captureFilterEnabled:t==="packet"&&(this.packetFilterEnabled=!this.packetFilterEnabled),this.applyFilters()}applyFilters(){let t=[];this.startedStatusFilterEnabled&&(t=t.concat(this.nodes.filter(e=>e.status==="started"))),this.stoppedStatusFilterEnabled&&(t=t.concat(this.nodes.filter(e=>e.status==="stopped"))),this.suspendedStatusFilterEnabled&&(t=t.concat(this.nodes.filter(e=>e.status==="suspended"))),!this.startedStatusFilterEnabled&&!this.stoppedStatusFilterEnabled&&!this.suspendedStatusFilterEnabled&&(t=t.concat(this.nodes)),this.captureFilterEnabled&&(t=this.checkCapturing(t)),this.packetFilterEnabled&&(t=this.checkPacketFilters(t)),this.sortingOrder==="asc"?this.filteredNodes=t.sort(this.compareAsc):this.filteredNodes=t.sort(this.compareDesc)}checkCapturing(t){let e=this.linksDataSource.getItems(),i=[];e.forEach(o=>{o.capturing&&o.nodes.forEach(f=>{i.push(f.node_id)})});let a=[];return t.forEach(o=>{i.includes(o.node_id)&&a.push(o)}),a}checkPacketFilters(t){let e=this.linksDataSource.getItems(),i=[];e.forEach(o=>{(o.filters.bpf||o.filters.corrupt||o.filters.corrupt||o.filters.packet_loss||o.filters.frequency_drop)&&o.nodes.forEach(f=>{i.push(f.node_id)})});let a=[];return t.forEach(o=>{i.includes(o.node_id)&&a.push(o)}),a}close(){this.closeTopologySummary.emit(!1)}truncateComputeName(t){if(!t)return"";let e=15;return t.length<=e?t:t.substring(0,e)+"..."}getComputeTooltip(t){return t?[`Name: ${t.name||"N/A"}`,`Host: ${t.host}:${t.port}`,`Connected: ${t.connected?"Yes":"No"}`,t.cpu_usage_percent!=null?`CPU: ${t.cpu_usage_percent.toFixed(1)}%`:null,t.memory_usage_percent!=null?`Memory: ${t.memory_usage_percent.toFixed(1)}%`:null].filter(Boolean).join(` +`):""}static \u0275fac=function(e){return new(e||n)};static \u0275cmp=C({type:n,selectors:[["app-topology-summary"]],inputs:{controller:"controller",project:"project"},outputs:{closeTopologySummary:"closeTopologySummary"},decls:2,vars:2,consts:[["mwlResizable","",1,"summaryWrapper",3,"ngStyle","ngClass","validateResize","resizeEdges","enableGhostResize"],[3,"mousemove","mouseup"],["mwlResizable","",1,"summaryWrapper",3,"mousedown","resizeStart","resizeEnd","ngStyle","ngClass","validateResize","resizeEdges","enableGhostResize"],[1,"summaryHeader"],["label","Map topology",3,"click"],[1,"tabContent"],[1,"summaryFilters"],["placeholder","Filter nodes","multiple",""],["label","Status filter"],["value","started",3,"onSelectionChange"],["value","suspended",3,"onSelectionChange"],["value","stopped",3,"onSelectionChange"],["label","Capture filter"],["value","capture",3,"onSelectionChange"],["value","packet",3,"onSelectionChange"],[1,"summarySorting"],["placeholder","Sorting",3,"selectionChange","valueChange","value"],["value","asc"],["value","desc"],[1,"divider"],[1,"summaryContent",3,"ngStyle"],[1,"nodeRow"],["label","Computes",3,"click"],[1,"summaryContentComputes"],["matTooltipPosition","above",1,"nodeRow",3,"matTooltip"],["width","10","height","10"],["x","0","y","0","width","10","height","10","fill","green",1,"status_started"],["x","0","y","0","width","10","height","10","fill","yellow",1,"status_suspended"],["x","0","y","0","width","10","height","10","fill","red",1,"status_stopped"]],template:function(e,i){e&1&&(g(0,_r,1,0,"div"),g(1,Mr,34,10,"div",0)),e&2&&(v(i.isDraggingEnabled?0:-1),c(),v(i.projectsStatistics?1:-1))},dependencies:[ki,vi,yi,pn,Ae,hn,Bn,Pn,zt,je,Nt,ve,$e,jn,We],styles:["@media screen and (max-width:600px){.summaryWrapper[_ngcontent-%COMP%]{visibility:hidden}}mat-tab-group[_ngcontent-%COMP%]{width:100%}.summaryWrapper[_ngcontent-%COMP%]{box-shadow:0 4px 16px color-mix(in srgb,var(--mat-sys-shadow) 25%,transparent);position:fixed;top:60px;right:0;height:400px;width:320px;background:var(--mat-sys-surface);color:var(--mat-sys-on-surface);overflow:hidden;font-size:12px;margin:16px;border-radius:4px}.summaryHeaderMenu[_ngcontent-%COMP%]{height:24px}.summaryHeader[_ngcontent-%COMP%]{width:100%;display:flex}.summaryFilters[_ngcontent-%COMP%], .summarySorting[_ngcontent-%COMP%]{height:25px;margin-left:8px;margin-right:8px}.tabContent[_ngcontent-%COMP%]{padding:10px}.summaryContent[_ngcontent-%COMP%]{overflow:auto;scrollbar-color:darkgrey var(--mat-sys-surface);scrollbar-width:thin}.summaryContentComputes[_ngcontent-%COMP%]{max-height:350px;overflow:auto;scrollbar-color:darkgrey var(--mat-sys-surface);scrollbar-width:thin}.titleButton[_ngcontent-%COMP%]{margin-left:8px;margin-top:4px;outline:none;border-radius:0}.marked[_ngcontent-%COMP%]{color:var(--mat-sys-primary);border-bottom:2px solid var(--mat-sys-primary)}.divider[_ngcontent-%COMP%]{margin-top:8px;margin-bottom:8px;width:100%;height:2px}.nodeRow[_ngcontent-%COMP%]{width:100%;display:flex;justify-content:space-between;padding-right:8px}.nodeRow[_ngcontent-%COMP%] > div[_ngcontent-%COMP%]{display:flex;align-items:center;gap:6px}.nodeRow[_ngcontent-%COMP%] svg[_ngcontent-%COMP%]{flex-shrink:0;vertical-align:middle}.radio-group-wrapper[_ngcontent-%COMP%]{margin-top:8px}.radio-group[_ngcontent-%COMP%]{display:flex;justify-content:space-between}.closeButton[_ngcontent-%COMP%]{cursor:pointer;font-size:18px;margin-top:8px;margin-right:8px}.filterBox[_ngcontent-%COMP%]{display:flex;justify-content:space-between}.notvisible[_ngcontent-%COMP%]{display:none}"],changeDetection:0})}return n})();export{We as a,jn as b,Le as c,Xa as d,Pe as e,Ja as f,to as g,Be as h,ze as i,In as j,On as k,Fn as l,be as m,ue as n,_e as o,je as p,Ne as q,zt as r,Dn as s,En as t,Nt as u,Pn as v,El as w,Bn as x,$n as y,Un as z,Zt as A,Yn as B,qe as C,ad as D,Zn as E,Rl as F,Ll as G,$e as H,ve as I,Od as J,Rd as K,Ad as L,Ld as M,qn as N,ya as O,ka as P,Ae as Q,hn as R,Ma as S,Sa as T,ss as U,pn as V,Wn as W,Qn as X,Gn as Y,jt as Z,Vn as _,Hn as $,im as aa}; diff --git a/gns3server/static/web-ui/chunk-JVPSP3B2.js b/gns3server/static/web-ui/chunk-JVPSP3B2.js new file mode 100644 index 000000000..ea79498e6 --- /dev/null +++ b/gns3server/static/web-ui/chunk-JVPSP3B2.js @@ -0,0 +1 @@ +import{aa as a}from"./chunk-EXPJ4N52.js";import"./chunk-TYGV4UPE.js";export{a as TopologySummaryComponent}; diff --git a/gns3server/static/web-ui/chunk-KS2DZGNZ.js b/gns3server/static/web-ui/chunk-KS2DZGNZ.js deleted file mode 100644 index 30b92f66f..000000000 --- a/gns3server/static/web-ui/chunk-KS2DZGNZ.js +++ /dev/null @@ -1,8 +0,0 @@ -import{$ as ft,$a as E,$e as ut,A as ht,Aa as de,B as ie,Ba as vt,Da as I,Dc as st,Dd as we,Ee as Rt,F as ne,Fb as yt,Fc as y,G as rt,Gc as xt,Hb as Wt,He as it,I as f,Ia as K,Ib as tt,Id as z,Kb as Ct,Kd as Qt,Lb as St,Ld as Te,Mb as It,Md as De,Me as Fe,N,Nb as _e,Ne as Re,Ob as et,Od as ct,Pa as k,Pb as U,Pd as dt,Pe as Ne,Qb as W,Qd as ot,R as ae,Ra as ue,Rd as Ot,Sa as ge,Sc as ye,Td as xe,Ua as me,Ub as kt,Ud as Ae,V as re,Vb as wt,Vd as Ee,Wb as O,Xb as Tt,Xd as Mt,Y as pt,Zd as Oe,_a as A,_e as Pe,a as M,ab as L,ad as Ce,ae as Ft,b as Xt,ca as se,cb as he,ce as Me,cf as Le,da as D,db as Z,ea as x,eb as Y,ef as Be,g as te,ga as h,gc as be,gd as At,gf as je,ha as S,i as Ht,ia as l,j as T,k as ee,kd as Se,l as Vt,lf as Nt,md as Et,nb as g,nf as ze,oa as Ut,od as Ie,of as Ge,pa as le,pb as pe,q as Q,qa as F,qc as ve,r as m,ra as J,rb as fe,rd as $t,u as oe,ua as P,ud as ke,v as q,va as ce,vd as lt,wa as _t,wb as B,wc as Dt,wd as j,xb as v,ya as bt,yb as w,zb as X}from"./chunk-LG2N72QL.js";var co=["determinateSpinner"];function uo(i,s){if(i&1&&(Ut(),v(0,"svg",11),X(1,"circle",12),w()),i&2){let t=Ct();g("viewBox",t._viewBox()),k(),wt("stroke-dasharray",t._strokeCircumference(),"px")("stroke-dashoffset",t._strokeCircumference()/2,"px")("stroke-width",t._circleStrokeWidth(),"%"),g("r",t._circleRadius())}}var go=new h("mat-progress-spinner-default-options",{providedIn:"root",factory:()=>({diameter:He})}),He=100,mo=10,ni=(()=>{class i{_elementRef=l(I);_noopAnimations;get color(){return this._color||this._defaultColor}set color(t){this._color=t}_color;_defaultColor="primary";_determinateCircle;constructor(){let t=l(go),e=Pe(),o=this._elementRef.nativeElement;this._noopAnimations=e==="di-disabled"&&!!t&&!t._forceAnimations,this.mode=o.nodeName.toLowerCase()==="mat-spinner"?"indeterminate":"determinate",!this._noopAnimations&&e==="reduced-motion"&&o.classList.add("mat-progress-spinner-reduced-motion"),t&&(t.color&&(this.color=this._defaultColor=t.color),t.diameter&&(this.diameter=t.diameter),t.strokeWidth&&(this.strokeWidth=t.strokeWidth))}mode;get value(){return this.mode==="determinate"?this._value:0}set value(t){this._value=Math.max(0,Math.min(100,t||0))}_value=0;get diameter(){return this._diameter}set diameter(t){this._diameter=t||0}_diameter=He;get strokeWidth(){return this._strokeWidth??this.diameter/10}set strokeWidth(t){this._strokeWidth=t||0}_strokeWidth;_circleRadius(){return(this.diameter-mo)/2}_viewBox(){let t=this._circleRadius()*2+this.strokeWidth;return`0 0 ${t} ${t}`}_strokeCircumference(){return 2*Math.PI*this._circleRadius()}_strokeDashOffset(){return this.mode==="determinate"?this._strokeCircumference()*(100-this._value)/100:null}_circleStrokeWidth(){return this.strokeWidth/this.diameter*100}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=A({type:i,selectors:[["mat-progress-spinner"],["mat-spinner"]],viewQuery:function(e,o){if(e&1&&et(co,5),e&2){let n;U(n=W())&&(o._determinateCircle=n.first)}},hostAttrs:["role","progressbar","tabindex","-1",1,"mat-mdc-progress-spinner","mdc-circular-progress"],hostVars:18,hostBindings:function(e,o){e&2&&(g("aria-valuemin",0)("aria-valuemax",100)("aria-valuenow",o.mode==="determinate"?o.value:null)("mode",o.mode),Tt("mat-"+o.color),wt("width",o.diameter,"px")("height",o.diameter,"px")("--mat-progress-spinner-size",o.diameter+"px")("--mat-progress-spinner-active-indicator-width",o.diameter+"px"),O("_mat-animation-noopable",o._noopAnimations)("mdc-circular-progress--indeterminate",o.mode==="indeterminate"))},inputs:{color:"color",mode:"mode",value:[2,"value","value",xt],diameter:[2,"diameter","diameter",xt],strokeWidth:[2,"strokeWidth","strokeWidth",xt]},exportAs:["matProgressSpinner"],decls:14,vars:11,consts:[["circle",""],["determinateSpinner",""],["aria-hidden","true",1,"mdc-circular-progress__determinate-container"],["xmlns","http://www.w3.org/2000/svg","focusable","false",1,"mdc-circular-progress__determinate-circle-graphic"],["cx","50%","cy","50%",1,"mdc-circular-progress__determinate-circle"],["aria-hidden","true",1,"mdc-circular-progress__indeterminate-container"],[1,"mdc-circular-progress__spinner-layer"],[1,"mdc-circular-progress__circle-clipper","mdc-circular-progress__circle-left"],[3,"ngTemplateOutlet"],[1,"mdc-circular-progress__gap-patch"],[1,"mdc-circular-progress__circle-clipper","mdc-circular-progress__circle-right"],["xmlns","http://www.w3.org/2000/svg","focusable","false",1,"mdc-circular-progress__indeterminate-circle-graphic"],["cx","50%","cy","50%"]],template:function(e,o){if(e&1&&(Y(0,uo,2,8,"ng-template",null,0,ve),v(2,"div",2,1),Ut(),v(4,"svg",3),X(5,"circle",4),w()(),le(),v(6,"div",5)(7,"div",6)(8,"div",7),yt(9,8),w(),v(10,"div",9),yt(11,8),w(),v(12,"div",10),yt(13,8),w()()()),e&2){let n=kt(1);k(4),g("viewBox",o._viewBox()),k(),wt("stroke-dasharray",o._strokeCircumference(),"px")("stroke-dashoffset",o._strokeDashOffset(),"px")("stroke-width",o._circleStrokeWidth(),"%"),g("r",o._circleRadius()),k(4),B("ngTemplateOutlet",n),k(2),B("ngTemplateOutlet",n),k(2),B("ngTemplateOutlet",n)}},dependencies:[ye],styles:[`.mat-mdc-progress-spinner{--mat-progress-spinner-animation-multiplier: 1;display:block;overflow:hidden;line-height:0;position:relative;direction:ltr;transition:opacity 250ms cubic-bezier(0.4, 0, 0.6, 1)}.mat-mdc-progress-spinner circle{stroke-width:var(--mat-progress-spinner-active-indicator-width, 4px)}.mat-mdc-progress-spinner._mat-animation-noopable,.mat-mdc-progress-spinner._mat-animation-noopable .mdc-circular-progress__determinate-circle{transition:none !important}.mat-mdc-progress-spinner._mat-animation-noopable .mdc-circular-progress__indeterminate-circle-graphic,.mat-mdc-progress-spinner._mat-animation-noopable .mdc-circular-progress__spinner-layer,.mat-mdc-progress-spinner._mat-animation-noopable .mdc-circular-progress__indeterminate-container{animation:none !important}.mat-mdc-progress-spinner._mat-animation-noopable .mdc-circular-progress__indeterminate-container circle{stroke-dasharray:0 !important}@media(forced-colors: active){.mat-mdc-progress-spinner .mdc-circular-progress__indeterminate-circle-graphic,.mat-mdc-progress-spinner .mdc-circular-progress__determinate-circle{stroke:currentColor;stroke:CanvasText}}.mat-progress-spinner-reduced-motion{--mat-progress-spinner-animation-multiplier: 1.25}.mdc-circular-progress__determinate-container,.mdc-circular-progress__indeterminate-circle-graphic,.mdc-circular-progress__indeterminate-container,.mdc-circular-progress__spinner-layer{position:absolute;width:100%;height:100%}.mdc-circular-progress__determinate-container{transform:rotate(-90deg)}.mdc-circular-progress--indeterminate .mdc-circular-progress__determinate-container{opacity:0}.mdc-circular-progress__indeterminate-container{font-size:0;letter-spacing:0;white-space:nowrap;opacity:0}.mdc-circular-progress--indeterminate .mdc-circular-progress__indeterminate-container{opacity:1;animation:mdc-circular-progress-container-rotate calc(1568.2352941176ms*var(--mat-progress-spinner-animation-multiplier)) linear infinite}.mdc-circular-progress__determinate-circle-graphic,.mdc-circular-progress__indeterminate-circle-graphic{fill:rgba(0,0,0,0)}.mat-mdc-progress-spinner .mdc-circular-progress__determinate-circle,.mat-mdc-progress-spinner .mdc-circular-progress__indeterminate-circle-graphic{stroke:var(--mat-progress-spinner-active-indicator-color, var(--mat-sys-primary))}@media(forced-colors: active){.mat-mdc-progress-spinner .mdc-circular-progress__determinate-circle,.mat-mdc-progress-spinner .mdc-circular-progress__indeterminate-circle-graphic{stroke:CanvasText}}.mdc-circular-progress__determinate-circle{transition:stroke-dashoffset 500ms cubic-bezier(0, 0, 0.2, 1)}.mdc-circular-progress__gap-patch{position:absolute;top:0;left:47.5%;box-sizing:border-box;width:5%;height:100%;overflow:hidden}.mdc-circular-progress__gap-patch .mdc-circular-progress__indeterminate-circle-graphic{left:-900%;width:2000%;transform:rotate(180deg)}.mdc-circular-progress__circle-clipper .mdc-circular-progress__indeterminate-circle-graphic{width:200%}.mdc-circular-progress__circle-right .mdc-circular-progress__indeterminate-circle-graphic{left:-100%}.mdc-circular-progress--indeterminate .mdc-circular-progress__circle-left .mdc-circular-progress__indeterminate-circle-graphic{animation:mdc-circular-progress-left-spin calc(1333ms*var(--mat-progress-spinner-animation-multiplier)) cubic-bezier(0.4, 0, 0.2, 1) infinite both}.mdc-circular-progress--indeterminate .mdc-circular-progress__circle-right .mdc-circular-progress__indeterminate-circle-graphic{animation:mdc-circular-progress-right-spin calc(1333ms*var(--mat-progress-spinner-animation-multiplier)) cubic-bezier(0.4, 0, 0.2, 1) infinite both}.mdc-circular-progress__circle-clipper{display:inline-flex;position:relative;width:50%;height:100%;overflow:hidden}.mdc-circular-progress--indeterminate .mdc-circular-progress__spinner-layer{animation:mdc-circular-progress-spinner-layer-rotate calc(5332ms*var(--mat-progress-spinner-animation-multiplier)) cubic-bezier(0.4, 0, 0.2, 1) infinite both}@keyframes mdc-circular-progress-container-rotate{to{transform:rotate(360deg)}}@keyframes mdc-circular-progress-spinner-layer-rotate{12.5%{transform:rotate(135deg)}25%{transform:rotate(270deg)}37.5%{transform:rotate(405deg)}50%{transform:rotate(540deg)}62.5%{transform:rotate(675deg)}75%{transform:rotate(810deg)}87.5%{transform:rotate(945deg)}100%{transform:rotate(1080deg)}}@keyframes mdc-circular-progress-left-spin{from{transform:rotate(265deg)}50%{transform:rotate(130deg)}to{transform:rotate(265deg)}}@keyframes mdc-circular-progress-right-spin{from{transform:rotate(-265deg)}50%{transform:rotate(-130deg)}to{transform:rotate(-265deg)}} -`],encapsulation:2,changeDetection:0})}return i})();var ai=(()=>{class i{static \u0275fac=function(e){return new(e||i)};static \u0275mod=E({type:i});static \u0275inj=x({imports:[j]})}return i})();function Ve(i){return Error(`Unable to find icon with the name "${i}"`)}function po(){return Error("Could not find HttpClient for use with Angular Material icons. Please add provideHttpClient() to your providers.")}function Ue(i){return Error(`The URL provided to MatIconRegistry was not trusted as a resource URL via Angular's DomSanitizer. Attempted URL was "${i}".`)}function We(i){return Error(`The literal provided to MatIconRegistry was not trusted as safe HTML by Angular's DomSanitizer. Attempted literal was "${i}".`)}var R=class{url;svgText;options;svgElement=null;constructor(s,t,e){this.url=s,this.svgText=t,this.options=e}},Qe=(()=>{class i{_httpClient;_sanitizer;_errorHandler;_document;_svgIconConfigs=new Map;_iconSetConfigs=new Map;_cachedIconsByUrl=new Map;_inProgressUrlFetches=new Map;_fontCssClassesByAlias=new Map;_resolvers=[];_defaultFontSetClass=["material-icons","mat-ligature-font"];constructor(t,e,o,n){this._httpClient=t,this._sanitizer=e,this._errorHandler=n,this._document=o}addSvgIcon(t,e,o){return this.addSvgIconInNamespace("",t,e,o)}addSvgIconLiteral(t,e,o){return this.addSvgIconLiteralInNamespace("",t,e,o)}addSvgIconInNamespace(t,e,o,n){return this._addSvgIconConfig(t,e,new R(o,null,n))}addSvgIconResolver(t){return this._resolvers.push(t),this}addSvgIconLiteralInNamespace(t,e,o,n){let a=this._sanitizer.sanitize(K.HTML,o);if(!a)throw We(o);let r=it(a);return this._addSvgIconConfig(t,e,new R("",r,n))}addSvgIconSet(t,e){return this.addSvgIconSetInNamespace("",t,e)}addSvgIconSetLiteral(t,e){return this.addSvgIconSetLiteralInNamespace("",t,e)}addSvgIconSetInNamespace(t,e,o){return this._addSvgIconSetConfig(t,new R(e,null,o))}addSvgIconSetLiteralInNamespace(t,e,o){let n=this._sanitizer.sanitize(K.HTML,e);if(!n)throw We(e);let a=it(n);return this._addSvgIconSetConfig(t,new R("",a,o))}registerFontClassAlias(t,e=t){return this._fontCssClassesByAlias.set(t,e),this}classNameForFontAlias(t){return this._fontCssClassesByAlias.get(t)||t}setDefaultFontSetClass(...t){return this._defaultFontSetClass=t,this}getDefaultFontSetClass(){return this._defaultFontSetClass}getSvgIconFromUrl(t){let e=this._sanitizer.sanitize(K.RESOURCE_URL,t);if(!e)throw Ue(t);let o=this._cachedIconsByUrl.get(e);return o?Q(Pt(o)):this._loadSvgIconFromConfig(new R(t,null)).pipe(ft(n=>this._cachedIconsByUrl.set(e,n)),q(n=>Pt(n)))}getNamedSvgIcon(t,e=""){let o=$e(e,t),n=this._svgIconConfigs.get(o);if(n)return this._getSvgFromConfig(n);if(n=this._getIconConfigFromResolvers(e,t),n)return this._svgIconConfigs.set(o,n),this._getSvgFromConfig(n);let a=this._iconSetConfigs.get(e);return a?this._getSvgFromIconSetConfigs(t,a):m(Ve(o))}ngOnDestroy(){this._resolvers=[],this._svgIconConfigs.clear(),this._iconSetConfigs.clear(),this._cachedIconsByUrl.clear()}_getSvgFromConfig(t){return t.svgText?Q(Pt(this._svgElementFromConfig(t))):this._loadSvgIconFromConfig(t).pipe(q(e=>Pt(e)))}_getSvgFromIconSetConfigs(t,e){let o=this._extractIconWithNameFromAnySet(t,e);if(o)return Q(o);let n=e.filter(a=>!a.svgText).map(a=>this._loadSvgIconSetFromConfig(a).pipe(f(r=>{let c=`Loading icon set URL: ${this._sanitizer.sanitize(K.RESOURCE_URL,a.url)} failed: ${r.message}`;return this._errorHandler.handleError(new Error(c)),Q(null)})));return ie(n).pipe(q(()=>{let a=this._extractIconWithNameFromAnySet(t,e);if(!a)throw Ve(t);return a}))}_extractIconWithNameFromAnySet(t,e){for(let o=e.length-1;o>=0;o--){let n=e[o];if(n.svgText&&n.svgText.toString().indexOf(t)>-1){let a=this._svgElementFromConfig(n),r=this._extractSvgIconFromSet(a,t,n.options);if(r)return r}}return null}_loadSvgIconFromConfig(t){return this._fetchIcon(t).pipe(ft(e=>t.svgText=e),q(()=>this._svgElementFromConfig(t)))}_loadSvgIconSetFromConfig(t){return t.svgText?Q(null):this._fetchIcon(t).pipe(ft(e=>t.svgText=e))}_extractSvgIconFromSet(t,e,o){let n=t.querySelector(`[id="${e}"]`);if(!n)return null;let a=n.cloneNode(!0);if(a.removeAttribute("id"),a.nodeName.toLowerCase()==="svg")return this._setSvgAttributes(a,o);if(a.nodeName.toLowerCase()==="symbol")return this._setSvgAttributes(this._toSvgElement(a),o);let r=this._svgElementFromString(it(""));return r.appendChild(a),this._setSvgAttributes(r,o)}_svgElementFromString(t){let e=this._document.createElement("DIV");e.innerHTML=t;let o=e.querySelector("svg");if(!o)throw Error(" tag not found");return o}_toSvgElement(t){let e=this._svgElementFromString(it("")),o=t.attributes;for(let n=0;nit(c)),ae(()=>this._inProgressUrlFetches.delete(a)),re());return this._inProgressUrlFetches.set(a,d),d}_addSvgIconConfig(t,e,o){return this._svgIconConfigs.set($e(t,e),o),this}_addSvgIconSetConfig(t,e){let o=this._iconSetConfigs.get(t);return o?o.push(e):this._iconSetConfigs.set(t,[e]),this}_svgElementFromConfig(t){if(!t.svgElement){let e=this._svgElementFromString(t.svgText);this._setSvgAttributes(e,t.options),t.svgElement=e}return t.svgElement}_getIconConfigFromResolvers(t,e){for(let o=0;o{let i=l(J),s=i?i.location:null;return{getPathname:()=>s?s.pathname+s.search:""}}}),qe=["clip-path","color-profile","src","cursor","fill","filter","marker","marker-start","marker-mid","marker-end","mask","stroke"],yo=qe.map(i=>`[${i}]`).join(", "),Co=/^url\(['"]?#(.*?)['"]?\)$/,Ti=(()=>{class i{_elementRef=l(I);_iconRegistry=l(Qe);_location=l(vo);_errorHandler=l(_t);_defaultColor;get color(){return this._color||this._defaultColor}set color(t){this._color=t}_color;inline=!1;get svgIcon(){return this._svgIcon}set svgIcon(t){t!==this._svgIcon&&(t?this._updateSvgIcon(t):this._svgIcon&&this._clearSvgElement(),this._svgIcon=t)}_svgIcon;get fontSet(){return this._fontSet}set fontSet(t){let e=this._cleanupFontValue(t);e!==this._fontSet&&(this._fontSet=e,this._updateFontIconClasses())}_fontSet;get fontIcon(){return this._fontIcon}set fontIcon(t){let e=this._cleanupFontValue(t);e!==this._fontIcon&&(this._fontIcon=e,this._updateFontIconClasses())}_fontIcon;_previousFontSetClass=[];_previousFontIconClass;_svgName=null;_svgNamespace=null;_previousPath;_elementsWithExternalReferences;_currentIconFetch=te.EMPTY;constructor(){let t=l(new Dt("aria-hidden"),{optional:!0}),e=l(bo,{optional:!0});e&&(e.color&&(this.color=this._defaultColor=e.color),e.fontSet&&(this.fontSet=e.fontSet)),t||this._elementRef.nativeElement.setAttribute("aria-hidden","true")}_splitIconName(t){if(!t)return["",""];let e=t.split(":");switch(e.length){case 1:return["",e[0]];case 2:return e;default:throw Error(`Invalid icon name: "${t}"`)}}ngOnInit(){this._updateFontIconClasses()}ngAfterViewChecked(){let t=this._elementsWithExternalReferences;if(t&&t.size){let e=this._location.getPathname();e!==this._previousPath&&(this._previousPath=e,this._prependPathToReferences(e))}}ngOnDestroy(){this._currentIconFetch.unsubscribe(),this._elementsWithExternalReferences&&this._elementsWithExternalReferences.clear()}_usingFontIcon(){return!this.svgIcon}_setSvgElement(t){this._clearSvgElement();let e=this._location.getPathname();this._previousPath=e,this._cacheChildrenWithExternalReferences(t),this._prependPathToReferences(e),this._elementRef.nativeElement.appendChild(t)}_clearSvgElement(){let t=this._elementRef.nativeElement,e=t.childNodes.length;for(this._elementsWithExternalReferences&&this._elementsWithExternalReferences.clear();e--;){let o=t.childNodes[e];(o.nodeType!==1||o.nodeName.toLowerCase()==="svg")&&o.remove()}}_updateFontIconClasses(){if(!this._usingFontIcon())return;let t=this._elementRef.nativeElement,e=(this.fontSet?this._iconRegistry.classNameForFontAlias(this.fontSet).split(/ +/):this._iconRegistry.getDefaultFontSetClass()).filter(o=>o.length>0);this._previousFontSetClass.forEach(o=>t.classList.remove(o)),e.forEach(o=>t.classList.add(o)),this._previousFontSetClass=e,this.fontIcon!==this._previousFontIconClass&&!e.includes("mat-ligature-font")&&(this._previousFontIconClass&&t.classList.remove(this._previousFontIconClass),this.fontIcon&&t.classList.add(this.fontIcon),this._previousFontIconClass=this.fontIcon)}_cleanupFontValue(t){return typeof t=="string"?t.trim().split(" ")[0]:t}_prependPathToReferences(t){let e=this._elementsWithExternalReferences;e&&e.forEach((o,n)=>{o.forEach(a=>{n.setAttribute(a.name,`url('${t}#${a.value}')`)})})}_cacheChildrenWithExternalReferences(t){let e=t.querySelectorAll(yo),o=this._elementsWithExternalReferences=this._elementsWithExternalReferences||new Map;for(let n=0;n{let r=e[n],d=r.getAttribute(a),c=d?d.match(Co):null;if(c){let C=o.get(r);C||(C=[],o.set(r,C)),C.push({name:a,value:c[1]})}})}_updateSvgIcon(t){if(this._svgNamespace=null,this._svgName=null,this._currentIconFetch.unsubscribe(),t){let[e,o]=this._splitIconName(t);e&&(this._svgNamespace=e),o&&(this._svgName=o),this._currentIconFetch=this._iconRegistry.getNamedSvgIcon(o,e).pipe(N(1)).subscribe(n=>this._setSvgElement(n),n=>{let a=`Error retrieving icon ${e}:${o}! ${n.message}`;this._errorHandler.handleError(new Error(a))})}}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=A({type:i,selectors:[["mat-icon"]],hostAttrs:["role","img",1,"mat-icon","notranslate"],hostVars:10,hostBindings:function(e,o){e&2&&(g("data-mat-icon-type",o._usingFontIcon()?"font":"svg")("data-mat-icon-name",o._svgName||o.fontIcon)("data-mat-icon-namespace",o._svgNamespace||o.fontSet)("fontIcon",o._usingFontIcon()?o.fontIcon:null),Tt(o.color?"mat-"+o.color:""),O("mat-icon-inline",o.inline)("mat-icon-no-color",o.color!=="primary"&&o.color!=="accent"&&o.color!=="warn"))},inputs:{color:"color",inline:[2,"inline","inline",y],svgIcon:"svgIcon",fontSet:"fontSet",fontIcon:"fontIcon"},exportAs:["matIcon"],ngContentSelectors:_o,decls:1,vars:0,template:function(e,o){e&1&&(St(),It(0))},styles:[`mat-icon,mat-icon.mat-primary,mat-icon.mat-accent,mat-icon.mat-warn{color:var(--mat-icon-color, inherit)}.mat-icon{-webkit-user-select:none;user-select:none;background-repeat:no-repeat;display:inline-block;fill:currentColor;height:24px;width:24px;overflow:hidden}.mat-icon.mat-icon-inline{font-size:inherit;height:inherit;line-height:inherit;width:inherit}.mat-icon.mat-ligature-font[fontIcon]::before{content:attr(fontIcon)}[dir=rtl] .mat-icon-rtl-mirror{transform:scale(-1, 1)}.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-prefix .mat-icon,.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-suffix .mat-icon{display:block}.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-prefix .mat-icon-button .mat-icon,.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-suffix .mat-icon-button .mat-icon{margin:auto} -`],encapsulation:2,changeDetection:0})}return i})(),Di=(()=>{class i{static \u0275fac=function(e){return new(e||i)};static \u0275mod=E({type:i});static \u0275inj=x({imports:[j]})}return i})();function So(i,s){}var G=class{viewContainerRef;injector;id;role="dialog";panelClass="";hasBackdrop=!0;backdropClass="";disableClose=!1;closePredicate;width="";height="";minWidth;minHeight;maxWidth;maxHeight;positionStrategy;data=null;direction;ariaDescribedBy=null;ariaLabelledBy=null;ariaLabel=null;ariaModal=!1;autoFocus="first-tabbable";restoreFocus=!0;scrollStrategy;closeOnNavigation=!0;closeOnDestroy=!0;closeOnOverlayDetachments=!0;disableAnimations=!1;providers;container;templateContext};var Jt=(()=>{class i extends De{_elementRef=l(I);_focusTrapFactory=l(Re);_config;_interactivityChecker=l(Fe);_ngZone=l(ce);_focusMonitor=l(Rt);_renderer=l(me);_changeDetectorRef=l(st);_injector=l(F);_platform=l(ke);_document=l(J);_portalOutlet;_focusTrapped=new T;_focusTrap=null;_elementFocusedBeforeDialogWasOpened=null;_closeInteractionType=null;_ariaLabelledByQueue=[];_isDestroyed=!1;constructor(){super(),this._config=l(G,{optional:!0})||new G,this._config.ariaLabelledBy&&this._ariaLabelledByQueue.push(this._config.ariaLabelledBy)}_addAriaLabelledBy(t){this._ariaLabelledByQueue.push(t),this._changeDetectorRef.markForCheck()}_removeAriaLabelledBy(t){let e=this._ariaLabelledByQueue.indexOf(t);e>-1&&(this._ariaLabelledByQueue.splice(e,1),this._changeDetectorRef.markForCheck())}_contentAttached(){this._initializeFocusTrap(),this._captureInitialFocus()}_captureInitialFocus(){this._trapFocus()}ngOnDestroy(){this._focusTrapped.complete(),this._isDestroyed=!0,this._restoreFocus()}attachComponentPortal(t){this._portalOutlet.hasAttached();let e=this._portalOutlet.attachComponentPortal(t);return this._contentAttached(),e}attachTemplatePortal(t){this._portalOutlet.hasAttached();let e=this._portalOutlet.attachTemplatePortal(t);return this._contentAttached(),e}attachDomPortal=t=>{this._portalOutlet.hasAttached();let e=this._portalOutlet.attachDomPortal(t);return this._contentAttached(),e};_recaptureFocus(){this._containsFocus()||this._trapFocus()}_forceFocus(t,e){this._interactivityChecker.isFocusable(t)||(t.tabIndex=-1,this._ngZone.runOutsideAngular(()=>{let o=()=>{n(),a(),t.removeAttribute("tabindex")},n=this._renderer.listen(t,"blur",o),a=this._renderer.listen(t,"mousedown",o)})),t.focus(e)}_focusByCssSelector(t,e){let o=this._elementRef.nativeElement.querySelector(t);o&&this._forceFocus(o,e)}_trapFocus(t){this._isDestroyed||ue(()=>{let e=this._elementRef.nativeElement;switch(this._config.autoFocus){case!1:case"dialog":this._containsFocus()||e.focus(t);break;case!0:case"first-tabbable":this._focusTrap?.focusInitialElement(t)||this._focusDialogContainer(t);break;case"first-heading":this._focusByCssSelector('h1, h2, h3, h4, h5, h6, [role="heading"]',t);break;default:this._focusByCssSelector(this._config.autoFocus,t);break}this._focusTrapped.next()},{injector:this._injector})}_restoreFocus(){let t=this._config.restoreFocus,e=null;if(typeof t=="string"?e=this._document.querySelector(t):typeof t=="boolean"?e=t?this._elementFocusedBeforeDialogWasOpened:null:t&&(e=t),this._config.restoreFocus&&e&&typeof e.focus=="function"){let o=Et(),n=this._elementRef.nativeElement;(!o||o===this._document.body||o===n||n.contains(o))&&(this._focusMonitor?(this._focusMonitor.focusVia(e,this._closeInteractionType),this._closeInteractionType=null):e.focus())}this._focusTrap&&this._focusTrap.destroy()}_focusDialogContainer(t){this._elementRef.nativeElement.focus?.(t)}_containsFocus(){let t=this._elementRef.nativeElement,e=Et();return t===e||t.contains(e)}_initializeFocusTrap(){this._platform.isBrowser&&(this._focusTrap=this._focusTrapFactory.create(this._elementRef.nativeElement),this._document&&(this._elementFocusedBeforeDialogWasOpened=Et()))}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=A({type:i,selectors:[["cdk-dialog-container"]],viewQuery:function(e,o){if(e&1&&et(ct,7),e&2){let n;U(n=W())&&(o._portalOutlet=n.first)}},hostAttrs:["tabindex","-1",1,"cdk-dialog-container"],hostVars:6,hostBindings:function(e,o){e&2&&g("id",o._config.id||null)("role",o._config.role)("aria-modal",o._config.ariaModal)("aria-labelledby",o._config.ariaLabel?null:o._ariaLabelledByQueue[0])("aria-label",o._config.ariaLabel)("aria-describedby",o._config.ariaDescribedBy||null)},features:[Z],decls:1,vars:0,consts:[["cdkPortalOutlet",""]],template:function(e,o){e&1&&Y(0,So,0,0,"ng-template",0)},dependencies:[ct],styles:[`.cdk-dialog-container{display:block;width:100%;height:100%;min-height:inherit;max-height:inherit} -`],encapsulation:2})}return i})(),gt=class{overlayRef;config;componentInstance=null;componentRef=null;containerInstance;disableClose;closed=new T;backdropClick;keydownEvents;outsidePointerEvents;id;_detachSubscription;constructor(s,t){this.overlayRef=s,this.config=t,this.disableClose=t.disableClose,this.backdropClick=s.backdropClick(),this.keydownEvents=s.keydownEvents(),this.outsidePointerEvents=s.outsidePointerEvents(),this.id=t.id,this.keydownEvents.subscribe(e=>{e.keyCode===27&&!this.disableClose&&!ot(e)&&(e.preventDefault(),this.close(void 0,{focusOrigin:"keyboard"}))}),this.backdropClick.subscribe(()=>{!this.disableClose&&this._canClose()?this.close(void 0,{focusOrigin:"mouse"}):this.containerInstance._recaptureFocus?.()}),this._detachSubscription=s.detachments().subscribe(()=>{t.closeOnOverlayDetachments!==!1&&this.close()})}close(s,t){if(this._canClose(s)){let e=this.closed;this.containerInstance._closeInteractionType=t?.focusOrigin||"program",this._detachSubscription.unsubscribe(),this.overlayRef.dispose(),e.next(s),e.complete(),this.componentInstance=this.containerInstance=null}}updatePosition(){return this.overlayRef.updatePosition(),this}updateSize(s="",t=""){return this.overlayRef.updateSize({width:s,height:t}),this}addPanelClass(s){return this.overlayRef.addPanelClass(s),this}removePanelClass(s){return this.overlayRef.removePanelClass(s),this}_canClose(s){let t=this.config;return!!this.containerInstance&&(!t.closePredicate||t.closePredicate(s,t,this.componentInstance))}},Io=new h("DialogScrollStrategy",{providedIn:"root",factory:()=>{let i=l(F);return()=>Ot(i)}}),ko=new h("DialogData"),wo=new h("DefaultDialogConfig");function To(i){let s=bt(i),t=new P;return{valueSignal:s,get value(){return s()},change:t,ngOnDestroy(){t.complete()}}}var Kt=(()=>{class i{_injector=l(F);_defaultOptions=l(wo,{optional:!0});_parentDialog=l(i,{optional:!0,skipSelf:!0});_overlayContainer=l(Ae);_idGenerator=l(z);_openDialogsAtThisLevel=[];_afterAllClosedAtThisLevel=new T;_afterOpenedAtThisLevel=new T;_ariaHiddenElements=new Map;_scrollStrategy=l(Io);get openDialogs(){return this._parentDialog?this._parentDialog.openDialogs:this._openDialogsAtThisLevel}get afterOpened(){return this._parentDialog?this._parentDialog.afterOpened:this._afterOpenedAtThisLevel}afterAllClosed=ht(()=>this.openDialogs.length?this._getAfterAllClosed():this._getAfterAllClosed().pipe(pt(void 0)));constructor(){}open(t,e){let o=this._defaultOptions||new G;e=M(M({},o),e),e.id=e.id||this._idGenerator.getId("cdk-dialog-"),e.id&&this.getDialogById(e.id);let n=this._getOverlayConfig(e),a=Oe(this._injector,n),r=new gt(a,e),d=this._attachContainer(a,r,e);if(r.containerInstance=d,!this.openDialogs.length){let c=this._overlayContainer.getContainerElement();d._focusTrapped?d._focusTrapped.pipe(N(1)).subscribe(()=>{this._hideNonDialogContentFromAssistiveTechnology(c)}):this._hideNonDialogContentFromAssistiveTechnology(c)}return this._attachDialogContent(t,r,d,e),this.openDialogs.push(r),r.closed.subscribe(()=>this._removeOpenDialog(r,!0)),this.afterOpened.next(r),r}closeAll(){qt(this.openDialogs,t=>t.close())}getDialogById(t){return this.openDialogs.find(e=>e.id===t)}ngOnDestroy(){qt(this._openDialogsAtThisLevel,t=>{t.config.closeOnDestroy===!1&&this._removeOpenDialog(t,!1)}),qt(this._openDialogsAtThisLevel,t=>t.close()),this._afterAllClosedAtThisLevel.complete(),this._afterOpenedAtThisLevel.complete(),this._openDialogsAtThisLevel=[]}_getOverlayConfig(t){let e=new xe({positionStrategy:t.positionStrategy||Mt().centerHorizontally().centerVertically(),scrollStrategy:t.scrollStrategy||this._scrollStrategy(),panelClass:t.panelClass,hasBackdrop:t.hasBackdrop,direction:t.direction,minWidth:t.minWidth,minHeight:t.minHeight,maxWidth:t.maxWidth,maxHeight:t.maxHeight,width:t.width,height:t.height,disposeOnNavigation:t.closeOnNavigation,disableAnimations:t.disableAnimations});return t.backdropClass&&(e.backdropClass=t.backdropClass),e}_attachContainer(t,e,o){let n=o.injector||o.viewContainerRef?.injector,a=[{provide:G,useValue:o},{provide:gt,useValue:e},{provide:Ee,useValue:t}],r;o.container?typeof o.container=="function"?r=o.container:(r=o.container.type,a.push(...o.container.providers(o))):r=Jt;let d=new Qt(r,o.viewContainerRef,F.create({parent:n||this._injector,providers:a}));return t.attach(d).instance}_attachDialogContent(t,e,o,n){if(t instanceof ge){let a=this._createInjector(n,e,o,void 0),r={$implicit:n.data,dialogRef:e};n.templateContext&&(r=M(M({},r),typeof n.templateContext=="function"?n.templateContext():n.templateContext)),o.attachTemplatePortal(new Te(t,null,r,a))}else{let a=this._createInjector(n,e,o,this._injector),r=o.attachComponentPortal(new Qt(t,n.viewContainerRef,a));e.componentRef=r,e.componentInstance=r.instance}}_createInjector(t,e,o,n){let a=t.injector||t.viewContainerRef?.injector,r=[{provide:ko,useValue:t.data},{provide:gt,useValue:e}];return t.providers&&(typeof t.providers=="function"?r.push(...t.providers(e,t,o)):r.push(...t.providers)),t.direction&&(!a||!a.get(lt,null,{optional:!0}))&&r.push({provide:lt,useValue:To(t.direction)}),F.create({parent:a||n,providers:r})}_removeOpenDialog(t,e){let o=this.openDialogs.indexOf(t);o>-1&&(this.openDialogs.splice(o,1),this.openDialogs.length||(this._ariaHiddenElements.forEach((n,a)=>{n?a.setAttribute("aria-hidden",n):a.removeAttribute("aria-hidden")}),this._ariaHiddenElements.clear(),e&&this._getAfterAllClosed().next()))}_hideNonDialogContentFromAssistiveTechnology(t){if(t.parentElement){let e=t.parentElement.children;for(let o=e.length-1;o>-1;o--){let n=e[o];n!==t&&n.nodeName!=="SCRIPT"&&n.nodeName!=="STYLE"&&!n.hasAttribute("aria-live")&&!n.hasAttribute("popover")&&(this._ariaHiddenElements.set(n,n.getAttribute("aria-hidden")),n.setAttribute("aria-hidden","true"))}}}_getAfterAllClosed(){let t=this._parentDialog;return t?t._getAfterAllClosed():this._afterAllClosedAtThisLevel}static \u0275fac=function(e){return new(e||i)};static \u0275prov=D({token:i,factory:i.\u0275fac,providedIn:"root"})}return i})();function qt(i,s){let t=i.length;for(;t--;)s(i[t])}var Ke=(()=>{class i{static \u0275fac=function(e){return new(e||i)};static \u0275mod=E({type:i});static \u0275inj=x({providers:[Kt],imports:[Ft,dt,Ne,dt]})}return i})();function Do(i,s){}var Bt=class{viewContainerRef;injector;id;role="dialog";panelClass="";hasBackdrop=!0;backdropClass="";disableClose=!1;closePredicate;width="";height="";minWidth;minHeight;maxWidth;maxHeight;position;data=null;direction;ariaDescribedBy=null;ariaLabelledBy=null;ariaLabel=null;ariaModal=!1;autoFocus="first-tabbable";restoreFocus=!0;delayFocusTrap=!0;scrollStrategy;closeOnNavigation=!0;enterAnimationDuration;exitAnimationDuration},Zt="mdc-dialog--open",Ze="mdc-dialog--opening",Ye="mdc-dialog--closing",xo=150,Ao=75,Eo=(()=>{class i extends Jt{_animationStateChanged=new P;_animationsEnabled=!ut();_actionSectionCount=0;_hostElement=this._elementRef.nativeElement;_enterAnimationDuration=this._animationsEnabled?to(this._config.enterAnimationDuration)??xo:0;_exitAnimationDuration=this._animationsEnabled?to(this._config.exitAnimationDuration)??Ao:0;_animationTimer=null;_contentAttached(){super._contentAttached(),this._startOpenAnimation()}_startOpenAnimation(){this._animationStateChanged.emit({state:"opening",totalTime:this._enterAnimationDuration}),this._animationsEnabled?(this._hostElement.style.setProperty(Xe,`${this._enterAnimationDuration}ms`),this._requestAnimationFrame(()=>this._hostElement.classList.add(Ze,Zt)),this._waitForAnimationToComplete(this._enterAnimationDuration,this._finishDialogOpen)):(this._hostElement.classList.add(Zt),Promise.resolve().then(()=>this._finishDialogOpen()))}_startExitAnimation(){this._animationStateChanged.emit({state:"closing",totalTime:this._exitAnimationDuration}),this._hostElement.classList.remove(Zt),this._animationsEnabled?(this._hostElement.style.setProperty(Xe,`${this._exitAnimationDuration}ms`),this._requestAnimationFrame(()=>this._hostElement.classList.add(Ye)),this._waitForAnimationToComplete(this._exitAnimationDuration,this._finishDialogClose)):Promise.resolve().then(()=>this._finishDialogClose())}_updateActionSectionCount(t){this._actionSectionCount+=t,this._changeDetectorRef.markForCheck()}_finishDialogOpen=()=>{this._clearAnimationClasses(),this._openAnimationDone(this._enterAnimationDuration)};_finishDialogClose=()=>{this._clearAnimationClasses(),this._animationStateChanged.emit({state:"closed",totalTime:this._exitAnimationDuration})};_clearAnimationClasses(){this._hostElement.classList.remove(Ze,Ye)}_waitForAnimationToComplete(t,e){this._animationTimer!==null&&clearTimeout(this._animationTimer),this._animationTimer=setTimeout(e,t)}_requestAnimationFrame(t){this._ngZone.runOutsideAngular(()=>{typeof requestAnimationFrame=="function"?requestAnimationFrame(t):t()})}_captureInitialFocus(){this._config.delayFocusTrap||this._trapFocus()}_openAnimationDone(t){this._config.delayFocusTrap&&this._trapFocus(),this._animationStateChanged.next({state:"opened",totalTime:t})}ngOnDestroy(){super.ngOnDestroy(),this._animationTimer!==null&&clearTimeout(this._animationTimer)}attachComponentPortal(t){let e=super.attachComponentPortal(t);return e.location.nativeElement.classList.add("mat-mdc-dialog-component-host"),e}static \u0275fac=(()=>{let t;return function(o){return(t||(t=vt(i)))(o||i)}})();static \u0275cmp=A({type:i,selectors:[["mat-dialog-container"]],hostAttrs:["tabindex","-1",1,"mat-mdc-dialog-container","mdc-dialog"],hostVars:10,hostBindings:function(e,o){e&2&&(Wt("id",o._config.id),g("aria-modal",o._config.ariaModal)("role",o._config.role)("aria-labelledby",o._config.ariaLabel?null:o._ariaLabelledByQueue[0])("aria-label",o._config.ariaLabel)("aria-describedby",o._config.ariaDescribedBy||null),O("_mat-animation-noopable",!o._animationsEnabled)("mat-mdc-dialog-container-with-actions",o._actionSectionCount>0))},features:[Z],decls:3,vars:0,consts:[[1,"mat-mdc-dialog-inner-container","mdc-dialog__container"],[1,"mat-mdc-dialog-surface","mdc-dialog__surface"],["cdkPortalOutlet",""]],template:function(e,o){e&1&&(v(0,"div",0)(1,"div",1),Y(2,Do,0,0,"ng-template",2),w()())},dependencies:[ct],styles:[`.mat-mdc-dialog-container{width:100%;height:100%;display:block;box-sizing:border-box;max-height:inherit;min-height:inherit;min-width:inherit;max-width:inherit;outline:0}.cdk-overlay-pane.mat-mdc-dialog-panel{max-width:var(--mat-dialog-container-max-width, 560px);min-width:var(--mat-dialog-container-min-width, 280px)}@media(max-width: 599px){.cdk-overlay-pane.mat-mdc-dialog-panel{max-width:var(--mat-dialog-container-small-max-width, calc(100vw - 32px))}}.mat-mdc-dialog-inner-container{display:flex;flex-direction:row;align-items:center;justify-content:space-around;box-sizing:border-box;height:100%;opacity:0;transition:opacity linear var(--mat-dialog-transition-duration, 0ms);max-height:inherit;min-height:inherit;min-width:inherit;max-width:inherit}.mdc-dialog--closing .mat-mdc-dialog-inner-container{transition:opacity 75ms linear;transform:none}.mdc-dialog--open .mat-mdc-dialog-inner-container{opacity:1}._mat-animation-noopable .mat-mdc-dialog-inner-container{transition:none}.mat-mdc-dialog-surface{display:flex;flex-direction:column;flex-grow:0;flex-shrink:0;box-sizing:border-box;width:100%;height:100%;position:relative;overflow-y:auto;outline:0;transform:scale(0.8);transition:transform var(--mat-dialog-transition-duration, 0ms) cubic-bezier(0, 0, 0.2, 1);max-height:inherit;min-height:inherit;min-width:inherit;max-width:inherit;box-shadow:var(--mat-dialog-container-elevation-shadow, none);border-radius:var(--mat-dialog-container-shape, var(--mat-sys-corner-extra-large, 4px));background-color:var(--mat-dialog-container-color, var(--mat-sys-surface, white))}[dir=rtl] .mat-mdc-dialog-surface{text-align:right}.mdc-dialog--open .mat-mdc-dialog-surface,.mdc-dialog--closing .mat-mdc-dialog-surface{transform:none}._mat-animation-noopable .mat-mdc-dialog-surface{transition:none}.mat-mdc-dialog-surface::before{position:absolute;box-sizing:border-box;width:100%;height:100%;top:0;left:0;border:2px solid rgba(0,0,0,0);border-radius:inherit;content:"";pointer-events:none}.mat-mdc-dialog-title{display:block;position:relative;flex-shrink:0;box-sizing:border-box;margin:0 0 1px;padding:var(--mat-dialog-headline-padding, 6px 24px 13px)}.mat-mdc-dialog-title::before{display:inline-block;width:0;height:40px;content:"";vertical-align:0}[dir=rtl] .mat-mdc-dialog-title{text-align:right}.mat-mdc-dialog-container .mat-mdc-dialog-title{color:var(--mat-dialog-subhead-color, var(--mat-sys-on-surface, rgba(0, 0, 0, 0.87)));font-family:var(--mat-dialog-subhead-font, var(--mat-sys-headline-small-font, inherit));line-height:var(--mat-dialog-subhead-line-height, var(--mat-sys-headline-small-line-height, 1.5rem));font-size:var(--mat-dialog-subhead-size, var(--mat-sys-headline-small-size, 1rem));font-weight:var(--mat-dialog-subhead-weight, var(--mat-sys-headline-small-weight, 400));letter-spacing:var(--mat-dialog-subhead-tracking, var(--mat-sys-headline-small-tracking, 0.03125em))}.mat-mdc-dialog-content{display:block;flex-grow:1;box-sizing:border-box;margin:0;overflow:auto;max-height:65vh}.mat-mdc-dialog-content>:first-child{margin-top:0}.mat-mdc-dialog-content>:last-child{margin-bottom:0}.mat-mdc-dialog-container .mat-mdc-dialog-content{color:var(--mat-dialog-supporting-text-color, var(--mat-sys-on-surface-variant, rgba(0, 0, 0, 0.6)));font-family:var(--mat-dialog-supporting-text-font, var(--mat-sys-body-medium-font, inherit));line-height:var(--mat-dialog-supporting-text-line-height, var(--mat-sys-body-medium-line-height, 1.5rem));font-size:var(--mat-dialog-supporting-text-size, var(--mat-sys-body-medium-size, 1rem));font-weight:var(--mat-dialog-supporting-text-weight, var(--mat-sys-body-medium-weight, 400));letter-spacing:var(--mat-dialog-supporting-text-tracking, var(--mat-sys-body-medium-tracking, 0.03125em))}.mat-mdc-dialog-container .mat-mdc-dialog-content{padding:var(--mat-dialog-content-padding, 20px 24px)}.mat-mdc-dialog-container-with-actions .mat-mdc-dialog-content{padding:var(--mat-dialog-with-actions-content-padding, 20px 24px 0)}.mat-mdc-dialog-container .mat-mdc-dialog-title+.mat-mdc-dialog-content{padding-top:0}.mat-mdc-dialog-actions{display:flex;position:relative;flex-shrink:0;flex-wrap:wrap;align-items:center;box-sizing:border-box;min-height:52px;margin:0;border-top:1px solid rgba(0,0,0,0);padding:var(--mat-dialog-actions-padding, 16px 24px);justify-content:var(--mat-dialog-actions-alignment, flex-end)}@media(forced-colors: active){.mat-mdc-dialog-actions{border-top-color:CanvasText}}.mat-mdc-dialog-actions.mat-mdc-dialog-actions-align-start,.mat-mdc-dialog-actions[align=start]{justify-content:start}.mat-mdc-dialog-actions.mat-mdc-dialog-actions-align-center,.mat-mdc-dialog-actions[align=center]{justify-content:center}.mat-mdc-dialog-actions.mat-mdc-dialog-actions-align-end,.mat-mdc-dialog-actions[align=end]{justify-content:flex-end}.mat-mdc-dialog-actions .mat-button-base+.mat-button-base,.mat-mdc-dialog-actions .mat-mdc-button-base+.mat-mdc-button-base{margin-left:8px}[dir=rtl] .mat-mdc-dialog-actions .mat-button-base+.mat-button-base,[dir=rtl] .mat-mdc-dialog-actions .mat-mdc-button-base+.mat-mdc-button-base{margin-left:0;margin-right:8px}.mat-mdc-dialog-component-host{display:contents} -`],encapsulation:2})}return i})(),Xe="--mat-dialog-transition-duration";function to(i){return i==null?null:typeof i=="number"?i:i.endsWith("ms")?$t(i.substring(0,i.length-2)):i.endsWith("s")?$t(i.substring(0,i.length-1))*1e3:i==="0"?0:null}var Lt=(function(i){return i[i.OPEN=0]="OPEN",i[i.CLOSING=1]="CLOSING",i[i.CLOSED=2]="CLOSED",i})(Lt||{}),mt=class{_ref;_config;_containerInstance;componentInstance;componentRef=null;disableClose;id;_afterOpened=new Vt(1);_beforeClosed=new Vt(1);_result;_closeFallbackTimeout;_state=Lt.OPEN;_closeInteractionType;constructor(s,t,e){this._ref=s,this._config=t,this._containerInstance=e,this.disableClose=t.disableClose,this.id=s.id,s.addPanelClass("mat-mdc-dialog-panel"),e._animationStateChanged.pipe(rt(o=>o.state==="opened"),N(1)).subscribe(()=>{this._afterOpened.next(),this._afterOpened.complete()}),e._animationStateChanged.pipe(rt(o=>o.state==="closed"),N(1)).subscribe(()=>{clearTimeout(this._closeFallbackTimeout),this._finishDialogClose()}),s.overlayRef.detachments().subscribe(()=>{this._beforeClosed.next(this._result),this._beforeClosed.complete(),this._finishDialogClose()}),ne(this.backdropClick(),this.keydownEvents().pipe(rt(o=>o.keyCode===27&&!this.disableClose&&!ot(o)))).subscribe(o=>{this.disableClose||(o.preventDefault(),eo(this,o.type==="keydown"?"keyboard":"mouse"))})}close(s){let t=this._config.closePredicate;t&&!t(s,this._config,this.componentInstance)||(this._result=s,this._containerInstance._animationStateChanged.pipe(rt(e=>e.state==="closing"),N(1)).subscribe(e=>{this._beforeClosed.next(s),this._beforeClosed.complete(),this._ref.overlayRef.detachBackdrop(),this._closeFallbackTimeout=setTimeout(()=>this._finishDialogClose(),e.totalTime+100)}),this._state=Lt.CLOSING,this._containerInstance._startExitAnimation())}afterOpened(){return this._afterOpened}afterClosed(){return this._ref.closed}beforeClosed(){return this._beforeClosed}backdropClick(){return this._ref.backdropClick}keydownEvents(){return this._ref.keydownEvents}updatePosition(s){let t=this._ref.config.positionStrategy;return s&&(s.left||s.right)?s.left?t.left(s.left):t.right(s.right):t.centerHorizontally(),s&&(s.top||s.bottom)?s.top?t.top(s.top):t.bottom(s.bottom):t.centerVertically(),this._ref.updatePosition(),this}updateSize(s="",t=""){return this._ref.updateSize(s,t),this}addPanelClass(s){return this._ref.addPanelClass(s),this}removePanelClass(s){return this._ref.removePanelClass(s),this}getState(){return this._state}_finishDialogClose(){this._state=Lt.CLOSED,this._ref.close(this._result,{focusOrigin:this._closeInteractionType}),this.componentInstance=null}};function eo(i,s,t){return i._closeInteractionType=s,i.close(t)}var Oo=new h("MatMdcDialogData"),Mo=new h("mat-mdc-dialog-default-options"),Fo=new h("mat-mdc-dialog-scroll-strategy",{providedIn:"root",factory:()=>{let i=l(F);return()=>Ot(i)}}),Yt=(()=>{class i{_defaultOptions=l(Mo,{optional:!0});_scrollStrategy=l(Fo);_parentDialog=l(i,{optional:!0,skipSelf:!0});_idGenerator=l(z);_injector=l(F);_dialog=l(Kt);_animationsDisabled=ut();_openDialogsAtThisLevel=[];_afterAllClosedAtThisLevel=new T;_afterOpenedAtThisLevel=new T;dialogConfigClass=Bt;_dialogRefConstructor;_dialogContainerType;_dialogDataToken;get openDialogs(){return this._parentDialog?this._parentDialog.openDialogs:this._openDialogsAtThisLevel}get afterOpened(){return this._parentDialog?this._parentDialog.afterOpened:this._afterOpenedAtThisLevel}_getAfterAllClosed(){let t=this._parentDialog;return t?t._getAfterAllClosed():this._afterAllClosedAtThisLevel}afterAllClosed=ht(()=>this.openDialogs.length?this._getAfterAllClosed():this._getAfterAllClosed().pipe(pt(void 0)));constructor(){this._dialogRefConstructor=mt,this._dialogContainerType=Eo,this._dialogDataToken=Oo}open(t,e){let o;e=M(M({},this._defaultOptions||new Bt),e),e.id=e.id||this._idGenerator.getId("mat-mdc-dialog-"),e.scrollStrategy=e.scrollStrategy||this._scrollStrategy();let n=this._dialog.open(t,Xt(M({},e),{positionStrategy:Mt(this._injector).centerHorizontally().centerVertically(),disableClose:!0,closePredicate:void 0,closeOnDestroy:!1,closeOnOverlayDetachments:!1,disableAnimations:this._animationsDisabled||e.enterAnimationDuration?.toLocaleString()==="0"||e.exitAnimationDuration?.toString()==="0",container:{type:this._dialogContainerType,providers:()=>[{provide:this.dialogConfigClass,useValue:e},{provide:G,useValue:e}]},templateContext:()=>({dialogRef:o}),providers:(a,r,d)=>(o=new this._dialogRefConstructor(a,e,d),o.updatePosition(e?.position),[{provide:this._dialogContainerType,useValue:d},{provide:this._dialogDataToken,useValue:r.data},{provide:this._dialogRefConstructor,useValue:o}])}));return o.componentRef=n.componentRef,o.componentInstance=n.componentInstance,this.openDialogs.push(o),this.afterOpened.next(o),o.afterClosed().subscribe(()=>{let a=this.openDialogs.indexOf(o);a>-1&&(this.openDialogs.splice(a,1),this.openDialogs.length||this._getAfterAllClosed().next())}),o}closeAll(){this._closeDialogs(this.openDialogs)}getDialogById(t){return this.openDialogs.find(e=>e.id===t)}ngOnDestroy(){this._closeDialogs(this._openDialogsAtThisLevel),this._afterAllClosedAtThisLevel.complete(),this._afterOpenedAtThisLevel.complete()}_closeDialogs(t){let e=t.length;for(;e--;)t[e].close()}static \u0275fac=function(e){return new(e||i)};static \u0275prov=D({token:i,factory:i.\u0275fac,providedIn:"root"})}return i})(),pn=(()=>{class i{dialogRef=l(mt,{optional:!0});_elementRef=l(I);_dialog=l(Yt);ariaLabel;type="button";dialogResult;_matDialogClose;constructor(){}ngOnInit(){this.dialogRef||(this.dialogRef=io(this._elementRef,this._dialog.openDialogs))}ngOnChanges(t){let e=t._matDialogClose||t._matDialogCloseResult;e&&(this.dialogResult=e.currentValue)}_onButtonClick(t){eo(this.dialogRef,t.screenX===0&&t.screenY===0?"keyboard":"mouse",this.dialogResult)}static \u0275fac=function(e){return new(e||i)};static \u0275dir=L({type:i,selectors:[["","mat-dialog-close",""],["","matDialogClose",""]],hostVars:2,hostBindings:function(e,o){e&1&&tt("click",function(a){return o._onButtonClick(a)}),e&2&&g("aria-label",o.ariaLabel||null)("type",o.type)},inputs:{ariaLabel:[0,"aria-label","ariaLabel"],type:"type",dialogResult:[0,"mat-dialog-close","dialogResult"],_matDialogClose:[0,"matDialogClose","_matDialogClose"]},exportAs:["matDialogClose"],features:[de]})}return i})(),oo=(()=>{class i{_dialogRef=l(mt,{optional:!0});_elementRef=l(I);_dialog=l(Yt);constructor(){}ngOnInit(){this._dialogRef||(this._dialogRef=io(this._elementRef,this._dialog.openDialogs)),this._dialogRef&&Promise.resolve().then(()=>{this._onAdd()})}ngOnDestroy(){this._dialogRef?._containerInstance&&Promise.resolve().then(()=>{this._onRemove()})}static \u0275fac=function(e){return new(e||i)};static \u0275dir=L({type:i})}return i})(),fn=(()=>{class i extends oo{id=l(z).getId("mat-mdc-dialog-title-");_onAdd(){this._dialogRef._containerInstance?._addAriaLabelledBy?.(this.id)}_onRemove(){this._dialogRef?._containerInstance?._removeAriaLabelledBy?.(this.id)}static \u0275fac=(()=>{let t;return function(o){return(t||(t=vt(i)))(o||i)}})();static \u0275dir=L({type:i,selectors:[["","mat-dialog-title",""],["","matDialogTitle",""]],hostAttrs:[1,"mat-mdc-dialog-title","mdc-dialog__title"],hostVars:1,hostBindings:function(e,o){e&2&&Wt("id",o.id)},inputs:{id:"id"},exportAs:["matDialogTitle"],features:[Z]})}return i})(),_n=(()=>{class i{static \u0275fac=function(e){return new(e||i)};static \u0275dir=L({type:i,selectors:[["","mat-dialog-content",""],["mat-dialog-content"],["","matDialogContent",""]],hostAttrs:[1,"mat-mdc-dialog-content","mdc-dialog__content"],features:[he([we])]})}return i})(),bn=(()=>{class i extends oo{align;_onAdd(){this._dialogRef._containerInstance?._updateActionSectionCount?.(1)}_onRemove(){this._dialogRef._containerInstance?._updateActionSectionCount?.(-1)}static \u0275fac=(()=>{let t;return function(o){return(t||(t=vt(i)))(o||i)}})();static \u0275dir=L({type:i,selectors:[["","mat-dialog-actions",""],["mat-dialog-actions"],["","matDialogActions",""]],hostAttrs:[1,"mat-mdc-dialog-actions","mdc-dialog__actions"],hostVars:6,hostBindings:function(e,o){e&2&&O("mat-mdc-dialog-actions-align-start",o.align==="start")("mat-mdc-dialog-actions-align-center",o.align==="center")("mat-mdc-dialog-actions-align-end",o.align==="end")},inputs:{align:"align"},features:[Z]})}return i})();function io(i,s){let t=i.nativeElement.parentElement;for(;t&&!t.classList.contains("mat-mdc-dialog-container");)t=t.parentElement;return t?s.find(e=>e.id===t.id):null}var vn=(()=>{class i{static \u0275fac=function(e){return new(e||i)};static \u0275mod=E({type:i});static \u0275inj=x({providers:[Yt],imports:[Ke,Ft,dt,j]})}return i})();var zo=["button"],Go=["*"];function Ho(i,s){if(i&1&&(v(0,"div",2),X(1,"mat-pseudo-checkbox",6),w()),i&2){let t=Ct();k(),B("disabled",t.disabled)}}var no=new h("MAT_BUTTON_TOGGLE_DEFAULT_OPTIONS",{providedIn:"root",factory:()=>({hideSingleSelectionIndicator:!1,hideMultipleSelectionIndicator:!1,disabledInteractive:!1})}),ao=new h("MatButtonToggleGroup"),Vo={provide:Me,useExisting:se(()=>Uo),multi:!0},jt=class{source;value;constructor(s,t){this.source=s,this.value=t}},Uo=(()=>{class i{_changeDetector=l(st);_dir=l(lt,{optional:!0});_multiple=!1;_disabled=!1;_disabledInteractive=!1;_selectionModel;_rawValue;_controlValueAccessorChangeFn=()=>{};_onTouched=()=>{};_buttonToggles;appearance;get name(){return this._name}set name(t){this._name=t,this._markButtonsForCheck()}_name=l(z).getId("mat-button-toggle-group-");vertical=!1;get value(){let t=this._selectionModel?this._selectionModel.selected:[];return this.multiple?t.map(e=>e.value):t[0]?t[0].value:void 0}set value(t){this._setSelectionByValue(t),this.valueChange.emit(this.value)}valueChange=new P;get selected(){let t=this._selectionModel?this._selectionModel.selected:[];return this.multiple?t:t[0]||null}get multiple(){return this._multiple}set multiple(t){this._multiple=t,this._markButtonsForCheck()}get disabled(){return this._disabled}set disabled(t){this._disabled=t,this._markButtonsForCheck()}get disabledInteractive(){return this._disabledInteractive}set disabledInteractive(t){this._disabledInteractive=t,this._markButtonsForCheck()}get dir(){return this._dir&&this._dir.value==="rtl"?"rtl":"ltr"}change=new P;get hideSingleSelectionIndicator(){return this._hideSingleSelectionIndicator}set hideSingleSelectionIndicator(t){this._hideSingleSelectionIndicator=t,this._markButtonsForCheck()}_hideSingleSelectionIndicator;get hideMultipleSelectionIndicator(){return this._hideMultipleSelectionIndicator}set hideMultipleSelectionIndicator(t){this._hideMultipleSelectionIndicator=t,this._markButtonsForCheck()}_hideMultipleSelectionIndicator;constructor(){let t=l(no,{optional:!0});this.appearance=t&&t.appearance?t.appearance:"standard",this._hideSingleSelectionIndicator=t?.hideSingleSelectionIndicator??!1,this._hideMultipleSelectionIndicator=t?.hideMultipleSelectionIndicator??!1}ngOnInit(){this._selectionModel=new ze(this.multiple,void 0,!1)}ngAfterContentInit(){this._selectionModel.select(...this._buttonToggles.filter(t=>t.checked)),this.multiple||this._initializeTabIndex()}writeValue(t){this.value=t,this._changeDetector.markForCheck()}registerOnChange(t){this._controlValueAccessorChangeFn=t}registerOnTouched(t){this._onTouched=t}setDisabledState(t){this.disabled=t}_keydown(t){if(this.multiple||this.disabled||ot(t))return;let o=t.target.id,n=this._buttonToggles.toArray().findIndex(r=>r.buttonId===o),a=null;switch(t.keyCode){case 32:case 13:a=this._buttonToggles.get(n)||null;break;case 38:a=this._getNextButton(n,-1);break;case 37:a=this._getNextButton(n,this.dir==="ltr"?-1:1);break;case 40:a=this._getNextButton(n,1);break;case 39:a=this._getNextButton(n,this.dir==="ltr"?1:-1);break;default:return}a&&(t.preventDefault(),a._onButtonClick(),a.focus())}_emitChangeEvent(t){let e=new jt(t,this.value);this._rawValue=e.value,this._controlValueAccessorChangeFn(e.value),this.change.emit(e)}_syncButtonToggle(t,e,o=!1,n=!1){!this.multiple&&this.selected&&!t.checked&&(this.selected.checked=!1),this._selectionModel?e?this._selectionModel.select(t):this._selectionModel.deselect(t):n=!0,n?Promise.resolve().then(()=>this._updateModelValue(t,o)):this._updateModelValue(t,o)}_isSelected(t){return this._selectionModel&&this._selectionModel.isSelected(t)}_isPrechecked(t){return typeof this._rawValue>"u"?!1:this.multiple&&Array.isArray(this._rawValue)?this._rawValue.some(e=>t.value!=null&&e===t.value):t.value===this._rawValue}_initializeTabIndex(){if(this._buttonToggles.forEach(t=>{t.tabIndex=-1}),this.selected)this.selected.tabIndex=0;else for(let t=0;tthis._selectValue(o,e))):(this._clearSelection(),this._selectValue(t,e)),!this.multiple&&e.every(o=>o.tabIndex===-1)){for(let o of e)if(!o.disabled){o.tabIndex=0;break}}}_clearSelection(){this._selectionModel.clear(),this._buttonToggles.forEach(t=>{t.checked=!1,this.multiple||(t.tabIndex=-1)})}_selectValue(t,e){for(let o of e)if(o.value===t){o.checked=!0,this._selectionModel.select(o),this.multiple||(o.tabIndex=0);break}}_updateModelValue(t,e){e&&this._emitChangeEvent(t),this.valueChange.emit(this.value)}_markButtonsForCheck(){this._buttonToggles?.forEach(t=>t._markForCheck())}static \u0275fac=function(e){return new(e||i)};static \u0275dir=L({type:i,selectors:[["mat-button-toggle-group"]],contentQueries:function(e,o,n){if(e&1&&_e(n,ro,5),e&2){let a;U(a=W())&&(o._buttonToggles=a)}},hostAttrs:[1,"mat-button-toggle-group"],hostVars:6,hostBindings:function(e,o){e&1&&tt("keydown",function(a){return o._keydown(a)}),e&2&&(g("role",o.multiple?"group":"radiogroup")("aria-disabled",o.disabled),O("mat-button-toggle-vertical",o.vertical)("mat-button-toggle-group-appearance-standard",o.appearance==="standard"))},inputs:{appearance:"appearance",name:"name",vertical:[2,"vertical","vertical",y],value:"value",multiple:[2,"multiple","multiple",y],disabled:[2,"disabled","disabled",y],disabledInteractive:[2,"disabledInteractive","disabledInteractive",y],hideSingleSelectionIndicator:[2,"hideSingleSelectionIndicator","hideSingleSelectionIndicator",y],hideMultipleSelectionIndicator:[2,"hideMultipleSelectionIndicator","hideMultipleSelectionIndicator",y]},outputs:{valueChange:"valueChange",change:"change"},exportAs:["matButtonToggleGroup"],features:[be([Vo,{provide:ao,useExisting:i}])]})}return i})(),ro=(()=>{class i{_changeDetectorRef=l(st);_elementRef=l(I);_focusMonitor=l(Rt);_idGenerator=l(z);_animationDisabled=ut();_checked=!1;ariaLabel;ariaLabelledby=null;_buttonElement;buttonToggleGroup;get buttonId(){return`${this.id}-button`}id;name;value;get tabIndex(){return this._tabIndex()}set tabIndex(t){this._tabIndex.set(t)}_tabIndex;disableRipple=!1;get appearance(){return this.buttonToggleGroup?this.buttonToggleGroup.appearance:this._appearance}set appearance(t){this._appearance=t}_appearance;get checked(){return this.buttonToggleGroup?this.buttonToggleGroup._isSelected(this):this._checked}set checked(t){t!==this._checked&&(this._checked=t,this.buttonToggleGroup&&this.buttonToggleGroup._syncButtonToggle(this,this._checked),this._changeDetectorRef.markForCheck())}get disabled(){return this._disabled||this.buttonToggleGroup&&this.buttonToggleGroup.disabled}set disabled(t){this._disabled=t}_disabled=!1;get disabledInteractive(){return this._disabledInteractive||this.buttonToggleGroup!==null&&this.buttonToggleGroup.disabledInteractive}set disabledInteractive(t){this._disabledInteractive=t}_disabledInteractive;change=new P;constructor(){l(Ie).load(Be);let t=l(ao,{optional:!0}),e=l(new Dt("tabindex"),{optional:!0})||"",o=l(no,{optional:!0});this._tabIndex=bt(parseInt(e)||0),this.buttonToggleGroup=t,this._appearance=o&&o.appearance?o.appearance:"standard",this._disabledInteractive=o?.disabledInteractive??!1}ngOnInit(){let t=this.buttonToggleGroup;this.id=this.id||this._idGenerator.getId("mat-button-toggle-"),t&&(t._isPrechecked(this)?this.checked=!0:t._isSelected(this)!==this._checked&&t._syncButtonToggle(this,this._checked))}ngAfterViewInit(){this._animationDisabled||this._elementRef.nativeElement.classList.add("mat-button-toggle-animations-enabled"),this._focusMonitor.monitor(this._elementRef,!0)}ngOnDestroy(){let t=this.buttonToggleGroup;this._focusMonitor.stopMonitoring(this._elementRef),t&&t._isSelected(this)&&t._syncButtonToggle(this,!1,!1,!0)}focus(t){this._buttonElement.nativeElement.focus(t)}_onButtonClick(){if(this.disabled)return;let t=this.isSingleSelector()?!0:!this._checked;if(t!==this._checked&&(this._checked=t,this.buttonToggleGroup&&(this.buttonToggleGroup._syncButtonToggle(this,this._checked,!0),this.buttonToggleGroup._onTouched())),this.isSingleSelector()){let e=this.buttonToggleGroup._buttonToggles.find(o=>o.tabIndex===0);e&&(e.tabIndex=-1),this.tabIndex=0}this.change.emit(new jt(this,this.value))}_markForCheck(){this._changeDetectorRef.markForCheck()}_getButtonName(){return this.isSingleSelector()?this.buttonToggleGroup.name:this.name||null}isSingleSelector(){return this.buttonToggleGroup&&!this.buttonToggleGroup.multiple}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=A({type:i,selectors:[["mat-button-toggle"]],viewQuery:function(e,o){if(e&1&&et(zo,5),e&2){let n;U(n=W())&&(o._buttonElement=n.first)}},hostAttrs:["role","presentation",1,"mat-button-toggle"],hostVars:14,hostBindings:function(e,o){e&1&&tt("focus",function(){return o.focus()}),e&2&&(g("aria-label",null)("aria-labelledby",null)("id",o.id)("name",null),O("mat-button-toggle-standalone",!o.buttonToggleGroup)("mat-button-toggle-checked",o.checked)("mat-button-toggle-disabled",o.disabled)("mat-button-toggle-disabled-interactive",o.disabledInteractive)("mat-button-toggle-appearance-standard",o.appearance==="standard"))},inputs:{ariaLabel:[0,"aria-label","ariaLabel"],ariaLabelledby:[0,"aria-labelledby","ariaLabelledby"],id:"id",name:"name",value:"value",tabIndex:"tabIndex",disableRipple:[2,"disableRipple","disableRipple",y],appearance:"appearance",checked:[2,"checked","checked",y],disabled:[2,"disabled","disabled",y],disabledInteractive:[2,"disabledInteractive","disabledInteractive",y]},outputs:{change:"change"},exportAs:["matButtonToggle"],ngContentSelectors:Go,decls:7,vars:13,consts:[["button",""],["type","button",1,"mat-button-toggle-button","mat-focus-indicator",3,"click","id","disabled"],[1,"mat-button-toggle-checkbox-wrapper"],[1,"mat-button-toggle-label-content"],[1,"mat-button-toggle-focus-overlay"],["matRipple","",1,"mat-button-toggle-ripple",3,"matRippleTrigger","matRippleDisabled"],["state","checked","aria-hidden","true","appearance","minimal",3,"disabled"]],template:function(e,o){if(e&1&&(St(),v(0,"button",1,0),tt("click",function(){return o._onButtonClick()}),pe(2,Ho,2,1,"div",2),v(3,"span",3),It(4),w()(),X(5,"span",4)(6,"span",5)),e&2){let n=kt(1);B("id",o.buttonId)("disabled",o.disabled&&!o.disabledInteractive||null),g("role",o.isSingleSelector()?"radio":"button")("tabindex",o.disabled&&!o.disabledInteractive?-1:o.tabIndex)("aria-pressed",o.isSingleSelector()?null:o.checked)("aria-checked",o.isSingleSelector()?o.checked:null)("name",o._getButtonName())("aria-label",o.ariaLabel)("aria-labelledby",o.ariaLabelledby)("aria-disabled",o.disabled&&o.disabledInteractive?"true":null),k(2),fe(o.buttonToggleGroup&&(!o.buttonToggleGroup.multiple&&!o.buttonToggleGroup.hideSingleSelectionIndicator||o.buttonToggleGroup.multiple&&!o.buttonToggleGroup.hideMultipleSelectionIndicator)?2:-1),k(4),B("matRippleTrigger",n)("matRippleDisabled",o.disableRipple||o.disabled)}},dependencies:[Le,Ge],styles:[`.mat-button-toggle-standalone,.mat-button-toggle-group{position:relative;display:inline-flex;flex-direction:row;white-space:nowrap;overflow:hidden;-webkit-tap-highlight-color:rgba(0,0,0,0);border-radius:var(--mat-button-toggle-legacy-shape);transform:translateZ(0)}.mat-button-toggle-standalone:not([class*=mat-elevation-z]),.mat-button-toggle-group:not([class*=mat-elevation-z]){box-shadow:0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12)}@media(forced-colors: active){.mat-button-toggle-standalone,.mat-button-toggle-group{outline:solid 1px}}.mat-button-toggle-standalone.mat-button-toggle-appearance-standard,.mat-button-toggle-group-appearance-standard{border-radius:var(--mat-button-toggle-shape, var(--mat-sys-corner-extra-large));border:solid 1px var(--mat-button-toggle-divider-color, var(--mat-sys-outline))}.mat-button-toggle-standalone.mat-button-toggle-appearance-standard .mat-pseudo-checkbox,.mat-button-toggle-group-appearance-standard .mat-pseudo-checkbox{--mat-pseudo-checkbox-minimal-selected-checkmark-color: var(--mat-button-toggle-selected-state-text-color, var(--mat-sys-on-secondary-container))}.mat-button-toggle-standalone.mat-button-toggle-appearance-standard:not([class*=mat-elevation-z]),.mat-button-toggle-group-appearance-standard:not([class*=mat-elevation-z]){box-shadow:none}@media(forced-colors: active){.mat-button-toggle-standalone.mat-button-toggle-appearance-standard,.mat-button-toggle-group-appearance-standard{outline:0}}.mat-button-toggle-vertical{flex-direction:column}.mat-button-toggle-vertical .mat-button-toggle-label-content{display:block}.mat-button-toggle{white-space:nowrap;position:relative;color:var(--mat-button-toggle-legacy-text-color);font-family:var(--mat-button-toggle-legacy-label-text-font);font-size:var(--mat-button-toggle-legacy-label-text-size);line-height:var(--mat-button-toggle-legacy-label-text-line-height);font-weight:var(--mat-button-toggle-legacy-label-text-weight);letter-spacing:var(--mat-button-toggle-legacy-label-text-tracking);--mat-pseudo-checkbox-minimal-selected-checkmark-color: var(--mat-button-toggle-legacy-selected-state-text-color)}.mat-button-toggle.cdk-keyboard-focused .mat-button-toggle-focus-overlay{opacity:var(--mat-button-toggle-legacy-focus-state-layer-opacity)}.mat-button-toggle .mat-icon svg{vertical-align:top}.mat-button-toggle-checkbox-wrapper{display:inline-block;justify-content:flex-start;align-items:center;width:0;height:18px;line-height:18px;overflow:hidden;box-sizing:border-box;position:absolute;top:50%;left:16px;transform:translate3d(0, -50%, 0)}[dir=rtl] .mat-button-toggle-checkbox-wrapper{left:auto;right:16px}.mat-button-toggle-appearance-standard .mat-button-toggle-checkbox-wrapper{left:12px}[dir=rtl] .mat-button-toggle-appearance-standard .mat-button-toggle-checkbox-wrapper{left:auto;right:12px}.mat-button-toggle-checked .mat-button-toggle-checkbox-wrapper{width:18px}.mat-button-toggle-animations-enabled .mat-button-toggle-checkbox-wrapper{transition:width 150ms 45ms cubic-bezier(0.4, 0, 0.2, 1)}.mat-button-toggle-vertical .mat-button-toggle-checkbox-wrapper{transition:none}.mat-button-toggle-checked{color:var(--mat-button-toggle-legacy-selected-state-text-color);background-color:var(--mat-button-toggle-legacy-selected-state-background-color)}.mat-button-toggle-disabled{pointer-events:none;color:var(--mat-button-toggle-legacy-disabled-state-text-color);background-color:var(--mat-button-toggle-legacy-disabled-state-background-color);--mat-pseudo-checkbox-minimal-disabled-selected-checkmark-color: var(--mat-button-toggle-legacy-disabled-state-text-color)}.mat-button-toggle-disabled.mat-button-toggle-checked{background-color:var(--mat-button-toggle-legacy-disabled-selected-state-background-color)}.mat-button-toggle-disabled-interactive{pointer-events:auto}.mat-button-toggle-appearance-standard{color:var(--mat-button-toggle-text-color, var(--mat-sys-on-surface));background-color:var(--mat-button-toggle-background-color, transparent);font-family:var(--mat-button-toggle-label-text-font, var(--mat-sys-label-large-font));font-size:var(--mat-button-toggle-label-text-size, var(--mat-sys-label-large-size));line-height:var(--mat-button-toggle-label-text-line-height, var(--mat-sys-label-large-line-height));font-weight:var(--mat-button-toggle-label-text-weight, var(--mat-sys-label-large-weight));letter-spacing:var(--mat-button-toggle-label-text-tracking, var(--mat-sys-label-large-tracking))}.mat-button-toggle-group-appearance-standard .mat-button-toggle-appearance-standard+.mat-button-toggle-appearance-standard{border-left:solid 1px var(--mat-button-toggle-divider-color, var(--mat-sys-outline))}[dir=rtl] .mat-button-toggle-group-appearance-standard .mat-button-toggle-appearance-standard+.mat-button-toggle-appearance-standard{border-left:none;border-right:solid 1px var(--mat-button-toggle-divider-color, var(--mat-sys-outline))}.mat-button-toggle-group-appearance-standard.mat-button-toggle-vertical .mat-button-toggle-appearance-standard+.mat-button-toggle-appearance-standard{border-left:none;border-right:none;border-top:solid 1px var(--mat-button-toggle-divider-color, var(--mat-sys-outline))}.mat-button-toggle-appearance-standard.mat-button-toggle-checked{color:var(--mat-button-toggle-selected-state-text-color, var(--mat-sys-on-secondary-container));background-color:var(--mat-button-toggle-selected-state-background-color, var(--mat-sys-secondary-container))}.mat-button-toggle-appearance-standard.mat-button-toggle-disabled{color:var(--mat-button-toggle-disabled-state-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent));background-color:var(--mat-button-toggle-disabled-state-background-color, transparent)}.mat-button-toggle-appearance-standard.mat-button-toggle-disabled .mat-pseudo-checkbox{--mat-pseudo-checkbox-minimal-disabled-selected-checkmark-color: var(--mat-button-toggle-disabled-selected-state-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent))}.mat-button-toggle-appearance-standard.mat-button-toggle-disabled.mat-button-toggle-checked{color:var(--mat-button-toggle-disabled-selected-state-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent));background-color:var(--mat-button-toggle-disabled-selected-state-background-color, color-mix(in srgb, var(--mat-sys-on-surface) 12%, transparent))}.mat-button-toggle-appearance-standard .mat-button-toggle-focus-overlay{background-color:var(--mat-button-toggle-state-layer-color, var(--mat-sys-on-surface))}.mat-button-toggle-appearance-standard:hover .mat-button-toggle-focus-overlay{opacity:var(--mat-button-toggle-hover-state-layer-opacity, var(--mat-sys-hover-state-layer-opacity))}.mat-button-toggle-appearance-standard.cdk-keyboard-focused .mat-button-toggle-focus-overlay{opacity:var(--mat-button-toggle-focus-state-layer-opacity, var(--mat-sys-focus-state-layer-opacity))}@media(hover: none){.mat-button-toggle-appearance-standard:hover .mat-button-toggle-focus-overlay{display:none}}.mat-button-toggle-label-content{-webkit-user-select:none;user-select:none;display:inline-block;padding:0 16px;line-height:var(--mat-button-toggle-legacy-height);position:relative}.mat-button-toggle-appearance-standard .mat-button-toggle-label-content{padding:0 12px;line-height:var(--mat-button-toggle-height, 40px)}.mat-button-toggle-label-content>*{vertical-align:middle}.mat-button-toggle-focus-overlay{top:0;left:0;right:0;bottom:0;position:absolute;border-radius:inherit;pointer-events:none;opacity:0;background-color:var(--mat-button-toggle-legacy-state-layer-color)}@media(forced-colors: active){.mat-button-toggle-checked .mat-button-toggle-focus-overlay{border-bottom:solid 500px;opacity:.5;height:0}.mat-button-toggle-checked:hover .mat-button-toggle-focus-overlay{opacity:.6}.mat-button-toggle-checked.mat-button-toggle-appearance-standard .mat-button-toggle-focus-overlay{border-bottom:solid 500px}}.mat-button-toggle .mat-button-toggle-ripple{top:0;left:0;right:0;bottom:0;position:absolute;pointer-events:none}.mat-button-toggle-button{border:0;background:none;color:inherit;padding:0;margin:0;font:inherit;outline:none;width:100%;cursor:pointer}.mat-button-toggle-animations-enabled .mat-button-toggle-button{transition:padding 150ms 45ms cubic-bezier(0.4, 0, 0.2, 1)}.mat-button-toggle-vertical .mat-button-toggle-button{transition:none}.mat-button-toggle-disabled .mat-button-toggle-button{cursor:default}.mat-button-toggle-button::-moz-focus-inner{border:0}.mat-button-toggle-checked .mat-button-toggle-button:has(.mat-button-toggle-checkbox-wrapper){padding-left:30px}[dir=rtl] .mat-button-toggle-checked .mat-button-toggle-button:has(.mat-button-toggle-checkbox-wrapper){padding-left:0;padding-right:30px}.mat-button-toggle-standalone.mat-button-toggle-appearance-standard{--mat-focus-indicator-border-radius: var(--mat-button-toggle-shape, var(--mat-sys-corner-extra-large))}.mat-button-toggle-group-appearance-standard:not(.mat-button-toggle-vertical) .mat-button-toggle:last-of-type .mat-button-toggle-button::before{border-top-right-radius:var(--mat-button-toggle-shape, var(--mat-sys-corner-extra-large));border-bottom-right-radius:var(--mat-button-toggle-shape, var(--mat-sys-corner-extra-large))}.mat-button-toggle-group-appearance-standard:not(.mat-button-toggle-vertical) .mat-button-toggle:first-of-type .mat-button-toggle-button::before{border-top-left-radius:var(--mat-button-toggle-shape, var(--mat-sys-corner-extra-large));border-bottom-left-radius:var(--mat-button-toggle-shape, var(--mat-sys-corner-extra-large))}.mat-button-toggle-group-appearance-standard.mat-button-toggle-vertical .mat-button-toggle:last-of-type .mat-button-toggle-button::before{border-bottom-right-radius:var(--mat-button-toggle-shape, var(--mat-sys-corner-extra-large));border-bottom-left-radius:var(--mat-button-toggle-shape, var(--mat-sys-corner-extra-large))}.mat-button-toggle-group-appearance-standard.mat-button-toggle-vertical .mat-button-toggle:first-of-type .mat-button-toggle-button::before{border-top-right-radius:var(--mat-button-toggle-shape, var(--mat-sys-corner-extra-large));border-top-left-radius:var(--mat-button-toggle-shape, var(--mat-sys-corner-extra-large))} -`],encapsulation:2,changeDetection:0})}return i})(),Vn=(()=>{class i{static \u0275fac=function(e){return new(e||i)};static \u0275mod=E({type:i});static \u0275inj=x({imports:[je,ro,j]})}return i})();function Wo(i){return i.metadata&&(typeof i.metadata=="string"?JSON.parse(i.metadata):i.metadata).copilot_mode||null}function Wn(i){return Wo(i)==="troubleshooting_injection"}var nt=(function(i){return i.NETWORK_ERROR="NETWORK_ERROR",i.PROJECT_NOT_OPENED="PROJECT_NOT_OPENED",i.LLM_NOT_CONFIGURED="LLM_NOT_CONFIGURED",i.SESSION_NOT_FOUND="SESSION_NOT_FOUND",i.UNAUTHORIZED="UNAUTHORIZED",i.UNKNOWN_ERROR="UNKNOWN_ERROR",i})(nt||{});var zt=class{authToken;id;name;location;host;port;path;ubridge_path;status="stopped";protocol;username;password;tokenExpired=!1};var so=(()=>{class i{httpController;controllerIds=[];serviceInitialized=new T;isServiceInitialized;constructor(t){this.httpController=t,this.controllerIds=this.getcontrollerIds(),this.isServiceInitialized=!0,this.serviceInitialized.next(this.isServiceInitialized)}getcontrollerIds(){let t=localStorage.getItem("controllerIds");if(t?.length>0){let e=t.split(",");return[...new Set(e)].filter(o=>o&&o.trim().length>0)}return[]}updatecontrollerIds(){localStorage.removeItem("controllerIds"),localStorage.setItem("controllerIds",this.controllerIds.toString())}get(t){let e=JSON.parse(localStorage.getItem(`controller-${t}`));return new Promise(n=>{n(e)})}create(t){if(this.findAllSync().some(r=>r.name===t.name))return Promise.reject(new Error(`Controller with name "${t.name}" already exists`));let o=this.controllerIds.map(r=>parseInt(r.replace("controller-",""),10)).filter(r=>!isNaN(r)),n=o.length>0?Math.max(...o):0;return t.id=n+1,localStorage.setItem(`controller-${t.id}`,JSON.stringify(t)),this.controllerIds.push(`controller-${t.id}`),this.updatecontrollerIds(),new Promise(r=>{r(t)})}findAllSync(){let t=[];return this.controllerIds.forEach(e=>{let o=localStorage.getItem(e);o&&t.push(JSON.parse(o))}),t}isControllerNameTaken(t){return this.findAllSync().some(o=>o.name===t)}update(t){return localStorage.removeItem(`controller-${t.id}`),localStorage.setItem(`controller-${t.id}`,JSON.stringify(t)),new Promise(o=>{o(t)})}findAll(){return new Promise(e=>{let o=[];this.controllerIds.forEach(n=>{let a=localStorage.getItem(n);if(a){let r=JSON.parse(a);o.push(r)}}),e(o)})}delete(t){return localStorage.removeItem(`controller-${t.id}`),this.controllerIds=this.controllerIds.filter(o=>o!==`controller-${t.id}`),this.updatecontrollerIds(),new Promise(o=>{o(t.id)})}getControllerUrl(t){return`${t.protocol}//${t.host}:${t.port}/`}checkControllerVersion(t){return this.httpController.get(t,"/version").pipe(oe(5e3),f(e=>{if(e.name==="TimeoutError"){let o=new Error("Connection timeout");return m(()=>o)}return m(()=>e)}))}getLocalController(t,e){return new Promise((n,a)=>{this.findAll().then(r=>{let d=r.find(c=>c.location==="bundled");if(d)d.host=t,d.port=e,d.protocol=location.protocol,this.update(d).then(c=>{n(c)},a);else{let c=new zt;c.name="local",c.host=t,c.port=e,c.location="bundled",c.protocol=location.protocol,this.create(c).then(C=>{n(C)},a)}},a)})}static \u0275fac=function(e){return new(e||i)(S(Nt))};static \u0275prov=D({token:i,factory:i.\u0275fac})}return i})();var ea=(()=>{class i{http;httpController;controllerService;currentProjectId=null;currentSessionId=null;isStreaming=new ee(!1);constructor(t,e,o){this.http=t,this.httpController=e,this.controllerService=o}injectFault(t,e,o){let n=`${this.getControllerUrl(t)}/v3/copilot/projects/${e}/chat/inject`,a=this.getAuthHeaders(t),r={"Content-Type":"application/json"};return a.keys().forEach(d=>{let c=a.get(d);c&&(r[d]=c)}),new Ht(d=>(fetch(n,{method:"POST",headers:r,body:JSON.stringify({message:o})}).then(async c=>{if(!c.ok){let u=`HTTP error! status: ${c.status}`;try{let b=await c.json();b.message&&(u=b.message)}catch{c.statusText&&(u=c.statusText)}let _=new Error(u);throw _.status=c.status,_.statusText=c.statusText,_.error={message:u},_}if(!c.body)throw new Error("Response body is null");let C=c.body.getReader(),Gt=new TextDecoder,H="";(async()=>{try{for(;;){let{done:u,value:_}=await C.read();if(u){d.complete();break}H+=Gt.decode(_,{stream:!0});let b=H.split(` -`);H=b.pop()||"";for(let at of b)if(at.startsWith("data: ")){let V=at.slice(6).trim();if(V)try{let p=JSON.parse(V);if(p.type==="heartbeat")continue;if(d.next(p),p.type==="done"||p.type==="error"){d.complete();break}}catch(p){console.error("Failed to parse SSE data:",V,p)}}if(d.closed)break}}catch(u){console.error("Stream processing error:",u),d.error(u)}finally{C.cancel()}})()}).catch(c=>{console.error("Fetch error:",c),d.error(c)}),()=>{})).pipe(f(d=>m(()=>d)))}streamChat(t,e,o){this.currentProjectId=e,this.isStreaming.next(!0);let n=`${this.getControllerUrl(t)}/v3/copilot/projects/${e}/chat/stream`,a=this.getAuthHeaders(t),r={"Content-Type":"application/json"};return a.keys().forEach(d=>{let c=a.get(d);c&&(r[d]=c)}),new Ht(d=>(fetch(n,{method:"POST",headers:r,body:JSON.stringify(o)}).then(async c=>{if(!c.ok){let u=`HTTP error! status: ${c.status}`;try{let b=await c.json();b.message&&(u=b.message)}catch{c.statusText&&(u=c.statusText)}let _=new Error(u);throw _.status=c.status,_.statusText=c.statusText,_.error={message:u},_}if(!c.body)throw new Error("Response body is null");let C=c.body.getReader(),Gt=new TextDecoder,H="";(async()=>{try{for(;;){let{done:u,value:_}=await C.read();if(u){d.complete();break}H+=Gt.decode(_,{stream:!0});let b=H.split(` -`);H=b.pop()||"";for(let at of b)if(at.startsWith("data: ")){let V=at.slice(6).trim();if(V)try{let p=JSON.parse(V);if(p.type==="heartbeat")continue;if(p.session_id&&(this.currentSessionId=p.session_id),d.next(p),p.type==="done"||p.type==="error"){d.complete();break}}catch(p){console.error("Failed to parse SSE data:",V,p)}}if(d.closed)break}}catch(u){console.error("Stream processing error:",u),d.error(u)}finally{C.cancel()}})()}).catch(c=>{console.error("Fetch error:",c),d.error(c)}).finally(()=>{this.isStreaming.next(!1)}),()=>{this.isStreaming.next(!1)})).pipe(f(d=>(this.isStreaming.next(!1),m(()=>d))))}getSessions(t,e){return this.httpController.get(t,`/copilot/projects/${e}/chat/sessions`).pipe(f(o=>(console.error("Failed to get sessions:",o),m(()=>o))))}getSessionHistory(t,e,o,n=100){let a=n?{limit:n}:void 0;return this.httpController.get(t,`/copilot/projects/${e}/chat/sessions/${o}/history`).pipe(f(r=>(console.error("Failed to get session history:",r),m(()=>r))))}renameSession(t,e,o,n){let a={title:n};return this.httpController.patch(t,`/copilot/projects/${e}/chat/sessions/${o}`,a).pipe(f(r=>(console.error("Failed to rename session:",r),m(()=>r))))}deleteSession(t,e,o){return this.httpController.delete(t,`/copilot/projects/${e}/chat/sessions/${o}`).pipe(f(n=>(console.error("Failed to delete session:",n),m(()=>n))))}pinSession(t,e,o){return this.httpController.put(t,`/copilot/projects/${e}/chat/sessions/${o}/pin`,null).pipe(f(n=>(console.error("Failed to pin session:",n),m(()=>n))))}unpinSession(t,e,o){return this.httpController.delete(t,`/copilot/projects/${e}/chat/sessions/${o}/pin`).pipe(f(n=>(console.error("Failed to unpin session:",n),m(()=>n))))}getStreamingState(){return this.isStreaming.asObservable()}abortChat(t,e,o){return this.httpController.post(t,`/copilot/projects/${e}/chat/sessions/${o}/abort`,null).pipe(f(n=>(console.error("Failed to abort chat:",n),m(()=>n))))}getCurrentSessionId(){return this.currentSessionId}resetCurrentSession(){this.currentSessionId=null,this.currentProjectId=null}reloadSkills(t){return this.httpController.post(t,"/copilot/reload/skills",null).pipe(f(e=>(console.error("Failed to reload skills:",e),m(()=>e))))}getControllerUrl(t){return`${t.protocol==="https:"?"https":"http"}://${t.host}:${t.port}`}getAuthHeaders(t){let e=new Ce;if(t.authToken)return e.set("Authorization",`Bearer ${t.authToken}`);if(t.username&&t.password){let o=btoa(`${t.username}:${t.password}`);return e.set("Authorization",`Basic ${o}`)}return e}createChatError(t){return t.status===401?{type:nt.UNAUTHORIZED,message:"Unauthorized access",details:t}:t.status===404?{type:nt.SESSION_NOT_FOUND,message:"Session not found",details:t}:t.message&&t.message.includes("fetch")?{type:nt.NETWORK_ERROR,message:"Network connection failed",details:t}:{type:nt.UNKNOWN_ERROR,message:t.message||"Unknown error",details:t}}static \u0275fac=function(e){return new(e||i)(S(At),S(Nt),S(so))};static \u0275prov=D({token:i,factory:i.\u0275fac,providedIn:"root"})}return i})();export{ni as a,ai as b,Qe as c,Ti as d,Di as e,zt as f,so as g,Jt as h,ko as i,Kt as j,Ke as k,mt as l,Oo as m,Yt as n,pn as o,fn as p,_n as q,bn as r,vn as s,Uo as t,ro as u,Vn as v,Wn as w,ea as x}; diff --git a/gns3server/static/web-ui/chunk-LG2N72QL.js b/gns3server/static/web-ui/chunk-LG2N72QL.js deleted file mode 100644 index 6b937fde8..000000000 --- a/gns3server/static/web-ui/chunk-LG2N72QL.js +++ /dev/null @@ -1,14 +0,0 @@ -var kE=Object.create;var fs=Object.defineProperty,FE=Object.defineProperties,PE=Object.getOwnPropertyDescriptor,LE=Object.getOwnPropertyDescriptors,VE=Object.getOwnPropertyNames,ds=Object.getOwnPropertySymbols,jE=Object.getPrototypeOf,El=Object.prototype.hasOwnProperty,Fp=Object.prototype.propertyIsEnumerable;var kp=(e,n,t)=>n in e?fs(e,n,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[n]=t,E=(e,n)=>{for(var t in n||={})El.call(n,t)&&kp(e,t,n[t]);if(ds)for(var t of ds(n))Fp.call(n,t)&&kp(e,t,n[t]);return e},V=(e,n)=>FE(e,LE(n));var BE=(e,n)=>{var t={};for(var r in e)El.call(e,r)&&n.indexOf(r)<0&&(t[r]=e[r]);if(e!=null&&ds)for(var r of ds(e))n.indexOf(r)<0&&Fp.call(e,r)&&(t[r]=e[r]);return t};var zN=(e,n)=>()=>(n||e((n={exports:{}}).exports,n),n.exports),GN=(e,n)=>{for(var t in n)fs(e,t,{get:n[t],enumerable:!0})},HE=(e,n,t,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let o of VE(n))!El.call(e,o)&&o!==t&&fs(e,o,{get:()=>n[o],enumerable:!(r=PE(n,o))||r.enumerable});return e};var WN=(e,n,t)=>(t=e!=null?kE(jE(e)):{},HE(n||!e||!e.__esModule?fs(t,"default",{value:e,enumerable:!0}):t,e));function T(e){return typeof e=="function"}function dn(e){let t=e(r=>{Error.call(r),r.stack=new Error().stack});return t.prototype=Object.create(Error.prototype),t.prototype.constructor=t,t}var hs=dn(e=>function(t){e(this),this.message=t?`${t.length} errors occurred during unsubscription: -${t.map((r,o)=>`${o+1}) ${r.toString()}`).join(` - `)}`:"",this.name="UnsubscriptionError",this.errors=t});function Vn(e,n){if(e){let t=e.indexOf(n);0<=t&&e.splice(t,1)}}var B=class e{constructor(n){this.initialTeardown=n,this.closed=!1,this._parentage=null,this._finalizers=null}unsubscribe(){let n;if(!this.closed){this.closed=!0;let{_parentage:t}=this;if(t)if(this._parentage=null,Array.isArray(t))for(let i of t)i.remove(this);else t.remove(this);let{initialTeardown:r}=this;if(T(r))try{r()}catch(i){n=i instanceof hs?i.errors:[i]}let{_finalizers:o}=this;if(o){this._finalizers=null;for(let i of o)try{Pp(i)}catch(s){n=n??[],s instanceof hs?n=[...n,...s.errors]:n.push(s)}}if(n)throw new hs(n)}}add(n){var t;if(n&&n!==this)if(this.closed)Pp(n);else{if(n instanceof e){if(n.closed||n._hasParent(this))return;n._addParent(this)}(this._finalizers=(t=this._finalizers)!==null&&t!==void 0?t:[]).push(n)}}_hasParent(n){let{_parentage:t}=this;return t===n||Array.isArray(t)&&t.includes(n)}_addParent(n){let{_parentage:t}=this;this._parentage=Array.isArray(t)?(t.push(n),t):t?[t,n]:n}_removeParent(n){let{_parentage:t}=this;t===n?this._parentage=null:Array.isArray(t)&&Vn(t,n)}remove(n){let{_finalizers:t}=this;t&&Vn(t,n),n instanceof e&&n._removeParent(this)}};B.EMPTY=(()=>{let e=new B;return e.closed=!0,e})();var wl=B.EMPTY;function ps(e){return e instanceof B||e&&"closed"in e&&T(e.remove)&&T(e.add)&&T(e.unsubscribe)}function Pp(e){T(e)?e():e.unsubscribe()}var dt={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var Sr={setTimeout(e,n,...t){let{delegate:r}=Sr;return r?.setTimeout?r.setTimeout(e,n,...t):setTimeout(e,n,...t)},clearTimeout(e){let{delegate:n}=Sr;return(n?.clearTimeout||clearTimeout)(e)},delegate:void 0};function ms(e){Sr.setTimeout(()=>{let{onUnhandledError:n}=dt;if(n)n(e);else throw e})}function jn(){}var Lp=Cl("C",void 0,void 0);function Vp(e){return Cl("E",void 0,e)}function jp(e){return Cl("N",e,void 0)}function Cl(e,n,t){return{kind:e,value:n,error:t}}var Bn=null;function Tr(e){if(dt.useDeprecatedSynchronousErrorHandling){let n=!Bn;if(n&&(Bn={errorThrown:!1,error:null}),e(),n){let{errorThrown:t,error:r}=Bn;if(Bn=null,t)throw r}}else e()}function Bp(e){dt.useDeprecatedSynchronousErrorHandling&&Bn&&(Bn.errorThrown=!0,Bn.error=e)}var Hn=class extends B{constructor(n){super(),this.isStopped=!1,n?(this.destination=n,ps(n)&&n.add(this)):this.destination=zE}static create(n,t,r){return new zt(n,t,r)}next(n){this.isStopped?Ml(jp(n),this):this._next(n)}error(n){this.isStopped?Ml(Vp(n),this):(this.isStopped=!0,this._error(n))}complete(){this.isStopped?Ml(Lp,this):(this.isStopped=!0,this._complete())}unsubscribe(){this.closed||(this.isStopped=!0,super.unsubscribe(),this.destination=null)}_next(n){this.destination.next(n)}_error(n){try{this.destination.error(n)}finally{this.unsubscribe()}}_complete(){try{this.destination.complete()}finally{this.unsubscribe()}}},UE=Function.prototype.bind;function Il(e,n){return UE.call(e,n)}var Sl=class{constructor(n){this.partialObserver=n}next(n){let{partialObserver:t}=this;if(t.next)try{t.next(n)}catch(r){gs(r)}}error(n){let{partialObserver:t}=this;if(t.error)try{t.error(n)}catch(r){gs(r)}else gs(n)}complete(){let{partialObserver:n}=this;if(n.complete)try{n.complete()}catch(t){gs(t)}}},zt=class extends Hn{constructor(n,t,r){super();let o;if(T(n)||!n)o={next:n??void 0,error:t??void 0,complete:r??void 0};else{let i;this&&dt.useDeprecatedNextContext?(i=Object.create(n),i.unsubscribe=()=>this.unsubscribe(),o={next:n.next&&Il(n.next,i),error:n.error&&Il(n.error,i),complete:n.complete&&Il(n.complete,i)}):o=n}this.destination=new Sl(o)}};function gs(e){dt.useDeprecatedSynchronousErrorHandling?Bp(e):ms(e)}function $E(e){throw e}function Ml(e,n){let{onStoppedNotification:t}=dt;t&&Sr.setTimeout(()=>t(e,n))}var zE={closed:!0,next:jn,error:$E,complete:jn};var xr=typeof Symbol=="function"&&Symbol.observable||"@@observable";function We(e){return e}function GE(...e){return Tl(e)}function Tl(e){return e.length===0?We:e.length===1?e[0]:function(t){return e.reduce((r,o)=>o(r),t)}}var k=(()=>{class e{constructor(t){t&&(this._subscribe=t)}lift(t){let r=new e;return r.source=this,r.operator=t,r}subscribe(t,r,o){let i=qE(t)?t:new zt(t,r,o);return Tr(()=>{let{operator:s,source:a}=this;i.add(s?s.call(i,a):a?this._subscribe(i):this._trySubscribe(i))}),i}_trySubscribe(t){try{return this._subscribe(t)}catch(r){t.error(r)}}forEach(t,r){return r=Hp(r),new r((o,i)=>{let s=new zt({next:a=>{try{t(a)}catch(c){i(c),s.unsubscribe()}},error:i,complete:o});this.subscribe(s)})}_subscribe(t){var r;return(r=this.source)===null||r===void 0?void 0:r.subscribe(t)}[xr](){return this}pipe(...t){return Tl(t)(this)}toPromise(t){return t=Hp(t),new t((r,o)=>{let i;this.subscribe(s=>i=s,s=>o(s),()=>r(i))})}}return e.create=n=>new e(n),e})();function Hp(e){var n;return(n=e??dt.Promise)!==null&&n!==void 0?n:Promise}function WE(e){return e&&T(e.next)&&T(e.error)&&T(e.complete)}function qE(e){return e&&e instanceof Hn||WE(e)&&ps(e)}var Up=dn(e=>function(){e(this),this.name="ObjectUnsubscribedError",this.message="object unsubscribed"});var R=(()=>{class e extends k{constructor(){super(),this.closed=!1,this.currentObservers=null,this.observers=[],this.isStopped=!1,this.hasError=!1,this.thrownError=null}lift(t){let r=new ys(this,this);return r.operator=t,r}_throwIfClosed(){if(this.closed)throw new Up}next(t){Tr(()=>{if(this._throwIfClosed(),!this.isStopped){this.currentObservers||(this.currentObservers=Array.from(this.observers));for(let r of this.currentObservers)r.next(t)}})}error(t){Tr(()=>{if(this._throwIfClosed(),!this.isStopped){this.hasError=this.isStopped=!0,this.thrownError=t;let{observers:r}=this;for(;r.length;)r.shift().error(t)}})}complete(){Tr(()=>{if(this._throwIfClosed(),!this.isStopped){this.isStopped=!0;let{observers:t}=this;for(;t.length;)t.shift().complete()}})}unsubscribe(){this.isStopped=this.closed=!0,this.observers=this.currentObservers=null}get observed(){var t;return((t=this.observers)===null||t===void 0?void 0:t.length)>0}_trySubscribe(t){return this._throwIfClosed(),super._trySubscribe(t)}_subscribe(t){return this._throwIfClosed(),this._checkFinalizedStatuses(t),this._innerSubscribe(t)}_innerSubscribe(t){let{hasError:r,isStopped:o,observers:i}=this;return r||o?wl:(this.currentObservers=null,i.push(t),new B(()=>{this.currentObservers=null,Vn(i,t)}))}_checkFinalizedStatuses(t){let{hasError:r,thrownError:o,isStopped:i}=this;r?t.error(o):i&&t.complete()}asObservable(){let t=new k;return t.source=this,t}}return e.create=(n,t)=>new ys(n,t),e})(),ys=class extends R{constructor(n,t){super(),this.destination=n,this.source=t}next(n){var t,r;(r=(t=this.destination)===null||t===void 0?void 0:t.next)===null||r===void 0||r.call(t,n)}error(n){var t,r;(r=(t=this.destination)===null||t===void 0?void 0:t.error)===null||r===void 0||r.call(t,n)}complete(){var n,t;(t=(n=this.destination)===null||n===void 0?void 0:n.complete)===null||t===void 0||t.call(n)}_subscribe(n){var t,r;return(r=(t=this.source)===null||t===void 0?void 0:t.subscribe(n))!==null&&r!==void 0?r:wl}};function xl(e){return T(e?.lift)}function A(e){return n=>{if(xl(n))return n.lift(function(t){try{return e(t,this)}catch(r){this.error(r)}});throw new TypeError("Unable to lift unknown Observable type")}}function x(e,n,t,r,o){return new Al(e,n,t,r,o)}var Al=class extends Hn{constructor(n,t,r,o,i,s){super(n),this.onFinalize=i,this.shouldUnsubscribe=s,this._next=t?function(a){try{t(a)}catch(c){n.error(c)}}:super._next,this._error=o?function(a){try{o(a)}catch(c){n.error(c)}finally{this.unsubscribe()}}:super._error,this._complete=r?function(){try{r()}catch(a){n.error(a)}finally{this.unsubscribe()}}:super._complete}unsubscribe(){var n;if(!this.shouldUnsubscribe||this.shouldUnsubscribe()){let{closed:t}=this;super.unsubscribe(),!t&&((n=this.onFinalize)===null||n===void 0||n.call(this))}}};function zp(e,n,t,r){function o(i){return i instanceof t?i:new t(function(s){s(i)})}return new(t||(t=Promise))(function(i,s){function a(u){try{l(r.next(u))}catch(d){s(d)}}function c(u){try{l(r.throw(u))}catch(d){s(d)}}function l(u){u.done?i(u.value):o(u.value).then(a,c)}l((r=r.apply(e,n||[])).next())})}function $p(e){var n=typeof Symbol=="function"&&Symbol.iterator,t=n&&e[n],r=0;if(t)return t.call(e);if(e&&typeof e.length=="number")return{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(n?"Object is not iterable.":"Symbol.iterator is not defined.")}function Un(e){return this instanceof Un?(this.v=e,this):new Un(e)}function Gp(e,n,t){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var r=t.apply(e,n||[]),o,i=[];return o=Object.create((typeof AsyncIterator=="function"?AsyncIterator:Object).prototype),a("next"),a("throw"),a("return",s),o[Symbol.asyncIterator]=function(){return this},o;function s(h){return function(m){return Promise.resolve(m).then(h,d)}}function a(h,m){r[h]&&(o[h]=function(b){return new Promise(function(_,C){i.push([h,b,_,C])>1||c(h,b)})},m&&(o[h]=m(o[h])))}function c(h,m){try{l(r[h](m))}catch(b){p(i[0][3],b)}}function l(h){h.value instanceof Un?Promise.resolve(h.value.v).then(u,d):p(i[0][2],h)}function u(h){c("next",h)}function d(h){c("throw",h)}function p(h,m){h(m),i.shift(),i.length&&c(i[0][0],i[0][1])}}function Wp(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var n=e[Symbol.asyncIterator],t;return n?n.call(e):(e=typeof $p=="function"?$p(e):e[Symbol.iterator](),t={},r("next"),r("throw"),r("return"),t[Symbol.asyncIterator]=function(){return this},t);function r(i){t[i]=e[i]&&function(s){return new Promise(function(a,c){s=e[i](s),o(a,c,s.done,s.value)})}}function o(i,s,a,c){Promise.resolve(c).then(function(l){i({value:l,done:a})},s)}}var Ar=e=>e&&typeof e.length=="number"&&typeof e!="function";function vs(e){return T(e?.then)}function bs(e){return T(e[xr])}function _s(e){return Symbol.asyncIterator&&T(e?.[Symbol.asyncIterator])}function Ds(e){return new TypeError(`You provided ${e!==null&&typeof e=="object"?"an invalid object":`'${e}'`} where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`)}function YE(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Es=YE();function ws(e){return T(e?.[Es])}function Cs(e){return Gp(this,arguments,function*(){let t=e.getReader();try{for(;;){let{value:r,done:o}=yield Un(t.read());if(o)return yield Un(void 0);yield yield Un(r)}}finally{t.releaseLock()}})}function Is(e){return T(e?.getReader)}function U(e){if(e instanceof k)return e;if(e!=null){if(bs(e))return ZE(e);if(Ar(e))return KE(e);if(vs(e))return XE(e);if(_s(e))return qp(e);if(ws(e))return QE(e);if(Is(e))return JE(e)}throw Ds(e)}function ZE(e){return new k(n=>{let t=e[xr]();if(T(t.subscribe))return t.subscribe(n);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function KE(e){return new k(n=>{for(let t=0;t{e.then(t=>{n.closed||(n.next(t),n.complete())},t=>n.error(t)).then(null,ms)})}function QE(e){return new k(n=>{for(let t of e)if(n.next(t),n.closed)return;n.complete()})}function qp(e){return new k(n=>{ew(e,n).catch(t=>n.error(t))})}function JE(e){return qp(Cs(e))}function ew(e,n){var t,r,o,i;return zp(this,void 0,void 0,function*(){try{for(t=Wp(e);r=yield t.next(),!r.done;){let s=r.value;if(n.next(s),n.closed)return}}catch(s){o={error:s}}finally{try{r&&!r.done&&(i=t.return)&&(yield i.call(t))}finally{if(o)throw o.error}}n.complete()})}function $n(e){return A((n,t)=>{U(e).subscribe(x(t,()=>t.complete(),jn)),!t.closed&&n.subscribe(t)})}function Yp(){return A((e,n)=>{let t=null;e._refCount++;let r=x(n,void 0,void 0,void 0,()=>{if(!e||e._refCount<=0||0<--e._refCount){t=null;return}let o=e._connection,i=t;t=null,o&&(!i||o===i)&&o.unsubscribe(),n.unsubscribe()});e.subscribe(r),r.closed||(t=e.connect())})}var Oo=class extends k{constructor(n,t){super(),this.source=n,this.subjectFactory=t,this._subject=null,this._refCount=0,this._connection=null,xl(n)&&(this.lift=n.lift)}_subscribe(n){return this.getSubject().subscribe(n)}getSubject(){let n=this._subject;return(!n||n.isStopped)&&(this._subject=this.subjectFactory()),this._subject}_teardown(){this._refCount=0;let{_connection:n}=this;this._subject=this._connection=null,n?.unsubscribe()}connect(){let n=this._connection;if(!n){n=this._connection=new B;let t=this.getSubject();n.add(this.source.subscribe(x(t,void 0,()=>{this._teardown(),t.complete()},r=>{this._teardown(),t.error(r)},()=>this._teardown()))),n.closed&&(this._connection=null,n=B.EMPTY)}return n}refCount(){return Yp()(this)}};var Nr={schedule(e){let n=requestAnimationFrame,t=cancelAnimationFrame,{delegate:r}=Nr;r&&(n=r.requestAnimationFrame,t=r.cancelAnimationFrame);let o=n(i=>{t=void 0,e(i)});return new B(()=>t?.(o))},requestAnimationFrame(...e){let{delegate:n}=Nr;return(n?.requestAnimationFrame||requestAnimationFrame)(...e)},cancelAnimationFrame(...e){let{delegate:n}=Nr;return(n?.cancelAnimationFrame||cancelAnimationFrame)(...e)},delegate:void 0};var zn=class extends R{constructor(n){super(),this._value=n}get value(){return this.getValue()}_subscribe(n){let t=super._subscribe(n);return!t.closed&&n.next(this._value),t}getValue(){let{hasError:n,thrownError:t,_value:r}=this;if(n)throw t;return this._throwIfClosed(),r}next(n){super.next(this._value=n)}};var ko={now(){return(ko.delegate||Date).now()},delegate:void 0};var Fo=class extends R{constructor(n=1/0,t=1/0,r=ko){super(),this._bufferSize=n,this._windowTime=t,this._timestampProvider=r,this._buffer=[],this._infiniteTimeWindow=!0,this._infiniteTimeWindow=t===1/0,this._bufferSize=Math.max(1,n),this._windowTime=Math.max(1,t)}next(n){let{isStopped:t,_buffer:r,_infiniteTimeWindow:o,_timestampProvider:i,_windowTime:s}=this;t||(r.push(n),!o&&r.push(i.now()+s)),this._trimBuffer(),super.next(n)}_subscribe(n){this._throwIfClosed(),this._trimBuffer();let t=this._innerSubscribe(n),{_infiniteTimeWindow:r,_buffer:o}=this,i=o.slice();for(let s=0;sZp(n)&&e()),n},clearImmediate(e){Zp(e)}};var{setImmediate:nw,clearImmediate:rw}=Kp,Lo={setImmediate(...e){let{delegate:n}=Lo;return(n?.setImmediate||nw)(...e)},clearImmediate(e){let{delegate:n}=Lo;return(n?.clearImmediate||rw)(e)},delegate:void 0};var Ss=class extends fn{constructor(n,t){super(n,t),this.scheduler=n,this.work=t}requestAsyncId(n,t,r=0){return r!==null&&r>0?super.requestAsyncId(n,t,r):(n.actions.push(this),n._scheduled||(n._scheduled=Lo.setImmediate(n.flush.bind(n,void 0))))}recycleAsyncId(n,t,r=0){var o;if(r!=null?r>0:this.delay>0)return super.recycleAsyncId(n,t,r);let{actions:i}=n;t!=null&&((o=i[i.length-1])===null||o===void 0?void 0:o.id)!==t&&(Lo.clearImmediate(t),n._scheduled===t&&(n._scheduled=void 0))}};var Rr=class e{constructor(n,t=e.now){this.schedulerActionCtor=n,this.now=t}schedule(n,t=0,r){return new this.schedulerActionCtor(this,n).schedule(r,t)}};Rr.now=ko.now;var hn=class extends Rr{constructor(n,t=Rr.now){super(n,t),this.actions=[],this._active=!1}flush(n){let{actions:t}=this;if(this._active){t.push(n);return}let r;this._active=!0;do if(r=n.execute(n.state,n.delay))break;while(n=t.shift());if(this._active=!1,r){for(;n=t.shift();)n.unsubscribe();throw r}}};var Ts=class extends hn{flush(n){this._active=!0;let t=this._scheduled;this._scheduled=void 0;let{actions:r}=this,o;n=n||r.shift();do if(o=n.execute(n.state,n.delay))break;while((n=r[0])&&n.id===t&&r.shift());if(this._active=!1,o){for(;(n=r[0])&&n.id===t&&r.shift();)n.unsubscribe();throw o}}};var Xp=new Ts(Ss);var ft=new hn(fn),Qp=ft;var xs=class extends fn{constructor(n,t){super(n,t),this.scheduler=n,this.work=t}requestAsyncId(n,t,r=0){return r!==null&&r>0?super.requestAsyncId(n,t,r):(n.actions.push(this),n._scheduled||(n._scheduled=Nr.requestAnimationFrame(()=>n.flush(void 0))))}recycleAsyncId(n,t,r=0){var o;if(r!=null?r>0:this.delay>0)return super.recycleAsyncId(n,t,r);let{actions:i}=n;t!=null&&t===n._scheduled&&((o=i[i.length-1])===null||o===void 0?void 0:o.id)!==t&&(Nr.cancelAnimationFrame(t),n._scheduled=void 0)}};var As=class extends hn{flush(n){this._active=!0;let t;n?t=n.id:(t=this._scheduled,this._scheduled=void 0);let{actions:r}=this,o;n=n||r.shift();do if(o=n.execute(n.state,n.delay))break;while((n=r[0])&&n.id===t&&r.shift());if(this._active=!1,o){for(;(n=r[0])&&n.id===t&&r.shift();)n.unsubscribe();throw o}}};var Jp=new As(xs);var Gn=new k(e=>e.complete());function Ns(e){return e&&T(e.schedule)}function Ol(e){return e[e.length-1]}function Rs(e){return T(Ol(e))?e.pop():void 0}function xt(e){return Ns(Ol(e))?e.pop():void 0}function em(e,n){return typeof Ol(e)=="number"?e.pop():n}function De(e,n,t,r=0,o=!1){let i=n.schedule(function(){t(),o?e.add(this.schedule(null,r)):this.unsubscribe()},r);if(e.add(i),!o)return i}function Os(e,n=0){return A((t,r)=>{t.subscribe(x(r,o=>De(r,e,()=>r.next(o),n),()=>De(r,e,()=>r.complete(),n),o=>De(r,e,()=>r.error(o),n)))})}function ks(e,n=0){return A((t,r)=>{r.add(e.schedule(()=>t.subscribe(r),n))})}function tm(e,n){return U(e).pipe(ks(n),Os(n))}function nm(e,n){return U(e).pipe(ks(n),Os(n))}function rm(e,n){return new k(t=>{let r=0;return n.schedule(function(){r===e.length?t.complete():(t.next(e[r++]),t.closed||this.schedule())})})}function om(e,n){return new k(t=>{let r;return De(t,n,()=>{r=e[Es](),De(t,n,()=>{let o,i;try{({value:o,done:i}=r.next())}catch(s){t.error(s);return}i?t.complete():t.next(o)},0,!0)}),()=>T(r?.return)&&r.return()})}function Fs(e,n){if(!e)throw new Error("Iterable cannot be null");return new k(t=>{De(t,n,()=>{let r=e[Symbol.asyncIterator]();De(t,n,()=>{r.next().then(o=>{o.done?t.complete():t.next(o.value)})},0,!0)})})}function im(e,n){return Fs(Cs(e),n)}function sm(e,n){if(e!=null){if(bs(e))return tm(e,n);if(Ar(e))return rm(e,n);if(vs(e))return nm(e,n);if(_s(e))return Fs(e,n);if(ws(e))return om(e,n);if(Is(e))return im(e,n)}throw Ds(e)}function tt(e,n){return n?sm(e,n):U(e)}function ke(...e){let n=xt(e);return tt(e,n)}function kl(e,n){let t=T(e)?e:()=>e,r=o=>o.error(t());return new k(n?o=>n.schedule(r,0,o):r)}function pn(e){return!!e&&(e instanceof k||T(e.lift)&&T(e.subscribe))}var Vo=dn(e=>function(){e(this),this.name="EmptyError",this.message="no elements in sequence"});function Ps(e){return e instanceof Date&&!isNaN(e)}var ow=dn(e=>function(t=null){e(this),this.message="Timeout has occurred",this.name="TimeoutError",this.info=t});function iw(e,n){let{first:t,each:r,with:o=sw,scheduler:i=n??ft,meta:s=null}=Ps(e)?{first:e}:typeof e=="number"?{each:e}:e;if(t==null&&r==null)throw new TypeError("No timeout provided.");return A((a,c)=>{let l,u,d=null,p=0,h=m=>{u=De(c,i,()=>{try{l.unsubscribe(),U(o({meta:s,lastValue:d,seen:p})).subscribe(c)}catch(b){c.error(b)}},m)};l=a.subscribe(x(c,m=>{u?.unsubscribe(),p++,c.next(d=m),r>0&&h(r)},void 0,void 0,()=>{u?.closed||u?.unsubscribe(),d=null})),!p&&h(t!=null?typeof t=="number"?t:+t-i.now():r)})}function sw(e){throw new ow(e)}function re(e,n){return A((t,r)=>{let o=0;t.subscribe(x(r,i=>{r.next(e.call(n,i,o++))}))})}var{isArray:aw}=Array;function cw(e,n){return aw(n)?e(...n):e(n)}function Or(e){return re(n=>cw(e,n))}var{isArray:lw}=Array,{getPrototypeOf:uw,prototype:dw,keys:fw}=Object;function Ls(e){if(e.length===1){let n=e[0];if(lw(n))return{args:n,keys:null};if(hw(n)){let t=fw(n);return{args:t.map(r=>n[r]),keys:t}}}return{args:e,keys:null}}function hw(e){return e&&typeof e=="object"&&uw(e)===dw}function Vs(e,n){return e.reduce((t,r,o)=>(t[r]=n[o],t),{})}function Fl(...e){let n=xt(e),t=Rs(e),{args:r,keys:o}=Ls(e);if(r.length===0)return tt([],n);let i=new k(pw(r,n,o?s=>Vs(o,s):We));return t?i.pipe(Or(t)):i}function pw(e,n,t=We){return r=>{am(n,()=>{let{length:o}=e,i=new Array(o),s=o,a=o;for(let c=0;c{let l=tt(e[c],n),u=!1;l.subscribe(x(r,d=>{i[c]=d,u||(u=!0,a--),a||r.next(t(i.slice()))},()=>{--s||r.complete()}))},r)},r)}}function am(e,n,t){e?De(t,e,n):n()}function cm(e,n,t,r,o,i,s,a){let c=[],l=0,u=0,d=!1,p=()=>{d&&!c.length&&!l&&n.complete()},h=b=>l{i&&n.next(b),l++;let _=!1;U(t(b,u++)).subscribe(x(n,C=>{o?.(C),i?h(C):n.next(C)},()=>{_=!0},void 0,()=>{if(_)try{for(l--;c.length&&lm(C)):m(C)}p()}catch(C){n.error(C)}}))};return e.subscribe(x(n,h,()=>{d=!0,p()})),()=>{a?.()}}function ht(e,n,t=1/0){return T(n)?ht((r,o)=>re((i,s)=>n(r,i,o,s))(U(e(r,o))),t):(typeof n=="number"&&(t=n),A((r,o)=>cm(r,o,e,t)))}function jo(e=1/0){return ht(We,e)}function lm(){return jo(1)}function mn(...e){return lm()(tt(e,xt(e)))}function mw(e){return new k(n=>{U(e()).subscribe(n)})}function Pl(...e){let n=Rs(e),{args:t,keys:r}=Ls(e),o=new k(i=>{let{length:s}=t;if(!s){i.complete();return}let a=new Array(s),c=s,l=s;for(let u=0;u{d||(d=!0,l--),a[u]=p},()=>c--,void 0,()=>{(!c||!d)&&(l||i.next(r?Vs(r,a):a),i.complete())}))}});return n?o.pipe(Or(n)):o}var gw=["addListener","removeListener"],yw=["addEventListener","removeEventListener"],vw=["on","off"];function Ll(e,n,t,r){if(T(t)&&(r=t,t=void 0),r)return Ll(e,n,t).pipe(Or(r));let[o,i]=Dw(e)?yw.map(s=>a=>e[s](n,a,t)):bw(e)?gw.map(um(e,n)):_w(e)?vw.map(um(e,n)):[];if(!o&&Ar(e))return ht(s=>Ll(s,n,t))(U(e));if(!o)throw new TypeError("Invalid event target");return new k(s=>{let a=(...c)=>s.next(1i(a)})}function um(e,n){return t=>r=>e[t](n,r)}function bw(e){return T(e.addListener)&&T(e.removeListener)}function _w(e){return T(e.on)&&T(e.off)}function Dw(e){return T(e.addEventListener)&&T(e.removeEventListener)}function Wn(e=0,n,t=Qp){let r=-1;return n!=null&&(Ns(n)?t=n:r=n),new k(o=>{let i=Ps(e)?+e-t.now():e;i<0&&(i=0);let s=0;return t.schedule(function(){o.closed||(o.next(s++),0<=r?this.schedule(void 0,r):o.complete())},i)})}function Ew(e=0,n=ft){return e<0&&(e=0),Wn(e,e,n)}function ww(...e){let n=xt(e),t=em(e,1/0),r=e;return r.length?r.length===1?U(r[0]):jo(t)(tt(r,n)):Gn}function Ee(e,n){return A((t,r)=>{let o=0;t.subscribe(x(r,i=>e.call(n,i,o++)&&r.next(i)))})}function dm(e){return A((n,t)=>{let r=!1,o=null,i=null,s=!1,a=()=>{if(i?.unsubscribe(),i=null,r){r=!1;let l=o;o=null,t.next(l)}s&&t.complete()},c=()=>{i=null,s&&t.complete()};n.subscribe(x(t,l=>{r=!0,o=l,i||U(e(l)).subscribe(i=x(t,a,c))},()=>{s=!0,(!r||!i||i.closed)&&t.complete()}))})}function js(e,n=ft){return dm(()=>Wn(e,n))}function Fe(e){return A((n,t)=>{let r=null,o=!1,i;r=n.subscribe(x(t,void 0,void 0,s=>{i=U(e(s,Fe(e)(n))),r?(r.unsubscribe(),r=null,i.subscribe(t)):o=!0})),o&&(r.unsubscribe(),r=null,i.subscribe(t))})}function fm(e,n,t,r,o){return(i,s)=>{let a=t,c=n,l=0;i.subscribe(x(s,u=>{let d=l++;c=a?e(c,u,d):(a=!0,u),r&&s.next(c)},o&&(()=>{a&&s.next(c),s.complete()})))}}function Vl(e,n){return A(fm(e,n,arguments.length>=2,!1,!0))}function jl(e,n){return T(n)?ht(e,n,1):ht(e,1)}function Cw(e){return Vl((n,t,r)=>!e||e(t,r)?n+1:n,0)}function qn(e,n=ft){return A((t,r)=>{let o=null,i=null,s=null,a=()=>{if(o){o.unsubscribe(),o=null;let l=i;i=null,r.next(l)}};function c(){let l=s+e,u=n.now();if(u{i=l,s=n.now(),o||(o=n.schedule(c,e),r.add(o))},()=>{a(),r.complete()},void 0,()=>{i=o=null}))})}function hm(e){return A((n,t)=>{let r=!1;n.subscribe(x(t,o=>{r=!0,t.next(o)},()=>{r||t.next(e),t.complete()}))})}function pt(e){return e<=0?()=>Gn:A((n,t)=>{let r=0;n.subscribe(x(t,o=>{++r<=e&&(t.next(o),e<=r&&t.complete())}))})}function pm(){return A((e,n)=>{e.subscribe(x(n,jn))})}function Bl(e){return re(()=>e)}function Hl(e,n){return n?t=>mn(n.pipe(pt(1),pm()),t.pipe(Hl(e))):ht((t,r)=>U(e(t,r)).pipe(pt(1),Bl(t)))}function Iw(e,n=ft){let t=Wn(e,n);return Hl(()=>t)}function Bs(e,n=We){return e=e??Mw,A((t,r)=>{let o,i=!0;t.subscribe(x(r,s=>{let a=n(s);(i||!e(o,a))&&(i=!1,o=a,r.next(s))}))})}function Mw(e,n){return e===n}function mm(e=Sw){return A((n,t)=>{let r=!1;n.subscribe(x(t,o=>{r=!0,t.next(o)},()=>r?t.complete():t.error(e())))})}function Sw(){return new Vo}function Hs(e){return A((n,t)=>{try{n.subscribe(t)}finally{t.add(e)}})}function Tw(e,n){let t=arguments.length>=2;return r=>r.pipe(e?Ee((o,i)=>e(o,i,r)):We,pt(1),t?hm(n):mm(()=>new Vo))}function xw(e){return e<=0?()=>Gn:A((n,t)=>{let r=[];n.subscribe(x(t,o=>{r.push(o),e{for(let o of r)t.next(o);t.complete()},void 0,()=>{r=null}))})}function gm(){return A((e,n)=>{let t,r=!1;e.subscribe(x(n,o=>{let i=t;t=o,r&&n.next([i,o]),r=!0}))})}function $l(e={}){let{connector:n=()=>new R,resetOnError:t=!0,resetOnComplete:r=!0,resetOnRefCountZero:o=!0}=e;return i=>{let s,a,c,l=0,u=!1,d=!1,p=()=>{a?.unsubscribe(),a=void 0},h=()=>{p(),s=c=void 0,u=d=!1},m=()=>{let b=s;h(),b?.unsubscribe()};return A((b,_)=>{l++,!d&&!u&&p();let C=c=c??n();_.add(()=>{l--,l===0&&!d&&!u&&(a=Ul(m,o))}),C.subscribe(_),!s&&l>0&&(s=new zt({next:ne=>C.next(ne),error:ne=>{d=!0,p(),a=Ul(h,t,ne),C.error(ne)},complete:()=>{u=!0,p(),a=Ul(h,r),C.complete()}}),U(b).subscribe(s))})(i)}}function Ul(e,n,...t){if(n===!0){e();return}if(n===!1)return;let r=new zt({next:()=>{r.unsubscribe(),e()}});return U(n(...t)).subscribe(r)}function ym(e,n,t){let r,o=!1;return e&&typeof e=="object"?{bufferSize:r=1/0,windowTime:n=1/0,refCount:o=!1,scheduler:t}=e:r=e??1/0,$l({connector:()=>new Fo(r,n,t),resetOnError:!0,resetOnComplete:!1,resetOnRefCountZero:o})}function Bo(e){return Ee((n,t)=>e<=t)}function Us(...e){let n=xt(e);return A((t,r)=>{(n?mn(e,t,n):mn(e,t)).subscribe(r)})}function $s(e,n){return A((t,r)=>{let o=null,i=0,s=!1,a=()=>s&&!o&&r.complete();t.subscribe(x(r,c=>{o?.unsubscribe();let l=0,u=i++;U(e(c,u)).subscribe(o=x(r,d=>r.next(n?n(c,d,u,l++):d),()=>{o=null,a()}))},()=>{s=!0,a()}))})}function zl(e,n=!1){return A((t,r)=>{let o=0;t.subscribe(x(r,i=>{let s=e(i,o++);(s||n)&&r.next(i),!s&&r.complete()}))})}function Gl(e,n,t){let r=T(e)||n||t?{next:e,error:n,complete:t}:e;return r?A((o,i)=>{var s;(s=r.subscribe)===null||s===void 0||s.call(r);let a=!0;o.subscribe(x(i,c=>{var l;(l=r.next)===null||l===void 0||l.call(r,c),i.next(c)},()=>{var c;a=!1,(c=r.complete)===null||c===void 0||c.call(r),i.complete()},c=>{var l;a=!1,(l=r.error)===null||l===void 0||l.call(r,c),i.error(c)},()=>{var c,l;a&&((c=r.unsubscribe)===null||c===void 0||c.call(r)),(l=r.finalize)===null||l===void 0||l.call(r)}))}):We}var we=null,zs=!1,Wl=1,Aw=null,oe=Symbol("SIGNAL");function M(e){let n=we;return we=e,n}function Gs(){return we}var gn={version:0,lastCleanEpoch:0,dirty:!1,producers:void 0,producersTail:void 0,consumers:void 0,consumersTail:void 0,recomputing:!1,consumerAllowSignalWrites:!1,consumerIsAlwaysLive:!1,kind:"unknown",producerMustRecompute:()=>!1,producerRecomputeValue:()=>{},consumerMarkedDirty:()=>{},consumerOnSignalRead:()=>{}};function Gt(e){if(zs)throw new Error("");if(we===null)return;we.consumerOnSignalRead(e);let n=we.producersTail;if(n!==void 0&&n.producer===e)return;let t,r=we.recomputing;if(r&&(t=n!==void 0?n.nextProducer:we.producers,t!==void 0&&t.producer===e)){we.producersTail=t,t.lastReadVersion=e.version;return}let o=e.consumersTail;if(o!==void 0&&o.consumer===we&&(!r||Rw(o,we)))return;let i=Pr(we),s={producer:e,consumer:we,nextProducer:t,prevConsumer:o,lastReadVersion:e.version,nextConsumer:void 0};we.producersTail=s,n!==void 0?n.nextProducer=s:we.producers=s,i&&Dm(e,s)}function vm(){Wl++}function Kn(e){if(!(Pr(e)&&!e.dirty)&&!(!e.dirty&&e.lastCleanEpoch===Wl)){if(!e.producerMustRecompute(e)&&!Fr(e)){kr(e);return}e.producerRecomputeValue(e),kr(e)}}function ql(e){if(e.consumers===void 0)return;let n=zs;zs=!0;try{for(let t=e.consumers;t!==void 0;t=t.nextConsumer){let r=t.consumer;r.dirty||Nw(r)}}finally{zs=n}}function Yl(){return we?.consumerAllowSignalWrites!==!1}function Nw(e){e.dirty=!0,ql(e),e.consumerMarkedDirty?.(e)}function kr(e){e.dirty=!1,e.lastCleanEpoch=Wl}function Wt(e){return e&&bm(e),M(e)}function bm(e){e.producersTail=void 0,e.recomputing=!0}function yn(e,n){M(n),e&&_m(e)}function _m(e){e.recomputing=!1;let n=e.producersTail,t=n!==void 0?n.nextProducer:e.producers;if(t!==void 0){if(Pr(e))do t=Zl(t);while(t!==void 0);n!==void 0?n.nextProducer=void 0:e.producers=void 0}}function Fr(e){for(let n=e.producers;n!==void 0;n=n.nextProducer){let t=n.producer,r=n.lastReadVersion;if(r!==t.version||(Kn(t),r!==t.version))return!0}return!1}function vn(e){if(Pr(e)){let n=e.producers;for(;n!==void 0;)n=Zl(n)}e.producers=void 0,e.producersTail=void 0,e.consumers=void 0,e.consumersTail=void 0}function Dm(e,n){let t=e.consumersTail,r=Pr(e);if(t!==void 0?(n.nextConsumer=t.nextConsumer,t.nextConsumer=n):(n.nextConsumer=void 0,e.consumers=n),n.prevConsumer=t,e.consumersTail=n,!r)for(let o=e.producers;o!==void 0;o=o.nextProducer)Dm(o.producer,o)}function Zl(e){let n=e.producer,t=e.nextProducer,r=e.nextConsumer,o=e.prevConsumer;if(e.nextConsumer=void 0,e.prevConsumer=void 0,r!==void 0?r.prevConsumer=o:n.consumersTail=o,o!==void 0)o.nextConsumer=r;else if(n.consumers=r,!Pr(n)){let i=n.producers;for(;i!==void 0;)i=Zl(i)}return t}function Pr(e){return e.consumerIsAlwaysLive||e.consumers!==void 0}function Ho(e){Aw?.(e)}function Rw(e,n){let t=n.producersTail;if(t!==void 0){let r=n.producers;do{if(r===e)return!0;if(r===t)break;r=r.nextProducer}while(r!==void 0)}return!1}function Uo(e,n){return Object.is(e,n)}function $o(e,n){let t=Object.create(Ow);t.computation=e,n!==void 0&&(t.equal=n);let r=()=>{if(Kn(t),Gt(t),t.value===At)throw t.error;return t.value};return r[oe]=t,Ho(t),r}var Yn=Symbol("UNSET"),Zn=Symbol("COMPUTING"),At=Symbol("ERRORED"),Ow=V(E({},gn),{value:Yn,dirty:!0,error:null,equal:Uo,kind:"computed",producerMustRecompute(e){return e.value===Yn||e.value===Zn},producerRecomputeValue(e){if(e.value===Zn)throw new Error("");let n=e.value;e.value=Zn;let t=Wt(e),r,o=!1;try{r=e.computation(),M(null),o=n!==Yn&&n!==At&&r!==At&&e.equal(n,r)}catch(i){r=At,e.error=i}finally{yn(e,t)}if(o){e.value=n;return}e.value=r,e.version++}});function kw(){throw new Error}var Em=kw;function wm(e){Em(e)}function Kl(e){Em=e}var Fw=null;function Xl(e,n){let t=Object.create(zo);t.value=e,n!==void 0&&(t.equal=n);let r=()=>Cm(t);return r[oe]=t,Ho(t),[r,s=>bn(t,s),s=>Ws(t,s)]}function Cm(e){return Gt(e),e.value}function bn(e,n){Yl()||wm(e),e.equal(e.value,n)||(e.value=n,Pw(e))}function Ws(e,n){Yl()||wm(e),bn(e,n(e.value))}var zo=V(E({},gn),{equal:Uo,value:void 0,kind:"signal"});function Pw(e){e.version++,vm(),ql(e),Fw?.(e)}var Ql=V(E({},gn),{consumerIsAlwaysLive:!0,consumerAllowSignalWrites:!0,dirty:!0,kind:"effect"});function Jl(e){if(e.dirty=!1,e.version>0&&!Fr(e))return;e.version++;let n=Wt(e);try{e.cleanup(),e.fn()}finally{yn(e,n)}}var eu;function qs(){return eu}function Nt(e){let n=eu;return eu=e,n}var Im=Symbol("NotFound");function Lr(e){return e===Im||e?.name==="\u0275NotFound"}function tu(e,n,t){let r=Object.create(Lw);r.source=e,r.computation=n,t!=null&&(r.equal=t);let i=()=>{if(Kn(r),Gt(r),r.value===At)throw r.error;return r.value};return i[oe]=r,Ho(r),i}function Mm(e,n){Kn(e),bn(e,n),kr(e)}function Sm(e,n){if(Kn(e),e.value===At)throw e.error;Ws(e,n),kr(e)}var Lw=V(E({},gn),{value:Yn,dirty:!0,error:null,equal:Uo,kind:"linkedSignal",producerMustRecompute(e){return e.value===Yn||e.value===Zn},producerRecomputeValue(e){if(e.value===Zn)throw new Error("");let n=e.value;e.value=Zn;let t=Wt(e),r,o=!1;try{let i=e.source(),s=n!==Yn&&n!==At,a=s?{source:e.sourceValue,value:n}:void 0;r=e.computation(i,a),e.sourceValue=i,M(null),o=s&&r!==At&&e.equal(n,r)}catch(i){r=At,e.error=i}finally{yn(e,t)}if(o){e.value=n;return}e.value=r,e.version++}});function Tm(e){let n=M(null);try{return e()}finally{M(n)}}var ea="https://angular.dev/best-practices/security#preventing-cross-site-scripting-xss",v=class extends Error{code;constructor(n,t){super(Ot(n,t)),this.code=n}};function Vw(e){return`NG0${Math.abs(e)}`}function Ot(e,n){return`${Vw(e)}${n?": "+n:""}`}var le=globalThis;function q(e){for(let n in e)if(e[n]===q)return n;throw Error("")}function Om(e,n){for(let t in n)n.hasOwnProperty(t)&&!e.hasOwnProperty(t)&&(e[t]=n[t])}function Xo(e){if(typeof e=="string")return e;if(Array.isArray(e))return`[${e.map(Xo).join(", ")}]`;if(e==null)return""+e;let n=e.overriddenName||e.name;if(n)return`${n}`;let t=e.toString();if(t==null)return""+t;let r=t.indexOf(` -`);return r>=0?t.slice(0,r):t}function ta(e,n){return e?n?`${e} ${n}`:e:n||""}var jw=q({__forward_ref__:q});function ve(e){return e.__forward_ref__=ve,e}function me(e){return pu(e)?e():e}function pu(e){return typeof e=="function"&&e.hasOwnProperty(jw)&&e.__forward_ref__===ve}function g(e){return{token:e.token,providedIn:e.providedIn||null,factory:e.factory,value:void 0}}function G(e){return{providers:e.providers||[],imports:e.imports||[]}}function Qo(e){return Hw(e,na)}function Bw(e){return Qo(e)!==null}function Hw(e,n){return e.hasOwnProperty(n)&&e[n]||null}function Uw(e){let n=e?.[na]??null;return n||null}function ru(e){return e&&e.hasOwnProperty(Zs)?e[Zs]:null}var na=q({\u0275prov:q}),Zs=q({\u0275inj:q}),y=class{_desc;ngMetadataName="InjectionToken";\u0275prov;constructor(n,t){this._desc=n,this.\u0275prov=void 0,typeof t=="number"?this.__NG_ELEMENT_ID__=t:t!==void 0&&(this.\u0275prov=g({token:this,providedIn:t.providedIn||"root",factory:t.factory}))}get multi(){return this}toString(){return`InjectionToken ${this._desc}`}};function mu(e){return e&&!!e.\u0275providers}var gu=q({\u0275cmp:q}),yu=q({\u0275dir:q}),vu=q({\u0275pipe:q}),bu=q({\u0275mod:q}),Wo=q({\u0275fac:q}),tr=q({__NG_ELEMENT_ID__:q}),xm=q({__NG_ENV_ID__:q});function _u(e){return oa(e,"@NgModule"),e[bu]||null}function kt(e){return oa(e,"@Component"),e[gu]||null}function ra(e){return oa(e,"@Directive"),e[yu]||null}function km(e){return oa(e,"@Pipe"),e[vu]||null}function oa(e,n){if(e==null)throw new v(-919,!1)}function Ft(e){return typeof e=="string"?e:e==null?"":String(e)}var Fm=q({ngErrorCode:q}),$w=q({ngErrorMessage:q}),zw=q({ngTokenPath:q});function Du(e,n){return Pm("",-200,n)}function ia(e,n){throw new v(-201,!1)}function Pm(e,n,t){let r=new v(n,e);return r[Fm]=n,r[$w]=e,t&&(r[zw]=t),r}function Gw(e){return e[Fm]}var ou;function Lm(){return ou}function Ae(e){let n=ou;return ou=e,n}function Eu(e,n,t){let r=Qo(e);if(r&&r.providedIn=="root")return r.value===void 0?r.value=r.factory():r.value;if(t&8)return null;if(n!==void 0)return n;ia(e,"")}var Ww={},Xn=Ww,qw="__NG_DI_FLAG__",iu=class{injector;constructor(n){this.injector=n}retrieve(n,t){let r=Qn(t)||0;try{return this.injector.get(n,r&8?null:Xn,r)}catch(o){if(Lr(o))return o;throw o}}};function Yw(e,n=0){let t=qs();if(t===void 0)throw new v(-203,!1);if(t===null)return Eu(e,void 0,n);{let r=Zw(n),o=t.retrieve(e,r);if(Lr(o)){if(r.optional)return null;throw o}return o}}function I(e,n=0){return(Lm()||Yw)(me(e),n)}function f(e,n){return I(e,Qn(n))}function Qn(e){return typeof e>"u"||typeof e=="number"?e:0|(e.optional&&8)|(e.host&&1)|(e.self&&2)|(e.skipSelf&&4)}function Zw(e){return{optional:!!(e&8),host:!!(e&1),self:!!(e&2),skipSelf:!!(e&4)}}function su(e){let n=[];for(let t=0;tArray.isArray(t)?sa(t,n):n(t))}function wu(e,n,t){n>=e.length?e.push(t):e.splice(n,0,t)}function Jo(e,n){return n>=e.length-1?e.pop():e.splice(n,1)[0]}function Bm(e,n){let t=[];for(let r=0;rn;){let i=o-2;e[o]=e[i],o--}e[n]=t,e[n+1]=r}}function aa(e,n,t){let r=jr(e,n);return r>=0?e[r|1]=t:(r=~r,Hm(e,r,n,t)),r}function ca(e,n){let t=jr(e,n);if(t>=0)return e[t|1]}function jr(e,n){return Xw(e,n,1)}function Xw(e,n,t){let r=0,o=e.length>>t;for(;o!==r;){let i=r+(o-r>>1),s=e[i<n?o=i:r=i+1}return~(o<{t.push(s)};return sa(n,s=>{let a=s;Ks(a,i,[],r)&&(o||=[],o.push(a))}),o!==void 0&&$m(o,i),t}function $m(e,n){for(let t=0;t{n(i,r)})}}function Ks(e,n,t,r){if(e=me(e),!e)return!1;let o=null,i=ru(e),s=!i&&kt(e);if(!i&&!s){let c=e.ngModule;if(i=ru(c),i)o=c;else return!1}else{if(s&&!s.standalone)return!1;o=e}let a=r.has(o);if(s){if(a)return!1;if(r.add(o),s.dependencies){let c=typeof s.dependencies=="function"?s.dependencies():s.dependencies;for(let l of c)Ks(l,n,t,r)}}else if(i){if(i.imports!=null&&!a){r.add(o);let l;sa(i.imports,u=>{Ks(u,n,t,r)&&(l||=[],l.push(u))}),l!==void 0&&$m(l,n)}if(!a){let l=_n(o)||(()=>new o);n({provide:o,useFactory:l,deps:Ce},o),n({provide:Iu,useValue:o,multi:!0},o),n({provide:Br,useValue:()=>I(o),multi:!0},o)}let c=i.providers;if(c!=null&&!a){let l=e;Su(c,u=>{n(u,l)})}}else return!1;return o!==e&&e.providers!==void 0}function Su(e,n){for(let t of e)mu(t)&&(t=t.\u0275providers),Array.isArray(t)?Su(t,n):n(t)}var Qw=q({provide:String,useValue:q});function zm(e){return e!==null&&typeof e=="object"&&Qw in e}function Jw(e){return!!(e&&e.useExisting)}function eC(e){return!!(e&&e.useFactory)}function Jn(e){return typeof e=="function"}function Gm(e){return!!e.useClass}var ei=new y(""),Ys={},Am={},nu;function Hr(){return nu===void 0&&(nu=new qo),nu}var ce=class{},er=class extends ce{parent;source;scopes;records=new Map;_ngOnDestroyHooks=new Set;_onDestroyHooks=[];get destroyed(){return this._destroyed}_destroyed=!1;injectorDefTypes;constructor(n,t,r,o){super(),this.parent=t,this.source=r,this.scopes=o,cu(n,s=>this.processProvider(s)),this.records.set(Cu,Vr(void 0,this)),o.has("environment")&&this.records.set(ce,Vr(void 0,this));let i=this.records.get(ei);i!=null&&typeof i.value=="string"&&this.scopes.add(i.value),this.injectorDefTypes=new Set(this.get(Iu,Ce,{self:!0}))}retrieve(n,t){let r=Qn(t)||0;try{return this.get(n,Xn,r)}catch(o){if(Lr(o))return o;throw o}}destroy(){Go(this),this._destroyed=!0;let n=M(null);try{for(let r of this._ngOnDestroyHooks)r.ngOnDestroy();let t=this._onDestroyHooks;this._onDestroyHooks=[];for(let r of t)r()}finally{this.records.clear(),this._ngOnDestroyHooks.clear(),this.injectorDefTypes.clear(),M(n)}}onDestroy(n){return Go(this),this._onDestroyHooks.push(n),()=>this.removeOnDestroy(n)}runInContext(n){Go(this);let t=Nt(this),r=Ae(void 0),o;try{return n()}finally{Nt(t),Ae(r)}}get(n,t=Xn,r){if(Go(this),n.hasOwnProperty(xm))return n[xm](this);let o=Qn(r),i,s=Nt(this),a=Ae(void 0);try{if(!(o&4)){let l=this.records.get(n);if(l===void 0){let u=iC(n)&&Qo(n);u&&this.injectableDefInScope(u)?l=Vr(au(n),Ys):l=null,this.records.set(n,l)}if(l!=null)return this.hydrate(n,l,o)}let c=o&2?Hr():this.parent;return t=o&8&&t===Xn?null:t,c.get(n,t)}catch(c){let l=Gw(c);throw l===-200||l===-201?new v(l,null):c}finally{Ae(a),Nt(s)}}resolveInjectorInitializers(){let n=M(null),t=Nt(this),r=Ae(void 0),o;try{let i=this.get(Br,Ce,{self:!0});for(let s of i)s()}finally{Nt(t),Ae(r),M(n)}}toString(){return"R3Injector[...]"}processProvider(n){n=me(n);let t=Jn(n)?n:me(n&&n.provide),r=nC(n);if(!Jn(n)&&n.multi===!0){let o=this.records.get(t);o||(o=Vr(void 0,Ys,!0),o.factory=()=>su(o.multi),this.records.set(t,o)),t=n,o.multi.push(n)}this.records.set(t,r)}hydrate(n,t,r){let o=M(null);try{if(t.value===Am)throw Du("");return t.value===Ys&&(t.value=Am,t.value=t.factory(void 0,r)),typeof t.value=="object"&&t.value&&oC(t.value)&&this._ngOnDestroyHooks.add(t.value),t.value}finally{M(o)}}injectableDefInScope(n){if(!n.providedIn)return!1;let t=me(n.providedIn);return typeof t=="string"?t==="any"||this.scopes.has(t):this.injectorDefTypes.has(t)}removeOnDestroy(n){let t=this._onDestroyHooks.indexOf(n);t!==-1&&this._onDestroyHooks.splice(t,1)}};function au(e){let n=Qo(e),t=n!==null?n.factory:_n(e);if(t!==null)return t;if(e instanceof y)throw new v(-204,!1);if(e instanceof Function)return tC(e);throw new v(-204,!1)}function tC(e){if(e.length>0)throw new v(-204,!1);let t=Uw(e);return t!==null?()=>t.factory(e):()=>new e}function nC(e){if(zm(e))return Vr(void 0,e.useValue);{let n=Tu(e);return Vr(n,Ys)}}function Tu(e,n,t){let r;if(Jn(e)){let o=me(e);return _n(o)||au(o)}else if(zm(e))r=()=>me(e.useValue);else if(eC(e))r=()=>e.useFactory(...su(e.deps||[]));else if(Jw(e))r=(o,i)=>I(me(e.useExisting),i!==void 0&&i&8?8:void 0);else{let o=me(e&&(e.useClass||e.provide));if(rC(e))r=()=>new o(...su(e.deps));else return _n(o)||au(o)}return r}function Go(e){if(e.destroyed)throw new v(-205,!1)}function Vr(e,n,t=!1){return{factory:e,value:n,multi:t?[]:void 0}}function rC(e){return!!e.deps}function oC(e){return e!==null&&typeof e=="object"&&typeof e.ngOnDestroy=="function"}function iC(e){return typeof e=="function"||typeof e=="object"&&e.ngMetadataName==="InjectionToken"}function cu(e,n){for(let t of e)Array.isArray(t)?cu(t,n):t&&mu(t)?cu(t.\u0275providers,n):n(t)}function Ur(e,n){let t;e instanceof er?(Go(e),t=e):t=new iu(e);let r,o=Nt(t),i=Ae(void 0);try{return n()}finally{Nt(o),Ae(i)}}function xu(){return Lm()!==void 0||qs()!=null}var gt=0,S=1,N=2,ge=3,rt=4,Ne=5,rr=6,$r=7,ie=8,Yt=9,yt=10,K=11,zr=12,Au=13,or=14,Ie=15,wn=16,ir=17,Pt=18,Zt=19,Nu=20,qt=21,la=22,Dn=23,qe=24,sr=25,Cn=26,ee=27,Wm=1,Ru=6,In=7,ti=8,ar=9,se=10;function Kt(e){return Array.isArray(e)&&typeof e[Wm]=="object"}function vt(e){return Array.isArray(e)&&e[Wm]===!0}function Ou(e){return(e.flags&4)!==0}function Lt(e){return e.componentOffset>-1}function Gr(e){return(e.flags&1)===1}function bt(e){return!!e.template}function Wr(e){return(e[N]&512)!==0}function cr(e){return(e[N]&256)===256}var ku="svg",qm="math";function ot(e){for(;Array.isArray(e);)e=e[gt];return e}function Fu(e,n){return ot(n[e])}function it(e,n){return ot(n[e.index])}function ua(e,n){return e.data[n]}function ni(e,n){return e[n]}function Pu(e,n,t,r){t>=e.data.length&&(e.data[t]=null,e.blueprint[t]=null),n[t]=r}function st(e,n){let t=n[e];return Kt(t)?t:t[gt]}function Ym(e){return(e[N]&4)===4}function da(e){return(e[N]&128)===128}function Zm(e){return vt(e[ge])}function Ye(e,n){return n==null?null:e[n]}function Lu(e){e[ir]=0}function Vu(e){e[N]&1024||(e[N]|=1024,da(e)&&lr(e))}function Km(e,n){for(;e>0;)n=n[or],e--;return n}function ri(e){return!!(e[N]&9216||e[qe]?.dirty)}function fa(e){e[yt].changeDetectionScheduler?.notify(8),e[N]&64&&(e[N]|=1024),ri(e)&&lr(e)}function lr(e){e[yt].changeDetectionScheduler?.notify(0);let n=En(e);for(;n!==null&&!(n[N]&8192||(n[N]|=8192,!da(n)));)n=En(n)}function ju(e,n){if(cr(e))throw new v(911,!1);e[qt]===null&&(e[qt]=[]),e[qt].push(n)}function Xm(e,n){if(e[qt]===null)return;let t=e[qt].indexOf(n);t!==-1&&e[qt].splice(t,1)}function En(e){let n=e[ge];return vt(n)?n[ge]:n}function Bu(e){return e[$r]??=[]}function Hu(e){return e.cleanup??=[]}function Qm(e,n,t,r){let o=Bu(n);o.push(t),e.firstCreatePass&&Hu(e).push(r,o.length-1)}var L={lFrame:dg(null),bindingsEnabled:!0,skipHydrationRootTNode:null};var lu=!1;function Jm(){return L.lFrame.elementDepthCount}function eg(){L.lFrame.elementDepthCount++}function Uu(){L.lFrame.elementDepthCount--}function ha(){return L.bindingsEnabled}function $u(){return L.skipHydrationRootTNode!==null}function zu(e){return L.skipHydrationRootTNode===e}function Gu(){L.skipHydrationRootTNode=null}function D(){return L.lFrame.lView}function J(){return L.lFrame.tView}function tg(e){return L.lFrame.contextLView=e,e[ie]}function ng(e){return L.lFrame.contextLView=null,e}function he(){let e=Wu();for(;e!==null&&e.type===64;)e=e.parent;return e}function Wu(){return L.lFrame.currentTNode}function rg(){let e=L.lFrame,n=e.currentTNode;return e.isParent?n:n.parent}function qr(e,n){let t=L.lFrame;t.currentTNode=e,t.isParent=n}function qu(){return L.lFrame.isParent}function Yu(){L.lFrame.isParent=!1}function og(){return L.lFrame.contextLView}function Zu(){return lu}function Yo(e){let n=lu;return lu=e,n}function Vt(){let e=L.lFrame,n=e.bindingRootIndex;return n===-1&&(n=e.bindingRootIndex=e.tView.bindingStartIndex),n}function Ku(){return L.lFrame.bindingIndex}function ig(e){return L.lFrame.bindingIndex=e}function Xt(){return L.lFrame.bindingIndex++}function oi(e){let n=L.lFrame,t=n.bindingIndex;return n.bindingIndex=n.bindingIndex+e,t}function sg(){return L.lFrame.inI18n}function ag(e,n){let t=L.lFrame;t.bindingIndex=t.bindingRootIndex=e,pa(n)}function cg(){return L.lFrame.currentDirectiveIndex}function pa(e){L.lFrame.currentDirectiveIndex=e}function lg(e){let n=L.lFrame.currentDirectiveIndex;return n===-1?null:e[n]}function ma(){return L.lFrame.currentQueryIndex}function ii(e){L.lFrame.currentQueryIndex=e}function sC(e){let n=e[S];return n.type===2?n.declTNode:n.type===1?e[Ne]:null}function Xu(e,n,t){if(t&4){let o=n,i=e;for(;o=o.parent,o===null&&!(t&1);)if(o=sC(i),o===null||(i=i[or],o.type&10))break;if(o===null)return!1;n=o,e=i}let r=L.lFrame=ug();return r.currentTNode=n,r.lView=e,!0}function ga(e){let n=ug(),t=e[S];L.lFrame=n,n.currentTNode=t.firstChild,n.lView=e,n.tView=t,n.contextLView=e,n.bindingIndex=t.bindingStartIndex,n.inI18n=!1}function ug(){let e=L.lFrame,n=e===null?null:e.child;return n===null?dg(e):n}function dg(e){let n={currentTNode:null,isParent:!0,lView:null,tView:null,selectedIndex:-1,contextLView:null,elementDepthCount:0,currentNamespace:null,currentDirectiveIndex:-1,bindingRootIndex:-1,bindingIndex:-1,currentQueryIndex:0,parent:e,child:null,inI18n:!1};return e!==null&&(e.child=n),n}function fg(){let e=L.lFrame;return L.lFrame=e.parent,e.currentTNode=null,e.lView=null,e}var Qu=fg;function ya(){let e=fg();e.isParent=!0,e.tView=null,e.selectedIndex=-1,e.contextLView=null,e.elementDepthCount=0,e.currentDirectiveIndex=-1,e.currentNamespace=null,e.bindingRootIndex=-1,e.bindingIndex=-1,e.currentQueryIndex=0}function hg(e){return(L.lFrame.contextLView=Km(e,L.lFrame.contextLView))[ie]}function _t(){return L.lFrame.selectedIndex}function Mn(e){L.lFrame.selectedIndex=e}function Yr(){let e=L.lFrame;return ua(e.tView,e.selectedIndex)}function pg(){L.lFrame.currentNamespace=ku}function mg(){aC()}function aC(){L.lFrame.currentNamespace=null}function gg(){return L.lFrame.currentNamespace}var yg=!0;function va(){return yg}function si(e){yg=e}function uu(e,n=null,t=null,r){let o=Ju(e,n,t,r);return o.resolveInjectorInitializers(),o}function Ju(e,n=null,t=null,r,o=new Set){let i=[t||Ce,Um(e)],s;return new er(i,n||Hr(),s||null,o)}var j=class e{static THROW_IF_NOT_FOUND=Xn;static NULL=new qo;static create(n,t){if(Array.isArray(n))return uu({name:""},t,n,"");{let r=n.name??"";return uu({name:r},n.parent,n.providers,r)}}static \u0275prov=g({token:e,providedIn:"any",factory:()=>I(Cu)});static __NG_ELEMENT_ID__=-1},F=new y(""),Pe=(()=>{class e{static __NG_ELEMENT_ID__=cC;static __NG_ENV_ID__=t=>t}return e})(),Xs=class extends Pe{_lView;constructor(n){super(),this._lView=n}get destroyed(){return cr(this._lView)}onDestroy(n){let t=this._lView;return ju(t,n),()=>Xm(t,n)}};function cC(){return new Xs(D())}var vg=!1,bg=new y(""),ur=(()=>{class e{taskId=0;pendingTasks=new Set;destroyed=!1;pendingTask=new zn(!1);debugTaskTracker=f(bg,{optional:!0});get hasPendingTasks(){return this.destroyed?!1:this.pendingTask.value}get hasPendingTasksObservable(){return this.destroyed?new k(t=>{t.next(!1),t.complete()}):this.pendingTask}add(){!this.hasPendingTasks&&!this.destroyed&&this.pendingTask.next(!0);let t=this.taskId++;return this.pendingTasks.add(t),this.debugTaskTracker?.add(t),t}has(t){return this.pendingTasks.has(t)}remove(t){this.pendingTasks.delete(t),this.debugTaskTracker?.remove(t),this.pendingTasks.size===0&&this.hasPendingTasks&&this.pendingTask.next(!1)}ngOnDestroy(){this.pendingTasks.clear(),this.hasPendingTasks&&this.pendingTask.next(!1),this.destroyed=!0,this.pendingTask.unsubscribe()}static \u0275prov=g({token:e,providedIn:"root",factory:()=>new e})}return e})(),du=class extends R{__isAsync;destroyRef=void 0;pendingTasks=void 0;constructor(n=!1){super(),this.__isAsync=n,xu()&&(this.destroyRef=f(Pe,{optional:!0})??void 0,this.pendingTasks=f(ur,{optional:!0})??void 0)}emit(n){let t=M(null);try{super.next(n)}finally{M(t)}}subscribe(n,t,r){let o=n,i=t||(()=>null),s=r;if(n&&typeof n=="object"){let c=n;o=c.next?.bind(c),i=c.error?.bind(c),s=c.complete?.bind(c)}this.__isAsync&&(i=this.wrapInTimeout(i),o&&(o=this.wrapInTimeout(o)),s&&(s=this.wrapInTimeout(s)));let a=super.subscribe({next:o,error:i,complete:s});return n instanceof B&&n.add(a),a}wrapInTimeout(n){return t=>{let r=this.pendingTasks?.add();setTimeout(()=>{try{n(t)}finally{r!==void 0&&this.pendingTasks?.remove(r)}})}}},H=du;function Qs(...e){}function ed(e){let n,t;function r(){e=Qs;try{t!==void 0&&typeof cancelAnimationFrame=="function"&&cancelAnimationFrame(t),n!==void 0&&clearTimeout(n)}catch{}}return n=setTimeout(()=>{e(),r()}),typeof requestAnimationFrame=="function"&&(t=requestAnimationFrame(()=>{e(),r()})),()=>r()}function _g(e){return queueMicrotask(()=>e()),()=>{e=Qs}}var td="isAngularZone",Zo=td+"_ID",lC=0,P=class e{hasPendingMacrotasks=!1;hasPendingMicrotasks=!1;isStable=!0;onUnstable=new H(!1);onMicrotaskEmpty=new H(!1);onStable=new H(!1);onError=new H(!1);constructor(n){let{enableLongStackTrace:t=!1,shouldCoalesceEventChangeDetection:r=!1,shouldCoalesceRunChangeDetection:o=!1,scheduleInRootZone:i=vg}=n;if(typeof Zone>"u")throw new v(908,!1);Zone.assertZonePatched();let s=this;s._nesting=0,s._outer=s._inner=Zone.current,Zone.TaskTrackingZoneSpec&&(s._inner=s._inner.fork(new Zone.TaskTrackingZoneSpec)),t&&Zone.longStackTraceZoneSpec&&(s._inner=s._inner.fork(Zone.longStackTraceZoneSpec)),s.shouldCoalesceEventChangeDetection=!o&&r,s.shouldCoalesceRunChangeDetection=o,s.callbackScheduled=!1,s.scheduleInRootZone=i,fC(s)}static isInAngularZone(){return typeof Zone<"u"&&Zone.current.get(td)===!0}static assertInAngularZone(){if(!e.isInAngularZone())throw new v(909,!1)}static assertNotInAngularZone(){if(e.isInAngularZone())throw new v(909,!1)}run(n,t,r){return this._inner.run(n,t,r)}runTask(n,t,r,o){let i=this._inner,s=i.scheduleEventTask("NgZoneEvent: "+o,n,uC,Qs,Qs);try{return i.runTask(s,t,r)}finally{i.cancelTask(s)}}runGuarded(n,t,r){return this._inner.runGuarded(n,t,r)}runOutsideAngular(n){return this._outer.run(n)}},uC={};function nd(e){if(e._nesting==0&&!e.hasPendingMicrotasks&&!e.isStable)try{e._nesting++,e.onMicrotaskEmpty.emit(null)}finally{if(e._nesting--,!e.hasPendingMicrotasks)try{e.runOutsideAngular(()=>e.onStable.emit(null))}finally{e.isStable=!0}}}function dC(e){if(e.isCheckStableRunning||e.callbackScheduled)return;e.callbackScheduled=!0;function n(){ed(()=>{e.callbackScheduled=!1,fu(e),e.isCheckStableRunning=!0,nd(e),e.isCheckStableRunning=!1})}e.scheduleInRootZone?Zone.root.run(()=>{n()}):e._outer.run(()=>{n()}),fu(e)}function fC(e){let n=()=>{dC(e)},t=lC++;e._inner=e._inner.fork({name:"angular",properties:{[td]:!0,[Zo]:t,[Zo+t]:!0},onInvokeTask:(r,o,i,s,a,c)=>{if(hC(c))return r.invokeTask(i,s,a,c);try{return Nm(e),r.invokeTask(i,s,a,c)}finally{(e.shouldCoalesceEventChangeDetection&&s.type==="eventTask"||e.shouldCoalesceRunChangeDetection)&&n(),Rm(e)}},onInvoke:(r,o,i,s,a,c,l)=>{try{return Nm(e),r.invoke(i,s,a,c,l)}finally{e.shouldCoalesceRunChangeDetection&&!e.callbackScheduled&&!pC(c)&&n(),Rm(e)}},onHasTask:(r,o,i,s)=>{r.hasTask(i,s),o===i&&(s.change=="microTask"?(e._hasPendingMicrotasks=s.microTask,fu(e),nd(e)):s.change=="macroTask"&&(e.hasPendingMacrotasks=s.macroTask))},onHandleError:(r,o,i,s)=>(r.handleError(i,s),e.runOutsideAngular(()=>e.onError.emit(s)),!1)})}function fu(e){e._hasPendingMicrotasks||(e.shouldCoalesceEventChangeDetection||e.shouldCoalesceRunChangeDetection)&&e.callbackScheduled===!0?e.hasPendingMicrotasks=!0:e.hasPendingMicrotasks=!1}function Nm(e){e._nesting++,e.isStable&&(e.isStable=!1,e.onUnstable.emit(null))}function Rm(e){e._nesting--,nd(e)}var Ko=class{hasPendingMicrotasks=!1;hasPendingMacrotasks=!1;isStable=!0;onUnstable=new H;onMicrotaskEmpty=new H;onStable=new H;onError=new H;run(n,t,r){return n.apply(t,r)}runGuarded(n,t,r){return n.apply(t,r)}runOutsideAngular(n){return n()}runTask(n,t,r,o){return n.apply(t,r)}};function hC(e){return Dg(e,"__ignore_ng_zone__")}function pC(e){return Dg(e,"__scheduler_tick__")}function Dg(e,n){return!Array.isArray(e)||e.length!==1?!1:e[0]?.data?.[n]===!0}var nt=class{_console=console;handleError(n){this._console.error("ERROR",n)}},Qt=new y("",{factory:()=>{let e=f(P),n=f(ce),t;return r=>{e.runOutsideAngular(()=>{n.destroyed&&!t?setTimeout(()=>{throw r}):(t??=n.get(nt),t.handleError(r))})}}}),Eg={provide:Br,useValue:()=>{let e=f(nt,{optional:!0})},multi:!0};function Me(e,n){let[t,r,o]=Xl(e,n?.equal),i=t,s=i[oe];return i.set=r,i.update=o,i.asReadonly=ai.bind(i),i}function ai(){let e=this[oe];if(e.readonlyFn===void 0){let n=()=>this();n[oe]=e,e.readonlyFn=n}return e.readonlyFn}var Zr=(()=>{class e{view;node;constructor(t,r){this.view=t,this.node=r}static __NG_ELEMENT_ID__=mC}return e})();function mC(){return new Zr(D(),he())}var Rt=class{},ci=new y("",{factory:()=>!0});var rd=new y(""),Kr=(()=>{class e{internalPendingTasks=f(ur);scheduler=f(Rt);errorHandler=f(Qt);add(){let t=this.internalPendingTasks.add();return()=>{this.internalPendingTasks.has(t)&&(this.scheduler.notify(11),this.internalPendingTasks.remove(t))}}run(t){let r=this.add();t().catch(this.errorHandler).finally(r)}static \u0275prov=g({token:e,providedIn:"root",factory:()=>new e})}return e})(),ba=(()=>{class e{static \u0275prov=g({token:e,providedIn:"root",factory:()=>new hu})}return e})(),hu=class{dirtyEffectCount=0;queues=new Map;add(n){this.enqueue(n),this.schedule(n)}schedule(n){n.dirty&&this.dirtyEffectCount++}remove(n){let t=n.zone,r=this.queues.get(t);r.has(n)&&(r.delete(n),n.dirty&&this.dirtyEffectCount--)}enqueue(n){let t=n.zone;this.queues.has(t)||this.queues.set(t,new Set);let r=this.queues.get(t);r.has(n)||r.add(n)}flush(){for(;this.dirtyEffectCount>0;){let n=!1;for(let[t,r]of this.queues)t===null?n||=this.flushQueue(r):n||=t.run(()=>this.flushQueue(r));n||(this.dirtyEffectCount=0)}}flushQueue(n){let t=!1;for(let r of n)r.dirty&&(this.dirtyEffectCount--,t=!0,r.run());return t}},Js=class{[oe];constructor(n){this[oe]=n}destroy(){this[oe].destroy()}};function li(e,n){let t=n?.injector??f(j),r=n?.manualCleanup!==!0?t.get(Pe):null,o,i=t.get(Zr,null,{optional:!0}),s=t.get(Rt);return i!==null?(o=vC(i.view,s,e),r instanceof Xs&&r._lView===i.view&&(r=null)):o=bC(e,t.get(ba),s),o.injector=t,r!==null&&(o.onDestroyFns=[r.onDestroy(()=>o.destroy())]),new Js(o)}var wg=V(E({},Ql),{cleanupFns:void 0,zone:null,onDestroyFns:null,run(){let e=Yo(!1);try{Jl(this)}finally{Yo(e)}},cleanup(){if(!this.cleanupFns?.length)return;let e=M(null);try{for(;this.cleanupFns.length;)this.cleanupFns.pop()()}finally{this.cleanupFns=[],M(e)}}}),gC=V(E({},wg),{consumerMarkedDirty(){this.scheduler.schedule(this),this.notifier.notify(12)},destroy(){if(vn(this),this.onDestroyFns!==null)for(let e of this.onDestroyFns)e();this.cleanup(),this.scheduler.remove(this)}}),yC=V(E({},wg),{consumerMarkedDirty(){this.view[N]|=8192,lr(this.view),this.notifier.notify(13)},destroy(){if(vn(this),this.onDestroyFns!==null)for(let e of this.onDestroyFns)e();this.cleanup(),this.view[Dn]?.delete(this)}});function vC(e,n,t){let r=Object.create(yC);return r.view=e,r.zone=typeof Zone<"u"?Zone.current:null,r.notifier=n,r.fn=Cg(r,t),e[Dn]??=new Set,e[Dn].add(r),r.consumerMarkedDirty(r),r}function bC(e,n,t){let r=Object.create(gC);return r.fn=Cg(r,e),r.scheduler=n,r.notifier=t,r.zone=typeof Zone<"u"?Zone.current:null,r.scheduler.add(r),r.notifier.notify(12),r}function Cg(e,n){return()=>{n(t=>(e.cleanupFns??=[]).push(t))}}function Di(e){return{toString:e}.toString()}function MC(e){return typeof e=="function"}function sy(e,n,t,r){n!==null?n.applyValueToInputSignal(n,r):e[t]=r}var xa=class{previousValue;currentValue;firstChange;constructor(n,t,r){this.previousValue=n,this.currentValue=t,this.firstChange=r}isFirstChange(){return this.firstChange}},Ke=(()=>{let e=()=>ay;return e.ngInherit=!0,e})();function ay(e){return e.type.prototype.ngOnChanges&&(e.setInput=TC),SC}function SC(){let e=ly(this),n=e?.current;if(n){let t=e.previous;if(t===mt)e.previous=n;else for(let r in n)t[r]=n[r];e.current=null,this.ngOnChanges(n)}}function TC(e,n,t,r,o){let i=this.declaredInputs[r],s=ly(e)||xC(e,{previous:mt,current:null}),a=s.current||(s.current={}),c=s.previous,l=c[i];a[i]=new xa(l&&l.currentValue,t,c===mt),sy(e,n,o,t)}var cy="__ngSimpleChanges__";function ly(e){return e[cy]||null}function xC(e,n){return e[cy]=n}var Ig=[];var Y=function(e,n=null,t){for(let r=0;r=r)break}else n[c]<0&&(e[ir]+=65536),(a>14>16&&(e[N]&3)===n&&(e[N]+=16384,Mg(a,i)):Mg(a,i)}var Qr=-1,fr=class{factory;name;injectImpl;resolving=!1;canSeeViewProviders;multi;componentProviders;index;providerFactory;constructor(n,t,r,o){this.factory=n,this.name=o,this.canSeeViewProviders=t,this.injectImpl=r}};function RC(e){return(e.flags&8)!==0}function OC(e){return(e.flags&16)!==0}function kC(e,n,t){let r=0;for(;rn){s=i-1;break}}}for(;i>16}function Na(e,n){let t=PC(e),r=n;for(;t>0;)r=r[or],t--;return r}var md=!0;function Ra(e){let n=md;return md=e,n}var LC=256,py=LC-1,my=5,VC=0,jt={};function jC(e,n,t){let r;typeof t=="string"?r=t.charCodeAt(0)||0:t.hasOwnProperty(tr)&&(r=t[tr]),r==null&&(r=t[tr]=VC++);let o=r&py,i=1<>my)]|=i}function Oa(e,n){let t=gy(e,n);if(t!==-1)return t;let r=n[S];r.firstCreatePass&&(e.injectorIndex=n.length,id(r.data,e),id(n,null),id(r.blueprint,null));let o=Jd(e,n),i=e.injectorIndex;if(hy(o)){let s=Aa(o),a=Na(o,n),c=a[S].data;for(let l=0;l<8;l++)n[i+l]=a[s+l]|c[s+l]}return n[i+8]=o,i}function id(e,n){e.push(0,0,0,0,0,0,0,0,n)}function gy(e,n){return e.injectorIndex===-1||e.parent&&e.parent.injectorIndex===e.injectorIndex||n[e.injectorIndex+8]===null?-1:e.injectorIndex}function Jd(e,n){if(e.parent&&e.parent.injectorIndex!==-1)return e.parent.injectorIndex;let t=0,r=null,o=n;for(;o!==null;){if(r=Dy(o),r===null)return Qr;if(t++,o=o[or],r.injectorIndex!==-1)return r.injectorIndex|t<<16}return Qr}function gd(e,n,t){jC(e,n,t)}function BC(e,n){if(n==="class")return e.classes;if(n==="style")return e.styles;let t=e.attrs;if(t){let r=t.length,o=0;for(;o>20,d=r?a:a+u,p=o?a+u:l;for(let h=d;h=c&&m.type===t)return h}if(o){let h=s[c];if(h&&bt(h)&&h.type===t)return c}return null}function pi(e,n,t,r,o){let i=e[t],s=n.data;if(i instanceof fr){let a=i;if(a.resolving)throw Du("");let c=Ra(a.canSeeViewProviders);a.resolving=!0;let l=s[t].type||s[t],u,d=a.injectImpl?Ae(a.injectImpl):null,p=Xu(e,r,0);try{i=e[t]=a.factory(void 0,o,s,e,r),n.firstCreatePass&&t>=r.directiveStart&&AC(t,s[t],n)}finally{d!==null&&Ae(d),Ra(c),a.resolving=!1,Qu()}}return i}function UC(e){if(typeof e=="string")return e.charCodeAt(0)||0;let n=e.hasOwnProperty(tr)?e[tr]:void 0;return typeof n=="number"?n>=0?n&py:$C:n}function Tg(e,n,t){let r=1<>my)]&r)}function xg(e,n){return!(e&2)&&!(e&1&&n)}var dr=class{_tNode;_lView;constructor(n,t){this._tNode=n,this._lView=t}get(n,t,r){return by(this._tNode,this._lView,n,Qn(r),t)}};function $C(){return new dr(he(),D())}function Ve(e){return Di(()=>{let n=e.prototype.constructor,t=n[Wo]||yd(n),r=Object.prototype,o=Object.getPrototypeOf(e.prototype).constructor;for(;o&&o!==r;){let i=o[Wo]||yd(o);if(i&&i!==t)return i;o=Object.getPrototypeOf(o)}return i=>new i})}function yd(e){return pu(e)?()=>{let n=yd(me(e));return n&&n()}:_n(e)}function zC(e,n,t,r,o){let i=e,s=n;for(;i!==null&&s!==null&&s[N]&2048&&!Wr(s);){let a=_y(i,s,t,r|2,jt);if(a!==jt)return a;let c=i.parent;if(!c){let l=s[Nu];if(l){let u=l.get(t,jt,r&-5);if(u!==jt)return u}c=Dy(s),s=s[or]}i=c}return o}function Dy(e){let n=e[S],t=n.type;return t===2?n.declTNode:t===1?e[Ne]:null}function ef(e){return BC(he(),e)}function GC(){return oo(he(),D())}function oo(e,n){return new z(it(e,n))}var z=(()=>{class e{nativeElement;constructor(t){this.nativeElement=t}static __NG_ELEMENT_ID__=GC}return e})();function Ey(e){return e instanceof z?e.nativeElement:e}function WC(){return this._results[Symbol.iterator]()}var Jt=class{_emitDistinctChangesOnly;dirty=!0;_onDirty=void 0;_results=[];_changesDetected=!1;_changes=void 0;length=0;first=void 0;last=void 0;get changes(){return this._changes??=new R}constructor(n=!1){this._emitDistinctChangesOnly=n}get(n){return this._results[n]}map(n){return this._results.map(n)}filter(n){return this._results.filter(n)}find(n){return this._results.find(n)}reduce(n,t){return this._results.reduce(n,t)}forEach(n){this._results.forEach(n)}some(n){return this._results.some(n)}toArray(){return this._results.slice()}toString(){return this._results.toString()}reset(n,t){this.dirty=!1;let r=jm(n);(this._changesDetected=!Vm(this._results,r,t))&&(this._results=r,this.length=r.length,this.last=r[this.length-1],this.first=r[0])}notifyOnChanges(){this._changes!==void 0&&(this._changesDetected||!this._emitDistinctChangesOnly)&&this._changes.next(this)}onDirty(n){this._onDirty=n}setDirty(){this.dirty=!0,this._onDirty?.()}destroy(){this._changes!==void 0&&(this._changes.complete(),this._changes.unsubscribe())}[Symbol.iterator]=WC};function wy(e){return(e.flags&128)===128}var tf=(function(e){return e[e.OnPush=0]="OnPush",e[e.Eager=1]="Eager",e[e.Default=1]="Default",e})(tf||{}),Cy=new Map,qC=0;function YC(){return qC++}function ZC(e){Cy.set(e[Zt],e)}function vd(e){Cy.delete(e[Zt])}var Ag="__ngContext__";function eo(e,n){Kt(n)?(e[Ag]=n[Zt],ZC(n)):e[Ag]=n}function Iy(e){return Sy(e[zr])}function My(e){return Sy(e[rt])}function Sy(e){for(;e!==null&&!vt(e);)e=e[rt];return e}var bd;function nf(e){bd=e}function Ty(){if(bd!==void 0)return bd;if(typeof document<"u")return document;throw new v(210,!1)}var xn=new y("",{factory:()=>KC}),KC="ng";var qa=new y(""),mr=new y("",{providedIn:"platform",factory:()=>"unknown"}),Ei=new y(""),io=new y("",{factory:()=>f(F).body?.querySelector("[ngCspNonce]")?.getAttribute("ngCspNonce")||null});var xy="r";var Ay="di";var Ny=!1,Ry=new y("",{factory:()=>Ny});var Oy=new y("");var XC=(e,n,t,r)=>{};function QC(e,n,t,r){XC(e,n,t,r)}function Ya(e){return(e.flags&32)===32}var JC=()=>null;function ky(e,n,t=!1){return JC(e,n,t)}function Fy(e,n){let t=e.contentQueries;if(t!==null){let r=M(null);try{for(let o=0;oe,createScript:e=>e,createScriptURL:e=>e})}catch{}return _a}function Za(e){return eI()?.createHTML(e)||e}var Da;function Py(){if(Da===void 0&&(Da=null,le.trustedTypes))try{Da=le.trustedTypes.createPolicy("angular#unsafe-bypass",{createHTML:e=>e,createScript:e=>e,createScriptURL:e=>e})}catch{}return Da}function Ng(e){return Py()?.createHTML(e)||e}function Rg(e){return Py()?.createScriptURL(e)||e}var en=class{changingThisBreaksApplicationSecurity;constructor(n){this.changingThisBreaksApplicationSecurity=n}toString(){return`SafeValue must use [property]=binding: ${this.changingThisBreaksApplicationSecurity} (see ${ea})`}},Dd=class extends en{getTypeName(){return"HTML"}},Ed=class extends en{getTypeName(){return"Style"}},wd=class extends en{getTypeName(){return"Script"}},Cd=class extends en{getTypeName(){return"URL"}},Id=class extends en{getTypeName(){return"ResourceURL"}};function Xe(e){return e instanceof en?e.changingThisBreaksApplicationSecurity:e}function Ht(e,n){let t=Ly(e);if(t!=null&&t!==n){if(t==="ResourceURL"&&n==="URL")return!0;throw new Error(`Required a safe ${n}, got a ${t} (see ${ea})`)}return t===n}function Ly(e){return e instanceof en&&e.getTypeName()||null}function of(e){return new Dd(e)}function sf(e){return new Ed(e)}function af(e){return new wd(e)}function cf(e){return new Cd(e)}function lf(e){return new Id(e)}function tI(e){let n=new Sd(e);return nI()?new Md(n):n}var Md=class{inertDocumentHelper;constructor(n){this.inertDocumentHelper=n}getInertBodyElement(n){n=""+n;try{let t=new window.DOMParser().parseFromString(Za(n),"text/html").body;return t===null?this.inertDocumentHelper.getInertBodyElement(n):(t.firstChild?.remove(),t)}catch{return null}}},Sd=class{defaultDoc;inertDocument;constructor(n){this.defaultDoc=n,this.inertDocument=this.defaultDoc.implementation.createHTMLDocument("sanitization-inert")}getInertBodyElement(n){let t=this.inertDocument.createElement("template");return t.innerHTML=Za(n),t}};function nI(){try{return!!new window.DOMParser().parseFromString(Za(""),"text/html")}catch{return!1}}var rI=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:\/?#]*(?:[\/?#]|$))/i;function wi(e){return e=String(e),e.match(rI)?e:"unsafe:"+e}function tn(e){let n={};for(let t of e.split(","))n[t]=!0;return n}function Ci(...e){let n={};for(let t of e)for(let r in t)t.hasOwnProperty(r)&&(n[r]=!0);return n}var Vy=tn("area,br,col,hr,img,wbr"),jy=tn("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),By=tn("rp,rt"),oI=Ci(By,jy),iI=Ci(jy,tn("address,article,aside,blockquote,caption,center,del,details,dialog,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,main,map,menu,nav,ol,pre,section,summary,table,ul")),sI=Ci(By,tn("a,abbr,acronym,audio,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,picture,q,ruby,rp,rt,s,samp,small,source,span,strike,strong,sub,sup,time,track,tt,u,var,video")),Og=Ci(Vy,iI,sI,oI),Hy=tn("background,cite,href,itemtype,longdesc,poster,src,xlink:href"),aI=tn("abbr,accesskey,align,alt,autoplay,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,controls,coords,datetime,default,dir,download,face,headers,height,hidden,hreflang,hspace,ismap,itemscope,itemprop,kind,label,lang,language,loop,media,muted,nohref,nowrap,open,preload,rel,rev,role,rows,rowspan,rules,scope,scrolling,shape,size,sizes,span,srclang,srcset,start,summary,tabindex,target,title,translate,type,usemap,valign,value,vspace,width"),cI=tn("aria-activedescendant,aria-atomic,aria-autocomplete,aria-busy,aria-checked,aria-colcount,aria-colindex,aria-colspan,aria-controls,aria-current,aria-describedby,aria-details,aria-disabled,aria-dropeffect,aria-errormessage,aria-expanded,aria-flowto,aria-grabbed,aria-haspopup,aria-hidden,aria-invalid,aria-keyshortcuts,aria-label,aria-labelledby,aria-level,aria-live,aria-modal,aria-multiline,aria-multiselectable,aria-orientation,aria-owns,aria-placeholder,aria-posinset,aria-pressed,aria-readonly,aria-relevant,aria-required,aria-roledescription,aria-rowcount,aria-rowindex,aria-rowspan,aria-selected,aria-setsize,aria-sort,aria-valuemax,aria-valuemin,aria-valuenow,aria-valuetext"),lI=Ci(Hy,aI,cI),uI=tn("script,style,template");var Td=class{sanitizedSomething=!1;buf=[];sanitizeChildren(n){let t=n.firstChild,r=!0,o=[];for(;t;){if(t.nodeType===Node.ELEMENT_NODE?r=this.startElement(t):t.nodeType===Node.TEXT_NODE?this.chars(t.nodeValue):this.sanitizedSomething=!0,r&&t.firstChild){o.push(t),t=hI(t);continue}for(;t;){t.nodeType===Node.ELEMENT_NODE&&this.endElement(t);let i=fI(t);if(i){t=i;break}t=o.pop()}}return this.buf.join("")}startElement(n){let t=kg(n).toLowerCase();if(!Og.hasOwnProperty(t))return this.sanitizedSomething=!0,!uI.hasOwnProperty(t);this.buf.push("<"),this.buf.push(t);let r=n.attributes;for(let o=0;o"),!0}endElement(n){let t=kg(n).toLowerCase();Og.hasOwnProperty(t)&&!Vy.hasOwnProperty(t)&&(this.buf.push(""))}chars(n){this.buf.push(Fg(n))}};function dI(e,n){return(e.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_CONTAINED_BY)!==Node.DOCUMENT_POSITION_CONTAINED_BY}function fI(e){let n=e.nextSibling;if(n&&e!==n.previousSibling)throw Uy(n);return n}function hI(e){let n=e.firstChild;if(n&&dI(e,n))throw Uy(n);return n}function kg(e){let n=e.nodeName;return typeof n=="string"?n:"FORM"}function Uy(e){return new Error(`Failed to sanitize html because the element is clobbered: ${e.outerHTML}`)}var pI=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,mI=/([^\#-~ |!])/g;function Fg(e){return e.replace(/&/g,"&").replace(pI,function(n){let t=n.charCodeAt(0),r=n.charCodeAt(1);return"&#"+((t-55296)*1024+(r-56320)+65536)+";"}).replace(mI,function(n){return"&#"+n.charCodeAt(0)+";"}).replace(//g,">")}var Ea;function Ka(e,n){let t=null;try{Ea=Ea||tI(e);let r=n?String(n):"";t=Ea.getInertBodyElement(r);let o=5,i=r;do{if(o===0)throw new Error("Failed to sanitize html because the input is unstable");o--,r=i,i=t.innerHTML,t=Ea.getInertBodyElement(r)}while(r!==i);let a=new Td().sanitizeChildren(Pg(t)||t);return Za(a)}finally{if(t){let r=Pg(t)||t;for(;r.firstChild;)r.firstChild.remove()}}}function Pg(e){return"content"in e&&gI(e)?e.content:null}function gI(e){return e.nodeType===Node.ELEMENT_NODE&&e.nodeName==="TEMPLATE"}var yI=/^>|^->||--!>|)/g,bI="\u200B$1\u200B";function _I(e){return e.replace(yI,n=>n.replace(vI,bI))}function DI(e,n){return e.createText(n)}function EI(e,n,t){e.setValue(n,t)}function wI(e,n){return e.createComment(_I(n))}function $y(e,n,t){return e.createElement(n,t)}function ka(e,n,t,r,o){e.insertBefore(n,t,r,o)}function zy(e,n,t){e.appendChild(n,t)}function Lg(e,n,t,r,o){r!==null?ka(e,n,t,r,o):zy(e,n,t)}function Gy(e,n,t,r){e.removeChild(null,n,t,r)}function CI(e,n,t){e.setAttribute(n,"style",t)}function II(e,n,t){t===""?e.removeAttribute(n,"class"):e.setAttribute(n,"class",t)}function Wy(e,n,t){let{mergedAttrs:r,classes:o,styles:i}=t;r!==null&&kC(e,n,r),o!==null&&II(e,n,o),i!==null&&CI(e,n,i)}var je=(function(e){return e[e.NONE=0]="NONE",e[e.HTML=1]="HTML",e[e.STYLE=2]="STYLE",e[e.SCRIPT=3]="SCRIPT",e[e.URL=4]="URL",e[e.RESOURCE_URL=5]="RESOURCE_URL",e})(je||{});function MI(e){let n=uf();return n?Ng(n.sanitize(je.HTML,e)||""):Ht(e,"HTML")?Ng(Xe(e)):Ka(Ty(),Ft(e))}function qy(e){let n=uf();return n?n.sanitize(je.URL,e)||"":Ht(e,"URL")?Xe(e):wi(Ft(e))}function Yy(e){let n=uf();if(n)return Rg(n.sanitize(je.RESOURCE_URL,e)||"");if(Ht(e,"ResourceURL"))return Rg(Xe(e));throw new v(904,!1)}var SI=new Set(["embed","frame","iframe","media","script"]),TI=new Set(["base","link","script"]);function xI(e,n){return n==="src"&&SI.has(e)||n==="href"&&TI.has(e)||n==="xlink:href"&&e==="script"?Yy:qy}function AI(e,n,t){return xI(n,t)(e)}function uf(){let e=D();return e&&e[yt].sanitizer}function NI(e){return e.ownerDocument.defaultView}function RI(e){return e.ownerDocument}function Zy(e){return e instanceof Function?e():e}function OI(e,n,t){let r=e.length;for(;;){let o=e.indexOf(n,t);if(o===-1)return o;if(o===0||e.charCodeAt(o-1)<=32){let i=n.length;if(o+i===r||e.charCodeAt(o+i)<=32)return o}t=o+1}}var Ky="ng-template";function kI(e,n,t,r){let o=0;if(r){for(;o-1){let i;for(;++oi?d="":d=o[u+1].toLowerCase(),r&2&&l!==d){if(Dt(r))return!1;s=!0}}}}return Dt(r)||s}function Dt(e){return(e&1)===0}function LI(e,n,t,r){if(n===null)return-1;let o=0;if(r||!t){let i=!1;for(;o-1)for(t++;t0?'="'+a+'"':"")+"]"}else r&8?o+="."+s:r&4&&(o+=" "+s);else o!==""&&!Dt(s)&&(n+=Vg(i,o),o=""),r=s,i=i||!Dt(r);t++}return o!==""&&(n+=Vg(i,o)),n}function $I(e){return e.map(UI).join(",")}function zI(e){let n=[],t=[],r=1,o=2;for(;r=0;i--){let s=t[i],a=s.parentNode;s===n?(t.splice(i,1),di.add(s),s.dispatchEvent(new CustomEvent("animationend",{detail:{cancel:!0}}))):(o&&s===o||a&&r&&a!==r)&&(t.splice(i,1),s.dispatchEvent(new CustomEvent("animationend",{detail:{cancel:!0}})),s.parentNode?.removeChild(s))}}function KI(e,n){let t=Ad.get(e);t?t.includes(n)||t.push(n):Ad.set(e,[n])}var hr=new Set,Qa=(function(e){return e[e.CHANGE_DETECTION=0]="CHANGE_DETECTION",e[e.AFTER_NEXT_RENDER=1]="AFTER_NEXT_RENDER",e})(Qa||{}),It=new y(""),jg=new Set;function nn(e){jg.has(e)||(jg.add(e),performance?.mark?.("mark_feature_usage",{detail:{feature:e}}))}var Ja=(()=>{class e{impl=null;execute(){this.impl?.execute()}static \u0275prov=g({token:e,providedIn:"root",factory:()=>new e})}return e})(),gf=[0,1,2,3],yf=(()=>{class e{ngZone=f(P);scheduler=f(Rt);errorHandler=f(nt,{optional:!0});sequences=new Set;deferredRegistrations=new Set;executing=!1;constructor(){f(It,{optional:!0})}execute(){let t=this.sequences.size>0;t&&Y($.AfterRenderHooksStart),this.executing=!0;for(let r of gf)for(let o of this.sequences)if(!(o.erroredOrDestroyed||!o.hooks[r]))try{o.pipelinedValue=this.ngZone.runOutsideAngular(()=>this.maybeTrace(()=>{let i=o.hooks[r];return i(o.pipelinedValue)},o.snapshot))}catch(i){o.erroredOrDestroyed=!0,this.errorHandler?.handleError(i)}this.executing=!1;for(let r of this.sequences)r.afterRun(),r.once&&(this.sequences.delete(r),r.destroy());for(let r of this.deferredRegistrations)this.sequences.add(r);this.deferredRegistrations.size>0&&this.scheduler.notify(7),this.deferredRegistrations.clear(),t&&Y($.AfterRenderHooksEnd)}register(t){let{view:r}=t;r!==void 0?((r[sr]??=[]).push(t),lr(r),r[N]|=8192):this.executing?this.deferredRegistrations.add(t):this.addSequence(t)}addSequence(t){this.sequences.add(t),this.scheduler.notify(7)}unregister(t){this.executing&&this.sequences.has(t)?(t.erroredOrDestroyed=!0,t.pipelinedValue=void 0,t.once=!0):(this.sequences.delete(t),this.deferredRegistrations.delete(t))}maybeTrace(t,r){return r?r.run(Qa.AFTER_NEXT_RENDER,t):t()}static \u0275prov=g({token:e,providedIn:"root",factory:()=>new e})}return e})(),mi=class{impl;hooks;view;once;snapshot;erroredOrDestroyed=!1;pipelinedValue=void 0;unregisterOnDestroy;constructor(n,t,r,o,i,s=null){this.impl=n,this.hooks=t,this.view=r,this.once=o,this.snapshot=s,this.unregisterOnDestroy=i?.onDestroy(()=>this.destroy())}afterRun(){this.erroredOrDestroyed=!1,this.pipelinedValue=void 0,this.snapshot?.dispose(),this.snapshot=null}destroy(){this.impl.unregister(this),this.unregisterOnDestroy?.();let n=this.view?.[sr];n&&(this.view[sr]=n.filter(t=>t!==this))}};function An(e,n){let t=n?.injector??f(j);return nn("NgAfterNextRender"),QI(e,t,n,!0)}function XI(e){return e instanceof Function?[void 0,void 0,e,void 0]:[e.earlyRead,e.write,e.mixedReadWrite,e.read]}function QI(e,n,t,r){let o=n.get(Ja);o.impl??=n.get(yf);let i=n.get(It,null,{optional:!0}),s=t?.manualCleanup!==!0?n.get(Pe):null,a=n.get(Zr,null,{optional:!0}),c=new mi(o.impl,XI(e),a?.view,r,s,i?.snapshot(null));return o.impl.register(c),c}var nv=new y("",{factory:()=>({queue:new Set,isScheduled:!1,scheduler:null,injector:f(ce)})});function rv(e,n,t){let r=e.get(nv);if(Array.isArray(n))for(let o of n)r.queue.add(o),t?.detachedLeaveAnimationFns?.push(o);else r.queue.add(n),t?.detachedLeaveAnimationFns?.push(n);r.scheduler&&r.scheduler(e)}function JI(e,n){let t=e.get(nv);if(n.detachedLeaveAnimationFns){for(let r of n.detachedLeaveAnimationFns)t.queue.delete(r);n.detachedLeaveAnimationFns=void 0}}function eM(e,n){for(let[t,r]of n)rv(e,r.animateFns)}function Bg(e,n,t,r){let o=e?.[Cn]?.enter;n!==null&&o&&o.has(t.index)&&eM(r,o)}function Xr(e,n,t,r,o,i,s,a){if(o!=null){let c,l=!1;vt(o)?c=o:Kt(o)&&(l=!0,o=o[gt]);let u=ot(o);e===0&&r!==null?(Bg(a,r,i,t),s==null?zy(n,r,u):ka(n,r,u,s||null,!0)):e===1&&r!==null?(Bg(a,r,i,t),ka(n,r,u,s||null,!0),ZI(i,u)):e===2?(a?.[Cn]?.leave?.has(i.index)&&KI(i,u),di.delete(u),Hg(a,i,t,d=>{if(di.has(u)){di.delete(u);return}Gy(n,u,l,d)})):e===3&&(di.delete(u),Hg(a,i,t,()=>{n.destroyNode(u)})),c!=null&&dM(n,e,t,c,i,r,s)}}function tM(e,n){ov(e,n),n[gt]=null,n[Ne]=null}function nM(e,n,t,r,o,i){r[gt]=o,r[Ne]=n,tc(e,r,t,1,o,i)}function ov(e,n){n[yt].changeDetectionScheduler?.notify(9),tc(e,n,n[K],2,null,null)}function rM(e){let n=e[zr];if(!n)return sd(e[S],e);for(;n;){let t=null;if(Kt(n))t=n[zr];else{let r=n[se];r&&(t=r)}if(!t){for(;n&&!n[rt]&&n!==e;)Kt(n)&&sd(n[S],n),n=n[ge];n===null&&(n=e),Kt(n)&&sd(n[S],n),t=n&&n[rt]}n=t}}function vf(e,n){let t=e[ar],r=t.indexOf(n);t.splice(r,1)}function ec(e,n){if(cr(n))return;let t=n[K];t.destroyNode&&tc(e,n,t,3,null,null),rM(n)}function sd(e,n){if(cr(n))return;let t=M(null);try{n[N]&=-129,n[N]|=256,n[qe]&&vn(n[qe]),sM(e,n),iM(e,n),n[S].type===1&&n[K].destroy();let r=n[wn];if(r!==null&&vt(n[ge])){r!==n[ge]&&vf(r,n);let o=n[Pt];o!==null&&o.detachView(e)}vd(n)}finally{M(t)}}function Hg(e,n,t,r){let o=e?.[Cn];if(o==null||o.leave==null||!o.leave.has(n.index))return r(!1);e&&hr.add(e[Zt]),rv(t,()=>{if(o.leave&&o.leave.has(n.index)){let s=o.leave.get(n.index),a=[];if(s){for(let c=0;c{e[Cn].running=void 0,hr.delete(e[Zt]),n(!0)});return}n(!1)}function iM(e,n){let t=e.cleanup,r=n[$r];if(t!==null)for(let s=0;s=0?r[a]():r[-a].unsubscribe(),s+=2}else{let a=r[t[s+1]];t[s].call(a)}r!==null&&(n[$r]=null);let o=n[qt];if(o!==null){n[qt]=null;for(let s=0;see&&tv(e,n,ee,!1);let a=s?$.TemplateUpdateStart:$.TemplateCreateStart;Y(a,o,t),t(r,o)}finally{Mn(i);let a=s?$.TemplateUpdateEnd:$.TemplateCreateEnd;Y(a,o,t)}}function nc(e,n,t){yM(e,n,t),(t.flags&64)===64&&vM(e,n,t)}function Ii(e,n,t=it){let r=n.localNames;if(r!==null){let o=n.index+1;for(let i=0;inull;function gM(e){return e==="class"?"className":e==="for"?"htmlFor":e==="formaction"?"formAction":e==="innerHtml"?"innerHTML":e==="readonly"?"readOnly":e==="tabindex"?"tabIndex":e}function uv(e,n,t,r,o,i){let s=n[S];if(rc(e,s,n,t,r)){Lt(e)&&fv(n,e.index);return}e.type&3&&(t=gM(t)),dv(e,n,t,r,o,i)}function dv(e,n,t,r,o,i){if(e.type&3){let s=it(e,n);r=i!=null?i(r,e.value||"",t):r,o.setProperty(s,t,r)}else e.type&12}function fv(e,n){let t=st(n,e);t[N]&16||(t[N]|=64)}function yM(e,n,t){let r=t.directiveStart,o=t.directiveEnd;Lt(t)&&qI(n,t,e.data[r+t.componentOffset]),e.firstCreatePass||Oa(t,n);let i=t.initialInputs;for(let s=r;s{lr(e.lView)},consumerOnSignalRead(){this.lView[qe]=this}});function AM(e){let n=e[qe]??Object.create(NM);return n.lView=e,n}var NM=V(E({},gn),{consumerIsAlwaysLive:!0,kind:"template",consumerMarkedDirty:e=>{let n=En(e.lView);for(;n&&!yv(n[S]);)n=En(n);n&&Vu(n)},consumerOnSignalRead(){this.lView[qe]=this}});function yv(e){return e.type!==2}function vv(e){if(e[Dn]===null)return;let n=!0;for(;n;){let t=!1;for(let r of e[Dn])r.dirty&&(t=!0,r.zone===null||Zone.current===r.zone?r.run():r.zone.run(()=>r.run()));n=t&&!!(e[N]&8192)}}var RM=100;function bv(e,n=0){let r=e[yt].rendererFactory,o=!1;o||r.begin?.();try{OM(e,n)}finally{o||r.end?.()}}function OM(e,n){let t=Zu();try{Yo(!0),Rd(e,n);let r=0;for(;ri(e);){if(r===RM)throw new v(103,!1);r++,Rd(e,1)}}finally{Yo(t)}}function kM(e,n,t,r){if(cr(n))return;let o=n[N],i=!1,s=!1;ga(n);let a=!0,c=null,l=null;i||(yv(e)?(l=MM(n),c=Wt(l)):Gs()===null?(a=!1,l=AM(n),c=Wt(l)):n[qe]&&(vn(n[qe]),n[qe]=null));try{Lu(n),ig(e.bindingStartIndex),t!==null&&lv(e,n,t,2,r);let u=(o&3)===3;if(!i)if(u){let h=e.preOrderCheckHooks;h!==null&&Ca(n,h,null)}else{let h=e.preOrderHooks;h!==null&&Ia(n,h,0,null),od(n,0)}if(s||FM(n),vv(n),_v(n,0),e.contentQueries!==null&&Fy(e,n),!i)if(u){let h=e.contentCheckHooks;h!==null&&Ca(n,h)}else{let h=e.contentHooks;h!==null&&Ia(n,h,1),od(n,1)}LM(e,n);let d=e.components;d!==null&&Ev(n,d,0);let p=e.viewQuery;if(p!==null&&_d(2,p,r),!i)if(u){let h=e.viewCheckHooks;h!==null&&Ca(n,h)}else{let h=e.viewHooks;h!==null&&Ia(n,h,2),od(n,2)}if(e.firstUpdatePass===!0&&(e.firstUpdatePass=!1),n[la]){for(let h of n[la])h();n[la]=null}i||(mv(n),n[N]&=-73)}catch(u){throw i||lr(n),u}finally{l!==null&&(yn(l,c),a&&TM(l)),ya()}}function _v(e,n){for(let t=Iy(e);t!==null;t=My(t))for(let r=se;r0&&(e[t-1][rt]=r[rt]);let i=Jo(e,se+n);tM(r[S],r);let s=i[Pt];s!==null&&s.detachView(i[S]),r[ge]=null,r[rt]=null,r[N]&=-129}return r}function VM(e,n,t,r){let o=se+r,i=t.length;r>0&&(t[o-1][rt]=n),r-1&&(yi(n,r),Jo(t,r))}this._attachedToViewContainer=!1}ec(this._lView[S],this._lView)}onDestroy(n){ju(this._lView,n)}markForCheck(){If(this._cdRefInjectingView||this._lView,4)}detach(){this._lView[N]&=-129}reattach(){fa(this._lView),this._lView[N]|=128}detectChanges(){this._lView[N]|=1024,bv(this._lView)}checkNoChanges(){}attachToViewContainerRef(){if(this._appRef)throw new v(902,!1);this._attachedToViewContainer=!0}detachFromAppRef(){this._appRef=null;let n=Wr(this._lView),t=this._lView[wn];t!==null&&!n&&vf(t,this._lView),ov(this._lView[S],this._lView)}attachToAppRef(n){if(this._attachedToViewContainer)throw new v(902,!1);this._appRef=n;let t=Wr(this._lView),r=this._lView[wn];r!==null&&!t&&Mv(r,this._lView),fa(this._lView)}};var Ze=(()=>{class e{_declarationLView;_declarationTContainer;elementRef;static __NG_ELEMENT_ID__=jM;constructor(t,r,o){this._declarationLView=t,this._declarationTContainer=r,this.elementRef=o}get ssrId(){return this._declarationTContainer.tView?.ssrId||null}createEmbeddedView(t,r){return this.createEmbeddedViewImpl(t,r)}createEmbeddedViewImpl(t,r,o){let i=Mi(this._declarationLView,this._declarationTContainer,t,{embeddedViewInjector:r,dehydratedView:o});return new Sn(i)}}return e})();function jM(){return oc(he(),D())}function oc(e,n){return e.type&4?new Ze(n,e,oo(e,n)):null}function so(e,n,t,r,o){let i=e.data[n];if(i===null)i=BM(e,n,t,r,o),sg()&&(i.flags|=32);else if(i.type&64){i.type=t,i.value=r,i.attrs=o;let s=rg();i.injectorIndex=s===null?-1:s.injectorIndex}return qr(i,!0),i}function BM(e,n,t,r,o){let i=Wu(),s=qu(),a=s?i:i&&i.parent,c=e.data[n]=UM(e,a,t,n,r,o);return HM(e,c,i,s),c}function HM(e,n,t,r){e.firstChild===null&&(e.firstChild=n),t!==null&&(r?t.child==null&&n.parent!==null&&(t.child=n):t.next===null&&(t.next=n,n.prev=t))}function UM(e,n,t,r,o,i){let s=n?n.injectorIndex:-1,a=0;return $u()&&(a|=128),{type:t,index:r,insertBeforeIndex:null,injectorIndex:s,directiveStart:-1,directiveEnd:-1,directiveStylingLast:-1,componentOffset:-1,controlDirectiveIndex:-1,customControlIndex:-1,propertyBindings:null,flags:a,providerIndexes:0,value:o,attrs:i,mergedAttrs:null,localNames:null,initialInputs:null,inputs:null,hostDirectiveInputs:null,outputs:null,hostDirectiveOutputs:null,directiveToIndex:null,tView:null,next:null,prev:null,projectionNext:null,child:null,parent:n,projection:null,styles:null,stylesWithoutHost:null,residualStyles:void 0,classes:null,classesWithoutHost:null,residualClasses:void 0,classBindings:0,styleBindings:0}}function $M(e){let n=e[Ru]??[],r=e[ge][K],o=[];for(let i of n)i.data[Ay]!==void 0?o.push(i):zM(i,r);e[Ru]=o}function zM(e,n){let t=0,r=e.firstChild;if(r){let o=e.data[xy];for(;tnull,WM=()=>null;function Fa(e,n){return GM(e,n)}function Sv(e,n,t){return WM(e,n,t)}var Tv=class{},ic=class{},Od=class{resolveComponentFactory(n){throw new v(917,!1)}},Ti=class{static NULL=new Od},be=class{},Be=(()=>{class e{destroyNode=null;static __NG_ELEMENT_ID__=()=>qM()}return e})();function qM(){let e=D(),n=he(),t=st(n.index,e);return(Kt(t)?t:e)[K]}var xv=(()=>{class e{static \u0275prov=g({token:e,providedIn:"root",factory:()=>null})}return e})();var Sa={},kd=class{injector;parentInjector;constructor(n,t){this.injector=n,this.parentInjector=t}get(n,t,r){let o=this.injector.get(n,Sa,r);return o!==Sa||t===Sa?o:this.parentInjector.get(n,t,r)}};function Pa(e,n,t){let r=t?e.styles:null,o=t?e.classes:null,i=0;if(n!==null)for(let s=0;s0&&(t.directiveToIndex=new Map);for(let p=0;p0;){let t=e[--n];if(typeof t=="number"&&t<0)return t}return 0}function nS(e,n,t){if(t){if(n.exportAs)for(let r=0;rr(ot(b[e.index])):e.index;Pv(m,n,t,i,a,h,!1)}}return l}function sS(e){return e.startsWith("animation")||e.startsWith("transition")}function aS(e,n,t,r){let o=e.cleanup;if(o!=null)for(let i=0;ic?a[c]:null}typeof s=="string"&&(i+=2)}return null}function Pv(e,n,t,r,o,i,s){let a=n.firstCreatePass?Hu(n):null,c=Bu(t),l=c.length;c.push(o,i),a&&a.push(r,e,l,(l+1)*(s?-1:1))}function qg(e,n,t,r,o,i){let s=n[t],a=n[S],l=a.data[t].outputs[r],d=s[l].subscribe(i);Pv(e.index,a,n,o,i,d,!0)}var Fd=Symbol("BINDING");function Lv(e){return e.debugInfo?.className||e.type.name||null}var La=class extends Ti{ngModule;constructor(n){super(),this.ngModule=n}resolveComponentFactory(n){let t=kt(n);return new Tn(t,this.ngModule)}};function cS(e){return Object.keys(e).map(n=>{let[t,r,o]=e[n],i={propName:t,templateName:n,isSignal:(r&Xa.SignalBased)!==0};return o&&(i.transform=o),i})}function lS(e){return Object.keys(e).map(n=>({propName:e[n],templateName:n}))}function uS(e,n,t){let r=n instanceof ce?n:n?.injector;return r&&e.getStandaloneInjector!==null&&(r=e.getStandaloneInjector(r)||r),r?new kd(t,r):t}function dS(e){let n=e.get(be,null);if(n===null)throw new v(407,!1);let t=e.get(xv,null),r=e.get(Rt,null),o=e.get(It,null,{optional:!0});return{rendererFactory:n,sanitizer:t,changeDetectionScheduler:r,ngReflect:!1,tracingService:o}}function fS(e,n){let t=Vv(e);return $y(n,t,t==="svg"?ku:t==="math"?qm:null)}function Vv(e){return(e.selectors[0][0]||"div").toLowerCase()}var Tn=class extends ic{componentDef;ngModule;selector;componentType;ngContentSelectors;isBoundToModule;cachedInputs=null;cachedOutputs=null;get inputs(){return this.cachedInputs??=cS(this.componentDef.inputs),this.cachedInputs}get outputs(){return this.cachedOutputs??=lS(this.componentDef.outputs),this.cachedOutputs}constructor(n,t){super(),this.componentDef=n,this.ngModule=t,this.componentType=n.type,this.selector=$I(n.selectors),this.ngContentSelectors=n.ngContentSelectors??[],this.isBoundToModule=!!t}create(n,t,r,o,i,s){Y($.DynamicComponentStart);let a=M(null);try{let c=this.componentDef,l=uS(c,o||this.ngModule,n),u=dS(l),d=u.tracingService;return d&&d.componentCreate?d.componentCreate(Lv(c),()=>this.createComponentRef(u,l,t,r,i,s)):this.createComponentRef(u,l,t,r,i,s)}finally{M(a)}}createComponentRef(n,t,r,o,i,s){let a=this.componentDef,c=hS(o,a,s,i),l=n.rendererFactory.createRenderer(null,a),u=o?hM(l,o,a.encapsulation,t):fS(a,l),d=s?.some(Yg)||i?.some(m=>typeof m!="function"&&m.bindings.some(Yg)),p=hf(null,c,null,512|Qy(a),null,null,n,l,t,null,ky(u,t,!0));p[ee]=u,ga(p);let h=null;try{let m=Mf(ee,p,2,"#host",()=>c.directiveRegistry,!0,0);Wy(l,u,m),eo(u,p),nc(c,p,m),rf(c,m,p),Sf(c,m),r!==void 0&&mS(m,this.ngContentSelectors,r),h=st(m.index,p),p[ie]=h[ie],Cf(c,p,null)}catch(m){throw h!==null&&vd(h),vd(p),m}finally{Y($.DynamicComponentEnd),ya()}return new Va(this.componentType,p,!!d)}};function hS(e,n,t,r){let o=e?["ng-version","21.2.6"]:zI(n.selectors[0]),i=null,s=null,a=0;if(t)for(let u of t)a+=u[Fd].requiredVars,u.create&&(u.targetIdx=0,(i??=[]).push(u)),u.update&&(u.targetIdx=0,(s??=[]).push(u));if(r)for(let u=0;u{if(t&1&&e)for(let r of e)r.create();if(t&2&&n)for(let r of n)r.update()}}function Yg(e){let n=e[Fd].kind;return n==="input"||n==="twoWay"}var Va=class extends Tv{_rootLView;_hasInputBindings;instance;hostView;changeDetectorRef;componentType;location;previousInputValues=null;_tNode;constructor(n,t,r){super(),this._rootLView=t,this._hasInputBindings=r,this._tNode=ua(t[S],ee),this.location=oo(this._tNode,t),this.instance=st(this._tNode.index,t)[ie],this.hostView=this.changeDetectorRef=new Sn(t,void 0),this.componentType=n}setInput(n,t){this._hasInputBindings;let r=this._tNode;if(this.previousInputValues??=new Map,this.previousInputValues.has(n)&&Object.is(this.previousInputValues.get(n),t))return;let o=this._rootLView,i=rc(r,o[S],o,n,t);this.previousInputValues.set(n,t);let s=st(r.index,o);If(s,1)}get injector(){return new dr(this._tNode,this._rootLView)}destroy(){this.hostView.destroy()}onDestroy(n){this.hostView.onDestroy(n)}};function mS(e,n,t){let r=e.projection=[];for(let o=0;o{class e{static __NG_ELEMENT_ID__=gS}return e})();function gS(){let e=he();return jv(e,D())}var Pd=class e extends He{_lContainer;_hostTNode;_hostLView;constructor(n,t,r){super(),this._lContainer=n,this._hostTNode=t,this._hostLView=r}get element(){return oo(this._hostTNode,this._hostLView)}get injector(){return new dr(this._hostTNode,this._hostLView)}get parentInjector(){let n=Jd(this._hostTNode,this._hostLView);if(hy(n)){let t=Na(n,this._hostLView),r=Aa(n),o=t[S].data[r+8];return new dr(o,t)}else return new dr(null,this._hostLView)}clear(){for(;this.length>0;)this.remove(this.length-1)}get(n){let t=Zg(this._lContainer);return t!==null&&t[n]||null}get length(){return this._lContainer.length-se}createEmbeddedView(n,t,r){let o,i;typeof r=="number"?o=r:r!=null&&(o=r.index,i=r.injector);let s=Fa(this._lContainer,n.ssrId),a=n.createEmbeddedViewImpl(t||{},i,s);return this.insertImpl(a,o,to(this._hostTNode,s)),a}createComponent(n,t,r,o,i,s,a){let c=n&&!MC(n),l;if(c)l=t;else{let _=t||{};l=_.index,r=_.injector,o=_.projectableNodes,i=_.environmentInjector||_.ngModuleRef,s=_.directives,a=_.bindings}let u=c?n:new Tn(kt(n)),d=r||this.parentInjector;if(!i&&u.ngModule==null){let C=(c?d:this.parentInjector).get(ce,null);C&&(i=C)}let p=kt(u.componentType??{}),h=Fa(this._lContainer,p?.id??null),m=h?.firstChild??null,b=u.create(d,o,m,i,s,a);return this.insertImpl(b.hostView,l,to(this._hostTNode,h)),b}insert(n,t){return this.insertImpl(n,t,!0)}insertImpl(n,t,r){let o=n._lView;if(Zm(o)){let a=this.indexOf(n);if(a!==-1)this.detach(a);else{let c=o[ge],l=new e(c,c[Ne],c[ge]);l.detach(l.indexOf(n))}}let i=this._adjustIndex(t),s=this._lContainer;return Si(s,o,i,r),n.attachToViewContainerRef(),wu(ad(s),i,n),n}move(n,t){return this.insert(n,t)}indexOf(n){let t=Zg(this._lContainer);return t!==null?t.indexOf(n):-1}remove(n){let t=this._adjustIndex(n,-1),r=yi(this._lContainer,t);r&&(Jo(ad(this._lContainer),t),ec(r[S],r))}detach(n){let t=this._adjustIndex(n,-1),r=yi(this._lContainer,t);return r&&Jo(ad(this._lContainer),t)!=null?new Sn(r):null}_adjustIndex(n,t=0){return n??this.length+t}};function Zg(e){return e[ti]}function ad(e){return e[ti]||(e[ti]=[])}function jv(e,n){let t,r=n[e.index];return vt(r)?t=r:(t=wv(r,n,null,e),n[e.index]=t,pf(n,t)),vS(t,n,e,r),new Pd(t,e,n)}function yS(e,n){let t=e[K],r=t.createComment(""),o=it(n,e),i=t.parentNode(o);return ka(t,i,r,t.nextSibling(o),!1),r}var vS=DS,bS=()=>!1;function _S(e,n,t){return bS(e,n,t)}function DS(e,n,t,r){if(e[In])return;let o;t.type&8?o=ot(r):o=yS(n,t),e[In]=o}var Ld=class e{queryList;matches=null;constructor(n){this.queryList=n}clone(){return new e(this.queryList)}setDirty(){this.queryList.setDirty()}},Vd=class e{queries;constructor(n=[]){this.queries=n}createEmbeddedView(n){let t=n.queries;if(t!==null){let r=n.contentQueries!==null?n.contentQueries[0]:t.length,o=[];for(let i=0;i0)r.push(s[a/2]);else{let l=i[a+1],u=n[-c];for(let d=se;dn.trim())}function zv(e,n,t){e.queries===null&&(e.queries=new jd),e.queries.track(new Bd(n,t))}function SS(e,n){let t=e.contentQueries||(e.contentQueries=[]),r=t.length?t[t.length-1]:-1;n!==r&&t.push(e.queries.length-1,n)}function Af(e,n){return e.queries.getByIndex(n)}function Gv(e,n){let t=e[S],r=Af(t,n);return r.crossesNgTemplate?Hd(t,e,n,[]):Bv(t,e,r,n)}function Nf(e,n,t){let r,o=$o(()=>{r._dirtyCounter();let i=TS(r,e);if(n&&i===void 0)throw new v(-951,!1);return i});return r=o[oe],r._dirtyCounter=Me(0),r._flatValue=void 0,o}function Rf(e){return Nf(!0,!1,e)}function Of(e){return Nf(!0,!0,e)}function Wv(e){return Nf(!1,!1,e)}function qv(e,n){let t=e[oe];t._lView=D(),t._queryIndex=n,t._queryList=xf(t._lView,n),t._queryList.onDirty(()=>t._dirtyCounter.update(r=>r+1))}function TS(e,n){let t=e._lView,r=e._queryIndex;if(t===void 0||r===void 0||t[N]&4)return n?void 0:Ce;let o=xf(t,r),i=Gv(t,r);return o.reset(i,Ey),n?o.first:o._changesDetected||e._flatValue===void 0?e._flatValue=o.toArray():e._flatValue}var Bt=class{},Yv=class{};var Ba=class extends Bt{ngModuleType;_parent;_bootstrapComponents=[];_r3Injector;instance;destroyCbs=[];componentFactoryResolver=new La(this);constructor(n,t,r,o=!0){super(),this.ngModuleType=n,this._parent=t;let i=_u(n);this._bootstrapComponents=Zy(i.bootstrap),this._r3Injector=Ju(n,t,[{provide:Bt,useValue:this},{provide:Ti,useValue:this.componentFactoryResolver},...r],Xo(n),new Set(["environment"])),o&&this.resolveInjectorInitializers()}resolveInjectorInitializers(){this._r3Injector.resolveInjectorInitializers(),this.instance=this._r3Injector.get(this.ngModuleType)}get injector(){return this._r3Injector}destroy(){let n=this._r3Injector;!n.destroyed&&n.destroy(),this.destroyCbs.forEach(t=>t()),this.destroyCbs=null}onDestroy(n){this.destroyCbs.push(n)}},Ha=class extends Yv{moduleType;constructor(n){super(),this.moduleType=n}create(n){return new Ba(this.moduleType,n,[])}};var bi=class extends Bt{injector;componentFactoryResolver=new La(this);instance=null;constructor(n){super();let t=new er([...n.providers,{provide:Bt,useValue:this},{provide:Ti,useValue:this.componentFactoryResolver}],n.parent||Hr(),n.debugName,new Set(["environment"]));this.injector=t,n.runEnvironmentInitializers&&t.resolveInjectorInitializers()}destroy(){this.injector.destroy()}onDestroy(n){this.injector.onDestroy(n)}};function Zv(e,n,t=null){return new bi({providers:e,parent:n,debugName:t,runEnvironmentInitializers:!0}).injector}var xS=(()=>{class e{_injector;cachedInjectors=new Map;constructor(t){this._injector=t}getOrCreateStandaloneInjector(t){if(!t.standalone)return null;if(!this.cachedInjectors.has(t)){let r=Mu(!1,t.type),o=r.length>0?Zv([r],this._injector,""):null;this.cachedInjectors.set(t,o)}return this.cachedInjectors.get(t)}ngOnDestroy(){try{for(let t of this.cachedInjectors.values())t!==null&&t.destroy()}finally{this.cachedInjectors.clear()}}static \u0275prov=g({token:e,providedIn:"environment",factory:()=>new e(I(ce))})}return e})();function Se(e){return Di(()=>{let n=Kv(e),t=V(E({},n),{decls:e.decls,vars:e.vars,template:e.template,consts:e.consts||null,ngContentSelectors:e.ngContentSelectors,onPush:e.changeDetection===tf.OnPush,directiveDefs:null,pipeDefs:null,dependencies:n.standalone&&e.dependencies||null,getStandaloneInjector:n.standalone?o=>o.get(xS).getOrCreateStandaloneInjector(t):null,getExternalStyles:null,signals:e.signals??!1,data:e.data||{},encapsulation:e.encapsulation||wt.Emulated,styles:e.styles||Ce,_:null,schemas:e.schemas||null,tView:null,id:""});n.standalone&&nn("NgStandalone"),Xv(t);let r=e.dependencies;return t.directiveDefs=Kg(r,AS),t.pipeDefs=Kg(r,km),t.id=OS(t),t})}function AS(e){return kt(e)||ra(e)}function Z(e){return Di(()=>({type:e.type,bootstrap:e.bootstrap||Ce,declarations:e.declarations||Ce,imports:e.imports||Ce,exports:e.exports||Ce,transitiveCompileScopes:null,schemas:e.schemas||null,id:e.id||null}))}function NS(e,n){if(e==null)return mt;let t={};for(let r in e)if(e.hasOwnProperty(r)){let o=e[r],i,s,a,c;Array.isArray(o)?(a=o[0],i=o[1],s=o[2]??i,c=o[3]||null):(i=o,s=o,a=Xa.None,c=null),t[i]=[r,a,c],n[i]=s}return t}function RS(e){if(e==null)return mt;let n={};for(let t in e)e.hasOwnProperty(t)&&(n[e[t]]=t);return n}function O(e){return Di(()=>{let n=Kv(e);return Xv(n),n})}function xi(e){return{type:e.type,name:e.name,factory:null,pure:e.pure!==!1,standalone:e.standalone??!0,onDestroy:e.type.prototype.ngOnDestroy||null}}function Kv(e){let n={};return{type:e.type,providersResolver:null,viewProvidersResolver:null,factory:null,hostBindings:e.hostBindings||null,hostVars:e.hostVars||0,hostAttrs:e.hostAttrs||null,contentQueries:e.contentQueries||null,declaredInputs:n,inputConfig:e.inputs||mt,exportAs:e.exportAs||null,standalone:e.standalone??!0,signals:e.signals===!0,selectors:e.selectors||Ce,viewQuery:e.viewQuery||null,features:e.features||null,setInput:null,resolveHostDirectives:null,hostDirectives:null,controlDef:null,inputs:NS(e.inputs,n),outputs:RS(e.outputs),debugInfo:null}}function Xv(e){e.features?.forEach(n=>n(e))}function Kg(e,n){return e?()=>{let t=typeof e=="function"?e():e,r=[];for(let o of t){let i=n(o);i!==null&&r.push(i)}return r}:null}function OS(e){let n=0,t=typeof e.consts=="function"?"":e.consts,r=[e.selectors,e.ngContentSelectors,e.hostVars,e.hostAttrs,t,e.vars,e.decls,e.encapsulation,e.standalone,e.signals,e.exportAs,JSON.stringify(e.inputs),JSON.stringify(e.outputs),Object.getOwnPropertyNames(e.type.prototype),!!e.contentQueries,!!e.viewQuery];for(let i of r.join("|"))n=Math.imul(31,n)+i.charCodeAt(0)<<0;return n+=2147483648,"c"+n}function kS(e){let n=t=>{let r=Array.isArray(e);t.hostDirectives===null?(t.resolveHostDirectives=FS,t.hostDirectives=r?e.map(Ud):[e]):r?t.hostDirectives.unshift(...e.map(Ud)):t.hostDirectives.unshift(e)};return n.ngInherit=!0,n}function FS(e){let n=[],t=!1,r=null,o=null;for(let i=0;i=0;r--){let o=e[r];o.hostVars=n+=o.hostVars,o.hostAttrs=Jr(o.hostAttrs,t=Jr(t,o.hostAttrs))}}function cd(e){return e===mt?{}:e===Ce?[]:e}function BS(e,n){let t=e.viewQuery;t?e.viewQuery=(r,o)=>{n(r,o),t(r,o)}:e.viewQuery=n}function HS(e,n){let t=e.contentQueries;t?e.contentQueries=(r,o,i)=>{n(r,o,i),t(r,o,i)}:e.contentQueries=n}function US(e,n){let t=e.hostBindings;t?e.hostBindings=(r,o)=>{n(r,o),t(r,o)}:e.hostBindings=n}function Jv(e,n,t,r,o,i,s,a){if(t.firstCreatePass){e.mergedAttrs=Jr(e.mergedAttrs,e.attrs);let u=e.tView=ff(2,e,o,i,s,t.directiveRegistry,t.pipeRegistry,null,t.schemas,t.consts,null);t.queries!==null&&(t.queries.template(t,e),u.queries=t.queries.embeddedTView(e))}a&&(e.flags|=a),qr(e,!1);let c=zS(t,n,e,r);va()&&bf(t,n,c,e),eo(c,n);let l=wv(c,n,c,e);n[r+ee]=l,pf(n,l),_S(l,e,n)}function $S(e,n,t,r,o,i,s,a,c,l,u){let d=t+ee,p;return n.firstCreatePass?(p=so(n,d,4,s||null,a||null),ha()&&Av(n,e,p,Ye(n.consts,l),Df),uy(n,p)):p=n.data[d],Jv(p,e,n,t,r,o,i,c),Gr(p)&&nc(n,e,p),l!=null&&Ii(e,p,u),p}function no(e,n,t,r,o,i,s,a,c,l,u){let d=t+ee,p;if(n.firstCreatePass){if(p=so(n,d,4,s||null,a||null),l!=null){let h=Ye(n.consts,l);p.localNames=[];for(let m=0;m{class e{log(t){console.log(t)}warn(t){console.warn(t)}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"platform"})}return e})();function co(e){return typeof e=="function"&&e[oe]!==void 0}function kf(e){return co(e)&&typeof e.set=="function"}var ac=new y(""),cc=new y(""),Ai=(()=>{class e{_ngZone;registry;_isZoneStable=!0;_callbacks=[];_taskTrackingZone=null;_destroyRef;constructor(t,r,o){this._ngZone=t,this.registry=r,xu()&&(this._destroyRef=f(Pe,{optional:!0})??void 0),Ff||(ob(o),o.addToWindow(r)),this._watchAngularEvents(),t.run(()=>{this._taskTrackingZone=typeof Zone>"u"?null:Zone.current.get("TaskTrackingZone")})}_watchAngularEvents(){let t=this._ngZone.onUnstable.subscribe({next:()=>{this._isZoneStable=!1}}),r=this._ngZone.runOutsideAngular(()=>this._ngZone.onStable.subscribe({next:()=>{P.assertNotInAngularZone(),queueMicrotask(()=>{this._isZoneStable=!0,this._runCallbacksIfReady()})}}));this._destroyRef?.onDestroy(()=>{t.unsubscribe(),r.unsubscribe()})}isStable(){return this._isZoneStable&&!this._ngZone.hasPendingMacrotasks}_runCallbacksIfReady(){if(this.isStable())queueMicrotask(()=>{for(;this._callbacks.length!==0;){let t=this._callbacks.pop();clearTimeout(t.timeoutId),t.doneCb()}});else{let t=this.getPendingTasks();this._callbacks=this._callbacks.filter(r=>r.updateCb&&r.updateCb(t)?(clearTimeout(r.timeoutId),!1):!0)}}getPendingTasks(){return this._taskTrackingZone?this._taskTrackingZone.macroTasks.map(t=>({source:t.source,creationLocation:t.creationLocation,data:t.data})):[]}addCallback(t,r,o){let i=-1;r&&r>0&&(i=setTimeout(()=>{this._callbacks=this._callbacks.filter(s=>s.timeoutId!==i),t()},r)),this._callbacks.push({doneCb:t,timeoutId:i,updateCb:o})}whenStable(t,r,o){if(o&&!this._taskTrackingZone)throw new Error('Task tracking zone is required when passing an update callback to whenStable(). Is "zone.js/plugins/task-tracking" loaded?');this.addCallback(t,r,o),this._runCallbacksIfReady()}registerApplication(t){this.registry.registerApplication(t,this)}unregisterApplication(t){this.registry.unregisterApplication(t)}findProviders(t,r,o){return[]}static \u0275fac=function(r){return new(r||e)(I(P),I(rb),I(cc))};static \u0275prov=g({token:e,factory:e.\u0275fac})}return e})(),rb=(()=>{class e{_applications=new Map;registerApplication(t,r){this._applications.set(t,r)}unregisterApplication(t){this._applications.delete(t)}unregisterAllApplications(){this._applications.clear()}getTestability(t){return this._applications.get(t)||null}getAllTestabilities(){return Array.from(this._applications.values())}getAllRootElements(){return Array.from(this._applications.keys())}findTestabilityInTree(t,r=!0){return Ff?.findTestabilityInTree(this,t,r)??null}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"platform"})}return e})();function ob(e){Ff=e}var Ff;function gr(e){return!!e&&typeof e.then=="function"}function lc(e){return!!e&&typeof e.subscribe=="function"}var Pf=new y("");function WS(e){return nr([{provide:Pf,multi:!0,useValue:e}])}var Lf=(()=>{class e{resolve;reject;initialized=!1;done=!1;donePromise=new Promise((t,r)=>{this.resolve=t,this.reject=r});appInits=f(Pf,{optional:!0})??[];injector=f(j);constructor(){}runInitializers(){if(this.initialized)return;let t=[];for(let o of this.appInits){let i=Ur(this.injector,o);if(gr(i))t.push(i);else if(lc(i)){let s=new Promise((a,c)=>{i.subscribe({complete:a,error:c})});t.push(s)}}let r=()=>{this.done=!0,this.resolve()};Promise.all(t).then(()=>{r()}).catch(o=>{this.reject(o)}),t.length===0&&r(),this.initialized=!0}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})(),ib=new y("");function sb(){Kl(()=>{let e="";throw new v(600,e)})}function ab(e){return e.isBoundToModule}var qS=10;var Qe=(()=>{class e{_runningTick=!1;_destroyed=!1;_destroyListeners=[];_views=[];internalErrorHandler=f(Qt);afterRenderManager=f(Ja);zonelessEnabled=f(ci);rootEffectScheduler=f(ba);dirtyFlags=0;tracingSnapshot=null;allTestViews=new Set;autoDetectTestViews=new Set;includeAllTestViews=!1;afterTick=new R;get allViews(){return[...(this.includeAllTestViews?this.allTestViews:this.autoDetectTestViews).keys(),...this._views]}get destroyed(){return this._destroyed}componentTypes=[];components=[];internalPendingTask=f(ur);get isStable(){return this.internalPendingTask.hasPendingTasksObservable.pipe(re(t=>!t))}constructor(){f(It,{optional:!0})}whenStable(){let t;return new Promise(r=>{t=this.isStable.subscribe({next:o=>{o&&r()}})}).finally(()=>{t.unsubscribe()})}_injector=f(ce);_rendererFactory=null;get injector(){return this._injector}bootstrap(t,r){return this.bootstrapImpl(t,r)}bootstrapImpl(t,r,o=j.NULL){return this._injector.get(P).run(()=>{Y($.BootstrapComponentStart);let s=t instanceof ic;if(!this._injector.get(Lf).done){let m="";throw new v(405,m)}let c;s?c=t:c=this._injector.get(Ti).resolveComponentFactory(t),this.componentTypes.push(c.componentType);let l=ab(c)?void 0:this._injector.get(Bt),u=r||c.selector,d=c.create(o,[],u,l),p=d.location.nativeElement,h=d.injector.get(ac,null);return h?.registerApplication(p),d.onDestroy(()=>{this.detachView(d.hostView),hi(this.components,d),h?.unregisterApplication(p)}),this._loadComponent(d),Y($.BootstrapComponentEnd,d),d})}tick(){this.zonelessEnabled||(this.dirtyFlags|=1),this._tick()}_tick(){Y($.ChangeDetectionStart),this.tracingSnapshot!==null?this.tracingSnapshot.run(Qa.CHANGE_DETECTION,this.tickImpl):this.tickImpl()}tickImpl=()=>{if(this._runningTick)throw Y($.ChangeDetectionEnd),new v(101,!1);let t=M(null);try{this._runningTick=!0,this.synchronize()}finally{this._runningTick=!1,this.tracingSnapshot?.dispose(),this.tracingSnapshot=null,M(t),this.afterTick.next(),Y($.ChangeDetectionEnd)}};synchronize(){this._rendererFactory===null&&!this._injector.destroyed&&(this._rendererFactory=this._injector.get(be,null,{optional:!0}));let t=0;for(;this.dirtyFlags!==0&&t++ri(t))){this.dirtyFlags|=2;return}else this.dirtyFlags&=-8}attachView(t){let r=t;this._views.push(r),r.attachToAppRef(this)}detachView(t){let r=t;hi(this._views,r),r.detachFromAppRef()}_loadComponent(t){this.attachView(t.hostView);try{this.tick()}catch(o){this.internalErrorHandler(o)}this.components.push(t),this._injector.get(ib,[]).forEach(o=>o(t))}ngOnDestroy(){if(!this._destroyed)try{this._destroyListeners.forEach(t=>t()),this._views.slice().forEach(t=>t.destroy())}finally{this._destroyed=!0,this._views=[],this._destroyListeners=[]}}onDestroy(t){return this._destroyListeners.push(t),()=>hi(this._destroyListeners,t)}destroy(){if(this._destroyed)throw new v(406,!1);let t=this._injector;t.destroy&&!t.destroyed&&t.destroy()}get viewCount(){return this._views.length}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();function hi(e,n){let t=e.indexOf(n);t>-1&&e.splice(t,1)}function cb(e,n){let t=D(),r=Xt();if(Le(t,r,n)){let o=J(),i=Yr();if(rc(i,o,t,e,n))Lt(i)&&fv(t,i.index);else{let a=it(i,t);hv(t[K],a,null,i.value,e,n,null)}}return cb}function rn(e,n,t,r){let o=D(),i=Xt();if(Le(o,i,n)){let s=J(),a=Yr();_M(a,o,e,n,t,r)}return rn}function YS(){return D()[Ie][ie]}var $d=class{destroy(n){}updateValue(n,t){}swap(n,t){let r=Math.min(n,t),o=Math.max(n,t),i=this.detach(o);if(o-r>1){let s=this.detach(r);this.attach(r,i),this.attach(o,s)}else this.attach(r,i)}move(n,t){this.attach(t,this.detach(n))}};function ld(e,n,t,r,o){return e===t&&Object.is(n,r)?1:Object.is(o(e,n),o(t,r))?-1:0}function ZS(e,n,t,r){let o,i,s=0,a=e.length-1,c=void 0;if(Array.isArray(n)){M(r);let l=n.length-1;for(M(null);s<=a&&s<=l;){let u=e.at(s),d=n[s],p=ld(s,u,s,d,t);if(p!==0){p<0&&e.updateValue(s,d),s++;continue}let h=e.at(a),m=n[l],b=ld(a,h,l,m,t);if(b!==0){b<0&&e.updateValue(a,m),a--,l--;continue}let _=t(s,u),C=t(a,h),ne=t(s,d);if(Object.is(ne,C)){let Ge=t(l,m);Object.is(Ge,_)?(e.swap(s,a),e.updateValue(a,m),l--,a--):e.move(a,s),e.updateValue(s,d),s++;continue}if(o??=new Ua,i??=ey(e,s,a,t),zd(e,o,s,ne))e.updateValue(s,d),s++,a++;else if(i.has(ne))o.set(_,e.detach(s)),a--;else{let Ge=e.create(s,n[s]);e.attach(s,Ge),s++,a++}}for(;s<=l;)Jg(e,o,t,s,n[s]),s++}else if(n!=null){M(r);let l=n[Symbol.iterator]();M(null);let u=l.next();for(;!u.done&&s<=a;){let d=e.at(s),p=u.value,h=ld(s,d,s,p,t);if(h!==0)h<0&&e.updateValue(s,p),s++,u=l.next();else{o??=new Ua,i??=ey(e,s,a,t);let m=t(s,p);if(zd(e,o,s,m))e.updateValue(s,p),s++,a++,u=l.next();else if(!i.has(m))e.attach(s,e.create(s,p)),s++,a++,u=l.next();else{let b=t(s,d);o.set(b,e.detach(s)),a--}}}for(;!u.done;)Jg(e,o,t,e.length,u.value),u=l.next()}for(;s<=a;)e.destroy(e.detach(a--));o?.forEach(l=>{e.destroy(l)})}function zd(e,n,t,r){return n!==void 0&&n.has(r)?(e.attach(t,n.get(r)),n.delete(r),!0):!1}function Jg(e,n,t,r,o){if(zd(e,n,r,t(r,o)))e.updateValue(r,o);else{let i=e.create(r,o);e.attach(r,i)}}function ey(e,n,t,r){let o=new Set;for(let i=n;i<=t;i++)o.add(r(i,e.at(i)));return o}var Ua=class{kvMap=new Map;_vMap=void 0;has(n){return this.kvMap.has(n)}delete(n){if(!this.has(n))return!1;let t=this.kvMap.get(n);return this._vMap!==void 0&&this._vMap.has(t)?(this.kvMap.set(n,this._vMap.get(t)),this._vMap.delete(t)):this.kvMap.delete(n),!0}get(n){return this.kvMap.get(n)}set(n,t){if(this.kvMap.has(n)){let r=this.kvMap.get(n);this._vMap===void 0&&(this._vMap=new Map);let o=this._vMap;for(;o.has(r);)r=o.get(r);o.set(r,t)}else this.kvMap.set(n,t)}forEach(n){for(let[t,r]of this.kvMap)if(n(r,t),this._vMap!==void 0){let o=this._vMap;for(;o.has(r);)r=o.get(r),n(r,t)}}};function KS(e,n,t,r,o,i,s,a){nn("NgControlFlow");let c=D(),l=J(),u=Ye(l.consts,i);return no(c,l,e,n,t,r,o,u,256,s,a),Vf}function Vf(e,n,t,r,o,i,s,a){nn("NgControlFlow");let c=D(),l=J(),u=Ye(l.consts,i);return no(c,l,e,n,t,r,o,u,512,s,a),Vf}function XS(e,n){nn("NgControlFlow");let t=D(),r=Xt(),o=t[r]!==_e?t[r]:-1,i=o!==-1?$a(t,ee+o):void 0,s=0;if(Le(t,r,e)){let a=M(null);try{if(i!==void 0&&Iv(i,s),e!==-1){let c=ee+e,l=$a(t,c),u=Yd(t[S],c),d=Sv(l,u,t),p=Mi(t,u,n,{dehydratedView:d});Si(l,p,s,to(u,d))}}finally{M(a)}}else if(i!==void 0){let a=Cv(i,s);a!==void 0&&(a[ie]=n)}}var Gd=class{lContainer;$implicit;$index;constructor(n,t,r){this.lContainer=n,this.$implicit=t,this.$index=r}get $count(){return this.lContainer.length-se}};function QS(e){return e}function JS(e,n){return n}var Wd=class{hasEmptyBlock;trackByFn;liveCollection;constructor(n,t,r){this.hasEmptyBlock=n,this.trackByFn=t,this.liveCollection=r}};function eT(e,n,t,r,o,i,s,a,c,l,u,d,p){nn("NgControlFlow");let h=D(),m=J(),b=c!==void 0,_=D(),C=a?s.bind(_[Ie][ie]):s,ne=new Wd(b,C);_[ee+e]=ne,no(h,m,e+1,n,t,r,o,Ye(m.consts,i),256),b&&no(h,m,e+2,c,l,u,d,Ye(m.consts,p),512)}var qd=class extends $d{lContainer;hostLView;templateTNode;operationsCounter=void 0;needsIndexUpdate=!1;constructor(n,t,r){super(),this.lContainer=n,this.hostLView=t,this.templateTNode=r}get length(){return this.lContainer.length-se}at(n){return this.getLView(n)[ie].$implicit}attach(n,t){let r=t[rr];this.needsIndexUpdate||=n!==this.length,Si(this.lContainer,t,n,to(this.templateTNode,r)),nT(this.lContainer,n)}detach(n){return this.needsIndexUpdate||=n!==this.length-1,rT(this.lContainer,n),oT(this.lContainer,n)}create(n,t){let r=Fa(this.lContainer,this.templateTNode.tView.ssrId);return Mi(this.hostLView,this.templateTNode,new Gd(this.lContainer,t,n),{dehydratedView:r})}destroy(n){ec(n[S],n)}updateValue(n,t){this.getLView(n)[ie].$implicit=t}reset(){this.needsIndexUpdate=!1}updateIndexes(){if(this.needsIndexUpdate)for(let n=0;n0){let i=r[Yt];JI(i,o),hr.delete(r[Zt]),o.detachedLeaveAnimationFns=void 0}}function rT(e,n){if(e.length<=se)return;let t=se+n,r=e[t],o=r?r[Cn]:void 0;o&&o.leave&&o.leave.size>0&&(o.detachedLeaveAnimationFns=[])}function oT(e,n){return yi(e,n)}function iT(e,n){return Cv(e,n)}function Yd(e,n){return ua(e,n)}function lb(e,n,t){let r=D(),o=Xt();if(Le(r,o,n)){let i=J(),s=Yr();uv(s,r,e,n,r[K],t)}return lb}function Zd(e,n,t,r,o){rc(n,e,t,o?"class":"style",r)}function za(e,n,t,r){let o=D(),i=o[S],s=e+ee,a=i.firstCreatePass?Mf(s,o,2,n,Df,ha(),t,r):i.data[s];if(Lt(a)){let c=o[yt].tracingService;if(c&&c.componentCreate){let l=i.data[a.directiveStart+a.componentOffset];return c.componentCreate(Lv(l),()=>(ty(e,n,o,a,r),za))}}return ty(e,n,o,a,r),za}function ty(e,n,t,r,o){if(Ef(r,t,e,n,db),Gr(r)){let i=t[S];nc(i,t,r),rf(i,r,t)}o!=null&&Ii(t,r)}function jf(){let e=J(),n=he(),t=wf(n);return e.firstCreatePass&&Sf(e,t),zu(t)&&Gu(),Uu(),t.classesWithoutHost!=null&&RC(t)&&Zd(e,t,D(),t.classesWithoutHost,!0),t.stylesWithoutHost!=null&&OC(t)&&Zd(e,t,D(),t.stylesWithoutHost,!1),jf}function ub(e,n,t,r){return za(e,n,t,r),jf(),ub}function lo(e,n,t,r){let o=D(),i=o[S],s=e+ee,a=i.firstCreatePass?oS(s,i,2,n,t,r):i.data[s];return Ef(a,o,e,n,db),r!=null&&Ii(o,a),lo}function uo(){let e=he(),n=wf(e);return zu(n)&&Gu(),Uu(),uo}function on(e,n,t,r){return lo(e,n,t,r),uo(),on}var db=(e,n,t,r,o)=>(si(!0),$y(n[K],r,gg()));function Bf(e,n,t){let r=D(),o=r[S],i=e+ee,s=o.firstCreatePass?Mf(i,r,8,"ng-container",Df,ha(),n,t):o.data[i];if(Ef(s,r,e,"ng-container",sT),Gr(s)){let a=r[S];nc(a,r,s),rf(a,s,r)}return t!=null&&Ii(r,s),Bf}function Hf(){let e=J(),n=he(),t=wf(n);return e.firstCreatePass&&Sf(e,t),Hf}function fb(e,n,t){return Bf(e,n,t),Hf(),fb}var sT=(e,n,t,r,o)=>(si(!0),wI(n[K],""));function aT(){return D()}function hb(e,n,t){let r=D(),o=Xt();if(Le(r,o,n)){let i=J(),s=Yr();dv(s,r,e,n,r[K],t)}return hb}var ui=void 0;function cT(e){let n=Math.floor(Math.abs(e)),t=e.toString().replace(/^[^.]*\.?/,"").length;return n===1&&t===0?1:5}var lT=["en",[["a","p"],["AM","PM"]],[["AM","PM"]],[["S","M","T","W","T","F","S"],["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],["Su","Mo","Tu","We","Th","Fr","Sa"]],ui,[["J","F","M","A","M","J","J","A","S","O","N","D"],["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],["January","February","March","April","May","June","July","August","September","October","November","December"]],ui,[["B","A"],["BC","AD"],["Before Christ","Anno Domini"]],0,[6,0],["M/d/yy","MMM d, y","MMMM d, y","EEEE, MMMM d, y"],["h:mm\u202Fa","h:mm:ss\u202Fa","h:mm:ss\u202Fa z","h:mm:ss\u202Fa zzzz"],["{1}, {0}",ui,ui,ui],[".",",",";","%","+","-","E","\xD7","\u2030","\u221E","NaN",":"],["#,##0.###","#,##0%","\xA4#,##0.00","#E0"],"USD","$","US Dollar",{},"ltr",cT],ud={};function Je(e){let n=uT(e),t=ny(n);if(t)return t;let r=n.split("-")[0];if(t=ny(r),t)return t;if(r==="en")return lT;throw new v(701,!1)}function ny(e){return e in ud||(ud[e]=le.ng&&le.ng.common&&le.ng.common.locales&&le.ng.common.locales[e]),ud[e]}var ue=(function(e){return e[e.LocaleId=0]="LocaleId",e[e.DayPeriodsFormat=1]="DayPeriodsFormat",e[e.DayPeriodsStandalone=2]="DayPeriodsStandalone",e[e.DaysFormat=3]="DaysFormat",e[e.DaysStandalone=4]="DaysStandalone",e[e.MonthsFormat=5]="MonthsFormat",e[e.MonthsStandalone=6]="MonthsStandalone",e[e.Eras=7]="Eras",e[e.FirstDayOfWeek=8]="FirstDayOfWeek",e[e.WeekendRange=9]="WeekendRange",e[e.DateFormat=10]="DateFormat",e[e.TimeFormat=11]="TimeFormat",e[e.DateTimeFormat=12]="DateTimeFormat",e[e.NumberSymbols=13]="NumberSymbols",e[e.NumberFormats=14]="NumberFormats",e[e.CurrencyCode=15]="CurrencyCode",e[e.CurrencySymbol=16]="CurrencySymbol",e[e.CurrencyName=17]="CurrencyName",e[e.Currencies=18]="Currencies",e[e.Directionality=19]="Directionality",e[e.PluralCase=20]="PluralCase",e[e.ExtraData=21]="ExtraData",e})(ue||{});function uT(e){return e.toLowerCase().replace(/_/g,"-")}var Ni="en-US";var dT=Ni;function pb(e){typeof e=="string"&&(dT=e.toLowerCase().replace(/_/g,"-"))}function yr(e,n,t){let r=D(),o=J(),i=he();return gb(o,r,r[K],i,e,n,t),yr}function mb(e,n,t){let r=D(),o=J(),i=he();return(i.type&3||t)&&Fv(i,o,r,t,r[K],e,n,Ta(i,r,n)),mb}function gb(e,n,t,r,o,i,s){let a=!0,c=null;if((r.type&3||s)&&(c??=Ta(r,n,i),Fv(r,e,n,s,t,o,i,c)&&(a=!1)),a){let l=r.outputs?.[o],u=r.hostDirectiveOutputs?.[o];if(u&&u.length)for(let d=0;d>17&32767}function yT(e){return(e&2)==2}function vT(e,n){return e&131071|n<<17}function Kd(e){return e|2}function ro(e){return(e&131068)>>2}function dd(e,n){return e&-131069|n<<2}function bT(e){return(e&1)===1}function Xd(e){return e|1}function _T(e,n,t,r,o,i){let s=i?n.classBindings:n.styleBindings,a=pr(s),c=ro(s);e[r]=t;let l=!1,u;if(Array.isArray(t)){let d=t;u=d[1],(u===null||jr(d,u)>0)&&(l=!0)}else u=t;if(o)if(c!==0){let p=pr(e[a+1]);e[r+1]=wa(p,a),p!==0&&(e[p+1]=dd(e[p+1],r)),e[a+1]=vT(e[a+1],r)}else e[r+1]=wa(a,0),a!==0&&(e[a+1]=dd(e[a+1],r)),a=r;else e[r+1]=wa(c,0),a===0?a=r:e[c+1]=dd(e[c+1],r),c=r;l&&(e[r+1]=Kd(e[r+1])),ry(e,u,r,!0),ry(e,u,r,!1),DT(n,u,e,r,i),s=wa(a,c),i?n.classBindings=s:n.styleBindings=s}function DT(e,n,t,r,o){let i=o?e.residualClasses:e.residualStyles;i!=null&&typeof n=="string"&&jr(i,n)>=0&&(t[r+1]=Xd(t[r+1]))}function ry(e,n,t,r){let o=e[t+1],i=n===null,s=r?pr(o):ro(o),a=!1;for(;s!==0&&(a===!1||i);){let c=e[s],l=e[s+1];ET(c,n)&&(a=!0,e[s+1]=r?Xd(l):Kd(l)),s=r?pr(l):ro(l)}a&&(e[t+1]=r?Kd(o):Xd(o))}function ET(e,n){return e===null||n==null||(Array.isArray(e)?e[1]:e)===n?!0:Array.isArray(e)&&typeof n=="string"?jr(e,n)>=0:!1}var Et={textEnd:0,key:0,keyEnd:0,value:0,valueEnd:0};function wT(e){return e.substring(Et.key,Et.keyEnd)}function CT(e){return IT(e),Eb(e,wb(e,0,Et.textEnd))}function Eb(e,n){let t=Et.textEnd;return t===n?-1:(n=Et.keyEnd=MT(e,Et.key=n,t),wb(e,n,t))}function IT(e){Et.key=0,Et.keyEnd=0,Et.value=0,Et.valueEnd=0,Et.textEnd=e.length}function wb(e,n,t){for(;n32;)n++;return n}function $f(e,n,t){return Cb(e,n,t,!1),$f}function Ue(e,n){return Cb(e,n,null,!0),Ue}function zf(e){TT(kT,ST,e,!0)}function ST(e,n){for(let t=CT(n);t>=0;t=Eb(n,t))aa(e,wT(n),!0)}function Cb(e,n,t,r){let o=D(),i=J(),s=oi(2);if(i.firstUpdatePass&&Mb(i,e,s,r),n!==_e&&Le(o,s,n)){let a=i.data[_t()];Sb(i,a,o,o[K],e,o[s+1]=PT(n,t),r,s)}}function TT(e,n,t,r){let o=J(),i=oi(2);o.firstUpdatePass&&Mb(o,null,i,r);let s=D();if(t!==_e&&Le(s,i,t)){let a=o.data[_t()];if(Tb(a,r)&&!Ib(o,i)){let c=r?a.classesWithoutHost:a.stylesWithoutHost;c!==null&&(t=ta(c,t||"")),Zd(o,a,s,t,r)}else FT(o,a,s,s[K],s[i+1],s[i+1]=OT(e,n,t),r,i)}}function Ib(e,n){return n>=e.expandoStartIndex}function Mb(e,n,t,r){let o=e.data;if(o[t+1]===null){let i=o[_t()],s=Ib(e,t);Tb(i,r)&&n===null&&!s&&(n=!1),n=xT(o,i,n,r),_T(o,i,n,t,s,r)}}function xT(e,n,t,r){let o=lg(e),i=r?n.residualClasses:n.residualStyles;if(o===null)(r?n.classBindings:n.styleBindings)===0&&(t=fd(null,e,n,t,r),t=_i(t,n.attrs,r),i=null);else{let s=n.directiveStylingLast;if(s===-1||e[s]!==o)if(t=fd(o,e,n,t,r),i===null){let c=AT(e,n,r);c!==void 0&&Array.isArray(c)&&(c=fd(null,e,n,c[1],r),c=_i(c,n.attrs,r),NT(e,n,r,c))}else i=RT(e,n,r)}return i!==void 0&&(r?n.residualClasses=i:n.residualStyles=i),t}function AT(e,n,t){let r=t?n.classBindings:n.styleBindings;if(ro(r)!==0)return e[pr(r)]}function NT(e,n,t,r){let o=t?n.classBindings:n.styleBindings;e[pr(o)]=r}function RT(e,n,t){let r,o=n.directiveEnd;for(let i=1+n.directiveStylingLast;i0;){let c=e[o],l=Array.isArray(c),u=l?c[1]:c,d=u===null,p=t[o+1];p===_e&&(p=d?Ce:void 0);let h=d?ca(p,r):u===r?p:void 0;if(l&&!Ga(h)&&(h=ca(c,r)),Ga(h)&&(a=h,s))return a;let m=e[o+1];o=s?pr(m):ro(m)}if(n!==null){let c=i?n.residualClasses:n.residualStyles;c!=null&&(a=ca(c,r))}return a}function Ga(e){return e!==void 0}function PT(e,n){return e==null||e===""||(typeof n=="string"?e=e+n:typeof e=="object"&&(e=Xo(Xe(e)))),e}function Tb(e,n){return(e.flags&(n?8:16))!==0}function LT(e,n=""){let t=D(),r=J(),o=e+ee,i=r.firstCreatePass?so(r,o,1,n,null):r.data[o],s=VT(r,t,i,n);t[o]=s,va()&&bf(r,t,s,i),qr(i,!1)}var VT=(e,n,t,r)=>(si(!0),DI(n[K],r));function xb(e,n,t,r=""){return Le(e,Xt(),t)?n+Ft(t)+r:_e}function jT(e,n,t,r,o,i=""){let s=Ku(),a=vi(e,s,t,o);return oi(2),a?n+Ft(t)+r+Ft(o)+i:_e}function BT(e,n,t,r,o,i,s,a=""){let c=Ku(),l=kv(e,c,t,o,s);return oi(3),l?n+Ft(t)+r+Ft(o)+i+Ft(s)+a:_e}function Ab(e){return Gf("",e),Ab}function Gf(e,n,t){let r=D(),o=xb(r,e,n,t);return o!==_e&&Wf(r,_t(),o),Gf}function Nb(e,n,t,r,o){let i=D(),s=jT(i,e,n,t,r,o);return s!==_e&&Wf(i,_t(),s),Nb}function Rb(e,n,t,r,o,i,s){let a=D(),c=BT(a,e,n,t,r,o,i,s);return c!==_e&&Wf(a,_t(),c),Rb}function Wf(e,n,t){let r=Fu(n,e);EI(e[K],r,t)}function Ob(e,n,t){kf(n)&&(n=n());let r=D(),o=Xt();if(Le(r,o,n)){let i=J(),s=Yr();uv(s,r,e,n,r[K],t)}return Ob}function HT(e,n){let t=kf(e);return t&&e.set(n),t}function kb(e,n){let t=D(),r=J(),o=he();return gb(r,t,t[K],o,e,n),kb}function UT(e,n,t=""){return xb(D(),e,n,t)}function $T(e,n,t){let r=Vt()+e,o=D();return o[r]===_e?ao(o,r,n(t,o)):Ov(o,r)}function iy(e,n,t){let r=J();r.firstCreatePass&&Fb(n,r.data,r.blueprint,bt(e),t)}function Fb(e,n,t,r,o){if(e=me(e),Array.isArray(e))for(let i=0;i>20;if(Jn(e)||!e.multi){let h=new fr(l,o,w,null),m=pd(c,n,o?u:u+p,d);m===-1?(gd(Oa(a,s),i,c),hd(i,e,n.length),n.push(c),a.directiveStart++,a.directiveEnd++,o&&(a.providerIndexes+=1048576),t.push(h),s.push(h)):(t[m]=h,s[m]=h)}else{let h=pd(c,n,u+p,d),m=pd(c,n,u,u+p),b=h>=0&&t[h],_=m>=0&&t[m];if(o&&!_||!o&&!b){gd(Oa(a,s),i,c);let C=WT(o?GT:zT,t.length,o,r,l,e);!o&&_&&(t[m].providerFactory=C),hd(i,e,n.length,0),n.push(c),a.directiveStart++,a.directiveEnd++,o&&(a.providerIndexes+=1048576),t.push(C),s.push(C)}else{let C=Pb(t[o?m:h],l,!o&&r);hd(i,e,h>-1?h:m,C)}!o&&r&&_&&t[m].componentProviders++}}}function hd(e,n,t,r){let o=Jn(n),i=Gm(n);if(o||i){let c=(i?me(n.useClass):n).prototype.ngOnDestroy;if(c){let l=e.destroyHooks||(e.destroyHooks=[]);if(!o&&n.multi){let u=l.indexOf(t);u===-1?l.push(t,[r,c]):l[u+1].push(r,c)}else l.push(t,c)}}}function Pb(e,n,t){return t&&e.componentProviders++,e.multi.push(n)-1}function pd(e,n,t,r){for(let o=t;o{t.providersResolver=(r,o)=>iy(r,o?o(e):e,!1),n&&(t.viewProvidersResolver=(r,o)=>iy(r,o?o(n):n,!0))}}function qT(e,n){let t=Vt()+e,r=D();return r[t]===_e?ao(r,t,n()):Ov(r,t)}function YT(e,n,t){return Lb(D(),Vt(),e,n,t)}function ZT(e,n,t,r){return Vb(D(),Vt(),e,n,t,r)}function KT(e,n,t,r,o){return jb(D(),Vt(),e,n,t,r,o)}function XT(e,n,t,r,o,i,s){return QT(D(),Vt(),e,n,t,r,o,i)}function uc(e,n){let t=e[n];return t===_e?void 0:t}function Lb(e,n,t,r,o,i){let s=n+t;return Le(e,s,o)?ao(e,s+1,i?r.call(i,o):r(o)):uc(e,s+1)}function Vb(e,n,t,r,o,i,s){let a=n+t;return vi(e,a,o,i)?ao(e,a+2,s?r.call(s,o,i):r(o,i)):uc(e,a+2)}function jb(e,n,t,r,o,i,s,a){let c=n+t;return kv(e,c,o,i,s)?ao(e,c+3,a?r.call(a,o,i,s):r(o,i,s)):uc(e,c+3)}function QT(e,n,t,r,o,i,s,a,c){let l=n+t;return iS(e,l,o,i,s,a)?ao(e,l+4,c?r.call(c,o,i,s,a):r(o,i,s,a)):uc(e,l+4)}function JT(e,n){let t=J(),r,o=e+ee;t.firstCreatePass?(r=e0(n,t.pipeRegistry),t.data[o]=r,r.onDestroy&&(t.destroyHooks??=[]).push(o,r.onDestroy)):r=t.data[o];let i=r.factory||(r.factory=_n(r.type,!0)),s,a=Ae(w);try{let c=Ra(!1),l=i();return Ra(c),Pu(t,D(),o,l),l}finally{Ae(a)}}function e0(e,n){if(n)for(let t=n.length-1;t>=0;t--){let r=n[t];if(e===r.name)return r}}function t0(e,n,t){let r=e+ee,o=D(),i=ni(o,r);return qf(o,r)?Lb(o,Vt(),n,i.transform,t,i):i.transform(t)}function n0(e,n,t,r){let o=e+ee,i=D(),s=ni(i,o);return qf(i,o)?Vb(i,Vt(),n,s.transform,t,r,s):s.transform(t,r)}function r0(e,n,t,r,o){let i=e+ee,s=D(),a=ni(s,i);return qf(s,i)?jb(s,Vt(),n,a.transform,t,r,o,a):a.transform(t,r,o)}function qf(e,n){return e[S].data[n].pure}function o0(e,n){return oc(e,n)}var Wa=class{ngModuleFactory;componentFactories;constructor(n,t){this.ngModuleFactory=n,this.componentFactories=t}},i0=(()=>{class e{compileModuleSync(t){return new Ha(t)}compileModuleAsync(t){return Promise.resolve(this.compileModuleSync(t))}compileModuleAndAllComponentsSync(t){let r=this.compileModuleSync(t),o=_u(t),i=Zy(o.declarations).reduce((s,a)=>{let c=kt(a);return c&&s.push(new Tn(c)),s},[]);return new Wa(r,i)}compileModuleAndAllComponentsAsync(t){return Promise.resolve(this.compileModuleAndAllComponentsSync(t))}clearCache(){}clearCacheFor(t){}getModuleId(t){}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();var Bb=(()=>{class e{applicationErrorHandler=f(Qt);appRef=f(Qe);taskService=f(ur);ngZone=f(P);zonelessEnabled=f(ci);tracing=f(It,{optional:!0});zoneIsDefined=typeof Zone<"u"&&!!Zone.root.run;schedulerTickApplyArgs=[{data:{__scheduler_tick__:!0}}];subscriptions=new B;angularZoneId=this.zoneIsDefined?this.ngZone._inner?.get(Zo):null;scheduleInRootZone=!this.zonelessEnabled&&this.zoneIsDefined&&(f(rd,{optional:!0})??!1);cancelScheduledCallback=null;useMicrotaskScheduler=!1;runningTick=!1;pendingRenderTaskId=null;constructor(){this.subscriptions.add(this.appRef.afterTick.subscribe(()=>{let t=this.taskService.add();if(!this.runningTick&&(this.cleanup(),!this.zonelessEnabled||this.appRef.includeAllTestViews)){this.taskService.remove(t);return}this.switchToMicrotaskScheduler(),this.taskService.remove(t)})),this.subscriptions.add(this.ngZone.onUnstable.subscribe(()=>{this.runningTick||this.cleanup()}))}switchToMicrotaskScheduler(){this.ngZone.runOutsideAngular(()=>{let t=this.taskService.add();this.useMicrotaskScheduler=!0,queueMicrotask(()=>{this.useMicrotaskScheduler=!1,this.taskService.remove(t)})})}notify(t){if(!this.zonelessEnabled&&t===5)return;switch(t){case 0:{this.appRef.dirtyFlags|=2;break}case 3:case 2:case 4:case 5:case 1:{this.appRef.dirtyFlags|=4;break}case 6:{this.appRef.dirtyFlags|=2;break}case 12:{this.appRef.dirtyFlags|=16;break}case 13:{this.appRef.dirtyFlags|=2;break}case 11:break;default:this.appRef.dirtyFlags|=8}if(this.appRef.tracingSnapshot=this.tracing?.snapshot(this.appRef.tracingSnapshot)??null,!this.shouldScheduleTick())return;let r=this.useMicrotaskScheduler?_g:ed;this.pendingRenderTaskId=this.taskService.add(),this.scheduleInRootZone?this.cancelScheduledCallback=Zone.root.run(()=>r(()=>this.tick())):this.cancelScheduledCallback=this.ngZone.runOutsideAngular(()=>r(()=>this.tick()))}shouldScheduleTick(){return!(this.appRef.destroyed||this.pendingRenderTaskId!==null||this.runningTick||this.appRef._runningTick||!this.zonelessEnabled&&this.zoneIsDefined&&Zone.current.get(Zo+this.angularZoneId))}tick(){if(this.runningTick||this.appRef.destroyed)return;if(this.appRef.dirtyFlags===0){this.cleanup();return}!this.zonelessEnabled&&this.appRef.dirtyFlags&7&&(this.appRef.dirtyFlags|=1);let t=this.taskService.add();try{this.ngZone.run(()=>{this.runningTick=!0,this.appRef._tick()},void 0,this.schedulerTickApplyArgs)}catch(r){this.applicationErrorHandler(r)}finally{this.taskService.remove(t),this.cleanup()}}ngOnDestroy(){this.subscriptions.unsubscribe(),this.cleanup()}cleanup(){if(this.runningTick=!1,this.cancelScheduledCallback?.(),this.cancelScheduledCallback=null,this.pendingRenderTaskId!==null){let t=this.pendingRenderTaskId;this.pendingRenderTaskId=null,this.taskService.remove(t)}}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();function s0(){return nn("NgZoneless"),nr([...Yf(),[]])}function Yf(){return[{provide:Rt,useExisting:Bb},{provide:P,useClass:Ko},{provide:ci,useValue:!0}]}function a0(){return typeof $localize<"u"&&$localize.locale||Ni}var Ri=new y("",{factory:()=>f(Ri,{optional:!0,skipSelf:!0})||a0()});var dc=class{destroyed=!1;listeners=null;errorHandler=f(nt,{optional:!0});destroyRef=f(Pe);constructor(){this.destroyRef.onDestroy(()=>{this.destroyed=!0,this.listeners=null})}subscribe(n){if(this.destroyed)throw new v(953,!1);return(this.listeners??=[]).push(n),{unsubscribe:()=>{let t=this.listeners?.indexOf(n);t!==void 0&&t!==-1&&this.listeners?.splice(t,1)}}}emit(n){if(this.destroyed){console.warn(Ot(953,!1));return}if(this.listeners===null)return;let t=M(null);try{for(let r of this.listeners)try{r(n)}catch(o){this.errorHandler?.handleError(o)}}finally{M(t)}}};function $e(e){return Tm(e)}function vr(e,n){return $o(e,n?.equal)}var c0=e=>e;function l0(e,n){if(typeof e=="function"){let t=tu(e,c0,n?.equal);return Hb(t,n?.debugName)}else{let t=tu(e.source,e.computation,e.equal);return Hb(t,e.debugName)}}function Hb(e,n){let t=e[oe],r=e;return r.set=o=>Mm(t,o),r.update=o=>Sm(t,o),r.asReadonly=ai.bind(e),r}var pc=Symbol("InputSignalNode#UNSET"),Qb=V(E({},zo),{transformFn:void 0,applyValueToInputSignal(e,n){bn(e,n)}});function Jb(e,n){let t=Object.create(Qb);t.value=e,t.transformFn=n?.transform;function r(){if(Gt(t),t.value===pc){let o=null;throw new v(-950,o)}return t.value}return r[oe]=t,r}var Ub=class{attributeName;constructor(n){this.attributeName=n}__NG_ELEMENT_ID__=()=>ef(this.attributeName);toString(){return`HostAttributeToken ${this.attributeName}`}},h8=(()=>{let e=new y("");return e.__NG_ELEMENT_ID__=n=>{let t=he();if(t===null)throw new v(-204,!1);if(t.type&2)return t.value;if(n&8)return null;throw new v(-204,!1)},e})();function $b(e,n){return Jb(e,n)}function v0(e){return Jb(pc,e)}var p8=($b.required=v0,$b);function zb(e,n){return Rf(n)}function b0(e,n){return Of(n)}var m8=(zb.required=b0,zb);function g8(e,n){return Wv(n)}function Gb(e,n){return Rf(n)}function _0(e,n){return Of(n)}var y8=(Gb.required=_0,Gb);function e_(e,n){let t=Object.create(Qb),r=new dc;t.value=e;function o(){return Gt(t),Wb(t.value),t.value}return o[oe]=t,o.asReadonly=ai.bind(o),o.set=i=>{t.equal(t.value,i)||(bn(t,i),r.emit(i))},o.update=i=>{Wb(t.value),o.set(i(t.value))},o.subscribe=r.subscribe.bind(r),o.destroyRef=r.destroyRef,o}function Wb(e){if(e===pc)throw new v(952,!1)}function qb(e,n){return e_(e,n)}function D0(e){return e_(pc,e)}var v8=(qb.required=D0,qb);var Kf=new y(""),E0=new y("");function Oi(e){return!e.moduleRef}function w0(e){let n=Oi(e)?e.r3Injector:e.moduleRef.injector,t=n.get(P);return t.run(()=>{Oi(e)?e.r3Injector.resolveInjectorInitializers():e.moduleRef.resolveInjectorInitializers();let r=n.get(Qt),o;if(t.runOutsideAngular(()=>{o=t.onError.subscribe({next:r})}),Oi(e)){let i=()=>n.destroy(),s=e.platformInjector.get(Kf);s.add(i),n.onDestroy(()=>{o.unsubscribe(),s.delete(i)})}else{let i=()=>e.moduleRef.destroy(),s=e.platformInjector.get(Kf);s.add(i),e.moduleRef.onDestroy(()=>{hi(e.allPlatformModules,e.moduleRef),o.unsubscribe(),s.delete(i)})}return I0(r,t,()=>{let i=n.get(ur),s=i.add(),a=n.get(Lf);return a.runInitializers(),a.donePromise.then(()=>{let c=n.get(Ri,Ni);if(pb(c||Ni),!n.get(E0,!0))return Oi(e)?n.get(Qe):(e.allPlatformModules.push(e.moduleRef),e.moduleRef);if(Oi(e)){let u=n.get(Qe);return e.rootComponent!==void 0&&u.bootstrap(e.rootComponent),u}else return C0?.(e.moduleRef,e.allPlatformModules),e.moduleRef}).finally(()=>{i.remove(s)})})})}var C0;function I0(e,n,t){try{let r=t();return gr(r)?r.catch(o=>{throw n.runOutsideAngular(()=>e(o)),o}):r}catch(r){throw n.runOutsideAngular(()=>e(r)),r}}var fc=null;function M0(e=[],n){return j.create({name:n,providers:[{provide:ei,useValue:"platform"},{provide:Kf,useValue:new Set([()=>fc=null])},...e]})}function S0(e=[]){if(fc)return fc;let n=M0(e);return fc=n,sb(),T0(n),n}function T0(e){let n=e.get(qa,null);Ur(e,()=>{n?.forEach(t=>t())})}var x0=1e4;var b8=x0-1e3;var ho=(()=>{class e{static __NG_ELEMENT_ID__=A0}return e})();function A0(e){return N0(he(),D(),(e&16)===16)}function N0(e,n,t){if(Lt(e)&&!t){let r=st(e.index,n);return new Sn(r,r)}else if(e.type&175){let r=n[Ie];return new Sn(r,n)}return null}var Xf=class{supports(n){return Tf(n)}create(n){return new Qf(n)}},R0=(e,n)=>n,Qf=class{length=0;collection;_linkedRecords=null;_unlinkedRecords=null;_previousItHead=null;_itHead=null;_itTail=null;_additionsHead=null;_additionsTail=null;_movesHead=null;_movesTail=null;_removalsHead=null;_removalsTail=null;_identityChangesHead=null;_identityChangesTail=null;_trackByFn;constructor(n){this._trackByFn=n||R0}forEachItem(n){let t;for(t=this._itHead;t!==null;t=t._next)n(t)}forEachOperation(n){let t=this._itHead,r=this._removalsHead,o=0,i=null;for(;t||r;){let s=!r||t&&t.currentIndex{s=this._trackByFn(o,a),t===null||!Object.is(t.trackById,s)?(t=this._mismatch(t,a,s,o),r=!0):(r&&(t=this._verifyReinsertion(t,a,s,o)),Object.is(t.item,a)||this._addIdentityChange(t,a)),t=t._next,o++}),this.length=o;return this._truncate(t),this.collection=n,this.isDirty}get isDirty(){return this._additionsHead!==null||this._movesHead!==null||this._removalsHead!==null||this._identityChangesHead!==null}_reset(){if(this.isDirty){let n;for(n=this._previousItHead=this._itHead;n!==null;n=n._next)n._nextPrevious=n._next;for(n=this._additionsHead;n!==null;n=n._nextAdded)n.previousIndex=n.currentIndex;for(this._additionsHead=this._additionsTail=null,n=this._movesHead;n!==null;n=n._nextMoved)n.previousIndex=n.currentIndex;this._movesHead=this._movesTail=null,this._removalsHead=this._removalsTail=null,this._identityChangesHead=this._identityChangesTail=null}}_mismatch(n,t,r,o){let i;return n===null?i=this._itTail:(i=n._prev,this._remove(n)),n=this._unlinkedRecords===null?null:this._unlinkedRecords.get(r,null),n!==null?(Object.is(n.item,t)||this._addIdentityChange(n,t),this._reinsertAfter(n,i,o)):(n=this._linkedRecords===null?null:this._linkedRecords.get(r,o),n!==null?(Object.is(n.item,t)||this._addIdentityChange(n,t),this._moveAfter(n,i,o)):n=this._addAfter(new Jf(t,r),i,o)),n}_verifyReinsertion(n,t,r,o){let i=this._unlinkedRecords===null?null:this._unlinkedRecords.get(r,null);return i!==null?n=this._reinsertAfter(i,n._prev,o):n.currentIndex!=o&&(n.currentIndex=o,this._addToMoves(n,o)),n}_truncate(n){for(;n!==null;){let t=n._next;this._addToRemovals(this._unlink(n)),n=t}this._unlinkedRecords!==null&&this._unlinkedRecords.clear(),this._additionsTail!==null&&(this._additionsTail._nextAdded=null),this._movesTail!==null&&(this._movesTail._nextMoved=null),this._itTail!==null&&(this._itTail._next=null),this._removalsTail!==null&&(this._removalsTail._nextRemoved=null),this._identityChangesTail!==null&&(this._identityChangesTail._nextIdentityChange=null)}_reinsertAfter(n,t,r){this._unlinkedRecords!==null&&this._unlinkedRecords.remove(n);let o=n._prevRemoved,i=n._nextRemoved;return o===null?this._removalsHead=i:o._nextRemoved=i,i===null?this._removalsTail=o:i._prevRemoved=o,this._insertAfter(n,t,r),this._addToMoves(n,r),n}_moveAfter(n,t,r){return this._unlink(n),this._insertAfter(n,t,r),this._addToMoves(n,r),n}_addAfter(n,t,r){return this._insertAfter(n,t,r),this._additionsTail===null?this._additionsTail=this._additionsHead=n:this._additionsTail=this._additionsTail._nextAdded=n,n}_insertAfter(n,t,r){let o=t===null?this._itHead:t._next;return n._next=o,n._prev=t,o===null?this._itTail=n:o._prev=n,t===null?this._itHead=n:t._next=n,this._linkedRecords===null&&(this._linkedRecords=new hc),this._linkedRecords.put(n),n.currentIndex=r,n}_remove(n){return this._addToRemovals(this._unlink(n))}_unlink(n){this._linkedRecords!==null&&this._linkedRecords.remove(n);let t=n._prev,r=n._next;return t===null?this._itHead=r:t._next=r,r===null?this._itTail=t:r._prev=t,n}_addToMoves(n,t){return n.previousIndex===t||(this._movesTail===null?this._movesTail=this._movesHead=n:this._movesTail=this._movesTail._nextMoved=n),n}_addToRemovals(n){return this._unlinkedRecords===null&&(this._unlinkedRecords=new hc),this._unlinkedRecords.put(n),n.currentIndex=null,n._nextRemoved=null,this._removalsTail===null?(this._removalsTail=this._removalsHead=n,n._prevRemoved=null):(n._prevRemoved=this._removalsTail,this._removalsTail=this._removalsTail._nextRemoved=n),n}_addIdentityChange(n,t){return n.item=t,this._identityChangesTail===null?this._identityChangesTail=this._identityChangesHead=n:this._identityChangesTail=this._identityChangesTail._nextIdentityChange=n,n}},Jf=class{item;trackById;currentIndex=null;previousIndex=null;_nextPrevious=null;_prev=null;_next=null;_prevDup=null;_nextDup=null;_prevRemoved=null;_nextRemoved=null;_nextAdded=null;_nextMoved=null;_nextIdentityChange=null;constructor(n,t){this.item=n,this.trackById=t}},eh=class{_head=null;_tail=null;add(n){this._head===null?(this._head=this._tail=n,n._nextDup=null,n._prevDup=null):(this._tail._nextDup=n,n._prevDup=this._tail,n._nextDup=null,this._tail=n)}get(n,t){let r;for(r=this._head;r!==null;r=r._nextDup)if((t===null||t<=r.currentIndex)&&Object.is(r.trackById,n))return r;return null}remove(n){let t=n._prevDup,r=n._nextDup;return t===null?this._head=r:t._nextDup=r,r===null?this._tail=t:r._prevDup=t,this._head===null}},hc=class{map=new Map;put(n){let t=n.trackById,r=this.map.get(t);r||(r=new eh,this.map.set(t,r)),r.add(n)}get(n,t){let r=n,o=this.map.get(r);return o?o.get(n,t):null}remove(n){let t=n.trackById;return this.map.get(t).remove(n)&&this.map.delete(t),n}get isEmpty(){return this.map.size===0}clear(){this.map.clear()}};function Yb(e,n,t){let r=e.previousIndex;if(r===null)return r;let o=0;return t&&r{if(t&&t.key===o)this._maybeAddToChanges(t,r),this._appendAfter=t,t=t._next;else{let i=this._getOrCreateRecordForKey(o,r);t=this._insertBeforeOrAppend(t,i)}}),t){t._prev&&(t._prev._next=null),this._removalsHead=t;for(let r=t;r!==null;r=r._nextRemoved)r===this._mapHead&&(this._mapHead=null),this._records.delete(r.key),r._nextRemoved=r._next,r.previousValue=r.currentValue,r.currentValue=null,r._prev=null,r._next=null}return this._changesTail&&(this._changesTail._nextChanged=null),this._additionsTail&&(this._additionsTail._nextAdded=null),this.isDirty}_insertBeforeOrAppend(n,t){if(n){let r=n._prev;return t._next=n,t._prev=r,n._prev=t,r&&(r._next=t),n===this._mapHead&&(this._mapHead=t),this._appendAfter=n,n}return this._appendAfter?(this._appendAfter._next=t,t._prev=this._appendAfter):this._mapHead=t,this._appendAfter=t,null}_getOrCreateRecordForKey(n,t){if(this._records.has(n)){let o=this._records.get(n);this._maybeAddToChanges(o,t);let i=o._prev,s=o._next;return i&&(i._next=s),s&&(s._prev=i),o._next=null,o._prev=null,o}let r=new rh(n);return this._records.set(n,r),r.currentValue=t,this._addToAdditions(r),r}_reset(){if(this.isDirty){let n;for(this._previousMapHead=this._mapHead,n=this._previousMapHead;n!==null;n=n._next)n._nextPrevious=n._next;for(n=this._changesHead;n!==null;n=n._nextChanged)n.previousValue=n.currentValue;for(n=this._additionsHead;n!=null;n=n._nextAdded)n.previousValue=n.currentValue;this._changesHead=this._changesTail=null,this._additionsHead=this._additionsTail=null,this._removalsHead=null}}_maybeAddToChanges(n,t){Object.is(t,n.currentValue)||(n.previousValue=n.currentValue,n.currentValue=t,this._addToChanges(n))}_addToAdditions(n){this._additionsHead===null?this._additionsHead=this._additionsTail=n:(this._additionsTail._nextAdded=n,this._additionsTail=n)}_addToChanges(n){this._changesHead===null?this._changesHead=this._changesTail=n:(this._changesTail._nextChanged=n,this._changesTail=n)}_forEach(n,t){n instanceof Map?n.forEach(t):Object.keys(n).forEach(r=>t(n[r],r))}},rh=class{key;previousValue=null;currentValue=null;_nextPrevious=null;_next=null;_prev=null;_nextAdded=null;_nextRemoved=null;_nextChanged=null;constructor(n){this.key=n}};function Zb(){return new mc([new Xf])}var mc=(()=>{class e{factories;static \u0275prov=g({token:e,providedIn:"root",factory:Zb});constructor(t){this.factories=t}static create(t,r){if(r!=null){let o=r.factories.slice();t=t.concat(o)}return new e(t)}static extend(t){return{provide:e,useFactory:()=>{let r=f(e,{optional:!0,skipSelf:!0});return e.create(t,r||Zb())}}}find(t){let r=this.factories.find(o=>o.supports(t));if(r!=null)return r;throw new v(901,!1)}}return e})();function Kb(){return new sh([new th])}var sh=(()=>{class e{static \u0275prov=g({token:e,providedIn:"root",factory:Kb});factories;constructor(t){this.factories=t}static create(t,r){if(r){let o=r.factories.slice();t=t.concat(o)}return new e(t)}static extend(t){return{provide:e,useFactory:()=>{let r=f(e,{optional:!0,skipSelf:!0});return e.create(t,r||Kb())}}}find(t){let r=this.factories.find(o=>o.supports(t));if(r)return r;throw new v(901,!1)}}return e})();var t_=(()=>{class e{constructor(t){}static \u0275fac=function(r){return new(r||e)(I(Qe))};static \u0275mod=Z({type:e});static \u0275inj=G({})}return e})();function n_(e){let{rootComponent:n,appProviders:t,platformProviders:r,platformRef:o}=e;Y($.BootstrapApplicationStart);try{let i=o?.injector??S0(r),s=[Yf(),Eg,...t||[]],a=new bi({providers:s,parent:i,debugName:"",runEnvironmentInitializers:!1});return w0({r3Injector:a.injector,platformInjector:i,rootComponent:n})}catch(i){return Promise.reject(i)}finally{Y($.BootstrapApplicationEnd)}}function de(e){return typeof e=="boolean"?e:e!=null&&e!=="false"}function ah(e,n=NaN){return!isNaN(parseFloat(e))&&!isNaN(Number(e))?Number(e):n}var Zf=Symbol("NOT_SET"),r_=new Set,O0=V(E({},zo),{kind:"afterRenderEffectPhase",consumerIsAlwaysLive:!0,consumerAllowSignalWrites:!0,value:Zf,cleanup:null,consumerMarkedDirty(){if(this.sequence.impl.executing){if(this.sequence.lastPhase===null||this.sequence.lastPhase(Gt(l),l.value),l.signal[oe]=l,l.registerCleanupFn=u=>(l.cleanup??=new Set).add(u),this.nodes[a]=l,this.hooks[a]=u=>l.phaseFn(u)}}afterRun(){super.afterRun(),this.lastPhase=null}destroy(){if(this.onDestroyFns!==null)for(let n of this.onDestroyFns)n();super.destroy();for(let n of this.nodes)if(n)try{for(let t of n.cleanup??r_)t()}finally{vn(n)}}};function _8(e,n){let t=n?.injector??f(j),r=t.get(Rt),o=t.get(Ja),i=t.get(It,null,{optional:!0});o.impl??=t.get(yf);let s=e;typeof s=="function"&&(s={mixedReadWrite:e});let a=t.get(Zr,null,{optional:!0}),c=new oh(o.impl,[s.earlyRead,s.write,s.mixedReadWrite,s.read],a?.view,r,t,i?.snapshot(null));return o.impl.register(c),c}function gc(e,n){let t=kt(e),r=n.elementInjector||Hr();return new Tn(t).create(r,n.projectableNodes,n.hostElement,n.environmentInjector,n.directives,n.bindings)}function D8(e){let n=kt(e);if(!n)return null;let t=new Tn(n);return{get selector(){return t.selector},get type(){return t.componentType},get inputs(){return t.inputs},get outputs(){return t.outputs},get ngContentSelectors(){return t.ngContentSelectors},get isStandalone(){return n.standalone},get isSignal(){return n.signals}}}var o_=null;function et(){return o_}function ch(e){o_??=e}var ki=class{},Rn=(()=>{class e{historyGo(t){throw new Error("")}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:()=>f(i_),providedIn:"platform"})}return e})(),k0=new y(""),i_=(()=>{class e extends Rn{_location;_history;_doc=f(F);constructor(){super(),this._location=window.location,this._history=window.history}getBaseHrefFromDOM(){return et().getBaseHref(this._doc)}onPopState(t){let r=et().getGlobalEventTarget(this._doc,"window");return r.addEventListener("popstate",t,!1),()=>r.removeEventListener("popstate",t)}onHashChange(t){let r=et().getGlobalEventTarget(this._doc,"window");return r.addEventListener("hashchange",t,!1),()=>r.removeEventListener("hashchange",t)}get href(){return this._location.href}get protocol(){return this._location.protocol}get hostname(){return this._location.hostname}get port(){return this._location.port}get pathname(){return this._location.pathname}get search(){return this._location.search}get hash(){return this._location.hash}set pathname(t){this._location.pathname=t}pushState(t,r,o){this._history.pushState(t,r,o)}replaceState(t,r,o){this._history.replaceState(t,r,o)}forward(){this._history.forward()}back(){this._history.back()}historyGo(t=0){this._history.go(t)}getState(){return this._history.state}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:()=>new e,providedIn:"platform"})}return e})();function yc(e,n){return e?n?e.endsWith("/")?n.startsWith("/")?e+n.slice(1):e+n:n.startsWith("/")?e+n:`${e}/${n}`:e:n}function s_(e){let n=e.search(/#|\?|$/);return e[n-1]==="/"?e.slice(0,n-1)+e.slice(n):e}function Mt(e){return e&&e[0]!=="?"?`?${e}`:e}var po=(()=>{class e{historyGo(t){throw new Error("")}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:()=>f(c_),providedIn:"root"})}return e})(),vc=new y(""),c_=(()=>{class e extends po{_platformLocation;_baseHref;_removeListenerFns=[];constructor(t,r){super(),this._platformLocation=t,this._baseHref=r??this._platformLocation.getBaseHrefFromDOM()??f(F).location?.origin??""}ngOnDestroy(){for(;this._removeListenerFns.length;)this._removeListenerFns.pop()()}onPopState(t){this._removeListenerFns.push(this._platformLocation.onPopState(t),this._platformLocation.onHashChange(t))}getBaseHref(){return this._baseHref}prepareExternalUrl(t){return yc(this._baseHref,t)}path(t=!1){let r=this._platformLocation.pathname+Mt(this._platformLocation.search),o=this._platformLocation.hash;return o&&t?`${r}${o}`:r}pushState(t,r,o,i){let s=this.prepareExternalUrl(o+Mt(i));this._platformLocation.pushState(t,r,s)}replaceState(t,r,o,i){let s=this.prepareExternalUrl(o+Mt(i));this._platformLocation.replaceState(t,r,s)}forward(){this._platformLocation.forward()}back(){this._platformLocation.back()}getState(){return this._platformLocation.getState()}historyGo(t=0){this._platformLocation.historyGo?.(t)}static \u0275fac=function(r){return new(r||e)(I(Rn),I(vc,8))};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();var bc=(()=>{class e{_subject=new R;_basePath;_locationStrategy;_urlChangeListeners=[];_urlChangeSubscription=null;constructor(t){this._locationStrategy=t;let r=this._locationStrategy.getBaseHref();this._basePath=L0(s_(a_(r))),this._locationStrategy.onPopState(o=>{this._subject.next({url:this.path(!0),pop:!0,state:o.state,type:o.type})})}ngOnDestroy(){this._urlChangeSubscription?.unsubscribe(),this._urlChangeListeners=[]}path(t=!1){return this.normalize(this._locationStrategy.path(t))}getState(){return this._locationStrategy.getState()}isCurrentPathEqualTo(t,r=""){return this.path()==this.normalize(t+Mt(r))}normalize(t){return e.stripTrailingSlash(P0(this._basePath,a_(t)))}prepareExternalUrl(t){return t&&t[0]!=="/"&&(t="/"+t),this._locationStrategy.prepareExternalUrl(t)}go(t,r="",o=null){this._locationStrategy.pushState(o,"",t,r),this._notifyUrlChangeListeners(this.prepareExternalUrl(t+Mt(r)),o)}replaceState(t,r="",o=null){this._locationStrategy.replaceState(o,"",t,r),this._notifyUrlChangeListeners(this.prepareExternalUrl(t+Mt(r)),o)}forward(){this._locationStrategy.forward()}back(){this._locationStrategy.back()}historyGo(t=0){this._locationStrategy.historyGo?.(t)}onUrlChange(t){return this._urlChangeListeners.push(t),this._urlChangeSubscription??=this.subscribe(r=>{this._notifyUrlChangeListeners(r.url,r.state)}),()=>{let r=this._urlChangeListeners.indexOf(t);this._urlChangeListeners.splice(r,1),this._urlChangeListeners.length===0&&(this._urlChangeSubscription?.unsubscribe(),this._urlChangeSubscription=null)}}_notifyUrlChangeListeners(t="",r){this._urlChangeListeners.forEach(o=>o(t,r))}subscribe(t,r,o){return this._subject.subscribe({next:t,error:r??void 0,complete:o??void 0})}static normalizeQueryParams=Mt;static joinWithSlash=yc;static stripTrailingSlash=s_;static \u0275fac=function(r){return new(r||e)(I(po))};static \u0275prov=g({token:e,factory:()=>F0(),providedIn:"root"})}return e})();function F0(){return new bc(I(po))}function P0(e,n){if(!e||!n.startsWith(e))return n;let t=n.substring(e.length);return t===""||["/",";","?","#"].includes(t[0])?t:n}function a_(e){return e.replace(/\/index.html$/,"")}function L0(e){if(new RegExp("^(https?:)?//").test(e)){let[,t]=e.split(/\/\/[^\/]+/);return t}return e}var V0=(()=>{class e extends po{_platformLocation;_baseHref="";_removeListenerFns=[];constructor(t,r){super(),this._platformLocation=t,r!=null&&(this._baseHref=r)}ngOnDestroy(){for(;this._removeListenerFns.length;)this._removeListenerFns.pop()()}onPopState(t){this._removeListenerFns.push(this._platformLocation.onPopState(t),this._platformLocation.onHashChange(t))}getBaseHref(){return this._baseHref}path(t=!1){let r=this._platformLocation.hash??"#";return r.length>0?r.substring(1):r}prepareExternalUrl(t){let r=yc(this._baseHref,t);return r.length>0?"#"+r:r}pushState(t,r,o,i){let s=this.prepareExternalUrl(o+Mt(i))||this._platformLocation.pathname;this._platformLocation.pushState(t,r,s)}replaceState(t,r,o,i){let s=this.prepareExternalUrl(o+Mt(i))||this._platformLocation.pathname;this._platformLocation.replaceState(t,r,s)}forward(){this._platformLocation.forward()}back(){this._platformLocation.back()}getState(){return this._platformLocation.getState()}historyGo(t=0){this._platformLocation.historyGo?.(t)}static \u0275fac=function(r){return new(r||e)(I(Rn),I(vc,8))};static \u0275prov=g({token:e,factory:e.\u0275fac})}return e})();var xe=(function(e){return e[e.Format=0]="Format",e[e.Standalone=1]="Standalone",e})(xe||{}),Q=(function(e){return e[e.Narrow=0]="Narrow",e[e.Abbreviated=1]="Abbreviated",e[e.Wide=2]="Wide",e[e.Short=3]="Short",e})(Q||{}),ze=(function(e){return e[e.Short=0]="Short",e[e.Medium=1]="Medium",e[e.Long=2]="Long",e[e.Full=3]="Full",e})(ze||{}),an={Decimal:0,Group:1,List:2,PercentSign:3,PlusSign:4,MinusSign:5,Exponential:6,SuperscriptingExponent:7,PerMille:8,Infinity:9,NaN:10,TimeSeparator:11,CurrencyDecimal:12,CurrencyGroup:13};function h_(e){return Je(e)[ue.LocaleId]}function p_(e,n,t){let r=Je(e),o=[r[ue.DayPeriodsFormat],r[ue.DayPeriodsStandalone]],i=at(o,n);return at(i,t)}function m_(e,n,t){let r=Je(e),o=[r[ue.DaysFormat],r[ue.DaysStandalone]],i=at(o,n);return at(i,t)}function g_(e,n,t){let r=Je(e),o=[r[ue.MonthsFormat],r[ue.MonthsStandalone]],i=at(o,n);return at(i,t)}function y_(e,n){let r=Je(e)[ue.Eras];return at(r,n)}function Fi(e,n){let t=Je(e);return at(t[ue.DateFormat],n)}function Pi(e,n){let t=Je(e);return at(t[ue.TimeFormat],n)}function Li(e,n){let r=Je(e)[ue.DateTimeFormat];return at(r,n)}function Vi(e,n){let t=Je(e),r=t[ue.NumberSymbols][n];if(typeof r>"u"){if(n===an.CurrencyDecimal)return t[ue.NumberSymbols][an.Decimal];if(n===an.CurrencyGroup)return t[ue.NumberSymbols][an.Group]}return r}function v_(e){if(!e[ue.ExtraData])throw new v(2303,!1)}function b_(e){let n=Je(e);return v_(n),(n[ue.ExtraData][2]||[]).map(r=>typeof r=="string"?lh(r):[lh(r[0]),lh(r[1])])}function __(e,n,t){let r=Je(e);v_(r);let o=[r[ue.ExtraData][0],r[ue.ExtraData][1]],i=at(o,n)||[];return at(i,t)||[]}function at(e,n){for(let t=n;t>-1;t--)if(typeof e[t]<"u")return e[t];throw new v(2304,!1)}function lh(e){let[n,t]=e.split(":");return{hours:+n,minutes:+t}}var j0=/^(\d{4,})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/,_c={},B0=/((?:[^BEGHLMOSWYZabcdhmswyz']+)|(?:'(?:[^']|'')*')|(?:G{1,5}|y{1,4}|Y{1,4}|M{1,5}|L{1,5}|w{1,2}|W{1}|d{1,2}|E{1,6}|c{1,6}|a{1,5}|b{1,5}|B{1,5}|h{1,2}|H{1,2}|m{1,2}|s{1,2}|S{1,3}|z{1,4}|Z{1,5}|O{1,4}))([\s\S]*)/;function D_(e,n,t,r){let o=Z0(e);n=sn(t,n)||n;let s=[],a;for(;n;)if(a=B0.exec(n),a){s=s.concat(a.slice(1));let u=s.pop();if(!u)break;n=u}else{s.push(n);break}let c=o.getTimezoneOffset();r&&(c=w_(r,c),o=Y0(o,r));let l="";return s.forEach(u=>{let d=W0(u);l+=d?d(o,t,c):u==="''"?"'":u.replace(/(^'|'$)/g,"").replace(/''/g,"'")}),l}function Ic(e,n,t){let r=new Date(0);return r.setFullYear(e,n,t),r.setHours(0,0,0),r}function sn(e,n){let t=h_(e);if(_c[t]??={},_c[t][n])return _c[t][n];let r="";switch(n){case"shortDate":r=Fi(e,ze.Short);break;case"mediumDate":r=Fi(e,ze.Medium);break;case"longDate":r=Fi(e,ze.Long);break;case"fullDate":r=Fi(e,ze.Full);break;case"shortTime":r=Pi(e,ze.Short);break;case"mediumTime":r=Pi(e,ze.Medium);break;case"longTime":r=Pi(e,ze.Long);break;case"fullTime":r=Pi(e,ze.Full);break;case"short":let o=sn(e,"shortTime"),i=sn(e,"shortDate");r=Dc(Li(e,ze.Short),[o,i]);break;case"medium":let s=sn(e,"mediumTime"),a=sn(e,"mediumDate");r=Dc(Li(e,ze.Medium),[s,a]);break;case"long":let c=sn(e,"longTime"),l=sn(e,"longDate");r=Dc(Li(e,ze.Long),[c,l]);break;case"full":let u=sn(e,"fullTime"),d=sn(e,"fullDate");r=Dc(Li(e,ze.Full),[u,d]);break}return r&&(_c[t][n]=r),r}function Dc(e,n){return n&&(e=e.replace(/\{([^}]+)}/g,function(t,r){return n!=null&&r in n?n[r]:t})),e}function St(e,n,t="-",r,o){let i="";(e<0||o&&e<=0)&&(o?e=-e+1:(e=-e,i=t));let s=String(e);for(;s.length0||a>-t)&&(a+=t),e===3)a===0&&t===-12&&(a=12);else if(e===6)return H0(a,n);let c=Vi(s,an.MinusSign);return St(a,n,c,r,o)}}function U0(e,n){switch(e){case 0:return n.getFullYear();case 1:return n.getMonth();case 2:return n.getDate();case 3:return n.getHours();case 4:return n.getMinutes();case 5:return n.getSeconds();case 6:return n.getMilliseconds();case 7:return n.getDay();default:throw new v(2301,!1)}}function te(e,n,t=xe.Format,r=!1){return function(o,i){return $0(o,i,e,n,t,r)}}function $0(e,n,t,r,o,i){switch(t){case 2:return g_(n,o,r)[e.getMonth()];case 1:return m_(n,o,r)[e.getDay()];case 0:let s=e.getHours(),a=e.getMinutes();if(i){let l=b_(n),u=__(n,o,r),d=l.findIndex(p=>{if(Array.isArray(p)){let[h,m]=p,b=s>=h.hours&&a>=h.minutes,_=s0?Math.floor(o/60):Math.ceil(o/60);switch(e){case 0:return(o>=0?"+":"")+St(s,2,i)+St(Math.abs(o%60),2,i);case 1:return"GMT"+(o>=0?"+":"")+St(s,1,i);case 2:return"GMT"+(o>=0?"+":"")+St(s,2,i)+":"+St(Math.abs(o%60),2,i);case 3:return r===0?"Z":(o>=0?"+":"")+St(s,2,i)+":"+St(Math.abs(o%60),2,i);default:throw new v(2310,!1)}}}var z0=0,Cc=4;function G0(e){let n=Ic(e,z0,1).getDay();return Ic(e,0,1+(n<=Cc?Cc:Cc+7)-n)}function E_(e){let n=e.getDay(),t=n===0?-3:Cc-n;return Ic(e.getFullYear(),e.getMonth(),e.getDate()+t)}function uh(e,n=!1){return function(t,r){let o;if(n){let i=new Date(t.getFullYear(),t.getMonth(),1).getDay()-1,s=t.getDate();o=1+Math.floor((s+i)/7)}else{let i=E_(t),s=G0(i.getFullYear()),a=i.getTime()-s.getTime();o=1+Math.round(a/6048e5)}return St(o,e,Vi(r,an.MinusSign))}}function wc(e,n=!1){return function(t,r){let i=E_(t).getFullYear();return St(i,e,Vi(r,an.MinusSign),n)}}var dh={};function W0(e){if(dh[e])return dh[e];let n;switch(e){case"G":case"GG":case"GGG":n=te(3,Q.Abbreviated);break;case"GGGG":n=te(3,Q.Wide);break;case"GGGGG":n=te(3,Q.Narrow);break;case"y":n=pe(0,1,0,!1,!0);break;case"yy":n=pe(0,2,0,!0,!0);break;case"yyy":n=pe(0,3,0,!1,!0);break;case"yyyy":n=pe(0,4,0,!1,!0);break;case"Y":n=wc(1);break;case"YY":n=wc(2,!0);break;case"YYY":n=wc(3);break;case"YYYY":n=wc(4);break;case"M":case"L":n=pe(1,1,1);break;case"MM":case"LL":n=pe(1,2,1);break;case"MMM":n=te(2,Q.Abbreviated);break;case"MMMM":n=te(2,Q.Wide);break;case"MMMMM":n=te(2,Q.Narrow);break;case"LLL":n=te(2,Q.Abbreviated,xe.Standalone);break;case"LLLL":n=te(2,Q.Wide,xe.Standalone);break;case"LLLLL":n=te(2,Q.Narrow,xe.Standalone);break;case"w":n=uh(1);break;case"ww":n=uh(2);break;case"W":n=uh(1,!0);break;case"d":n=pe(2,1);break;case"dd":n=pe(2,2);break;case"c":case"cc":n=pe(7,1);break;case"ccc":n=te(1,Q.Abbreviated,xe.Standalone);break;case"cccc":n=te(1,Q.Wide,xe.Standalone);break;case"ccccc":n=te(1,Q.Narrow,xe.Standalone);break;case"cccccc":n=te(1,Q.Short,xe.Standalone);break;case"E":case"EE":case"EEE":n=te(1,Q.Abbreviated);break;case"EEEE":n=te(1,Q.Wide);break;case"EEEEE":n=te(1,Q.Narrow);break;case"EEEEEE":n=te(1,Q.Short);break;case"a":case"aa":case"aaa":n=te(0,Q.Abbreviated);break;case"aaaa":n=te(0,Q.Wide);break;case"aaaaa":n=te(0,Q.Narrow);break;case"b":case"bb":case"bbb":n=te(0,Q.Abbreviated,xe.Standalone,!0);break;case"bbbb":n=te(0,Q.Wide,xe.Standalone,!0);break;case"bbbbb":n=te(0,Q.Narrow,xe.Standalone,!0);break;case"B":case"BB":case"BBB":n=te(0,Q.Abbreviated,xe.Format,!0);break;case"BBBB":n=te(0,Q.Wide,xe.Format,!0);break;case"BBBBB":n=te(0,Q.Narrow,xe.Format,!0);break;case"h":n=pe(3,1,-12);break;case"hh":n=pe(3,2,-12);break;case"H":n=pe(3,1);break;case"HH":n=pe(3,2);break;case"m":n=pe(4,1);break;case"mm":n=pe(4,2);break;case"s":n=pe(5,1);break;case"ss":n=pe(5,2);break;case"S":n=pe(6,1);break;case"SS":n=pe(6,2);break;case"SSS":n=pe(6,3);break;case"Z":case"ZZ":case"ZZZ":n=Ec(0);break;case"ZZZZZ":n=Ec(3);break;case"O":case"OO":case"OOO":case"z":case"zz":case"zzz":n=Ec(1);break;case"OOOO":case"ZZZZ":case"zzzz":n=Ec(2);break;default:return null}return dh[e]=n,n}function w_(e,n){e=e.replace(/:/g,"");let t=Date.parse("Jan 01, 1970 00:00:00 "+e)/6e4;return isNaN(t)?n:t}function q0(e,n){return e=new Date(e.getTime()),e.setMinutes(e.getMinutes()+n),e}function Y0(e,n,t){let o=e.getTimezoneOffset(),i=w_(n,o);return q0(e,-1*(i-o))}function Z0(e){if(l_(e))return e;if(typeof e=="number"&&!isNaN(e))return new Date(e);if(typeof e=="string"){if(e=e.trim(),/^(\d{4}(-\d{1,2}(-\d{1,2})?)?)$/.test(e)){let[o,i=1,s=1]=e.split("-").map(a=>+a);return Ic(o,i-1,s)}let t=parseFloat(e);if(!isNaN(e-t))return new Date(t);let r;if(r=e.match(j0))return K0(r)}let n=new Date(e);if(!l_(n))throw new v(2311,!1);return n}function K0(e){let n=new Date(0),t=0,r=0,o=e[8]?n.setUTCFullYear:n.setFullYear,i=e[8]?n.setUTCHours:n.setHours;e[9]&&(t=Number(e[9]+e[10]),r=Number(e[9]+e[11])),o.call(n,Number(e[1]),Number(e[2])-1,Number(e[3]));let s=Number(e[4]||0)-t,a=Number(e[5]||0)-r,c=Number(e[6]||0),l=Math.floor(parseFloat("0."+(e[7]||0))*1e3);return i.call(n,s,a,c,l),n}function l_(e){return e instanceof Date&&!isNaN(e.valueOf())}var fh=/\s+/,u_=[],X0=(()=>{class e{_ngEl;_renderer;initialClasses=u_;rawClass;stateMap=new Map;constructor(t,r){this._ngEl=t,this._renderer=r}set klass(t){this.initialClasses=t!=null?t.trim().split(fh):u_}set ngClass(t){this.rawClass=typeof t=="string"?t.trim().split(fh):t}ngDoCheck(){for(let r of this.initialClasses)this._updateState(r,!0);let t=this.rawClass;if(Array.isArray(t)||t instanceof Set)for(let r of t)this._updateState(r,!0);else if(t!=null)for(let r of Object.keys(t))this._updateState(r,!!t[r]);this._applyStateDiff()}_updateState(t,r){let o=this.stateMap.get(t);o!==void 0?(o.enabled!==r&&(o.changed=!0,o.enabled=r),o.touched=!0):this.stateMap.set(t,{enabled:r,changed:!0,touched:!0})}_applyStateDiff(){for(let t of this.stateMap){let r=t[0],o=t[1];o.changed?(this._toggleClass(r,o.enabled),o.changed=!1):o.touched||(o.enabled&&this._toggleClass(r,!1),this.stateMap.delete(r)),o.touched=!1}}_toggleClass(t,r){t=t.trim(),t.length>0&&t.split(fh).forEach(o=>{r?this._renderer.addClass(this._ngEl.nativeElement,o):this._renderer.removeClass(this._ngEl.nativeElement,o)})}static \u0275fac=function(r){return new(r||e)(w(z),w(Be))};static \u0275dir=O({type:e,selectors:[["","ngClass",""]],inputs:{klass:[0,"class","klass"],ngClass:"ngClass"}})}return e})();var Mc=class{$implicit;ngForOf;index;count;constructor(n,t,r,o){this.$implicit=n,this.ngForOf=t,this.index=r,this.count=o}get first(){return this.index===0}get last(){return this.index===this.count-1}get even(){return this.index%2===0}get odd(){return!this.even}},C_=(()=>{class e{_viewContainer;_template;_differs;set ngForOf(t){this._ngForOf=t,this._ngForOfDirty=!0}set ngForTrackBy(t){this._trackByFn=t}get ngForTrackBy(){return this._trackByFn}_ngForOf=null;_ngForOfDirty=!0;_differ=null;_trackByFn;constructor(t,r,o){this._viewContainer=t,this._template=r,this._differs=o}set ngForTemplate(t){t&&(this._template=t)}ngDoCheck(){if(this._ngForOfDirty){this._ngForOfDirty=!1;let t=this._ngForOf;!this._differ&&t&&(this._differ=this._differs.find(t).create(this.ngForTrackBy))}if(this._differ){let t=this._differ.diff(this._ngForOf);t&&this._applyChanges(t)}}_applyChanges(t){let r=this._viewContainer;t.forEachOperation((o,i,s)=>{if(o.previousIndex==null)r.createEmbeddedView(this._template,new Mc(o.item,this._ngForOf,-1,-1),s===null?void 0:s);else if(s==null)r.remove(i===null?void 0:i);else if(i!==null){let a=r.get(i);r.move(a,s),d_(a,o)}});for(let o=0,i=r.length;o{let i=r.get(o.currentIndex);d_(i,o)})}static ngTemplateContextGuard(t,r){return!0}static \u0275fac=function(r){return new(r||e)(w(He),w(Ze),w(mc))};static \u0275dir=O({type:e,selectors:[["","ngFor","","ngForOf",""]],inputs:{ngForOf:"ngForOf",ngForTrackBy:"ngForTrackBy",ngForTemplate:"ngForTemplate"}})}return e})();function d_(e,n){e.context.$implicit=n.item}var Q0=(()=>{class e{_viewContainer;_context=new Sc;_thenTemplateRef=null;_elseTemplateRef=null;_thenViewRef=null;_elseViewRef=null;constructor(t,r){this._viewContainer=t,this._thenTemplateRef=r}set ngIf(t){this._context.$implicit=this._context.ngIf=t,this._updateView()}set ngIfThen(t){f_(t,!1),this._thenTemplateRef=t,this._thenViewRef=null,this._updateView()}set ngIfElse(t){f_(t,!1),this._elseTemplateRef=t,this._elseViewRef=null,this._updateView()}_updateView(){this._context.$implicit?this._thenViewRef||(this._viewContainer.clear(),this._elseViewRef=null,this._thenTemplateRef&&(this._thenViewRef=this._viewContainer.createEmbeddedView(this._thenTemplateRef,this._context))):this._elseViewRef||(this._viewContainer.clear(),this._thenViewRef=null,this._elseTemplateRef&&(this._elseViewRef=this._viewContainer.createEmbeddedView(this._elseTemplateRef,this._context)))}static ngIfUseIfTypeGuard;static ngTemplateGuard_ngIf;static ngTemplateContextGuard(t,r){return!0}static \u0275fac=function(r){return new(r||e)(w(He),w(Ze))};static \u0275dir=O({type:e,selectors:[["","ngIf",""]],inputs:{ngIf:"ngIf",ngIfThen:"ngIfThen",ngIfElse:"ngIfElse"}})}return e})(),Sc=class{$implicit=null;ngIf=null};function f_(e,n){if(e&&!e.createEmbeddedView)throw new v(2020,!1)}var J0=(()=>{class e{_ngEl;_differs;_renderer;_ngStyle=null;_differ=null;constructor(t,r,o){this._ngEl=t,this._differs=r,this._renderer=o}set ngStyle(t){this._ngStyle=t,!this._differ&&t&&(this._differ=this._differs.find(t).create())}ngDoCheck(){if(this._differ){let t=this._differ.diff(this._ngStyle);t&&this._applyChanges(t)}}_setStyle(t,r){let[o,i]=t.split("."),s=o.indexOf("-")===-1?void 0:Ct.DashCase;r!=null?this._renderer.setStyle(this._ngEl.nativeElement,o,i?`${r}${i}`:r,s):this._renderer.removeStyle(this._ngEl.nativeElement,o,s)}_applyChanges(t){t.forEachRemovedItem(r=>this._setStyle(r.key,null)),t.forEachAddedItem(r=>this._setStyle(r.key,r.currentValue)),t.forEachChangedItem(r=>this._setStyle(r.key,r.currentValue))}static \u0275fac=function(r){return new(r||e)(w(z),w(sh),w(Be))};static \u0275dir=O({type:e,selectors:[["","ngStyle",""]],inputs:{ngStyle:"ngStyle"}})}return e})(),ex=(()=>{class e{_viewContainerRef;_viewRef=null;ngTemplateOutletContext=null;ngTemplateOutlet=null;ngTemplateOutletInjector=null;injector=f(j);constructor(t){this._viewContainerRef=t}ngOnChanges(t){if(this._shouldRecreateView(t)){let r=this._viewContainerRef;if(this._viewRef&&r.remove(r.indexOf(this._viewRef)),!this.ngTemplateOutlet){this._viewRef=null;return}let o=this._createContextForwardProxy();this._viewRef=r.createEmbeddedView(this.ngTemplateOutlet,o,{injector:this._getInjector()})}}_getInjector(){return this.ngTemplateOutletInjector==="outlet"?this.injector:this.ngTemplateOutletInjector??void 0}_shouldRecreateView(t){return!!t.ngTemplateOutlet||!!t.ngTemplateOutletInjector}_createContextForwardProxy(){return new Proxy({},{set:(t,r,o)=>this.ngTemplateOutletContext?Reflect.set(this.ngTemplateOutletContext,r,o):!1,get:(t,r,o)=>{if(this.ngTemplateOutletContext)return Reflect.get(this.ngTemplateOutletContext,r,o)}})}static \u0275fac=function(r){return new(r||e)(w(He))};static \u0275dir=O({type:e,selectors:[["","ngTemplateOutlet",""]],inputs:{ngTemplateOutletContext:"ngTemplateOutletContext",ngTemplateOutlet:"ngTemplateOutlet",ngTemplateOutletInjector:"ngTemplateOutletInjector"},features:[Ke]})}return e})();function mh(e,n){return new v(2100,!1)}var hh=class{createSubscription(n,t,r){return $e(()=>n.subscribe({next:t,error:r}))}dispose(n){$e(()=>n.unsubscribe())}},ph=class{createSubscription(n,t,r){return n.then(o=>t?.(o),o=>r?.(o)),{unsubscribe:()=>{t=null,r=null}}}dispose(n){n.unsubscribe()}},tx=new ph,nx=new hh,rx=(()=>{class e{_ref;_latestValue=null;markForCheckOnValueUpdate=!0;_subscription=null;_obj=null;_strategy=null;applicationErrorHandler=f(Qt);constructor(t){this._ref=t}ngOnDestroy(){this._subscription&&this._dispose(),this._ref=null}transform(t){if(!this._obj){if(t)try{this.markForCheckOnValueUpdate=!1,this._subscribe(t)}finally{this.markForCheckOnValueUpdate=!0}return this._latestValue}return t!==this._obj?(this._dispose(),this.transform(t)):this._latestValue}_subscribe(t){this._obj=t,this._strategy=this._selectStrategy(t),this._subscription=this._strategy.createSubscription(t,r=>this._updateLatestValue(t,r),r=>this.applicationErrorHandler(r))}_selectStrategy(t){if(gr(t))return tx;if(lc(t))return nx;throw mh(e,t)}_dispose(){this._strategy.dispose(this._subscription),this._latestValue=null,this._subscription=null,this._obj=null}_updateLatestValue(t,r){t===this._obj&&(this._latestValue=r,this.markForCheckOnValueUpdate&&this._ref?.markForCheck())}static \u0275fac=function(r){return new(r||e)(w(ho,16))};static \u0275pipe=xi({name:"async",type:e,pure:!1})}return e})();var ox=/(?:[0-9A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0560-\u0588\u05D0-\u05EA\u05EF-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u0870-\u0887\u0889-\u088E\u08A0-\u08C9\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C5D\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D04-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E86-\u0E8A\u0E8C-\u0EA3\u0EA5\u0EA7-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u1711\u171F-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1878\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4C\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1C90-\u1CBA\u1CBD-\u1CBF\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u1CFA\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312F\u3131-\u318E\u31A0-\u31BF\u31F0-\u31FF\u3400-\u4DBF\u4E00-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7CA\uA7D0\uA7D1\uA7D3\uA7D5-\uA7D9\uA7F2-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA8FE\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB69\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF2D-\uDF40\uDF42-\uDF49\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF]|\uD801[\uDC00-\uDC9D\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDD70-\uDD7A\uDD7C-\uDD8A\uDD8C-\uDD92\uDD94\uDD95\uDD97-\uDDA1\uDDA3-\uDDB1\uDDB3-\uDDB9\uDDBB\uDDBC\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67\uDF80-\uDF85\uDF87-\uDFB0\uDFB2-\uDFBA]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE35\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2\uDD00-\uDD23\uDE80-\uDEA9\uDEB0\uDEB1\uDF00-\uDF1C\uDF27\uDF30-\uDF45\uDF70-\uDF81\uDFB0-\uDFC4\uDFE0-\uDFF6]|\uD804[\uDC03-\uDC37\uDC71\uDC72\uDC75\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD44\uDD47\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC5F-\uDC61\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDEB8\uDF00-\uDF1A\uDF40-\uDF46]|\uD806[\uDC00-\uDC2B\uDCA0-\uDCDF\uDCFF-\uDD06\uDD09\uDD0C-\uDD13\uDD15\uDD16\uDD18-\uDD2F\uDD3F\uDD41\uDDA0-\uDDA7\uDDAA-\uDDD0\uDDE1\uDDE3\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE89\uDE9D\uDEB0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46\uDD60-\uDD65\uDD67\uDD68\uDD6A-\uDD89\uDD98\uDEE0-\uDEF2\uDFB0]|\uD808[\uDC00-\uDF99]|\uD809[\uDC80-\uDD43]|\uD80B[\uDF90-\uDFF0]|[\uD80C\uD81C-\uD820\uD822\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879\uD880-\uD883][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE70-\uDEBE\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDE40-\uDE7F\uDF00-\uDF4A\uDF50\uDF93-\uDF9F\uDFE0\uDFE1\uDFE3]|\uD821[\uDC00-\uDFF7]|\uD823[\uDC00-\uDCD5\uDD00-\uDD08]|\uD82B[\uDFF0-\uDFF3\uDFF5-\uDFFB\uDFFD\uDFFE]|\uD82C[\uDC00-\uDD22\uDD50-\uDD52\uDD64-\uDD67\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD837[\uDF00-\uDF1E]|\uD838[\uDD00-\uDD2C\uDD37-\uDD3D\uDD4E\uDE90-\uDEAD\uDEC0-\uDEEB]|\uD839[\uDFE0-\uDFE6\uDFE8-\uDFEB\uDFED\uDFEE\uDFF0-\uDFFE]|\uD83A[\uDC00-\uDCC4\uDD00-\uDD43\uDD4B]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDEDF\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF38\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]|\uD884[\uDC00-\uDF4A])\S*/g,ix=(()=>{class e{transform(t){return t==null?null:(sx(e,t),t.replace(ox,r=>r[0].toUpperCase()+r.slice(1).toLowerCase()))}static \u0275fac=function(r){return new(r||e)};static \u0275pipe=xi({name:"titlecase",type:e,pure:!0})}return e})();function sx(e,n){if(typeof n!="string")throw mh(e,n)}var ax="mediumDate",I_=new y(""),M_=new y(""),cx=(()=>{class e{locale;defaultTimezone;defaultOptions;constructor(t,r,o){this.locale=t,this.defaultTimezone=r,this.defaultOptions=o}transform(t,r,o,i){if(t==null||t===""||t!==t)return null;try{let s=r??this.defaultOptions?.dateFormat??ax,a=o??this.defaultOptions?.timezone??this.defaultTimezone??void 0;return D_(t,s,i||this.locale,a)}catch(s){throw mh(e,s.message)}}static \u0275fac=function(r){return new(r||e)(w(Ri,16),w(I_,24),w(M_,24))};static \u0275pipe=xi({name:"date",type:e,pure:!0})}return e})();var gh=(()=>{class e{static \u0275fac=function(r){return new(r||e)};static \u0275mod=Z({type:e});static \u0275inj=G({})}return e})();function ji(e,n){n=encodeURIComponent(n);for(let t of e.split(";")){let r=t.indexOf("="),[o,i]=r==-1?[t,""]:[t.slice(0,r),t.slice(r+1)];if(o.trim()===n)return decodeURIComponent(i)}return null}var br=class{};var vh="browser";function S_(e){return e===vh}var BG=(()=>{class e{static \u0275prov=g({token:e,providedIn:"root",factory:()=>new yh(f(F),window)})}return e})(),yh=class{document;window;offset=()=>[0,0];constructor(n,t){this.document=n,this.window=t}setOffset(n){Array.isArray(n)?this.offset=()=>n:this.offset=n}getScrollPosition(){return[this.window.scrollX,this.window.scrollY]}scrollToPosition(n,t){this.window.scrollTo(V(E({},t),{left:n[0],top:n[1]}))}scrollToAnchor(n,t){let r=lx(this.document,n);r&&(this.scrollToElement(r,t),r.focus())}setHistoryScrollRestoration(n){try{this.window.history.scrollRestoration=n}catch{console.warn(Ot(2400,!1))}}scrollToElement(n,t){let r=n.getBoundingClientRect(),o=r.left+this.window.pageXOffset,i=r.top+this.window.pageYOffset,s=this.offset();this.window.scrollTo(V(E({},t),{left:o-s[0],top:i-s[1]}))}};function lx(e,n){let t=e.getElementById(n)||e.getElementsByName(n)[0];if(t)return t;if(typeof e.createTreeWalker=="function"&&e.body&&typeof e.body.attachShadow=="function"){let r=e.createTreeWalker(e.body,NodeFilter.SHOW_ELEMENT),o=r.currentNode;for(;o;){let i=o.shadowRoot;if(i){let s=i.getElementById(n)||i.querySelector(`[name="${n}"]`);if(s)return s}o=r.nextNode()}}return null}var Bi=class{_doc;constructor(n){this._doc=n}manager},Tc=(()=>{class e extends Bi{constructor(t){super(t)}supports(t){return!0}addEventListener(t,r,o,i){return t.addEventListener(r,o,i),()=>this.removeEventListener(t,r,o,i)}removeEventListener(t,r,o,i){return t.removeEventListener(r,o,i)}static \u0275fac=function(r){return new(r||e)(I(F))};static \u0275prov=g({token:e,factory:e.\u0275fac})}return e})(),Nc=new y(""),Eh=(()=>{class e{_zone;_plugins;_eventNameToPlugin=new Map;constructor(t,r){this._zone=r,t.forEach(s=>{s.manager=this});let o=t.filter(s=>!(s instanceof Tc));this._plugins=o.slice().reverse();let i=t.find(s=>s instanceof Tc);i&&this._plugins.push(i)}addEventListener(t,r,o,i){return this._findPluginFor(r).addEventListener(t,r,o,i)}getZone(){return this._zone}_findPluginFor(t){let r=this._eventNameToPlugin.get(t);if(r)return r;if(r=this._plugins.find(i=>i.supports(t)),!r)throw new v(5101,!1);return this._eventNameToPlugin.set(t,r),r}static \u0275fac=function(r){return new(r||e)(I(Nc),I(P))};static \u0275prov=g({token:e,factory:e.\u0275fac})}return e})(),bh="ng-app-id";function T_(e){for(let n of e)n.remove()}function x_(e,n){let t=n.createElement("style");return t.textContent=e,t}function ux(e,n,t,r){let o=e.head?.querySelectorAll(`style[${bh}="${n}"],link[${bh}="${n}"]`);if(o)for(let i of o)i.removeAttribute(bh),i instanceof HTMLLinkElement?r.set(i.href.slice(i.href.lastIndexOf("/")+1),{usage:0,elements:[i]}):i.textContent&&t.set(i.textContent,{usage:0,elements:[i]})}function Dh(e,n){let t=n.createElement("link");return t.setAttribute("rel","stylesheet"),t.setAttribute("href",e),t}var wh=(()=>{class e{doc;appId;nonce;inline=new Map;external=new Map;hosts=new Set;constructor(t,r,o,i={}){this.doc=t,this.appId=r,this.nonce=o,ux(t,r,this.inline,this.external),this.hosts.add(t.head)}addStyles(t,r){for(let o of t)this.addUsage(o,this.inline,x_);r?.forEach(o=>this.addUsage(o,this.external,Dh))}removeStyles(t,r){for(let o of t)this.removeUsage(o,this.inline);r?.forEach(o=>this.removeUsage(o,this.external))}addUsage(t,r,o){let i=r.get(t);i?i.usage++:r.set(t,{usage:1,elements:[...this.hosts].map(s=>this.addElement(s,o(t,this.doc)))})}removeUsage(t,r){let o=r.get(t);o&&(o.usage--,o.usage<=0&&(T_(o.elements),r.delete(t)))}ngOnDestroy(){for(let[,{elements:t}]of[...this.inline,...this.external])T_(t);this.hosts.clear()}addHost(t){this.hosts.add(t);for(let[r,{elements:o}]of this.inline)o.push(this.addElement(t,x_(r,this.doc)));for(let[r,{elements:o}]of this.external)o.push(this.addElement(t,Dh(r,this.doc)))}removeHost(t){this.hosts.delete(t)}addElement(t,r){return this.nonce&&r.setAttribute("nonce",this.nonce),t.appendChild(r)}static \u0275fac=function(r){return new(r||e)(I(F),I(xn),I(io,8),I(mr))};static \u0275prov=g({token:e,factory:e.\u0275fac})}return e})(),_h={svg:"http://www.w3.org/2000/svg",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/",math:"http://www.w3.org/1998/Math/MathML"},Ch=/%COMP%/g;var N_="%COMP%",dx=`_nghost-${N_}`,fx=`_ngcontent-${N_}`,hx=!0,px=new y("",{factory:()=>hx});function mx(e){return fx.replace(Ch,e)}function gx(e){return dx.replace(Ch,e)}function R_(e,n){return n.map(t=>t.replace(Ch,e))}var Ih=(()=>{class e{eventManager;sharedStylesHost;appId;removeStylesOnCompDestroy;doc;ngZone;nonce;tracingService;rendererByCompId=new Map;defaultRenderer;constructor(t,r,o,i,s,a,c=null,l=null){this.eventManager=t,this.sharedStylesHost=r,this.appId=o,this.removeStylesOnCompDestroy=i,this.doc=s,this.ngZone=a,this.nonce=c,this.tracingService=l,this.defaultRenderer=new Hi(t,s,a,this.tracingService)}createRenderer(t,r){if(!t||!r)return this.defaultRenderer;let o=this.getOrCreateRenderer(t,r);return o instanceof Ac?o.applyToHost(t):o instanceof Ui&&o.applyStyles(),o}getOrCreateRenderer(t,r){let o=this.rendererByCompId,i=o.get(r.id);if(!i){let s=this.doc,a=this.ngZone,c=this.eventManager,l=this.sharedStylesHost,u=this.removeStylesOnCompDestroy,d=this.tracingService;switch(r.encapsulation){case wt.Emulated:i=new Ac(c,l,r,this.appId,u,s,a,d);break;case wt.ShadowDom:return new xc(c,t,r,s,a,this.nonce,d,l);case wt.ExperimentalIsolatedShadowDom:return new xc(c,t,r,s,a,this.nonce,d);default:i=new Ui(c,l,r,u,s,a,d);break}o.set(r.id,i)}return i}ngOnDestroy(){this.rendererByCompId.clear()}componentReplaced(t){this.rendererByCompId.delete(t)}static \u0275fac=function(r){return new(r||e)(I(Eh),I(wh),I(xn),I(px),I(F),I(P),I(io),I(It,8))};static \u0275prov=g({token:e,factory:e.\u0275fac})}return e})(),Hi=class{eventManager;doc;ngZone;tracingService;data=Object.create(null);throwOnSyntheticProps=!0;constructor(n,t,r,o){this.eventManager=n,this.doc=t,this.ngZone=r,this.tracingService=o}destroy(){}destroyNode=null;createElement(n,t){return t?this.doc.createElementNS(_h[t]||t,n):this.doc.createElement(n)}createComment(n){return this.doc.createComment(n)}createText(n){return this.doc.createTextNode(n)}appendChild(n,t){(A_(n)?n.content:n).appendChild(t)}insertBefore(n,t,r){n&&(A_(n)?n.content:n).insertBefore(t,r)}removeChild(n,t){t.remove()}selectRootElement(n,t){let r=typeof n=="string"?this.doc.querySelector(n):n;if(!r)throw new v(-5104,!1);return t||(r.textContent=""),r}parentNode(n){return n.parentNode}nextSibling(n){return n.nextSibling}setAttribute(n,t,r,o){if(o){t=o+":"+t;let i=_h[o];i?n.setAttributeNS(i,t,r):n.setAttribute(t,r)}else n.setAttribute(t,r)}removeAttribute(n,t,r){if(r){let o=_h[r];o?n.removeAttributeNS(o,t):n.removeAttribute(`${r}:${t}`)}else n.removeAttribute(t)}addClass(n,t){n.classList.add(t)}removeClass(n,t){n.classList.remove(t)}setStyle(n,t,r,o){o&(Ct.DashCase|Ct.Important)?n.style.setProperty(t,r,o&Ct.Important?"important":""):n.style[t]=r}removeStyle(n,t,r){r&Ct.DashCase?n.style.removeProperty(t):n.style[t]=""}setProperty(n,t,r){n!=null&&(n[t]=r)}setValue(n,t){n.nodeValue=t}listen(n,t,r,o){if(typeof n=="string"&&(n=et().getGlobalEventTarget(this.doc,n),!n))throw new v(5102,!1);let i=this.decoratePreventDefault(r);return this.tracingService?.wrapEventListener&&(i=this.tracingService.wrapEventListener(n,t,i)),this.eventManager.addEventListener(n,t,i,o)}decoratePreventDefault(n){return t=>{if(t==="__ngUnwrap__")return n;n(t)===!1&&t.preventDefault()}}};function A_(e){return e.tagName==="TEMPLATE"&&e.content!==void 0}var xc=class extends Hi{hostEl;sharedStylesHost;shadowRoot;constructor(n,t,r,o,i,s,a,c){super(n,o,i,a),this.hostEl=t,this.sharedStylesHost=c,this.shadowRoot=t.attachShadow({mode:"open"}),this.sharedStylesHost&&this.sharedStylesHost.addHost(this.shadowRoot);let l=r.styles;l=R_(r.id,l);for(let d of l){let p=document.createElement("style");s&&p.setAttribute("nonce",s),p.textContent=d,this.shadowRoot.appendChild(p)}let u=r.getExternalStyles?.();if(u)for(let d of u){let p=Dh(d,o);s&&p.setAttribute("nonce",s),this.shadowRoot.appendChild(p)}}nodeOrShadowRoot(n){return n===this.hostEl?this.shadowRoot:n}appendChild(n,t){return super.appendChild(this.nodeOrShadowRoot(n),t)}insertBefore(n,t,r){return super.insertBefore(this.nodeOrShadowRoot(n),t,r)}removeChild(n,t){return super.removeChild(null,t)}parentNode(n){return this.nodeOrShadowRoot(super.parentNode(this.nodeOrShadowRoot(n)))}destroy(){this.sharedStylesHost&&this.sharedStylesHost.removeHost(this.shadowRoot)}},Ui=class extends Hi{sharedStylesHost;removeStylesOnCompDestroy;styles;styleUrls;constructor(n,t,r,o,i,s,a,c){super(n,i,s,a),this.sharedStylesHost=t,this.removeStylesOnCompDestroy=o;let l=r.styles;this.styles=c?R_(c,l):l,this.styleUrls=r.getExternalStyles?.(c)}applyStyles(){this.sharedStylesHost.addStyles(this.styles,this.styleUrls)}destroy(){this.removeStylesOnCompDestroy&&hr.size===0&&this.sharedStylesHost.removeStyles(this.styles,this.styleUrls)}},Ac=class extends Ui{contentAttr;hostAttr;constructor(n,t,r,o,i,s,a,c){let l=o+"-"+r.id;super(n,t,r,i,s,a,c,l),this.contentAttr=mx(l),this.hostAttr=gx(l)}applyToHost(n){this.applyStyles(),this.setAttribute(n,this.hostAttr,"")}createElement(n,t){let r=super.createElement(n,t);return super.setAttribute(r,this.contentAttr,""),r}};var Rc=class e extends ki{supportsDOMEvents=!0;static makeCurrent(){ch(new e)}onAndCancel(n,t,r,o){return n.addEventListener(t,r,o),()=>{n.removeEventListener(t,r,o)}}dispatchEvent(n,t){n.dispatchEvent(t)}remove(n){n.remove()}createElement(n,t){return t=t||this.getDefaultDocument(),t.createElement(n)}createHtmlDocument(){return document.implementation.createHTMLDocument("fakeTitle")}getDefaultDocument(){return document}isElementNode(n){return n.nodeType===Node.ELEMENT_NODE}isShadowRoot(n){return n instanceof DocumentFragment}getGlobalEventTarget(n,t){return t==="window"?window:t==="document"?n:t==="body"?n.body:null}getBaseHref(n){let t=yx();return t==null?null:vx(t)}resetBaseElement(){$i=null}getUserAgent(){return window.navigator.userAgent}getCookie(n){return ji(document.cookie,n)}},$i=null;function yx(){return $i=$i||document.head.querySelector("base"),$i?$i.getAttribute("href"):null}function vx(e){return new URL(e,document.baseURI).pathname}var Oc=class{addToWindow(n){le.getAngularTestability=(r,o=!0)=>{let i=n.findTestabilityInTree(r,o);if(i==null)throw new v(5103,!1);return i},le.getAllAngularTestabilities=()=>n.getAllTestabilities(),le.getAllAngularRootElements=()=>n.getAllRootElements();let t=r=>{let o=le.getAllAngularTestabilities(),i=o.length,s=function(){i--,i==0&&r()};o.forEach(a=>{a.whenStable(s)})};le.frameworkStabilizers||(le.frameworkStabilizers=[]),le.frameworkStabilizers.push(t)}findTestabilityInTree(n,t,r){if(t==null)return null;let o=n.getTestability(t);return o??(r?et().isShadowRoot(t)?this.findTestabilityInTree(n,t.host,!0):this.findTestabilityInTree(n,t.parentElement,!0):null)}},bx=(()=>{class e{build(){return new XMLHttpRequest}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac})}return e})(),O_=["alt","control","meta","shift"],_x={"\b":"Backspace"," ":"Tab","\x7F":"Delete","\x1B":"Escape",Del:"Delete",Esc:"Escape",Left:"ArrowLeft",Right:"ArrowRight",Up:"ArrowUp",Down:"ArrowDown",Menu:"ContextMenu",Scroll:"ScrollLock",Win:"OS"},Dx={alt:e=>e.altKey,control:e=>e.ctrlKey,meta:e=>e.metaKey,shift:e=>e.shiftKey},k_=(()=>{class e extends Bi{constructor(t){super(t)}supports(t){return e.parseEventName(t)!=null}addEventListener(t,r,o,i){let s=e.parseEventName(r),a=e.eventCallback(s.fullKey,o,this.manager.getZone());return this.manager.getZone().runOutsideAngular(()=>et().onAndCancel(t,s.domEventName,a,i))}static parseEventName(t){let r=t.toLowerCase().split("."),o=r.shift();if(r.length===0||!(o==="keydown"||o==="keyup"))return null;let i=e._normalizeKey(r.pop()),s="",a=r.indexOf("code");if(a>-1&&(r.splice(a,1),s="code."),O_.forEach(l=>{let u=r.indexOf(l);u>-1&&(r.splice(u,1),s+=l+".")}),s+=i,r.length!=0||i.length===0)return null;let c={};return c.domEventName=o,c.fullKey=s,c}static matchEventFullKeyCode(t,r){let o=_x[t.key]||t.key,i="";return r.indexOf("code.")>-1&&(o=t.code,i="code."),o==null||!o?!1:(o=o.toLowerCase(),o===" "?o="space":o==="."&&(o="dot"),O_.forEach(s=>{if(s!==o){let a=Dx[s];a(t)&&(i+=s+".")}}),i+=o,i===r)}static eventCallback(t,r,o){return i=>{e.matchEventFullKeyCode(i,t)&&o.runGuarded(()=>r(i))}}static _normalizeKey(t){return t==="esc"?"escape":t}static \u0275fac=function(r){return new(r||e)(I(F))};static \u0275prov=g({token:e,factory:e.\u0275fac})}return e})();async function Ex(e,n,t){let r=E({rootComponent:e},wx(n,t));return n_(r)}function wx(e,n){return{platformRef:n?.platformRef,appProviders:[...F_,...e?.providers??[]],platformProviders:Sx}}function Cx(){Rc.makeCurrent()}function Ix(){return new nt}function Mx(){return nf(document),document}var Sx=[{provide:mr,useValue:vh},{provide:qa,useValue:Cx,multi:!0},{provide:F,useFactory:Mx}];var Tx=[{provide:cc,useClass:Oc},{provide:ac,useClass:Ai},{provide:Ai,useClass:Ai}],F_=[{provide:ei,useValue:"root"},{provide:nt,useFactory:Ix},{provide:Nc,useClass:Tc,multi:!0},{provide:Nc,useClass:k_,multi:!0},Ih,wh,Eh,{provide:be,useExisting:Ih},{provide:br,useClass:bx},[]],xx=(()=>{class e{constructor(){}static \u0275fac=function(r){return new(r||e)};static \u0275mod=Z({type:e});static \u0275inj=G({providers:[...F_,...Tx],imports:[gh,t_]})}return e})();var On=class e{headers;normalizedNames=new Map;lazyInit;lazyUpdate=null;constructor(n){n?typeof n=="string"?this.lazyInit=()=>{this.headers=new Map,n.split(` -`).forEach(t=>{let r=t.indexOf(":");if(r>0){let o=t.slice(0,r),i=t.slice(r+1).trim();this.addHeaderEntry(o,i)}})}:typeof Headers<"u"&&n instanceof Headers?(this.headers=new Map,n.forEach((t,r)=>{this.addHeaderEntry(r,t)})):this.lazyInit=()=>{this.headers=new Map,Object.entries(n).forEach(([t,r])=>{this.setHeaderEntries(t,r)})}:this.headers=new Map}has(n){return this.init(),this.headers.has(n.toLowerCase())}get(n){this.init();let t=this.headers.get(n.toLowerCase());return t&&t.length>0?t[0]:null}keys(){return this.init(),Array.from(this.normalizedNames.values())}getAll(n){return this.init(),this.headers.get(n.toLowerCase())||null}append(n,t){return this.clone({name:n,value:t,op:"a"})}set(n,t){return this.clone({name:n,value:t,op:"s"})}delete(n,t){return this.clone({name:n,value:t,op:"d"})}maybeSetNormalizedName(n,t){this.normalizedNames.has(t)||this.normalizedNames.set(t,n)}init(){this.lazyInit&&(this.lazyInit instanceof e?this.copyFrom(this.lazyInit):this.lazyInit(),this.lazyInit=null,this.lazyUpdate&&(this.lazyUpdate.forEach(n=>this.applyUpdate(n)),this.lazyUpdate=null))}copyFrom(n){n.init(),Array.from(n.headers.keys()).forEach(t=>{this.headers.set(t,n.headers.get(t)),this.normalizedNames.set(t,n.normalizedNames.get(t))})}clone(n){let t=new e;return t.lazyInit=this.lazyInit&&this.lazyInit instanceof e?this.lazyInit:this,t.lazyUpdate=(this.lazyUpdate||[]).concat([n]),t}applyUpdate(n){let t=n.name.toLowerCase();switch(n.op){case"a":case"s":let r=n.value;if(typeof r=="string"&&(r=[r]),r.length===0)return;this.maybeSetNormalizedName(n.name,t);let o=(n.op==="a"?this.headers.get(t):void 0)||[];o.push(...r),this.headers.set(t,o);break;case"d":let i=n.value;if(!i)this.headers.delete(t),this.normalizedNames.delete(t);else{let s=this.headers.get(t);if(!s)return;s=s.filter(a=>i.indexOf(a)===-1),s.length===0?(this.headers.delete(t),this.normalizedNames.delete(t)):this.headers.set(t,s)}break}}addHeaderEntry(n,t){let r=n.toLowerCase();this.maybeSetNormalizedName(n,r),this.headers.has(r)?this.headers.get(r).push(t):this.headers.set(r,[t])}setHeaderEntries(n,t){let r=(Array.isArray(t)?t:[t]).map(i=>i.toString()),o=n.toLowerCase();this.headers.set(o,r),this.maybeSetNormalizedName(n,o)}forEach(n){this.init(),Array.from(this.normalizedNames.keys()).forEach(t=>n(this.normalizedNames.get(t),this.headers.get(t)))}};var Fc=class{map=new Map;set(n,t){return this.map.set(n,t),this}get(n){return this.map.has(n)||this.map.set(n,n.defaultValue()),this.map.get(n)}delete(n){return this.map.delete(n),this}has(n){return this.map.has(n)}keys(){return this.map.keys()}},Pc=class{encodeKey(n){return P_(n)}encodeValue(n){return P_(n)}decodeKey(n){return decodeURIComponent(n)}decodeValue(n){return decodeURIComponent(n)}};function Ax(e,n){let t=new Map;return e.length>0&&e.replace(/^\?/,"").split("&").forEach(o=>{let i=o.indexOf("="),[s,a]=i==-1?[n.decodeKey(o),""]:[n.decodeKey(o.slice(0,i)),n.decodeValue(o.slice(i+1))],c=t.get(s)||[];c.push(a),t.set(s,c)}),t}var Nx=/%(\d[a-f0-9])/gi,Rx={40:"@","3A":":",24:"$","2C":",","3B":";","3D":"=","3F":"?","2F":"/"};function P_(e){return encodeURIComponent(e).replace(Nx,(n,t)=>Rx[t]??n)}function kc(e){return`${e}`}var cn=class e{map;encoder;updates=null;cloneFrom=null;constructor(n={}){if(this.encoder=n.encoder||new Pc,n.fromString){if(n.fromObject)throw new v(2805,!1);this.map=Ax(n.fromString,this.encoder)}else n.fromObject?(this.map=new Map,Object.keys(n.fromObject).forEach(t=>{let r=n.fromObject[t],o=Array.isArray(r)?r.map(kc):[kc(r)];this.map.set(t,o)})):this.map=null}has(n){return this.init(),this.map.has(n)}get(n){this.init();let t=this.map.get(n);return t?t[0]:null}getAll(n){return this.init(),this.map.get(n)||null}keys(){return this.init(),Array.from(this.map.keys())}append(n,t){return this.clone({param:n,value:t,op:"a"})}appendAll(n){let t=[];return Object.keys(n).forEach(r=>{let o=n[r];Array.isArray(o)?o.forEach(i=>{t.push({param:r,value:i,op:"a"})}):t.push({param:r,value:o,op:"a"})}),this.clone(t)}set(n,t){return this.clone({param:n,value:t,op:"s"})}delete(n,t){return this.clone({param:n,value:t,op:"d"})}toString(){return this.init(),this.keys().map(n=>{let t=this.encoder.encodeKey(n);return this.map.get(n).map(r=>t+"="+this.encoder.encodeValue(r)).join("&")}).filter(n=>n!=="").join("&")}clone(n){let t=new e({encoder:this.encoder});return t.cloneFrom=this.cloneFrom||this,t.updates=(this.updates||[]).concat(n),t}init(){this.map===null&&(this.map=new Map),this.cloneFrom!==null&&(this.cloneFrom.init(),this.cloneFrom.keys().forEach(n=>this.map.set(n,this.cloneFrom.map.get(n))),this.updates.forEach(n=>{switch(n.op){case"a":case"s":let t=(n.op==="a"?this.map.get(n.param):void 0)||[];t.push(kc(n.value)),this.map.set(n.param,t);break;case"d":if(n.value!==void 0){let r=this.map.get(n.param)||[],o=r.indexOf(kc(n.value));o!==-1&&r.splice(o,1),r.length>0?this.map.set(n.param,r):this.map.delete(n.param)}else{this.map.delete(n.param);break}}}),this.cloneFrom=this.updates=null)}};function Ox(e){switch(e){case"DELETE":case"GET":case"HEAD":case"OPTIONS":case"JSONP":return!1;default:return!0}}function L_(e){return typeof ArrayBuffer<"u"&&e instanceof ArrayBuffer}function V_(e){return typeof Blob<"u"&&e instanceof Blob}function j_(e){return typeof FormData<"u"&&e instanceof FormData}function kx(e){return typeof URLSearchParams<"u"&&e instanceof URLSearchParams}var B_="Content-Type",H_="Accept",$_="text/plain",z_="application/json",Fx=`${z_}, ${$_}, */*`,mo=class e{url;body=null;headers;context;reportProgress=!1;withCredentials=!1;credentials;keepalive=!1;cache;priority;mode;redirect;referrer;integrity;referrerPolicy;responseType="json";method;params;urlWithParams;transferCache;timeout;constructor(n,t,r,o){this.url=t,this.method=n.toUpperCase();let i;if(Ox(this.method)||o?(this.body=r!==void 0?r:null,i=o):i=r,i){if(this.reportProgress=!!i.reportProgress,this.withCredentials=!!i.withCredentials,this.keepalive=!!i.keepalive,i.responseType&&(this.responseType=i.responseType),i.headers&&(this.headers=i.headers),i.context&&(this.context=i.context),i.params&&(this.params=i.params),i.priority&&(this.priority=i.priority),i.cache&&(this.cache=i.cache),i.credentials&&(this.credentials=i.credentials),typeof i.timeout=="number"){if(i.timeout<1||!Number.isInteger(i.timeout))throw new v(2822,"");this.timeout=i.timeout}i.mode&&(this.mode=i.mode),i.redirect&&(this.redirect=i.redirect),i.integrity&&(this.integrity=i.integrity),i.referrer&&(this.referrer=i.referrer),i.referrerPolicy&&(this.referrerPolicy=i.referrerPolicy),this.transferCache=i.transferCache}if(this.headers??=new On,this.context??=new Fc,!this.params)this.params=new cn,this.urlWithParams=t;else{let s=this.params.toString();if(s.length===0)this.urlWithParams=t;else{let a=t.indexOf("?"),c=a===-1?"?":aRo.set(Ln,n.setHeaders[Ln]),Ge)),n.setParams&&(ye=Object.keys(n.setParams).reduce((Ro,Ln)=>Ro.set(Ln,n.setParams[Ln]),ye)),new e(t,r,_,{params:ye,headers:Ge,context:No,reportProgress:ne,responseType:o,withCredentials:C,transferCache:m,keepalive:i,cache:a,priority:s,timeout:b,mode:c,redirect:l,credentials:u,referrer:d,integrity:p,referrerPolicy:h})}},_r=(function(e){return e[e.Sent=0]="Sent",e[e.UploadProgress=1]="UploadProgress",e[e.ResponseHeader=2]="ResponseHeader",e[e.DownloadProgress=3]="DownloadProgress",e[e.Response=4]="Response",e[e.User=5]="User",e})(_r||{}),yo=class{headers;status;statusText;url;ok;type;redirected;responseType;constructor(n,t=200,r="OK"){this.headers=n.headers||new On,this.status=n.status!==void 0?n.status:t,this.statusText=n.statusText||r,this.url=n.url||null,this.redirected=n.redirected,this.responseType=n.responseType,this.ok=this.status>=200&&this.status<300}},Lc=class e extends yo{constructor(n={}){super(n)}type=_r.ResponseHeader;clone(n={}){return new e({headers:n.headers||this.headers,status:n.status!==void 0?n.status:this.status,statusText:n.statusText||this.statusText,url:n.url||this.url||void 0})}},zi=class e extends yo{body;constructor(n={}){super(n),this.body=n.body!==void 0?n.body:null}type=_r.Response;clone(n={}){return new e({body:n.body!==void 0?n.body:this.body,headers:n.headers||this.headers,status:n.status!==void 0?n.status:this.status,statusText:n.statusText||this.statusText,url:n.url||this.url||void 0,redirected:n.redirected??this.redirected,responseType:n.responseType??this.responseType})}},go=class extends yo{name="HttpErrorResponse";message;error;ok=!1;constructor(n){super(n,0,"Unknown Error"),this.status>=200&&this.status<300?this.message=`Http failure during parsing for ${n.url||"(unknown url)"}`:this.message=`Http failure response for ${n.url||"(unknown url)"}: ${n.status} ${n.statusText}`,this.error=n.error||null}},Px=200,Lx=204;var Vx=new y("");var jx=/^\)\]\}',?\n/;var Sh=(()=>{class e{xhrFactory;tracingService=f(It,{optional:!0});constructor(t){this.xhrFactory=t}maybePropagateTrace(t){return this.tracingService?.propagate?this.tracingService.propagate(t):t}handle(t){if(t.method==="JSONP")throw new v(-2800,!1);let r=this.xhrFactory;return ke(null).pipe($s(()=>new k(i=>{let s=r.build();if(s.open(t.method,t.urlWithParams),t.withCredentials&&(s.withCredentials=!0),t.headers.forEach((_,C)=>s.setRequestHeader(_,C.join(","))),t.headers.has(H_)||s.setRequestHeader(H_,Fx),!t.headers.has(B_)){let _=t.detectContentTypeHeader();_!==null&&s.setRequestHeader(B_,_)}if(t.timeout&&(s.timeout=t.timeout),t.responseType){let _=t.responseType.toLowerCase();s.responseType=_!=="json"?_:"text"}let a=t.serializeBody(),c=null,l=()=>{if(c!==null)return c;let _=s.statusText||"OK",C=new On(s.getAllResponseHeaders()),ne=s.responseURL||t.url;return c=new Lc({headers:C,status:s.status,statusText:_,url:ne}),c},u=this.maybePropagateTrace(()=>{let{headers:_,status:C,statusText:ne,url:Ge}=l(),ye=null;C!==Lx&&(ye=typeof s.response>"u"?s.responseText:s.response),C===0&&(C=ye?Px:0);let No=C>=200&&C<300;if(t.responseType==="json"&&typeof ye=="string"){let Ro=ye;ye=ye.replace(jx,"");try{ye=ye!==""?JSON.parse(ye):null}catch(Ln){ye=Ro,No&&(No=!1,ye={error:Ln,text:ye})}}No?(i.next(new zi({body:ye,headers:_,status:C,statusText:ne,url:Ge||void 0})),i.complete()):i.error(new go({error:ye,headers:_,status:C,statusText:ne,url:Ge||void 0}))}),d=this.maybePropagateTrace(_=>{let{url:C}=l(),ne=new go({error:_,status:s.status||0,statusText:s.statusText||"Unknown Error",url:C||void 0});i.error(ne)}),p=d;t.timeout&&(p=this.maybePropagateTrace(_=>{let{url:C}=l(),ne=new go({error:new DOMException("Request timed out","TimeoutError"),status:s.status||0,statusText:s.statusText||"Request timeout",url:C||void 0});i.error(ne)}));let h=!1,m=this.maybePropagateTrace(_=>{h||(i.next(l()),h=!0);let C={type:_r.DownloadProgress,loaded:_.loaded};_.lengthComputable&&(C.total=_.total),t.responseType==="text"&&s.responseText&&(C.partialText=s.responseText),i.next(C)}),b=this.maybePropagateTrace(_=>{let C={type:_r.UploadProgress,loaded:_.loaded};_.lengthComputable&&(C.total=_.total),i.next(C)});return s.addEventListener("load",u),s.addEventListener("error",d),s.addEventListener("timeout",p),s.addEventListener("abort",d),t.reportProgress&&(s.addEventListener("progress",m),a!==null&&s.upload&&s.upload.addEventListener("progress",b)),s.send(a),i.next({type:_r.Sent}),()=>{s.removeEventListener("error",d),s.removeEventListener("abort",d),s.removeEventListener("load",u),s.removeEventListener("timeout",p),t.reportProgress&&(s.removeEventListener("progress",m),a!==null&&s.upload&&s.upload.removeEventListener("progress",b)),s.readyState!==s.DONE&&s.abort()}})))}static \u0275fac=function(r){return new(r||e)(I(br))};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();function G_(e,n){return n(e)}function Bx(e,n){return(t,r)=>n.intercept(t,{handle:o=>e(o,r)})}function Hx(e,n,t){return(r,o)=>Ur(t,()=>n(r,i=>e(i,o)))}var W_=new y(""),Th=new y("",{factory:()=>[]}),q_=new y(""),xh=new y("",{factory:()=>!0});function Ux(){let e=null;return(n,t)=>{e===null&&(e=(f(W_,{optional:!0})??[]).reduceRight(Bx,G_));let r=f(Kr);if(f(xh)){let i=r.add();return e(n,t).pipe(Hs(i))}else return e(n,t)}}var Ah=(()=>{class e{static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:function(r){let o=null;return r?o=new(r||e):o=I(Sh),o},providedIn:"root"})}return e})();var Vc=(()=>{class e{backend;injector;chain=null;pendingTasks=f(Kr);contributeToStability=f(xh);constructor(t,r){this.backend=t,this.injector=r}handle(t){if(this.chain===null){let r=Array.from(new Set([...this.injector.get(Th),...this.injector.get(q_,[])]));this.chain=r.reduceRight((o,i)=>Hx(o,i,this.injector),G_)}if(this.contributeToStability){let r=this.pendingTasks.add();return this.chain(t,o=>this.backend.handle(o)).pipe(Hs(r))}else return this.chain(t,r=>this.backend.handle(r))}static \u0275fac=function(r){return new(r||e)(I(Ah),I(ce))};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})(),Nh=(()=>{class e{static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:function(r){let o=null;return r?o=new(r||e):o=I(Vc),o},providedIn:"root"})}return e})();function Mh(e,n){return{body:n,headers:e.headers,context:e.context,observe:e.observe,params:e.params,reportProgress:e.reportProgress,responseType:e.responseType,withCredentials:e.withCredentials,credentials:e.credentials,transferCache:e.transferCache,timeout:e.timeout,keepalive:e.keepalive,priority:e.priority,cache:e.cache,mode:e.mode,redirect:e.redirect,integrity:e.integrity,referrer:e.referrer,referrerPolicy:e.referrerPolicy}}var jc=(()=>{class e{handler;constructor(t){this.handler=t}request(t,r,o={}){let i;if(t instanceof mo)i=t;else{let c;o.headers instanceof On?c=o.headers:c=new On(o.headers);let l;o.params&&(o.params instanceof cn?l=o.params:l=new cn({fromObject:o.params})),i=new mo(t,r,o.body!==void 0?o.body:null,{headers:c,context:o.context,params:l,reportProgress:o.reportProgress,responseType:o.responseType||"json",withCredentials:o.withCredentials,transferCache:o.transferCache,keepalive:o.keepalive,priority:o.priority,cache:o.cache,mode:o.mode,redirect:o.redirect,credentials:o.credentials,referrer:o.referrer,referrerPolicy:o.referrerPolicy,integrity:o.integrity,timeout:o.timeout})}let s=ke(i).pipe(jl(c=>this.handler.handle(c)));if(t instanceof mo||o.observe==="events")return s;let a=s.pipe(Ee(c=>c instanceof zi));switch(o.observe||"body"){case"body":switch(i.responseType){case"arraybuffer":return a.pipe(re(c=>{if(c.body!==null&&!(c.body instanceof ArrayBuffer))throw new v(2806,!1);return c.body}));case"blob":return a.pipe(re(c=>{if(c.body!==null&&!(c.body instanceof Blob))throw new v(2807,!1);return c.body}));case"text":return a.pipe(re(c=>{if(c.body!==null&&typeof c.body!="string")throw new v(2808,!1);return c.body}));default:return a.pipe(re(c=>c.body))}case"response":return a;default:throw new v(2809,!1)}}delete(t,r={}){return this.request("DELETE",t,r)}get(t,r={}){return this.request("GET",t,r)}head(t,r={}){return this.request("HEAD",t,r)}jsonp(t,r){return this.request("JSONP",t,{params:new cn().append(r,"JSONP_CALLBACK"),observe:"body",responseType:"json"})}options(t,r={}){return this.request("OPTIONS",t,r)}patch(t,r,o={}){return this.request("PATCH",t,Mh(o,r))}post(t,r,o={}){return this.request("POST",t,Mh(o,r))}put(t,r,o={}){return this.request("PUT",t,Mh(o,r))}static \u0275fac=function(r){return new(r||e)(I(Nh))};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();var $x=new y("",{factory:()=>!0}),zx="XSRF-TOKEN",Gx=new y("",{factory:()=>zx}),Wx="X-XSRF-TOKEN",qx=new y("",{factory:()=>Wx}),Yx=(()=>{class e{cookieName=f(Gx);doc=f(F);lastCookieString="";lastToken=null;parseCount=0;getToken(){let t=this.doc.cookie||"";return t!==this.lastCookieString&&(this.parseCount++,this.lastToken=ji(t,this.cookieName),this.lastCookieString=t),this.lastToken}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})(),Y_=(()=>{class e{static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:function(r){let o=null;return r?o=new(r||e):o=I(Yx),o},providedIn:"root"})}return e})();function Zx(e,n){if(!f($x)||e.method==="GET"||e.method==="HEAD")return n(e);try{let o=f(Rn).href,{origin:i}=new URL(o),{origin:s}=new URL(e.url,i);if(i!==s)return n(e)}catch{return n(e)}let t=f(Y_).getToken(),r=f(qx);return t!=null&&!e.headers.has(r)&&(e=e.clone({headers:e.headers.set(r,t)})),n(e)}var Rh=(function(e){return e[e.Interceptors=0]="Interceptors",e[e.LegacyInterceptors=1]="LegacyInterceptors",e[e.CustomXsrfConfiguration=2]="CustomXsrfConfiguration",e[e.NoXsrfProtection=3]="NoXsrfProtection",e[e.JsonpSupport=4]="JsonpSupport",e[e.RequestsMadeViaParent=5]="RequestsMadeViaParent",e[e.Fetch=6]="Fetch",e})(Rh||{});function Kx(e,n){return{\u0275kind:e,\u0275providers:n}}function Z_(...e){let n=[jc,Vc,{provide:Nh,useExisting:Vc},{provide:Ah,useFactory:()=>f(Vx,{optional:!0})??f(Sh)},{provide:Th,useValue:Zx,multi:!0}];for(let t of e)n.push(...t.\u0275providers);return nr(n)}var U_=new y("");function K_(){return Kx(Rh.LegacyInterceptors,[{provide:U_,useFactory:Ux},{provide:Th,useExisting:U_,multi:!0}])}var Xx=(()=>{class e{static \u0275fac=function(r){return new(r||e)};static \u0275mod=Z({type:e});static \u0275inj=G({providers:[Z_(K_())]})}return e})();var t3=(()=>{class e{_doc;constructor(t){this._doc=t}getTitle(){return this._doc.title}setTitle(t){this._doc.title=t||""}static \u0275fac=function(r){return new(r||e)(I(F))};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();function Jx(e,n){if(typeof COMPILED>"u"||!COMPILED){let t=le.ng=le.ng||{};t[e]=n}}var Oh=class{msPerTick;numTicks;constructor(n,t){this.msPerTick=n,this.numTicks=t}},kh=class{appRef;constructor(n){this.appRef=n.injector.get(Qe)}timeChangeDetection(n){let t=n&&n.record,r="Change Detection";t&&"profile"in console&&typeof console.profile=="function"&&console.profile(r);let o=performance.now(),i=0;for(;i<5||performance.now()-o<500;)this.appRef.tick(),i++;let s=performance.now();t&&"profileEnd"in console&&typeof console.profileEnd=="function"&&console.profileEnd(r);let a=(s-o)/i;return console.log(`ran ${i} change detection cycles`),console.log(`${a.toFixed(2)} ms per check`),new Oh(a,i)}},eA="profiler";function n3(e){return Jx(eA,new kh(e)),e}var Fh=(()=>{class e{static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:function(r){let o=null;return r?o=new(r||e):o=I(tA),o},providedIn:"root"})}return e})(),tA=(()=>{class e extends Fh{_doc;constructor(t){super(),this._doc=t}sanitize(t,r){if(r==null)return null;switch(t){case je.NONE:return r;case je.HTML:return Ht(r,"HTML")?Xe(r):Ka(this._doc,String(r)).toString();case je.STYLE:return Ht(r,"Style")?Xe(r):r;case je.SCRIPT:if(Ht(r,"Script"))return Xe(r);throw new v(5200,!1);case je.URL:return Ht(r,"URL")?Xe(r):wi(String(r));case je.RESOURCE_URL:if(Ht(r,"ResourceURL"))return Xe(r);throw new v(5201,!1);default:throw new v(5202,!1)}}bypassSecurityTrustHtml(t){return of(t)}bypassSecurityTrustStyle(t){return sf(t)}bypassSecurityTrustScript(t){return af(t)}bypassSecurityTrustUrl(t){return cf(t)}bypassSecurityTrustResourceUrl(t){return lf(t)}static \u0275fac=function(r){return new(r||e)(I(F))};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();function Gi(e){return e.buttons===0||e.detail===0}function Wi(e){let n=e.touches&&e.touches[0]||e.changedTouches&&e.changedTouches[0];return!!n&&n.identifier===-1&&(n.radiusX==null||n.radiusX===1)&&(n.radiusY==null||n.radiusY===1)}var Ph;function X_(){if(Ph==null){let e=typeof document<"u"?document.head:null;Ph=!!(e&&(e.createShadowRoot||e.attachShadow))}return Ph}function Lh(e){if(X_()){let n=e.getRootNode?e.getRootNode():null;if(typeof ShadowRoot<"u"&&ShadowRoot&&n instanceof ShadowRoot)return n}return null}function nA(){let e=typeof document<"u"&&document?document.activeElement:null;for(;e&&e.shadowRoot;){let n=e.shadowRoot.activeElement;if(n===e)break;e=n}return e}function Re(e){return e.composedPath?e.composedPath()[0]:e.target}var Vh;try{Vh=typeof Intl<"u"&&Intl.v8BreakIterator}catch{Vh=!1}var ae=(()=>{class e{_platformId=f(mr);isBrowser=this._platformId?S_(this._platformId):typeof document=="object"&&!!document;EDGE=this.isBrowser&&/(edge)/i.test(navigator.userAgent);TRIDENT=this.isBrowser&&/(msie|trident)/i.test(navigator.userAgent);BLINK=this.isBrowser&&!!(window.chrome||Vh)&&typeof CSS<"u"&&!this.EDGE&&!this.TRIDENT;WEBKIT=this.isBrowser&&/AppleWebKit/i.test(navigator.userAgent)&&!this.BLINK&&!this.EDGE&&!this.TRIDENT;IOS=this.isBrowser&&/iPad|iPhone|iPod/.test(navigator.userAgent)&&!("MSStream"in window);FIREFOX=this.isBrowser&&/(firefox|minefield)/i.test(navigator.userAgent);ANDROID=this.isBrowser&&/android/i.test(navigator.userAgent)&&!this.TRIDENT;SAFARI=this.isBrowser&&/safari/i.test(navigator.userAgent)&&this.WEBKIT;constructor(){}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();var qi;function Q_(){if(qi==null&&typeof window<"u")try{window.addEventListener("test",null,Object.defineProperty({},"passive",{get:()=>qi=!0}))}finally{qi=qi||!1}return qi}function vo(e){return Q_()?e:!!e.capture}function Bc(e,n=0){return J_(e)?Number(e):arguments.length===2?n:0}function J_(e){return!isNaN(parseFloat(e))&&!isNaN(Number(e))}function ct(e){return e instanceof z?e.nativeElement:e}var eD=new y("cdk-input-modality-detector-options"),tD={ignoreKeys:[18,17,224,91,16]},nD=650,jh={passive:!0,capture:!0},rD=(()=>{class e{_platform=f(ae);_listenerCleanups;modalityDetected;modalityChanged;get mostRecentModality(){return this._modality.value}_mostRecentTarget=null;_modality=new zn(null);_options;_lastTouchMs=0;_onKeydown=t=>{this._options?.ignoreKeys?.some(r=>r===t.keyCode)||(this._modality.next("keyboard"),this._mostRecentTarget=Re(t))};_onMousedown=t=>{Date.now()-this._lastTouchMs{if(Wi(t)){this._modality.next("keyboard");return}this._lastTouchMs=Date.now(),this._modality.next("touch"),this._mostRecentTarget=Re(t)};constructor(){let t=f(P),r=f(F),o=f(eD,{optional:!0});if(this._options=E(E({},tD),o),this.modalityDetected=this._modality.pipe(Bo(1)),this.modalityChanged=this.modalityDetected.pipe(Bs()),this._platform.isBrowser){let i=f(be).createRenderer(null,null);this._listenerCleanups=t.runOutsideAngular(()=>[i.listen(r,"keydown",this._onKeydown,jh),i.listen(r,"mousedown",this._onMousedown,jh),i.listen(r,"touchstart",this._onTouchstart,jh)])}}ngOnDestroy(){this._modality.complete(),this._listenerCleanups?.forEach(t=>t())}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})(),Yi=(function(e){return e[e.IMMEDIATE=0]="IMMEDIATE",e[e.EVENTUAL=1]="EVENTUAL",e})(Yi||{}),oD=new y("cdk-focus-monitor-default-options"),Hc=vo({passive:!0,capture:!0}),Uc=(()=>{class e{_ngZone=f(P);_platform=f(ae);_inputModalityDetector=f(rD);_origin=null;_lastFocusOrigin=null;_windowFocused=!1;_windowFocusTimeoutId;_originTimeoutId;_originFromTouchInteraction=!1;_elementInfo=new Map;_monitoredElementCount=0;_rootNodeFocusListenerCount=new Map;_detectionMode;_windowFocusListener=()=>{this._windowFocused=!0,this._windowFocusTimeoutId=setTimeout(()=>this._windowFocused=!1)};_document=f(F);_stopInputModalityDetector=new R;constructor(){let t=f(oD,{optional:!0});this._detectionMode=t?.detectionMode||Yi.IMMEDIATE}_rootNodeFocusAndBlurListener=t=>{let r=Re(t);for(let o=r;o;o=o.parentElement)t.type==="focus"?this._onFocus(t,o):this._onBlur(t,o)};monitor(t,r=!1){let o=ct(t);if(!this._platform.isBrowser||o.nodeType!==1)return ke();let i=Lh(o)||this._document,s=this._elementInfo.get(o);if(s)return r&&(s.checkChildren=!0),s.subject;let a={checkChildren:r,subject:new R,rootNode:i};return this._elementInfo.set(o,a),this._registerGlobalListeners(a),a.subject}stopMonitoring(t){let r=ct(t),o=this._elementInfo.get(r);o&&(o.subject.complete(),this._setClasses(r),this._elementInfo.delete(r),this._removeGlobalListeners(o))}focusVia(t,r,o){let i=ct(t),s=this._document.activeElement;i===s?this._getClosestElementsInfo(i).forEach(([a,c])=>this._originChanged(a,r,c)):(this._setOrigin(r),typeof i.focus=="function"&&i.focus(o))}ngOnDestroy(){this._elementInfo.forEach((t,r)=>this.stopMonitoring(r))}_getWindow(){return this._document.defaultView||window}_getFocusOrigin(t){return this._origin?this._originFromTouchInteraction?this._shouldBeAttributedToTouch(t)?"touch":"program":this._origin:this._windowFocused&&this._lastFocusOrigin?this._lastFocusOrigin:t&&this._isLastInteractionFromInputLabel(t)?"mouse":"program"}_shouldBeAttributedToTouch(t){return this._detectionMode===Yi.EVENTUAL||!!t?.contains(this._inputModalityDetector._mostRecentTarget)}_setClasses(t,r){t.classList.toggle("cdk-focused",!!r),t.classList.toggle("cdk-touch-focused",r==="touch"),t.classList.toggle("cdk-keyboard-focused",r==="keyboard"),t.classList.toggle("cdk-mouse-focused",r==="mouse"),t.classList.toggle("cdk-program-focused",r==="program")}_setOrigin(t,r=!1){this._ngZone.runOutsideAngular(()=>{if(this._origin=t,this._originFromTouchInteraction=t==="touch"&&r,this._detectionMode===Yi.IMMEDIATE){clearTimeout(this._originTimeoutId);let o=this._originFromTouchInteraction?nD:1;this._originTimeoutId=setTimeout(()=>this._origin=null,o)}})}_onFocus(t,r){let o=this._elementInfo.get(r),i=Re(t);!o||!o.checkChildren&&r!==i||this._originChanged(r,this._getFocusOrigin(i),o)}_onBlur(t,r){let o=this._elementInfo.get(r);!o||o.checkChildren&&t.relatedTarget instanceof Node&&r.contains(t.relatedTarget)||(this._setClasses(r),this._emitOrigin(o,null))}_emitOrigin(t,r){t.subject.observers.length&&this._ngZone.run(()=>t.subject.next(r))}_registerGlobalListeners(t){if(!this._platform.isBrowser)return;let r=t.rootNode,o=this._rootNodeFocusListenerCount.get(r)||0;o||this._ngZone.runOutsideAngular(()=>{r.addEventListener("focus",this._rootNodeFocusAndBlurListener,Hc),r.addEventListener("blur",this._rootNodeFocusAndBlurListener,Hc)}),this._rootNodeFocusListenerCount.set(r,o+1),++this._monitoredElementCount===1&&(this._ngZone.runOutsideAngular(()=>{this._getWindow().addEventListener("focus",this._windowFocusListener)}),this._inputModalityDetector.modalityDetected.pipe($n(this._stopInputModalityDetector)).subscribe(i=>{this._setOrigin(i,!0)}))}_removeGlobalListeners(t){let r=t.rootNode;if(this._rootNodeFocusListenerCount.has(r)){let o=this._rootNodeFocusListenerCount.get(r);o>1?this._rootNodeFocusListenerCount.set(r,o-1):(r.removeEventListener("focus",this._rootNodeFocusAndBlurListener,Hc),r.removeEventListener("blur",this._rootNodeFocusAndBlurListener,Hc),this._rootNodeFocusListenerCount.delete(r))}--this._monitoredElementCount||(this._getWindow().removeEventListener("focus",this._windowFocusListener),this._stopInputModalityDetector.next(),clearTimeout(this._windowFocusTimeoutId),clearTimeout(this._originTimeoutId))}_originChanged(t,r,o){this._setClasses(t,r),this._emitOrigin(o,r),this._lastFocusOrigin=r}_getClosestElementsInfo(t){let r=[];return this._elementInfo.forEach((o,i)=>{(i===t||o.checkChildren&&i.contains(t))&&r.push([i,o])}),r}_isLastInteractionFromInputLabel(t){let{_mostRecentTarget:r,mostRecentModality:o}=this._inputModalityDetector;if(o!=="mouse"||!r||r===t||t.nodeName!=="INPUT"&&t.nodeName!=="TEXTAREA"||t.disabled)return!1;let i=t.labels;if(i){for(let s=0;s{class e{_elementRef=f(z);_focusMonitor=f(Uc);_monitorSubscription;_focusOrigin=null;cdkFocusChange=new H;constructor(){}get focusOrigin(){return this._focusOrigin}ngAfterViewInit(){let t=this._elementRef.nativeElement;this._monitorSubscription=this._focusMonitor.monitor(t,t.nodeType===1&&t.hasAttribute("cdkMonitorSubtreeFocus")).subscribe(r=>{this._focusOrigin=r,this.cdkFocusChange.emit(r)})}ngOnDestroy(){this._focusMonitor.stopMonitoring(this._elementRef),this._monitorSubscription?.unsubscribe()}static \u0275fac=function(r){return new(r||e)};static \u0275dir=O({type:e,selectors:[["","cdkMonitorElementFocus",""],["","cdkMonitorSubtreeFocus",""]],outputs:{cdkFocusChange:"cdkFocusChange"},exportAs:["cdkMonitorFocus"]})}return e})();var $c=new WeakMap,lt=(()=>{class e{_appRef;_injector=f(j);_environmentInjector=f(ce);load(t){let r=this._appRef=this._appRef||this._injector.get(Qe),o=$c.get(r);o||(o={loaders:new Set,refs:[]},$c.set(r,o),r.onDestroy(()=>{$c.get(r)?.refs.forEach(i=>i.destroy()),$c.delete(r)})),o.loaders.has(t)||(o.loaders.add(t),o.refs.push(gc(t,{environmentInjector:this._environmentInjector})))}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();var Gc=(()=>{class e{static \u0275fac=function(r){return new(r||e)};static \u0275cmp=Se({type:e,selectors:[["ng-component"]],exportAs:["cdkVisuallyHidden"],decls:0,vars:0,template:function(r,o){},styles:[`.cdk-visually-hidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;white-space:nowrap;outline:0;-webkit-appearance:none;-moz-appearance:none;left:0}[dir=rtl] .cdk-visually-hidden{left:auto;right:0} -`],encapsulation:2,changeDetection:0})}return e})(),zc;function oA(){if(zc===void 0&&(zc=null,typeof window<"u")){let e=window;e.trustedTypes!==void 0&&(zc=e.trustedTypes.createPolicy("angular#components",{createHTML:n=>n}))}return zc}function iA(e){return oA()?.createHTML(e)||e}function iD(e,n,t){let r=t.sanitize(je.HTML,n);e.innerHTML=iA(r||"")}function Dr(e){return Array.isArray(e)?e:[e]}var sD=new Set,Er,Wc=(()=>{class e{_platform=f(ae);_nonce=f(io,{optional:!0});_matchMedia;constructor(){this._matchMedia=this._platform.isBrowser&&window.matchMedia?window.matchMedia.bind(window):aA}matchMedia(t){return(this._platform.WEBKIT||this._platform.BLINK)&&sA(t,this._nonce),this._matchMedia(t)}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();function sA(e,n){if(!sD.has(e))try{Er||(Er=document.createElement("style"),n&&Er.setAttribute("nonce",n),Er.setAttribute("type","text/css"),document.head.appendChild(Er)),Er.sheet&&(Er.sheet.insertRule(`@media ${e} {body{ }}`,0),sD.add(e))}catch(t){console.error(t)}}function aA(e){return{matches:e==="all"||e==="",media:e,addListener:()=>{},removeListener:()=>{}}}var Bh=(()=>{class e{_mediaMatcher=f(Wc);_zone=f(P);_queries=new Map;_destroySubject=new R;constructor(){}ngOnDestroy(){this._destroySubject.next(),this._destroySubject.complete()}isMatched(t){return aD(Dr(t)).some(o=>this._registerQuery(o).mql.matches)}observe(t){let o=aD(Dr(t)).map(s=>this._registerQuery(s).observable),i=Fl(o);return i=mn(i.pipe(pt(1)),i.pipe(Bo(1),qn(0))),i.pipe(re(s=>{let a={matches:!1,breakpoints:{}};return s.forEach(({matches:c,query:l})=>{a.matches=a.matches||c,a.breakpoints[l]=c}),a}))}_registerQuery(t){if(this._queries.has(t))return this._queries.get(t);let r=this._mediaMatcher.matchMedia(t),i={observable:new k(s=>{let a=c=>this._zone.run(()=>s.next(c));return r.addListener(a),()=>{r.removeListener(a)}}).pipe(Us(r),re(({matches:s})=>({query:t,matches:s})),$n(this._destroySubject)),mql:r};return this._queries.set(t,i),i}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();function aD(e){return e.map(n=>n.split(",")).reduce((n,t)=>n.concat(t)).map(n=>n.trim())}function cA(e){if(e.type==="characterData"&&e.target instanceof Comment)return!0;if(e.type==="childList"){for(let n=0;n{class e{create(t){return typeof MutationObserver>"u"?null:new MutationObserver(t)}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})(),lD=(()=>{class e{_mutationObserverFactory=f(cD);_observedElements=new Map;_ngZone=f(P);constructor(){}ngOnDestroy(){this._observedElements.forEach((t,r)=>this._cleanupObserver(r))}observe(t){let r=ct(t);return new k(o=>{let s=this._observeElement(r).pipe(re(a=>a.filter(c=>!cA(c))),Ee(a=>!!a.length)).subscribe(a=>{this._ngZone.run(()=>{o.next(a)})});return()=>{s.unsubscribe(),this._unobserveElement(r)}})}_observeElement(t){return this._ngZone.runOutsideAngular(()=>{if(this._observedElements.has(t))this._observedElements.get(t).count++;else{let r=new R,o=this._mutationObserverFactory.create(i=>r.next(i));o&&o.observe(t,{characterData:!0,childList:!0,subtree:!0}),this._observedElements.set(t,{observer:o,stream:r,count:1})}return this._observedElements.get(t).stream})}_unobserveElement(t){this._observedElements.has(t)&&(this._observedElements.get(t).count--,this._observedElements.get(t).count||this._cleanupObserver(t))}_cleanupObserver(t){if(this._observedElements.has(t)){let{observer:r,stream:o}=this._observedElements.get(t);r&&r.disconnect(),o.complete(),this._observedElements.delete(t)}}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})(),o4=(()=>{class e{_contentObserver=f(lD);_elementRef=f(z);event=new H;get disabled(){return this._disabled}set disabled(t){this._disabled=t,this._disabled?this._unsubscribe():this._subscribe()}_disabled=!1;get debounce(){return this._debounce}set debounce(t){this._debounce=Bc(t),this._subscribe()}_debounce;_currentSubscription=null;constructor(){}ngAfterContentInit(){!this._currentSubscription&&!this.disabled&&this._subscribe()}ngOnDestroy(){this._unsubscribe()}_subscribe(){this._unsubscribe();let t=this._contentObserver.observe(this._elementRef);this._currentSubscription=(this.debounce?t.pipe(qn(this.debounce)):t).subscribe(this.event)}_unsubscribe(){this._currentSubscription?.unsubscribe()}static \u0275fac=function(r){return new(r||e)};static \u0275dir=O({type:e,selectors:[["","cdkObserveContent",""]],inputs:{disabled:[2,"cdkObserveContentDisabled","disabled",de],debounce:"debounce"},outputs:{event:"cdkObserveContent"},exportAs:["cdkObserveContent"]})}return e})(),uD=(()=>{class e{static \u0275fac=function(r){return new(r||e)};static \u0275mod=Z({type:e});static \u0275inj=G({providers:[cD]})}return e})();var lA=(()=>{class e{_platform=f(ae);constructor(){}isDisabled(t){return t.hasAttribute("disabled")}isVisible(t){return dA(t)&&getComputedStyle(t).visibility==="visible"}isTabbable(t){if(!this._platform.isBrowser)return!1;let r=uA(bA(t));if(r&&(dD(r)===-1||!this.isVisible(r)))return!1;let o=t.nodeName.toLowerCase(),i=dD(t);return t.hasAttribute("contenteditable")?i!==-1:o==="iframe"||o==="object"||this._platform.WEBKIT&&this._platform.IOS&&!yA(t)?!1:o==="audio"?t.hasAttribute("controls")?i!==-1:!1:o==="video"?i===-1?!1:i!==null?!0:this._platform.FIREFOX||t.hasAttribute("controls"):t.tabIndex>=0}isFocusable(t,r){return vA(t)&&!this.isDisabled(t)&&(r?.ignoreVisibility||this.isVisible(t))}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();function uA(e){try{return e.frameElement}catch{return null}}function dA(e){return!!(e.offsetWidth||e.offsetHeight||typeof e.getClientRects=="function"&&e.getClientRects().length)}function fA(e){let n=e.nodeName.toLowerCase();return n==="input"||n==="select"||n==="button"||n==="textarea"}function hA(e){return mA(e)&&e.type=="hidden"}function pA(e){return gA(e)&&e.hasAttribute("href")}function mA(e){return e.nodeName.toLowerCase()=="input"}function gA(e){return e.nodeName.toLowerCase()=="a"}function pD(e){if(!e.hasAttribute("tabindex")||e.tabIndex===void 0)return!1;let n=e.getAttribute("tabindex");return!!(n&&!isNaN(parseInt(n,10)))}function dD(e){if(!pD(e))return null;let n=parseInt(e.getAttribute("tabindex")||"",10);return isNaN(n)?-1:n}function yA(e){let n=e.nodeName.toLowerCase(),t=n==="input"&&e.type;return t==="text"||t==="password"||n==="select"||n==="textarea"}function vA(e){return hA(e)?!1:fA(e)||pA(e)||e.hasAttribute("contenteditable")||pD(e)}function bA(e){return e.ownerDocument&&e.ownerDocument.defaultView||window}var Uh=class{_element;_checker;_ngZone;_document;_injector;_startAnchor=null;_endAnchor=null;_hasAttached=!1;startAnchorListener=()=>this.focusLastTabbableElement();endAnchorListener=()=>this.focusFirstTabbableElement();get enabled(){return this._enabled}set enabled(n){this._enabled=n,this._startAnchor&&this._endAnchor&&(this._toggleAnchorTabIndex(n,this._startAnchor),this._toggleAnchorTabIndex(n,this._endAnchor))}_enabled=!0;constructor(n,t,r,o,i=!1,s){this._element=n,this._checker=t,this._ngZone=r,this._document=o,this._injector=s,i||this.attachAnchors()}destroy(){let n=this._startAnchor,t=this._endAnchor;n&&(n.removeEventListener("focus",this.startAnchorListener),n.remove()),t&&(t.removeEventListener("focus",this.endAnchorListener),t.remove()),this._startAnchor=this._endAnchor=null,this._hasAttached=!1}attachAnchors(){return this._hasAttached?!0:(this._ngZone.runOutsideAngular(()=>{this._startAnchor||(this._startAnchor=this._createAnchor(),this._startAnchor.addEventListener("focus",this.startAnchorListener)),this._endAnchor||(this._endAnchor=this._createAnchor(),this._endAnchor.addEventListener("focus",this.endAnchorListener))}),this._element.parentNode&&(this._element.parentNode.insertBefore(this._startAnchor,this._element),this._element.parentNode.insertBefore(this._endAnchor,this._element.nextSibling),this._hasAttached=!0),this._hasAttached)}focusInitialElementWhenReady(n){return new Promise(t=>{this._executeOnStable(()=>t(this.focusInitialElement(n)))})}focusFirstTabbableElementWhenReady(n){return new Promise(t=>{this._executeOnStable(()=>t(this.focusFirstTabbableElement(n)))})}focusLastTabbableElementWhenReady(n){return new Promise(t=>{this._executeOnStable(()=>t(this.focusLastTabbableElement(n)))})}_getRegionBoundary(n){let t=this._element.querySelectorAll(`[cdk-focus-region-${n}], [cdkFocusRegion${n}], [cdk-focus-${n}]`);return n=="start"?t.length?t[0]:this._getFirstTabbableElement(this._element):t.length?t[t.length-1]:this._getLastTabbableElement(this._element)}focusInitialElement(n){let t=this._element.querySelector("[cdk-focus-initial], [cdkFocusInitial]");if(t){if(!this._checker.isFocusable(t)){let r=this._getFirstTabbableElement(t);return r?.focus(n),!!r}return t.focus(n),!0}return this.focusFirstTabbableElement(n)}focusFirstTabbableElement(n){let t=this._getRegionBoundary("start");return t&&t.focus(n),!!t}focusLastTabbableElement(n){let t=this._getRegionBoundary("end");return t&&t.focus(n),!!t}hasAttached(){return this._hasAttached}_getFirstTabbableElement(n){if(this._checker.isFocusable(n)&&this._checker.isTabbable(n))return n;let t=n.children;for(let r=0;r=0;r--){let o=t[r].nodeType===this._document.ELEMENT_NODE?this._getLastTabbableElement(t[r]):null;if(o)return o}return null}_createAnchor(){let n=this._document.createElement("div");return this._toggleAnchorTabIndex(this._enabled,n),n.classList.add("cdk-visually-hidden"),n.classList.add("cdk-focus-trap-anchor"),n.setAttribute("aria-hidden","true"),n}_toggleAnchorTabIndex(n,t){n?t.setAttribute("tabindex","0"):t.removeAttribute("tabindex")}toggleAnchors(n){this._startAnchor&&this._endAnchor&&(this._toggleAnchorTabIndex(n,this._startAnchor),this._toggleAnchorTabIndex(n,this._endAnchor))}_executeOnStable(n){this._injector?An(n,{injector:this._injector}):setTimeout(n)}},_A=(()=>{class e{_checker=f(lA);_ngZone=f(P);_document=f(F);_injector=f(j);constructor(){f(lt).load(Gc)}create(t,r=!1){return new Uh(t,this._checker,this._ngZone,this._document,r,this._injector)}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();var mD=new y("liveAnnouncerElement",{providedIn:"root",factory:()=>null}),gD=new y("LIVE_ANNOUNCER_DEFAULT_OPTIONS"),DA=0,EA=(()=>{class e{_ngZone=f(P);_defaultOptions=f(gD,{optional:!0});_liveElement;_document=f(F);_sanitizer=f(Fh);_previousTimeout;_currentPromise;_currentResolve;constructor(){let t=f(mD,{optional:!0});this._liveElement=t||this._createLiveElement()}announce(t,...r){let o=this._defaultOptions,i,s;return r.length===1&&typeof r[0]=="number"?s=r[0]:[i,s]=r,this.clear(),clearTimeout(this._previousTimeout),i||(i=o&&o.politeness?o.politeness:"polite"),s==null&&o&&(s=o.duration),this._liveElement.setAttribute("aria-live",i),this._liveElement.id&&this._exposeAnnouncerToModals(this._liveElement.id),this._ngZone.runOutsideAngular(()=>(this._currentPromise||(this._currentPromise=new Promise(a=>this._currentResolve=a)),clearTimeout(this._previousTimeout),this._previousTimeout=setTimeout(()=>{!t||typeof t=="string"?this._liveElement.textContent=t:iD(this._liveElement,t,this._sanitizer),typeof s=="number"&&(this._previousTimeout=setTimeout(()=>this.clear(),s)),this._currentResolve?.(),this._currentPromise=this._currentResolve=void 0},100),this._currentPromise))}clear(){this._liveElement&&(this._liveElement.textContent="")}ngOnDestroy(){clearTimeout(this._previousTimeout),this._liveElement?.remove(),this._liveElement=null,this._currentResolve?.(),this._currentPromise=this._currentResolve=void 0}_createLiveElement(){let t="cdk-live-announcer-element",r=this._document.getElementsByClassName(t),o=this._document.createElement("div");for(let i=0;i .cdk-overlay-container [aria-modal="true"]');for(let o=0;o{class e{_platform=f(ae);_hasCheckedHighContrastMode=!1;_document=f(F);_breakpointSubscription;constructor(){this._breakpointSubscription=f(Bh).observe("(forced-colors: active)").subscribe(()=>{this._hasCheckedHighContrastMode&&(this._hasCheckedHighContrastMode=!1,this._applyBodyHighContrastModeCssClasses())})}getHighContrastMode(){if(!this._platform.isBrowser)return kn.NONE;let t=this._document.createElement("div");t.style.backgroundColor="rgb(1,2,3)",t.style.position="absolute",this._document.body.appendChild(t);let r=this._document.defaultView||window,o=r&&r.getComputedStyle?r.getComputedStyle(t):null,i=(o&&o.backgroundColor||"").replace(/ /g,"");switch(t.remove(),i){case"rgb(0,0,0)":case"rgb(45,50,54)":case"rgb(32,32,32)":return kn.WHITE_ON_BLACK;case"rgb(255,255,255)":case"rgb(255,250,239)":return kn.BLACK_ON_WHITE}return kn.NONE}ngOnDestroy(){this._breakpointSubscription.unsubscribe()}_applyBodyHighContrastModeCssClasses(){if(!this._hasCheckedHighContrastMode&&this._platform.isBrowser&&this._document.body){let t=this._document.body.classList;t.remove(Hh,fD,hD),this._hasCheckedHighContrastMode=!0;let r=this.getHighContrastMode();r===kn.BLACK_ON_WHITE?t.add(Hh,fD):r===kn.WHITE_ON_BLACK&&t.add(Hh,hD)}}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})(),wA=(()=>{class e{constructor(){f(yD)._applyBodyHighContrastModeCssClasses()}static \u0275fac=function(r){return new(r||e)};static \u0275mod=Z({type:e});static \u0275inj=G({imports:[uD]})}return e})();var $h={},Zi=class e{_appId=f(xn);static _infix=`a${Math.floor(Math.random()*1e5).toString()}`;getId(n,t=!1){return this._appId!=="ng"&&(n+=this._appId),$h.hasOwnProperty(n)||($h[n]=0),`${n}${t?e._infix+"-":""}${$h[n]++}`}static \u0275fac=function(t){return new(t||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})};var CA=200,bo=class{_letterKeyStream=new R;_items=[];_selectedItemIndex=-1;_pressedLetters=[];_skipPredicateFn;_selectedItem=new R;selectedItem=this._selectedItem;constructor(n,t){let r=typeof t?.debounceInterval=="number"?t.debounceInterval:CA;t?.skipPredicate&&(this._skipPredicateFn=t.skipPredicate),this.setItems(n),this._setupKeyHandler(r)}destroy(){this._pressedLetters=[],this._letterKeyStream.complete(),this._selectedItem.complete()}setCurrentSelectedItemIndex(n){this._selectedItemIndex=n}setItems(n){this._items=n}handleKey(n){let t=n.keyCode;n.key&&n.key.length===1?this._letterKeyStream.next(n.key.toLocaleUpperCase()):(t>=65&&t<=90||t>=48&&t<=57)&&this._letterKeyStream.next(String.fromCharCode(t))}isTyping(){return this._pressedLetters.length>0}reset(){this._pressedLetters=[]}_setupKeyHandler(n){this._letterKeyStream.pipe(Gl(t=>this._pressedLetters.push(t)),qn(n),Ee(()=>this._pressedLetters.length>0),re(()=>this._pressedLetters.join("").toLocaleUpperCase())).subscribe(t=>{for(let r=1;re[t]):e.altKey||e.shiftKey||e.ctrlKey||e.metaKey}var _o=class{_items;_activeItemIndex=Me(-1);_activeItem=Me(null);_wrap=!1;_typeaheadSubscription=B.EMPTY;_itemChangesSubscription;_vertical=!0;_horizontal=null;_allowedModifierKeys=[];_homeAndEnd=!1;_pageUpAndDown={enabled:!1,delta:10};_effectRef;_typeahead;_skipPredicateFn=n=>n.disabled;constructor(n,t){this._items=n,n instanceof Jt?this._itemChangesSubscription=n.changes.subscribe(r=>this._itemsChanged(r.toArray())):co(n)&&(this._effectRef=li(()=>this._itemsChanged(n()),{injector:t}))}tabOut=new R;change=new R;skipPredicate(n){return this._skipPredicateFn=n,this}withWrap(n=!0){return this._wrap=n,this}withVerticalOrientation(n=!0){return this._vertical=n,this}withHorizontalOrientation(n){return this._horizontal=n,this}withAllowedModifierKeys(n){return this._allowedModifierKeys=n,this}withTypeAhead(n=200){this._typeaheadSubscription.unsubscribe();let t=this._getItemsArray();return this._typeahead=new bo(t,{debounceInterval:typeof n=="number"?n:void 0,skipPredicate:r=>this._skipPredicateFn(r)}),this._typeaheadSubscription=this._typeahead.selectedItem.subscribe(r=>{this.setActiveItem(r)}),this}cancelTypeahead(){return this._typeahead?.reset(),this}withHomeAndEnd(n=!0){return this._homeAndEnd=n,this}withPageUpDown(n=!0,t=10){return this._pageUpAndDown={enabled:n,delta:t},this}setActiveItem(n){let t=this._activeItem();this.updateActiveItem(n),this._activeItem()!==t&&this.change.next(this._activeItemIndex())}onKeydown(n){let t=n.keyCode,o=["altKey","ctrlKey","metaKey","shiftKey"].every(i=>!n[i]||this._allowedModifierKeys.indexOf(i)>-1);switch(t){case 9:this.tabOut.next();return;case 40:if(this._vertical&&o){this.setNextItemActive();break}else return;case 38:if(this._vertical&&o){this.setPreviousItemActive();break}else return;case 39:if(this._horizontal&&o){this._horizontal==="rtl"?this.setPreviousItemActive():this.setNextItemActive();break}else return;case 37:if(this._horizontal&&o){this._horizontal==="rtl"?this.setNextItemActive():this.setPreviousItemActive();break}else return;case 36:if(this._homeAndEnd&&o){this.setFirstItemActive();break}else return;case 35:if(this._homeAndEnd&&o){this.setLastItemActive();break}else return;case 33:if(this._pageUpAndDown.enabled&&o){let i=this._activeItemIndex()-this._pageUpAndDown.delta;this._setActiveItemByIndex(i>0?i:0,1);break}else return;case 34:if(this._pageUpAndDown.enabled&&o){let i=this._activeItemIndex()+this._pageUpAndDown.delta,s=this._getItemsArray().length;this._setActiveItemByIndex(i-1&&r!==this._activeItemIndex()&&(this._activeItemIndex.set(r),this._typeahead?.setCurrentSelectedItemIndex(r))}}};var zh=class extends _o{setActiveItem(n){this.activeItem&&this.activeItem.setInactiveStyles(),super.setActiveItem(n),this.activeItem&&this.activeItem.setActiveStyles()}};var Gh=class extends _o{_origin="program";setFocusOrigin(n){return this._origin=n,this}setActiveItem(n){super.setActiveItem(n),this.activeItem&&this.activeItem.focus(this._origin)}};function Wh(e){return pn(e)?e:ke(e)}var qh=class{_activeItemIndex=-1;_activeItem=null;_shouldActivationFollowFocus=!1;_horizontalOrientation="ltr";_skipPredicateFn=n=>!1;_trackByFn=n=>n;_items=[];_typeahead;_typeaheadSubscription=B.EMPTY;_hasInitialFocused=!1;_initializeFocus(){if(this._hasInitialFocused||this._items.length===0)return;let n=0;for(let r=0;rthis._itemsChanged(r.toArray()))):pn(n)?n.subscribe(r=>this._itemsChanged(r)):(this._items=n,this._initializeFocus()),typeof t.shouldActivationFollowFocus=="boolean"&&(this._shouldActivationFollowFocus=t.shouldActivationFollowFocus),t.horizontalOrientation&&(this._horizontalOrientation=t.horizontalOrientation),t.skipPredicate&&(this._skipPredicateFn=t.skipPredicate),t.trackBy&&(this._trackByFn=t.trackBy),typeof t.typeAheadDebounceInterval<"u"&&this._setTypeAhead(t.typeAheadDebounceInterval)}change=new R;destroy(){this._typeaheadSubscription.unsubscribe(),this._typeahead?.destroy(),this.change.complete()}onKeydown(n){switch(n.key){case"Tab":return;case"ArrowDown":this._focusNextItem();break;case"ArrowUp":this._focusPreviousItem();break;case"ArrowRight":this._horizontalOrientation==="rtl"?this._collapseCurrentItem():this._expandCurrentItem();break;case"ArrowLeft":this._horizontalOrientation==="rtl"?this._expandCurrentItem():this._collapseCurrentItem();break;case"Home":this._focusFirstItem();break;case"End":this._focusLastItem();break;case"Enter":case" ":this._activateCurrentItem();break;default:if(n.key==="*"){this._expandAllItemsAtCurrentItemLevel();break}this._typeahead?.handleKey(n);return}this._typeahead?.reset(),n.preventDefault()}getActiveItemIndex(){return this._activeItemIndex}getActiveItem(){return this._activeItem}_itemsChanged(n){this._hasInitialFocused&&this._activeItem&&!n.includes(this._activeItem)&&(this._activeItem=null,this._hasInitialFocused=!1),this._items=n,this._typeahead?.setItems(this._items),this._updateActiveItemIndex(this._items),this._initializeFocus()}_focusFirstItem(){this.focusItem(this._findNextAvailableItemIndex(-1))}_focusLastItem(){this.focusItem(this._findPreviousAvailableItemIndex(this._items.length))}_focusNextItem(){this.focusItem(this._findNextAvailableItemIndex(this._activeItemIndex))}_focusPreviousItem(){this.focusItem(this._findPreviousAvailableItemIndex(this._activeItemIndex))}focusItem(n,t={}){t.emitChangeEvent??=!0;let r=typeof n=="number"?n:this._items.findIndex(s=>this._trackByFn(s)===this._trackByFn(n));if(r<0||r>=this._items.length)return;let o=this._items[r];if(this._activeItem!==null&&this._trackByFn(o)===this._trackByFn(this._activeItem))return;let i=this._activeItem;this._activeItem=o??null,this._activeItemIndex=r,this._typeahead?.setCurrentSelectedItemIndex(r),this._activeItem?.focus(),i?.unfocus(),t.emitChangeEvent&&this.change.next(this._activeItem),this._shouldActivationFollowFocus&&this._activateCurrentItem()}_updateActiveItemIndex(n){let t=this._activeItem;if(!t)return;let r=n.findIndex(o=>this._trackByFn(o)===this._trackByFn(t));r>-1&&r!==this._activeItemIndex&&(this._activeItemIndex=r,this._typeahead?.setCurrentSelectedItemIndex(r))}_setTypeAhead(n){this._typeahead=new bo(this._items,{debounceInterval:typeof n=="number"?n:void 0,skipPredicate:t=>this._skipPredicateFn(t)}),this._typeaheadSubscription=this._typeahead.selectedItem.subscribe(t=>{this.focusItem(t)})}_findNextAvailableItemIndex(n){for(let t=n+1;t=0;t--)if(!this._skipPredicateFn(this._items[t]))return t;return n}_collapseCurrentItem(){if(this._activeItem)if(this._isCurrentItemExpanded())this._activeItem.collapse();else{let n=this._activeItem.getParent();if(!n||this._skipPredicateFn(n))return;this.focusItem(n)}}_expandCurrentItem(){this._activeItem&&(this._isCurrentItemExpanded()?Wh(this._activeItem.getChildren()).pipe(pt(1)).subscribe(n=>{let t=n.find(r=>!this._skipPredicateFn(r));t&&this.focusItem(t)}):this._activeItem.expand())}_isCurrentItemExpanded(){return this._activeItem?typeof this._activeItem.isExpanded=="boolean"?this._activeItem.isExpanded:this._activeItem.isExpanded():!1}_isItemDisabled(n){return typeof n.isDisabled=="boolean"?n.isDisabled:n.isDisabled?.()}_expandAllItemsAtCurrentItemLevel(){if(!this._activeItem)return;let n=this._activeItem.getParent(),t;n?t=Wh(n.getChildren()):t=ke(this._items.filter(r=>r.getParent()===null)),t.pipe(pt(1)).subscribe(r=>{for(let o of r)o.expand()})}_activateCurrentItem(){this._activeItem?.activate()}},e5=new y("tree-key-manager",{providedIn:"root",factory:()=>(e,n)=>new qh(e,n)});var bD=" ";function IA(e,n,t){let r=Zc(e,n);t=t.trim(),!r.some(o=>o.trim()===t)&&(r.push(t),e.setAttribute(n,r.join(bD)))}function MA(e,n,t){let r=Zc(e,n);t=t.trim();let o=r.filter(i=>i!==t);o.length?e.setAttribute(n,o.join(bD)):e.removeAttribute(n)}function Zc(e,n){return e.getAttribute(n)?.match(/\S+/g)??[]}var _D="cdk-describedby-message",Yc="cdk-describedby-host",Zh=0,u5=(()=>{class e{_platform=f(ae);_document=f(F);_messageRegistry=new Map;_messagesContainer=null;_id=`${Zh++}`;constructor(){f(lt).load(Gc),this._id=f(xn)+"-"+Zh++}describe(t,r,o){if(!this._canBeDescribed(t,r))return;let i=Yh(r,o);typeof r!="string"?(vD(r,this._id),this._messageRegistry.set(i,{messageElement:r,referenceCount:0})):this._messageRegistry.has(i)||this._createMessageElement(r,o),this._isElementDescribedByMessage(t,i)||this._addMessageReference(t,i)}removeDescription(t,r,o){if(!r||!this._isElementNode(t))return;let i=Yh(r,o);if(this._isElementDescribedByMessage(t,i)&&this._removeMessageReference(t,i),typeof r=="string"){let s=this._messageRegistry.get(i);s&&s.referenceCount===0&&this._deleteMessageElement(i)}this._messagesContainer?.childNodes.length===0&&(this._messagesContainer.remove(),this._messagesContainer=null)}ngOnDestroy(){let t=this._document.querySelectorAll(`[${Yc}="${this._id}"]`);for(let r=0;ro.indexOf(_D)!=0);t.setAttribute("aria-describedby",r.join(" "))}_addMessageReference(t,r){let o=this._messageRegistry.get(r);IA(t,"aria-describedby",o.messageElement.id),t.setAttribute(Yc,this._id),o.referenceCount++}_removeMessageReference(t,r){let o=this._messageRegistry.get(r);o.referenceCount--,MA(t,"aria-describedby",o.messageElement.id),t.removeAttribute(Yc)}_isElementDescribedByMessage(t,r){let o=Zc(t,"aria-describedby"),i=this._messageRegistry.get(r),s=i&&i.messageElement.id;return!!s&&o.indexOf(s)!=-1}_canBeDescribed(t,r){if(!this._isElementNode(t))return!1;if(r&&typeof r=="object")return!0;let o=r==null?"":`${r}`.trim(),i=t.getAttribute("aria-label");return o?!i||i.trim()!==o:!1}_isElementNode(t){return t.nodeType===this._document.ELEMENT_NODE}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();function Yh(e,n){return typeof e=="string"?`${n||""}/${e}`:e}function vD(e,n){e.id||(e.id=`${_D}-${n}-${Zh++}`)}var Tt=(function(e){return e[e.NORMAL=0]="NORMAL",e[e.NEGATED=1]="NEGATED",e[e.INVERTED=2]="INVERTED",e})(Tt||{}),Kc,wr;function Xc(){if(wr==null){if(typeof document!="object"||!document||typeof Element!="function"||!Element)return wr=!1,wr;if(document.documentElement?.style&&"scrollBehavior"in document.documentElement.style)wr=!0;else{let e=Element.prototype.scrollTo;e?wr=!/\{\s*\[native code\]\s*\}/.test(e.toString()):wr=!1}}return wr}function Do(){if(typeof document!="object"||!document)return Tt.NORMAL;if(Kc==null){let e=document.createElement("div"),n=e.style;e.dir="rtl",n.width="1px",n.overflow="auto",n.visibility="hidden",n.pointerEvents="none",n.position="absolute";let t=document.createElement("div"),r=t.style;r.width="2px",r.height="1px",e.appendChild(t),document.body.appendChild(e),Kc=Tt.NORMAL,e.scrollLeft===0&&(e.scrollLeft=1,Kc=e.scrollLeft===0?Tt.NEGATED:Tt.INVERTED),e.remove()}return Kc}function Kh(){return typeof __karma__<"u"&&!!__karma__||typeof jasmine<"u"&&!!jasmine||typeof jest<"u"&&!!jest||typeof Mocha<"u"&&!!Mocha}var Eo,DD=["color","button","checkbox","date","datetime-local","email","file","hidden","image","month","number","password","radio","range","reset","search","submit","tel","text","time","url","week"];function _5(){if(Eo)return Eo;if(typeof document!="object"||!document)return Eo=new Set(DD),Eo;let e=document.createElement("input");return Eo=new Set(DD.filter(n=>(e.setAttribute("type",n),e.type===n))),Eo}var I5={XSmall:"(max-width: 599.98px)",Small:"(min-width: 600px) and (max-width: 959.98px)",Medium:"(min-width: 960px) and (max-width: 1279.98px)",Large:"(min-width: 1280px) and (max-width: 1919.98px)",XLarge:"(min-width: 1920px)",Handset:"(max-width: 599.98px) and (orientation: portrait), (max-width: 959.98px) and (orientation: landscape)",Tablet:"(min-width: 600px) and (max-width: 839.98px) and (orientation: portrait), (min-width: 960px) and (max-width: 1279.98px) and (orientation: landscape)",Web:"(min-width: 840px) and (orientation: portrait), (min-width: 1280px) and (orientation: landscape)",HandsetPortrait:"(max-width: 599.98px) and (orientation: portrait)",TabletPortrait:"(min-width: 600px) and (max-width: 839.98px) and (orientation: portrait)",WebPortrait:"(min-width: 840px) and (orientation: portrait)",HandsetLandscape:"(max-width: 959.98px) and (orientation: landscape)",TabletLandscape:"(min-width: 960px) and (max-width: 1279.98px) and (orientation: landscape)",WebLandscape:"(min-width: 1280px) and (orientation: landscape)"};var SA=new y("MATERIAL_ANIMATIONS"),ED=null;function TA(){return f(SA,{optional:!0})?.animationsDisabled||f(Ei,{optional:!0})==="NoopAnimations"?"di-disabled":(ED??=f(Wc).matchMedia("(prefers-reduced-motion)").matches,ED?"reduced-motion":"enabled")}function Fn(){return TA()!=="enabled"}function fe(e){return e==null?"":typeof e=="string"?e:`${e}px`}function R5(e){return e!=null&&`${e}`!="false"}var ut=(function(e){return e[e.FADING_IN=0]="FADING_IN",e[e.VISIBLE=1]="VISIBLE",e[e.FADING_OUT=2]="FADING_OUT",e[e.HIDDEN=3]="HIDDEN",e})(ut||{}),Xh=class{_renderer;element;config;_animationForciblyDisabledThroughCss;state=ut.HIDDEN;constructor(n,t,r,o=!1){this._renderer=n,this.element=t,this.config=r,this._animationForciblyDisabledThroughCss=o}fadeOut(){this._renderer.fadeOutRipple(this)}},wD=vo({passive:!0,capture:!0}),Qh=class{_events=new Map;addHandler(n,t,r,o){let i=this._events.get(t);if(i){let s=i.get(r);s?s.add(o):i.set(r,new Set([o]))}else this._events.set(t,new Map([[r,new Set([o])]])),n.runOutsideAngular(()=>{document.addEventListener(t,this._delegateEventHandler,wD)})}removeHandler(n,t,r){let o=this._events.get(n);if(!o)return;let i=o.get(t);i&&(i.delete(r),i.size===0&&o.delete(t),o.size===0&&(this._events.delete(n),document.removeEventListener(n,this._delegateEventHandler,wD)))}_delegateEventHandler=n=>{let t=Re(n);t&&this._events.get(n.type)?.forEach((r,o)=>{(o===t||o.contains(t))&&r.forEach(i=>i.handleEvent(n))})}},Ki={enterDuration:225,exitDuration:150},xA=800,CD=vo({passive:!0,capture:!0}),ID=["mousedown","touchstart"],MD=["mouseup","mouseleave","touchend","touchcancel"],AA=(()=>{class e{static \u0275fac=function(r){return new(r||e)};static \u0275cmp=Se({type:e,selectors:[["ng-component"]],hostAttrs:["mat-ripple-style-loader",""],decls:0,vars:0,template:function(r,o){},styles:[`.mat-ripple{overflow:hidden;position:relative}.mat-ripple:not(:empty){transform:translateZ(0)}.mat-ripple.mat-ripple-unbounded{overflow:visible}.mat-ripple-element{position:absolute;border-radius:50%;pointer-events:none;transition:opacity,transform 0ms cubic-bezier(0, 0, 0.2, 1);transform:scale3d(0, 0, 0);background-color:var(--mat-ripple-color, color-mix(in srgb, var(--mat-sys-on-surface) 10%, transparent))}@media(forced-colors: active){.mat-ripple-element{display:none}}.cdk-drag-preview .mat-ripple-element,.cdk-drag-placeholder .mat-ripple-element{display:none} -`],encapsulation:2,changeDetection:0})}return e})(),Xi=class e{_target;_ngZone;_platform;_containerElement;_triggerElement=null;_isPointerDown=!1;_activeRipples=new Map;_mostRecentTransientRipple=null;_lastTouchStartEvent;_pointerUpEventsRegistered=!1;_containerRect=null;static _eventManager=new Qh;constructor(n,t,r,o,i){this._target=n,this._ngZone=t,this._platform=o,o.isBrowser&&(this._containerElement=ct(r)),i&&i.get(lt).load(AA)}fadeInRipple(n,t,r={}){let o=this._containerRect=this._containerRect||this._containerElement.getBoundingClientRect(),i=E(E({},Ki),r.animation);r.centered&&(n=o.left+o.width/2,t=o.top+o.height/2);let s=r.radius||NA(n,t,o),a=n-o.left,c=t-o.top,l=i.enterDuration,u=document.createElement("div");u.classList.add("mat-ripple-element"),u.style.left=`${a-s}px`,u.style.top=`${c-s}px`,u.style.height=`${s*2}px`,u.style.width=`${s*2}px`,r.color!=null&&(u.style.backgroundColor=r.color),u.style.transitionDuration=`${l}ms`,this._containerElement.appendChild(u);let d=window.getComputedStyle(u),p=d.transitionProperty,h=d.transitionDuration,m=p==="none"||h==="0s"||h==="0s, 0s"||o.width===0&&o.height===0,b=new Xh(this,u,r,m);u.style.transform="scale3d(1, 1, 1)",b.state=ut.FADING_IN,r.persistent||(this._mostRecentTransientRipple=b);let _=null;return!m&&(l||i.exitDuration)&&this._ngZone.runOutsideAngular(()=>{let C=()=>{_&&(_.fallbackTimer=null),clearTimeout(Ge),this._finishRippleTransition(b)},ne=()=>this._destroyRipple(b),Ge=setTimeout(ne,l+100);u.addEventListener("transitionend",C),u.addEventListener("transitioncancel",ne),_={onTransitionEnd:C,onTransitionCancel:ne,fallbackTimer:Ge}}),this._activeRipples.set(b,_),(m||!l)&&this._finishRippleTransition(b),b}fadeOutRipple(n){if(n.state===ut.FADING_OUT||n.state===ut.HIDDEN)return;let t=n.element,r=E(E({},Ki),n.config.animation);t.style.transitionDuration=`${r.exitDuration}ms`,t.style.opacity="0",n.state=ut.FADING_OUT,(n._animationForciblyDisabledThroughCss||!r.exitDuration)&&this._finishRippleTransition(n)}fadeOutAll(){this._getActiveRipples().forEach(n=>n.fadeOut())}fadeOutAllNonPersistent(){this._getActiveRipples().forEach(n=>{n.config.persistent||n.fadeOut()})}setupTriggerEvents(n){let t=ct(n);!this._platform.isBrowser||!t||t===this._triggerElement||(this._removeTriggerEvents(),this._triggerElement=t,ID.forEach(r=>{e._eventManager.addHandler(this._ngZone,r,t,this)}))}handleEvent(n){n.type==="mousedown"?this._onMousedown(n):n.type==="touchstart"?this._onTouchStart(n):this._onPointerUp(),this._pointerUpEventsRegistered||(this._ngZone.runOutsideAngular(()=>{MD.forEach(t=>{this._triggerElement.addEventListener(t,this,CD)})}),this._pointerUpEventsRegistered=!0)}_finishRippleTransition(n){n.state===ut.FADING_IN?this._startFadeOutTransition(n):n.state===ut.FADING_OUT&&this._destroyRipple(n)}_startFadeOutTransition(n){let t=n===this._mostRecentTransientRipple,{persistent:r}=n.config;n.state=ut.VISIBLE,!r&&(!t||!this._isPointerDown)&&n.fadeOut()}_destroyRipple(n){let t=this._activeRipples.get(n)??null;this._activeRipples.delete(n),this._activeRipples.size||(this._containerRect=null),n===this._mostRecentTransientRipple&&(this._mostRecentTransientRipple=null),n.state=ut.HIDDEN,t!==null&&(n.element.removeEventListener("transitionend",t.onTransitionEnd),n.element.removeEventListener("transitioncancel",t.onTransitionCancel),t.fallbackTimer!==null&&clearTimeout(t.fallbackTimer)),n.element.remove()}_onMousedown(n){let t=Gi(n),r=this._lastTouchStartEvent&&Date.now(){let t=n.state===ut.VISIBLE||n.config.terminateOnPointerUp&&n.state===ut.FADING_IN;!n.config.persistent&&t&&n.fadeOut()}))}_getActiveRipples(){return Array.from(this._activeRipples.keys())}_removeTriggerEvents(){let n=this._triggerElement;n&&(ID.forEach(t=>e._eventManager.removeHandler(t,n,this)),this._pointerUpEventsRegistered&&(MD.forEach(t=>n.removeEventListener(t,this,CD)),this._pointerUpEventsRegistered=!1))}};function NA(e,n,t){let r=Math.max(Math.abs(e-t.left),Math.abs(e-t.right)),o=Math.max(Math.abs(n-t.top),Math.abs(n-t.bottom));return Math.sqrt(r*r+o*o)}var Jh=new y("mat-ripple-global-options"),q5=(()=>{class e{_elementRef=f(z);_animationsDisabled=Fn();color;unbounded=!1;centered=!1;radius=0;animation;get disabled(){return this._disabled}set disabled(t){t&&this.fadeOutAllNonPersistent(),this._disabled=t,this._setupTriggerEventsIfEnabled()}_disabled=!1;get trigger(){return this._trigger||this._elementRef.nativeElement}set trigger(t){this._trigger=t,this._setupTriggerEventsIfEnabled()}_trigger;_rippleRenderer;_globalOptions;_isInitialized=!1;constructor(){let t=f(P),r=f(ae),o=f(Jh,{optional:!0}),i=f(j);this._globalOptions=o||{},this._rippleRenderer=new Xi(this,t,this._elementRef,r,i)}ngOnInit(){this._isInitialized=!0,this._setupTriggerEventsIfEnabled()}ngOnDestroy(){this._rippleRenderer._removeTriggerEvents()}fadeOutAll(){this._rippleRenderer.fadeOutAll()}fadeOutAllNonPersistent(){this._rippleRenderer.fadeOutAllNonPersistent()}get rippleConfig(){return{centered:this.centered,radius:this.radius,color:this.color,animation:E(E(E({},this._globalOptions.animation),this._animationsDisabled?{enterDuration:0,exitDuration:0}:{}),this.animation),terminateOnPointerUp:this._globalOptions.terminateOnPointerUp}}get rippleDisabled(){return this.disabled||!!this._globalOptions.disabled}_setupTriggerEventsIfEnabled(){!this.disabled&&this._isInitialized&&this._rippleRenderer.setupTriggerEvents(this.trigger)}launch(t,r=0,o){return typeof t=="number"?this._rippleRenderer.fadeInRipple(t,r,E(E({},this.rippleConfig),o)):this._rippleRenderer.fadeInRipple(0,0,E(E({},this.rippleConfig),t))}static \u0275fac=function(r){return new(r||e)};static \u0275dir=O({type:e,selectors:[["","mat-ripple",""],["","matRipple",""]],hostAttrs:[1,"mat-ripple"],hostVars:2,hostBindings:function(r,o){r&2&&Ue("mat-ripple-unbounded",o.unbounded)},inputs:{color:[0,"matRippleColor","color"],unbounded:[0,"matRippleUnbounded","unbounded"],centered:[0,"matRippleCentered","centered"],radius:[0,"matRippleRadius","radius"],animation:[0,"matRippleAnimation","animation"],disabled:[0,"matRippleDisabled","disabled"],trigger:[0,"matRippleTrigger","trigger"]},exportAs:["matRipple"]})}return e})();var RA={capture:!0},OA=["focus","mousedown","mouseenter","touchstart"],ep="mat-ripple-loader-uninitialized",tp="mat-ripple-loader-class-name",SD="mat-ripple-loader-centered",Qc="mat-ripple-loader-disabled",TD=(()=>{class e{_document=f(F);_animationsDisabled=Fn();_globalRippleOptions=f(Jh,{optional:!0});_platform=f(ae);_ngZone=f(P);_injector=f(j);_eventCleanups;_hosts=new Map;constructor(){let t=f(be).createRenderer(null,null);this._eventCleanups=this._ngZone.runOutsideAngular(()=>OA.map(r=>t.listen(this._document,r,this._onInteraction,RA)))}ngOnDestroy(){let t=this._hosts.keys();for(let r of t)this.destroyRipple(r);this._eventCleanups.forEach(r=>r())}configureRipple(t,r){t.setAttribute(ep,this._globalRippleOptions?.namespace??""),(r.className||!t.hasAttribute(tp))&&t.setAttribute(tp,r.className||""),r.centered&&t.setAttribute(SD,""),r.disabled&&t.setAttribute(Qc,"")}setDisabled(t,r){let o=this._hosts.get(t);o?(o.target.rippleDisabled=r,!r&&!o.hasSetUpEvents&&(o.hasSetUpEvents=!0,o.renderer.setupTriggerEvents(t))):r?t.setAttribute(Qc,""):t.removeAttribute(Qc)}_onInteraction=t=>{let r=Re(t);if(r instanceof HTMLElement){let o=r.closest(`[${ep}="${this._globalRippleOptions?.namespace??""}"]`);o&&this._createRipple(o)}};_createRipple(t){if(!this._document||this._hosts.has(t))return;t.querySelector(".mat-ripple")?.remove();let r=this._document.createElement("span");r.classList.add("mat-ripple",t.getAttribute(tp)),t.append(r);let o=this._globalRippleOptions,i=this._animationsDisabled?0:o?.animation?.enterDuration??Ki.enterDuration,s=this._animationsDisabled?0:o?.animation?.exitDuration??Ki.exitDuration,a={rippleDisabled:this._animationsDisabled||o?.disabled||t.hasAttribute(Qc),rippleConfig:{centered:t.hasAttribute(SD),terminateOnPointerUp:o?.terminateOnPointerUp,animation:{enterDuration:i,exitDuration:s}}},c=new Xi(a,this._ngZone,r,this._platform,this._injector),l=!a.rippleDisabled;l&&c.setupTriggerEvents(t),this._hosts.set(t,{target:a,renderer:c,hasSetUpEvents:l}),t.removeAttribute(ep)}destroyRipple(t){let r=this._hosts.get(t);r&&(r.renderer._removeTriggerEvents(),this._hosts.delete(t))}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();var xD=(()=>{class e{static \u0275fac=function(r){return new(r||e)};static \u0275cmp=Se({type:e,selectors:[["structural-styles"]],decls:0,vars:0,template:function(r,o){},styles:[`.mat-focus-indicator{position:relative}.mat-focus-indicator::before{top:0;left:0;right:0;bottom:0;position:absolute;box-sizing:border-box;pointer-events:none;display:var(--mat-focus-indicator-display, none);border-width:var(--mat-focus-indicator-border-width, 3px);border-style:var(--mat-focus-indicator-border-style, solid);border-color:var(--mat-focus-indicator-border-color, transparent);border-radius:var(--mat-focus-indicator-border-radius, 4px)}.mat-focus-indicator:focus-visible::before{content:""}@media(forced-colors: active){html{--mat-focus-indicator-display: block}} -`],encapsulation:2,changeDetection:0})}return e})();var kA=["mat-icon-button",""],FA=["*"],PA=new y("MAT_BUTTON_CONFIG");function AD(e){return e==null?void 0:ah(e)}var np=(()=>{class e{_elementRef=f(z);_ngZone=f(P);_animationsDisabled=Fn();_config=f(PA,{optional:!0});_focusMonitor=f(Uc);_cleanupClick;_renderer=f(Be);_rippleLoader=f(TD);_isAnchor;_isFab=!1;color;get disableRipple(){return this._disableRipple}set disableRipple(t){this._disableRipple=t,this._updateRippleDisabled()}_disableRipple=!1;get disabled(){return this._disabled}set disabled(t){this._disabled=t,this._updateRippleDisabled()}_disabled=!1;ariaDisabled;disabledInteractive;tabIndex;set _tabindex(t){this.tabIndex=t}constructor(){f(lt).load(xD);let t=this._elementRef.nativeElement;this._isAnchor=t.tagName==="A",this.disabledInteractive=this._config?.disabledInteractive??!1,this.color=this._config?.color??null,this._rippleLoader?.configureRipple(t,{className:"mat-mdc-button-ripple"})}ngAfterViewInit(){this._focusMonitor.monitor(this._elementRef,!0),this._isAnchor&&this._setupAsAnchor()}ngOnDestroy(){this._cleanupClick?.(),this._focusMonitor.stopMonitoring(this._elementRef),this._rippleLoader?.destroyRipple(this._elementRef.nativeElement)}focus(t="program",r){t?this._focusMonitor.focusVia(this._elementRef.nativeElement,t,r):this._elementRef.nativeElement.focus(r)}_getAriaDisabled(){return this.ariaDisabled!=null?this.ariaDisabled:this._isAnchor?this.disabled||null:this.disabled&&this.disabledInteractive?!0:null}_getDisabledAttribute(){return this.disabledInteractive||!this.disabled?null:!0}_updateRippleDisabled(){this._rippleLoader?.setDisabled(this._elementRef.nativeElement,this.disableRipple||this.disabled)}_getTabIndex(){return this._isAnchor?this.disabled&&!this.disabledInteractive?-1:this.tabIndex:this.tabIndex}_setupAsAnchor(){this._cleanupClick=this._ngZone.runOutsideAngular(()=>this._renderer.listen(this._elementRef.nativeElement,"click",t=>{this.disabled&&(t.preventDefault(),t.stopImmediatePropagation())}))}static \u0275fac=function(r){return new(r||e)};static \u0275dir=O({type:e,hostAttrs:[1,"mat-mdc-button-base"],hostVars:13,hostBindings:function(r,o){r&2&&(rn("disabled",o._getDisabledAttribute())("aria-disabled",o._getAriaDisabled())("tabindex",o._getTabIndex()),zf(o.color?"mat-"+o.color:""),Ue("mat-mdc-button-disabled",o.disabled)("mat-mdc-button-disabled-interactive",o.disabledInteractive)("mat-unthemed",!o.color)("_mat-animation-noopable",o._animationsDisabled))},inputs:{color:"color",disableRipple:[2,"disableRipple","disableRipple",de],disabled:[2,"disabled","disabled",de],ariaDisabled:[2,"aria-disabled","ariaDisabled",de],disabledInteractive:[2,"disabledInteractive","disabledInteractive",de],tabIndex:[2,"tabIndex","tabIndex",AD],_tabindex:[2,"tabindex","_tabindex",AD]}})}return e})(),LA=(()=>{class e extends np{constructor(){super(),this._rippleLoader.configureRipple(this._elementRef.nativeElement,{centered:!0})}static \u0275fac=function(r){return new(r||e)};static \u0275cmp=Se({type:e,selectors:[["button","mat-icon-button",""],["a","mat-icon-button",""],["button","matIconButton",""],["a","matIconButton",""]],hostAttrs:[1,"mdc-icon-button","mat-mdc-icon-button"],exportAs:["matButton","matAnchor"],features:[X],attrs:kA,ngContentSelectors:FA,decls:4,vars:0,consts:[[1,"mat-mdc-button-persistent-ripple","mdc-icon-button__ripple"],[1,"mat-focus-indicator"],[1,"mat-mdc-button-touch-target"]],template:function(r,o){r&1&&(fo(),on(0,"span",0),Nn(1),on(2,"span",1)(3,"span",2))},styles:[`.mat-mdc-icon-button{-webkit-user-select:none;user-select:none;display:inline-block;position:relative;box-sizing:border-box;border:none;outline:none;background-color:rgba(0,0,0,0);fill:currentColor;text-decoration:none;cursor:pointer;z-index:0;overflow:visible;border-radius:var(--mat-icon-button-container-shape, var(--mat-sys-corner-full, 50%));flex-shrink:0;text-align:center;width:var(--mat-icon-button-state-layer-size, 40px);height:var(--mat-icon-button-state-layer-size, 40px);padding:calc(calc(var(--mat-icon-button-state-layer-size, 40px) - var(--mat-icon-button-icon-size, 24px)) / 2);font-size:var(--mat-icon-button-icon-size, 24px);color:var(--mat-icon-button-icon-color, var(--mat-sys-on-surface-variant));-webkit-tap-highlight-color:rgba(0,0,0,0)}.mat-mdc-icon-button .mat-mdc-button-ripple,.mat-mdc-icon-button .mat-mdc-button-persistent-ripple,.mat-mdc-icon-button .mat-mdc-button-persistent-ripple::before{top:0;left:0;right:0;bottom:0;position:absolute;pointer-events:none;border-radius:inherit}.mat-mdc-icon-button .mat-mdc-button-ripple{overflow:hidden}.mat-mdc-icon-button .mat-mdc-button-persistent-ripple::before{content:"";opacity:0}.mat-mdc-icon-button .mdc-button__label,.mat-mdc-icon-button .mat-icon{z-index:1;position:relative}.mat-mdc-icon-button .mat-focus-indicator{top:0;left:0;right:0;bottom:0;position:absolute;border-radius:inherit}.mat-mdc-icon-button:focus-visible>.mat-focus-indicator::before{content:"";border-radius:inherit}.mat-mdc-icon-button .mat-ripple-element{background-color:var(--mat-icon-button-ripple-color, color-mix(in srgb, var(--mat-sys-on-surface-variant) calc(var(--mat-sys-pressed-state-layer-opacity) * 100%), transparent))}.mat-mdc-icon-button .mat-mdc-button-persistent-ripple::before{background-color:var(--mat-icon-button-state-layer-color, var(--mat-sys-on-surface-variant))}.mat-mdc-icon-button.mat-mdc-button-disabled .mat-mdc-button-persistent-ripple::before{background-color:var(--mat-icon-button-disabled-state-layer-color, var(--mat-sys-on-surface-variant))}.mat-mdc-icon-button:hover>.mat-mdc-button-persistent-ripple::before{opacity:var(--mat-icon-button-hover-state-layer-opacity, var(--mat-sys-hover-state-layer-opacity))}.mat-mdc-icon-button.cdk-program-focused>.mat-mdc-button-persistent-ripple::before,.mat-mdc-icon-button.cdk-keyboard-focused>.mat-mdc-button-persistent-ripple::before,.mat-mdc-icon-button.mat-mdc-button-disabled-interactive:focus>.mat-mdc-button-persistent-ripple::before{opacity:var(--mat-icon-button-focus-state-layer-opacity, var(--mat-sys-focus-state-layer-opacity))}.mat-mdc-icon-button:active>.mat-mdc-button-persistent-ripple::before{opacity:var(--mat-icon-button-pressed-state-layer-opacity, var(--mat-sys-pressed-state-layer-opacity))}.mat-mdc-icon-button .mat-mdc-button-touch-target{position:absolute;top:50%;height:var(--mat-icon-button-touch-target-size, 48px);display:var(--mat-icon-button-touch-target-display, block);left:50%;width:var(--mat-icon-button-touch-target-size, 48px);transform:translate(-50%, -50%)}.mat-mdc-icon-button._mat-animation-noopable{transition:none !important;animation:none !important}.mat-mdc-icon-button[disabled],.mat-mdc-icon-button.mat-mdc-button-disabled{cursor:default;pointer-events:none;color:var(--mat-icon-button-disabled-icon-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent))}.mat-mdc-icon-button.mat-mdc-button-disabled-interactive{pointer-events:auto}.mat-mdc-icon-button img,.mat-mdc-icon-button svg{width:var(--mat-icon-button-icon-size, 24px);height:var(--mat-icon-button-icon-size, 24px);vertical-align:baseline}.mat-mdc-icon-button .mat-mdc-button-persistent-ripple{border-radius:var(--mat-icon-button-container-shape, var(--mat-sys-corner-full, 50%))}.mat-mdc-icon-button[hidden]{display:none}.mat-mdc-icon-button.mat-unthemed:not(.mdc-ripple-upgraded):focus::before,.mat-mdc-icon-button.mat-primary:not(.mdc-ripple-upgraded):focus::before,.mat-mdc-icon-button.mat-accent:not(.mdc-ripple-upgraded):focus::before,.mat-mdc-icon-button.mat-warn:not(.mdc-ripple-upgraded):focus::before{background:rgba(0,0,0,0);opacity:1} -`,`@media(forced-colors: active){.mat-mdc-button:not(.mdc-button--outlined),.mat-mdc-unelevated-button:not(.mdc-button--outlined),.mat-mdc-raised-button:not(.mdc-button--outlined),.mat-mdc-outlined-button:not(.mdc-button--outlined),.mat-mdc-button-base.mat-tonal-button,.mat-mdc-icon-button.mat-mdc-icon-button,.mat-mdc-outlined-button .mdc-button__ripple{outline:solid 1px}} -`],encapsulation:2,changeDetection:0})}return e})();var VA=new y("cdk-dir-doc",{providedIn:"root",factory:()=>f(F)}),jA=/^(ar|ckb|dv|he|iw|fa|nqo|ps|sd|ug|ur|yi|.*[-_](Adlm|Arab|Hebr|Nkoo|Rohg|Thaa))(?!.*[-_](Latn|Cyrl)($|-|_))($|-|_)/i;function ND(e){let n=e?.toLowerCase()||"";return n==="auto"&&typeof navigator<"u"&&navigator?.language?jA.test(navigator.language)?"rtl":"ltr":n==="rtl"?"rtl":"ltr"}var wo=(()=>{class e{get value(){return this.valueSignal()}valueSignal=Me("ltr");change=new H;constructor(){let t=f(VA,{optional:!0});if(t){let r=t.body?t.body.dir:null,o=t.documentElement?t.documentElement.dir:null;this.valueSignal.set(ND(r||o||"ltr"))}}ngOnDestroy(){this.change.complete()}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();var ln=(()=>{class e{static \u0275fac=function(r){return new(r||e)};static \u0275mod=Z({type:e});static \u0275inj=G({})}return e})();var RD=(()=>{class e{static \u0275fac=function(r){return new(r||e)};static \u0275mod=Z({type:e});static \u0275inj=G({imports:[ln]})}return e})();var BA=["matButton",""],HA=[[["",8,"material-icons",3,"iconPositionEnd",""],["mat-icon",3,"iconPositionEnd",""],["","matButtonIcon","",3,"iconPositionEnd",""]],"*",[["","iconPositionEnd","",8,"material-icons"],["mat-icon","iconPositionEnd",""],["","matButtonIcon","","iconPositionEnd",""]]],UA=[".material-icons:not([iconPositionEnd]), mat-icon:not([iconPositionEnd]), [matButtonIcon]:not([iconPositionEnd])","*",".material-icons[iconPositionEnd], mat-icon[iconPositionEnd], [matButtonIcon][iconPositionEnd]"];var OD=new Map([["text",["mat-mdc-button"]],["filled",["mdc-button--unelevated","mat-mdc-unelevated-button"]],["elevated",["mdc-button--raised","mat-mdc-raised-button"]],["outlined",["mdc-button--outlined","mat-mdc-outlined-button"]],["tonal",["mat-tonal-button"]]]),Iq=(()=>{class e extends np{get appearance(){return this._appearance}set appearance(t){this.setAppearance(t||this._config?.defaultAppearance||"text")}_appearance=null;constructor(){super();let t=$A(this._elementRef.nativeElement);t&&this.setAppearance(t)}setAppearance(t){if(t===this._appearance)return;let r=this._elementRef.nativeElement.classList,o=this._appearance?OD.get(this._appearance):null,i=OD.get(t);o&&r.remove(...o),r.add(...i),this._appearance=t}static \u0275fac=function(r){return new(r||e)};static \u0275cmp=Se({type:e,selectors:[["button","matButton",""],["a","matButton",""],["button","mat-button",""],["button","mat-raised-button",""],["button","mat-flat-button",""],["button","mat-stroked-button",""],["a","mat-button",""],["a","mat-raised-button",""],["a","mat-flat-button",""],["a","mat-stroked-button",""]],hostAttrs:[1,"mdc-button"],inputs:{appearance:[0,"matButton","appearance"]},exportAs:["matButton","matAnchor"],features:[X],attrs:BA,ngContentSelectors:UA,decls:7,vars:4,consts:[[1,"mat-mdc-button-persistent-ripple"],[1,"mdc-button__label"],[1,"mat-focus-indicator"],[1,"mat-mdc-button-touch-target"]],template:function(r,o){r&1&&(fo(HA),on(0,"span",0),Nn(1),lo(2,"span",1),Nn(3,1),uo(),Nn(4,2),on(5,"span",2)(6,"span",3)),r&2&&Ue("mdc-button__ripple",!o._isFab)("mdc-fab__ripple",o._isFab)},styles:[`.mat-mdc-button-base{text-decoration:none}.mat-mdc-button-base .mat-icon{min-height:fit-content;flex-shrink:0}@media(hover: none){.mat-mdc-button-base:hover>span.mat-mdc-button-persistent-ripple::before{opacity:0}}.mdc-button{-webkit-user-select:none;user-select:none;position:relative;display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;min-width:64px;border:none;outline:none;line-height:inherit;-webkit-appearance:none;overflow:visible;vertical-align:middle;background:rgba(0,0,0,0);padding:0 8px}.mdc-button::-moz-focus-inner{padding:0;border:0}.mdc-button:active{outline:none}.mdc-button:hover{cursor:pointer}.mdc-button:disabled{cursor:default;pointer-events:none}.mdc-button[hidden]{display:none}.mdc-button .mdc-button__label{position:relative}.mat-mdc-button{padding:0 var(--mat-button-text-horizontal-padding, 12px);height:var(--mat-button-text-container-height, 40px);font-family:var(--mat-button-text-label-text-font, var(--mat-sys-label-large-font));font-size:var(--mat-button-text-label-text-size, var(--mat-sys-label-large-size));letter-spacing:var(--mat-button-text-label-text-tracking, var(--mat-sys-label-large-tracking));text-transform:var(--mat-button-text-label-text-transform);font-weight:var(--mat-button-text-label-text-weight, var(--mat-sys-label-large-weight))}.mat-mdc-button,.mat-mdc-button .mdc-button__ripple{border-radius:var(--mat-button-text-container-shape, var(--mat-sys-corner-full))}.mat-mdc-button:not(:disabled){color:var(--mat-button-text-label-text-color, var(--mat-sys-primary))}.mat-mdc-button[disabled],.mat-mdc-button.mat-mdc-button-disabled{cursor:default;pointer-events:none;color:var(--mat-button-text-disabled-label-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent))}.mat-mdc-button.mat-mdc-button-disabled-interactive{pointer-events:auto}.mat-mdc-button:has(.material-icons,mat-icon,[matButtonIcon]){padding:0 var(--mat-button-text-with-icon-horizontal-padding, 16px)}.mat-mdc-button>.mat-icon{margin-right:var(--mat-button-text-icon-spacing, 8px);margin-left:var(--mat-button-text-icon-offset, -4px)}[dir=rtl] .mat-mdc-button>.mat-icon{margin-right:var(--mat-button-text-icon-offset, -4px);margin-left:var(--mat-button-text-icon-spacing, 8px)}.mat-mdc-button .mdc-button__label+.mat-icon{margin-right:var(--mat-button-text-icon-offset, -4px);margin-left:var(--mat-button-text-icon-spacing, 8px)}[dir=rtl] .mat-mdc-button .mdc-button__label+.mat-icon{margin-right:var(--mat-button-text-icon-spacing, 8px);margin-left:var(--mat-button-text-icon-offset, -4px)}.mat-mdc-button .mat-ripple-element{background-color:var(--mat-button-text-ripple-color, color-mix(in srgb, var(--mat-sys-primary) calc(var(--mat-sys-pressed-state-layer-opacity) * 100%), transparent))}.mat-mdc-button .mat-mdc-button-persistent-ripple::before{background-color:var(--mat-button-text-state-layer-color, var(--mat-sys-primary))}.mat-mdc-button.mat-mdc-button-disabled .mat-mdc-button-persistent-ripple::before{background-color:var(--mat-button-text-disabled-state-layer-color, var(--mat-sys-on-surface-variant))}.mat-mdc-button:hover>.mat-mdc-button-persistent-ripple::before{opacity:var(--mat-button-text-hover-state-layer-opacity, var(--mat-sys-hover-state-layer-opacity))}.mat-mdc-button.cdk-program-focused>.mat-mdc-button-persistent-ripple::before,.mat-mdc-button.cdk-keyboard-focused>.mat-mdc-button-persistent-ripple::before,.mat-mdc-button.mat-mdc-button-disabled-interactive:focus>.mat-mdc-button-persistent-ripple::before{opacity:var(--mat-button-text-focus-state-layer-opacity, var(--mat-sys-focus-state-layer-opacity))}.mat-mdc-button:active>.mat-mdc-button-persistent-ripple::before{opacity:var(--mat-button-text-pressed-state-layer-opacity, var(--mat-sys-pressed-state-layer-opacity))}.mat-mdc-button .mat-mdc-button-touch-target{position:absolute;top:50%;height:var(--mat-button-text-touch-target-size, 48px);display:var(--mat-button-text-touch-target-display, block);left:0;right:0;transform:translateY(-50%)}.mat-mdc-unelevated-button{transition:box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);height:var(--mat-button-filled-container-height, 40px);font-family:var(--mat-button-filled-label-text-font, var(--mat-sys-label-large-font));font-size:var(--mat-button-filled-label-text-size, var(--mat-sys-label-large-size));letter-spacing:var(--mat-button-filled-label-text-tracking, var(--mat-sys-label-large-tracking));text-transform:var(--mat-button-filled-label-text-transform);font-weight:var(--mat-button-filled-label-text-weight, var(--mat-sys-label-large-weight));padding:0 var(--mat-button-filled-horizontal-padding, 24px)}.mat-mdc-unelevated-button>.mat-icon{margin-right:var(--mat-button-filled-icon-spacing, 8px);margin-left:var(--mat-button-filled-icon-offset, -8px)}[dir=rtl] .mat-mdc-unelevated-button>.mat-icon{margin-right:var(--mat-button-filled-icon-offset, -8px);margin-left:var(--mat-button-filled-icon-spacing, 8px)}.mat-mdc-unelevated-button .mdc-button__label+.mat-icon{margin-right:var(--mat-button-filled-icon-offset, -8px);margin-left:var(--mat-button-filled-icon-spacing, 8px)}[dir=rtl] .mat-mdc-unelevated-button .mdc-button__label+.mat-icon{margin-right:var(--mat-button-filled-icon-spacing, 8px);margin-left:var(--mat-button-filled-icon-offset, -8px)}.mat-mdc-unelevated-button .mat-ripple-element{background-color:var(--mat-button-filled-ripple-color, color-mix(in srgb, var(--mat-sys-on-primary) calc(var(--mat-sys-pressed-state-layer-opacity) * 100%), transparent))}.mat-mdc-unelevated-button .mat-mdc-button-persistent-ripple::before{background-color:var(--mat-button-filled-state-layer-color, var(--mat-sys-on-primary))}.mat-mdc-unelevated-button.mat-mdc-button-disabled .mat-mdc-button-persistent-ripple::before{background-color:var(--mat-button-filled-disabled-state-layer-color, var(--mat-sys-on-surface-variant))}.mat-mdc-unelevated-button:hover>.mat-mdc-button-persistent-ripple::before{opacity:var(--mat-button-filled-hover-state-layer-opacity, var(--mat-sys-hover-state-layer-opacity))}.mat-mdc-unelevated-button.cdk-program-focused>.mat-mdc-button-persistent-ripple::before,.mat-mdc-unelevated-button.cdk-keyboard-focused>.mat-mdc-button-persistent-ripple::before,.mat-mdc-unelevated-button.mat-mdc-button-disabled-interactive:focus>.mat-mdc-button-persistent-ripple::before{opacity:var(--mat-button-filled-focus-state-layer-opacity, var(--mat-sys-focus-state-layer-opacity))}.mat-mdc-unelevated-button:active>.mat-mdc-button-persistent-ripple::before{opacity:var(--mat-button-filled-pressed-state-layer-opacity, var(--mat-sys-pressed-state-layer-opacity))}.mat-mdc-unelevated-button .mat-mdc-button-touch-target{position:absolute;top:50%;height:var(--mat-button-filled-touch-target-size, 48px);display:var(--mat-button-filled-touch-target-display, block);left:0;right:0;transform:translateY(-50%)}.mat-mdc-unelevated-button:not(:disabled){color:var(--mat-button-filled-label-text-color, var(--mat-sys-on-primary));background-color:var(--mat-button-filled-container-color, var(--mat-sys-primary))}.mat-mdc-unelevated-button,.mat-mdc-unelevated-button .mdc-button__ripple{border-radius:var(--mat-button-filled-container-shape, var(--mat-sys-corner-full))}.mat-mdc-unelevated-button[disabled],.mat-mdc-unelevated-button.mat-mdc-button-disabled{cursor:default;pointer-events:none;color:var(--mat-button-filled-disabled-label-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent));background-color:var(--mat-button-filled-disabled-container-color, color-mix(in srgb, var(--mat-sys-on-surface) 12%, transparent))}.mat-mdc-unelevated-button.mat-mdc-button-disabled-interactive{pointer-events:auto}.mat-mdc-raised-button{transition:box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);box-shadow:var(--mat-button-protected-container-elevation-shadow, var(--mat-sys-level1));height:var(--mat-button-protected-container-height, 40px);font-family:var(--mat-button-protected-label-text-font, var(--mat-sys-label-large-font));font-size:var(--mat-button-protected-label-text-size, var(--mat-sys-label-large-size));letter-spacing:var(--mat-button-protected-label-text-tracking, var(--mat-sys-label-large-tracking));text-transform:var(--mat-button-protected-label-text-transform);font-weight:var(--mat-button-protected-label-text-weight, var(--mat-sys-label-large-weight));padding:0 var(--mat-button-protected-horizontal-padding, 24px)}.mat-mdc-raised-button>.mat-icon{margin-right:var(--mat-button-protected-icon-spacing, 8px);margin-left:var(--mat-button-protected-icon-offset, -8px)}[dir=rtl] .mat-mdc-raised-button>.mat-icon{margin-right:var(--mat-button-protected-icon-offset, -8px);margin-left:var(--mat-button-protected-icon-spacing, 8px)}.mat-mdc-raised-button .mdc-button__label+.mat-icon{margin-right:var(--mat-button-protected-icon-offset, -8px);margin-left:var(--mat-button-protected-icon-spacing, 8px)}[dir=rtl] .mat-mdc-raised-button .mdc-button__label+.mat-icon{margin-right:var(--mat-button-protected-icon-spacing, 8px);margin-left:var(--mat-button-protected-icon-offset, -8px)}.mat-mdc-raised-button .mat-ripple-element{background-color:var(--mat-button-protected-ripple-color, color-mix(in srgb, var(--mat-sys-primary) calc(var(--mat-sys-pressed-state-layer-opacity) * 100%), transparent))}.mat-mdc-raised-button .mat-mdc-button-persistent-ripple::before{background-color:var(--mat-button-protected-state-layer-color, var(--mat-sys-primary))}.mat-mdc-raised-button.mat-mdc-button-disabled .mat-mdc-button-persistent-ripple::before{background-color:var(--mat-button-protected-disabled-state-layer-color, var(--mat-sys-on-surface-variant))}.mat-mdc-raised-button:hover>.mat-mdc-button-persistent-ripple::before{opacity:var(--mat-button-protected-hover-state-layer-opacity, var(--mat-sys-hover-state-layer-opacity))}.mat-mdc-raised-button.cdk-program-focused>.mat-mdc-button-persistent-ripple::before,.mat-mdc-raised-button.cdk-keyboard-focused>.mat-mdc-button-persistent-ripple::before,.mat-mdc-raised-button.mat-mdc-button-disabled-interactive:focus>.mat-mdc-button-persistent-ripple::before{opacity:var(--mat-button-protected-focus-state-layer-opacity, var(--mat-sys-focus-state-layer-opacity))}.mat-mdc-raised-button:active>.mat-mdc-button-persistent-ripple::before{opacity:var(--mat-button-protected-pressed-state-layer-opacity, var(--mat-sys-pressed-state-layer-opacity))}.mat-mdc-raised-button .mat-mdc-button-touch-target{position:absolute;top:50%;height:var(--mat-button-protected-touch-target-size, 48px);display:var(--mat-button-protected-touch-target-display, block);left:0;right:0;transform:translateY(-50%)}.mat-mdc-raised-button:not(:disabled){color:var(--mat-button-protected-label-text-color, var(--mat-sys-primary));background-color:var(--mat-button-protected-container-color, var(--mat-sys-surface))}.mat-mdc-raised-button,.mat-mdc-raised-button .mdc-button__ripple{border-radius:var(--mat-button-protected-container-shape, var(--mat-sys-corner-full))}@media(hover: hover){.mat-mdc-raised-button:hover{box-shadow:var(--mat-button-protected-hover-container-elevation-shadow, var(--mat-sys-level2))}}.mat-mdc-raised-button:focus{box-shadow:var(--mat-button-protected-focus-container-elevation-shadow, var(--mat-sys-level1))}.mat-mdc-raised-button:active,.mat-mdc-raised-button:focus:active{box-shadow:var(--mat-button-protected-pressed-container-elevation-shadow, var(--mat-sys-level1))}.mat-mdc-raised-button[disabled],.mat-mdc-raised-button.mat-mdc-button-disabled{cursor:default;pointer-events:none;color:var(--mat-button-protected-disabled-label-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent));background-color:var(--mat-button-protected-disabled-container-color, color-mix(in srgb, var(--mat-sys-on-surface) 12%, transparent))}.mat-mdc-raised-button[disabled].mat-mdc-button-disabled,.mat-mdc-raised-button.mat-mdc-button-disabled.mat-mdc-button-disabled{box-shadow:var(--mat-button-protected-disabled-container-elevation-shadow, var(--mat-sys-level0))}.mat-mdc-raised-button.mat-mdc-button-disabled-interactive{pointer-events:auto}.mat-mdc-outlined-button{border-style:solid;transition:border 280ms cubic-bezier(0.4, 0, 0.2, 1);height:var(--mat-button-outlined-container-height, 40px);font-family:var(--mat-button-outlined-label-text-font, var(--mat-sys-label-large-font));font-size:var(--mat-button-outlined-label-text-size, var(--mat-sys-label-large-size));letter-spacing:var(--mat-button-outlined-label-text-tracking, var(--mat-sys-label-large-tracking));text-transform:var(--mat-button-outlined-label-text-transform);font-weight:var(--mat-button-outlined-label-text-weight, var(--mat-sys-label-large-weight));border-radius:var(--mat-button-outlined-container-shape, var(--mat-sys-corner-full));border-width:var(--mat-button-outlined-outline-width, 1px);padding:0 var(--mat-button-outlined-horizontal-padding, 24px)}.mat-mdc-outlined-button>.mat-icon{margin-right:var(--mat-button-outlined-icon-spacing, 8px);margin-left:var(--mat-button-outlined-icon-offset, -8px)}[dir=rtl] .mat-mdc-outlined-button>.mat-icon{margin-right:var(--mat-button-outlined-icon-offset, -8px);margin-left:var(--mat-button-outlined-icon-spacing, 8px)}.mat-mdc-outlined-button .mdc-button__label+.mat-icon{margin-right:var(--mat-button-outlined-icon-offset, -8px);margin-left:var(--mat-button-outlined-icon-spacing, 8px)}[dir=rtl] .mat-mdc-outlined-button .mdc-button__label+.mat-icon{margin-right:var(--mat-button-outlined-icon-spacing, 8px);margin-left:var(--mat-button-outlined-icon-offset, -8px)}.mat-mdc-outlined-button .mat-ripple-element{background-color:var(--mat-button-outlined-ripple-color, color-mix(in srgb, var(--mat-sys-primary) calc(var(--mat-sys-pressed-state-layer-opacity) * 100%), transparent))}.mat-mdc-outlined-button .mat-mdc-button-persistent-ripple::before{background-color:var(--mat-button-outlined-state-layer-color, var(--mat-sys-primary))}.mat-mdc-outlined-button.mat-mdc-button-disabled .mat-mdc-button-persistent-ripple::before{background-color:var(--mat-button-outlined-disabled-state-layer-color, var(--mat-sys-on-surface-variant))}.mat-mdc-outlined-button:hover>.mat-mdc-button-persistent-ripple::before{opacity:var(--mat-button-outlined-hover-state-layer-opacity, var(--mat-sys-hover-state-layer-opacity))}.mat-mdc-outlined-button.cdk-program-focused>.mat-mdc-button-persistent-ripple::before,.mat-mdc-outlined-button.cdk-keyboard-focused>.mat-mdc-button-persistent-ripple::before,.mat-mdc-outlined-button.mat-mdc-button-disabled-interactive:focus>.mat-mdc-button-persistent-ripple::before{opacity:var(--mat-button-outlined-focus-state-layer-opacity, var(--mat-sys-focus-state-layer-opacity))}.mat-mdc-outlined-button:active>.mat-mdc-button-persistent-ripple::before{opacity:var(--mat-button-outlined-pressed-state-layer-opacity, var(--mat-sys-pressed-state-layer-opacity))}.mat-mdc-outlined-button .mat-mdc-button-touch-target{position:absolute;top:50%;height:var(--mat-button-outlined-touch-target-size, 48px);display:var(--mat-button-outlined-touch-target-display, block);left:0;right:0;transform:translateY(-50%)}.mat-mdc-outlined-button:not(:disabled){color:var(--mat-button-outlined-label-text-color, var(--mat-sys-primary));border-color:var(--mat-button-outlined-outline-color, var(--mat-sys-outline))}.mat-mdc-outlined-button[disabled],.mat-mdc-outlined-button.mat-mdc-button-disabled{cursor:default;pointer-events:none;color:var(--mat-button-outlined-disabled-label-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent));border-color:var(--mat-button-outlined-disabled-outline-color, color-mix(in srgb, var(--mat-sys-on-surface) 12%, transparent))}.mat-mdc-outlined-button.mat-mdc-button-disabled-interactive{pointer-events:auto}.mat-tonal-button{transition:box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);height:var(--mat-button-tonal-container-height, 40px);font-family:var(--mat-button-tonal-label-text-font, var(--mat-sys-label-large-font));font-size:var(--mat-button-tonal-label-text-size, var(--mat-sys-label-large-size));letter-spacing:var(--mat-button-tonal-label-text-tracking, var(--mat-sys-label-large-tracking));text-transform:var(--mat-button-tonal-label-text-transform);font-weight:var(--mat-button-tonal-label-text-weight, var(--mat-sys-label-large-weight));padding:0 var(--mat-button-tonal-horizontal-padding, 24px)}.mat-tonal-button:not(:disabled){color:var(--mat-button-tonal-label-text-color, var(--mat-sys-on-secondary-container));background-color:var(--mat-button-tonal-container-color, var(--mat-sys-secondary-container))}.mat-tonal-button,.mat-tonal-button .mdc-button__ripple{border-radius:var(--mat-button-tonal-container-shape, var(--mat-sys-corner-full))}.mat-tonal-button[disabled],.mat-tonal-button.mat-mdc-button-disabled{cursor:default;pointer-events:none;color:var(--mat-button-tonal-disabled-label-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent));background-color:var(--mat-button-tonal-disabled-container-color, color-mix(in srgb, var(--mat-sys-on-surface) 12%, transparent))}.mat-tonal-button.mat-mdc-button-disabled-interactive{pointer-events:auto}.mat-tonal-button>.mat-icon{margin-right:var(--mat-button-tonal-icon-spacing, 8px);margin-left:var(--mat-button-tonal-icon-offset, -8px)}[dir=rtl] .mat-tonal-button>.mat-icon{margin-right:var(--mat-button-tonal-icon-offset, -8px);margin-left:var(--mat-button-tonal-icon-spacing, 8px)}.mat-tonal-button .mdc-button__label+.mat-icon{margin-right:var(--mat-button-tonal-icon-offset, -8px);margin-left:var(--mat-button-tonal-icon-spacing, 8px)}[dir=rtl] .mat-tonal-button .mdc-button__label+.mat-icon{margin-right:var(--mat-button-tonal-icon-spacing, 8px);margin-left:var(--mat-button-tonal-icon-offset, -8px)}.mat-tonal-button .mat-ripple-element{background-color:var(--mat-button-tonal-ripple-color, color-mix(in srgb, var(--mat-sys-on-secondary-container) calc(var(--mat-sys-pressed-state-layer-opacity) * 100%), transparent))}.mat-tonal-button .mat-mdc-button-persistent-ripple::before{background-color:var(--mat-button-tonal-state-layer-color, var(--mat-sys-on-secondary-container))}.mat-tonal-button.mat-mdc-button-disabled .mat-mdc-button-persistent-ripple::before{background-color:var(--mat-button-tonal-disabled-state-layer-color, var(--mat-sys-on-surface-variant))}.mat-tonal-button:hover>.mat-mdc-button-persistent-ripple::before{opacity:var(--mat-button-tonal-hover-state-layer-opacity, var(--mat-sys-hover-state-layer-opacity))}.mat-tonal-button.cdk-program-focused>.mat-mdc-button-persistent-ripple::before,.mat-tonal-button.cdk-keyboard-focused>.mat-mdc-button-persistent-ripple::before,.mat-tonal-button.mat-mdc-button-disabled-interactive:focus>.mat-mdc-button-persistent-ripple::before{opacity:var(--mat-button-tonal-focus-state-layer-opacity, var(--mat-sys-focus-state-layer-opacity))}.mat-tonal-button:active>.mat-mdc-button-persistent-ripple::before{opacity:var(--mat-button-tonal-pressed-state-layer-opacity, var(--mat-sys-pressed-state-layer-opacity))}.mat-tonal-button .mat-mdc-button-touch-target{position:absolute;top:50%;height:var(--mat-button-tonal-touch-target-size, 48px);display:var(--mat-button-tonal-touch-target-display, block);left:0;right:0;transform:translateY(-50%)}.mat-mdc-button,.mat-mdc-unelevated-button,.mat-mdc-raised-button,.mat-mdc-outlined-button,.mat-tonal-button{-webkit-tap-highlight-color:rgba(0,0,0,0)}.mat-mdc-button .mat-mdc-button-ripple,.mat-mdc-button .mat-mdc-button-persistent-ripple,.mat-mdc-button .mat-mdc-button-persistent-ripple::before,.mat-mdc-unelevated-button .mat-mdc-button-ripple,.mat-mdc-unelevated-button .mat-mdc-button-persistent-ripple,.mat-mdc-unelevated-button .mat-mdc-button-persistent-ripple::before,.mat-mdc-raised-button .mat-mdc-button-ripple,.mat-mdc-raised-button .mat-mdc-button-persistent-ripple,.mat-mdc-raised-button .mat-mdc-button-persistent-ripple::before,.mat-mdc-outlined-button .mat-mdc-button-ripple,.mat-mdc-outlined-button .mat-mdc-button-persistent-ripple,.mat-mdc-outlined-button .mat-mdc-button-persistent-ripple::before,.mat-tonal-button .mat-mdc-button-ripple,.mat-tonal-button .mat-mdc-button-persistent-ripple,.mat-tonal-button .mat-mdc-button-persistent-ripple::before{top:0;left:0;right:0;bottom:0;position:absolute;pointer-events:none;border-radius:inherit}.mat-mdc-button .mat-mdc-button-ripple,.mat-mdc-unelevated-button .mat-mdc-button-ripple,.mat-mdc-raised-button .mat-mdc-button-ripple,.mat-mdc-outlined-button .mat-mdc-button-ripple,.mat-tonal-button .mat-mdc-button-ripple{overflow:hidden}.mat-mdc-button .mat-mdc-button-persistent-ripple::before,.mat-mdc-unelevated-button .mat-mdc-button-persistent-ripple::before,.mat-mdc-raised-button .mat-mdc-button-persistent-ripple::before,.mat-mdc-outlined-button .mat-mdc-button-persistent-ripple::before,.mat-tonal-button .mat-mdc-button-persistent-ripple::before{content:"";opacity:0}.mat-mdc-button .mdc-button__label,.mat-mdc-button .mat-icon,.mat-mdc-unelevated-button .mdc-button__label,.mat-mdc-unelevated-button .mat-icon,.mat-mdc-raised-button .mdc-button__label,.mat-mdc-raised-button .mat-icon,.mat-mdc-outlined-button .mdc-button__label,.mat-mdc-outlined-button .mat-icon,.mat-tonal-button .mdc-button__label,.mat-tonal-button .mat-icon{z-index:1;position:relative}.mat-mdc-button .mat-focus-indicator,.mat-mdc-unelevated-button .mat-focus-indicator,.mat-mdc-raised-button .mat-focus-indicator,.mat-mdc-outlined-button .mat-focus-indicator,.mat-tonal-button .mat-focus-indicator{top:0;left:0;right:0;bottom:0;position:absolute;border-radius:inherit}.mat-mdc-button:focus-visible>.mat-focus-indicator::before,.mat-mdc-unelevated-button:focus-visible>.mat-focus-indicator::before,.mat-mdc-raised-button:focus-visible>.mat-focus-indicator::before,.mat-mdc-outlined-button:focus-visible>.mat-focus-indicator::before,.mat-tonal-button:focus-visible>.mat-focus-indicator::before{content:"";border-radius:inherit}.mat-mdc-button._mat-animation-noopable,.mat-mdc-unelevated-button._mat-animation-noopable,.mat-mdc-raised-button._mat-animation-noopable,.mat-mdc-outlined-button._mat-animation-noopable,.mat-tonal-button._mat-animation-noopable{transition:none !important;animation:none !important}.mat-mdc-button>.mat-icon,.mat-mdc-unelevated-button>.mat-icon,.mat-mdc-raised-button>.mat-icon,.mat-mdc-outlined-button>.mat-icon,.mat-tonal-button>.mat-icon{display:inline-block;position:relative;vertical-align:top;font-size:1.125rem;height:1.125rem;width:1.125rem}.mat-mdc-outlined-button .mat-mdc-button-ripple,.mat-mdc-outlined-button .mdc-button__ripple{top:-1px;left:-1px;bottom:-1px;right:-1px}.mat-mdc-unelevated-button .mat-focus-indicator::before,.mat-tonal-button .mat-focus-indicator::before,.mat-mdc-raised-button .mat-focus-indicator::before{margin:calc(calc(var(--mat-focus-indicator-border-width, 3px) + 2px)*-1)}.mat-mdc-outlined-button .mat-focus-indicator::before{margin:calc(calc(var(--mat-focus-indicator-border-width, 3px) + 3px)*-1)} -`,`@media(forced-colors: active){.mat-mdc-button:not(.mdc-button--outlined),.mat-mdc-unelevated-button:not(.mdc-button--outlined),.mat-mdc-raised-button:not(.mdc-button--outlined),.mat-mdc-outlined-button:not(.mdc-button--outlined),.mat-mdc-button-base.mat-tonal-button,.mat-mdc-icon-button.mat-mdc-icon-button,.mat-mdc-outlined-button .mdc-button__ripple{outline:solid 1px}} -`],encapsulation:2,changeDetection:0})}return e})();function $A(e){return e.hasAttribute("mat-raised-button")?"elevated":e.hasAttribute("mat-stroked-button")?"outlined":e.hasAttribute("mat-flat-button")?"filled":e.hasAttribute("mat-button")?"text":null}var Mq=(()=>{class e{static \u0275fac=function(r){return new(r||e)};static \u0275mod=Z({type:e});static \u0275inj=G({imports:[RD,ln]})}return e})();var rp={production:!0,electron:!1,githubio:!1,solarputty_download_url:"",current_version:"v3",compute_id:"local"};var Qi=class{};function zA(e){return e&&typeof e.connect=="function"&&!(e instanceof Oo)}var op=class extends Qi{_data;constructor(n){super(),this._data=n}connect(){return pn(this._data)?this._data:ke(this._data)}disconnect(){}},Ut=(function(e){return e[e.REPLACED=0]="REPLACED",e[e.INSERTED=1]="INSERTED",e[e.MOVED=2]="MOVED",e[e.REMOVED=3]="REMOVED",e})(Ut||{}),ip=class{viewCacheSize=20;_viewCache=[];applyChanges(n,t,r,o,i){n.forEachOperation((s,a,c)=>{let l,u;if(s.previousIndex==null){let d=()=>r(s,a,c);l=this._insertView(d,c,t,o(s)),u=l?Ut.INSERTED:Ut.REPLACED}else c==null?(this._detachAndCacheView(a,t),u=Ut.REMOVED):(l=this._moveView(a,c,t,o(s)),u=Ut.MOVED);i&&i({context:l?.context,operation:u,record:s})})}detach(){for(let n of this._viewCache)n.destroy();this._viewCache=[]}_insertView(n,t,r,o){let i=this._insertViewFromCache(t,r);if(i){i.context.$implicit=o;return}let s=n();return r.createEmbeddedView(s.templateRef,s.context,s.index)}_detachAndCacheView(n,t){let r=t.detach(n);this._maybeCacheView(r,t)}_moveView(n,t,r,o){let i=r.get(n);return r.move(i,t),i.context.$implicit=o,i}_maybeCacheView(n,t){if(this._viewCache.length{class e{_ngZone=f(P);_platform=f(ae);_renderer=f(be).createRenderer(null,null);_cleanupGlobalListener;constructor(){}_scrolled=new R;_scrolledCount=0;scrollContainers=new Map;register(t){this.scrollContainers.has(t)||this.scrollContainers.set(t,t.elementScrolled().subscribe(()=>this._scrolled.next(t)))}deregister(t){let r=this.scrollContainers.get(t);r&&(r.unsubscribe(),this.scrollContainers.delete(t))}scrolled(t=GA){return this._platform.isBrowser?new k(r=>{this._cleanupGlobalListener||(this._cleanupGlobalListener=this._ngZone.runOutsideAngular(()=>this._renderer.listen("document","scroll",()=>this._scrolled.next())));let o=t>0?this._scrolled.pipe(js(t)).subscribe(r):this._scrolled.subscribe(r);return this._scrolledCount++,()=>{o.unsubscribe(),this._scrolledCount--,this._scrolledCount||(this._cleanupGlobalListener?.(),this._cleanupGlobalListener=void 0)}}):ke()}ngOnDestroy(){this._cleanupGlobalListener?.(),this._cleanupGlobalListener=void 0,this.scrollContainers.forEach((t,r)=>this.deregister(r)),this._scrolled.complete()}ancestorScrolled(t,r){let o=this.getAncestorScrollContainers(t);return this.scrolled(r).pipe(Ee(i=>!i||o.indexOf(i)>-1))}getAncestorScrollContainers(t){let r=[];return this.scrollContainers.forEach((o,i)=>{this._scrollableContainsElement(i,t)&&r.push(i)}),r}_scrollableContainsElement(t,r){let o=ct(r),i=t.getElementRef().nativeElement;do if(o==i)return!0;while(o=o.parentElement);return!1}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})(),WA=(()=>{class e{elementRef=f(z);scrollDispatcher=f(Ji);ngZone=f(P);dir=f(wo,{optional:!0});_scrollElement=this.elementRef.nativeElement;_destroyed=new R;_renderer=f(Be);_cleanupScroll;_elementScrolled=new R;constructor(){}ngOnInit(){this._cleanupScroll=this.ngZone.runOutsideAngular(()=>this._renderer.listen(this._scrollElement,"scroll",t=>this._elementScrolled.next(t))),this.scrollDispatcher.register(this)}ngOnDestroy(){this._cleanupScroll?.(),this._elementScrolled.complete(),this.scrollDispatcher.deregister(this),this._destroyed.next(),this._destroyed.complete()}elementScrolled(){return this._elementScrolled}getElementRef(){return this.elementRef}scrollTo(t){let r=this.elementRef.nativeElement,o=this.dir&&this.dir.value=="rtl";t.left==null&&(t.left=o?t.end:t.start),t.right==null&&(t.right=o?t.start:t.end),t.bottom!=null&&(t.top=r.scrollHeight-r.clientHeight-t.bottom),o&&Do()!=Tt.NORMAL?(t.left!=null&&(t.right=r.scrollWidth-r.clientWidth-t.left),Do()==Tt.INVERTED?t.left=t.right:Do()==Tt.NEGATED&&(t.left=t.right?-t.right:t.right)):t.right!=null&&(t.left=r.scrollWidth-r.clientWidth-t.right),this._applyScrollToOptions(t)}_applyScrollToOptions(t){let r=this.elementRef.nativeElement;Xc()?r.scrollTo(t):(t.top!=null&&(r.scrollTop=t.top),t.left!=null&&(r.scrollLeft=t.left))}measureScrollOffset(t){let r="left",o="right",i=this.elementRef.nativeElement;if(t=="top")return i.scrollTop;if(t=="bottom")return i.scrollHeight-i.clientHeight-i.scrollTop;let s=this.dir&&this.dir.value=="rtl";return t=="start"?t=s?o:r:t=="end"&&(t=s?r:o),s&&Do()==Tt.INVERTED?t==r?i.scrollWidth-i.clientWidth-i.scrollLeft:i.scrollLeft:s&&Do()==Tt.NEGATED?t==r?i.scrollLeft+i.scrollWidth-i.clientWidth:-i.scrollLeft:t==r?i.scrollLeft:i.scrollWidth-i.clientWidth-i.scrollLeft}static \u0275fac=function(r){return new(r||e)};static \u0275dir=O({type:e,selectors:[["","cdk-scrollable",""],["","cdkScrollable",""]]})}return e})(),qA=20,Co=(()=>{class e{_platform=f(ae);_listeners;_viewportSize=null;_change=new R;_document=f(F);constructor(){let t=f(P),r=f(be).createRenderer(null,null);t.runOutsideAngular(()=>{if(this._platform.isBrowser){let o=i=>this._change.next(i);this._listeners=[r.listen("window","resize",o),r.listen("window","orientationchange",o)]}this.change().subscribe(()=>this._viewportSize=null)})}ngOnDestroy(){this._listeners?.forEach(t=>t()),this._change.complete()}getViewportSize(){this._viewportSize||this._updateViewportSize();let t={width:this._viewportSize.width,height:this._viewportSize.height};return this._platform.isBrowser||(this._viewportSize=null),t}getViewportRect(){let t=this.getViewportScrollPosition(),{width:r,height:o}=this.getViewportSize();return{top:t.top,left:t.left,bottom:t.top+o,right:t.left+r,height:o,width:r}}getViewportScrollPosition(){if(!this._platform.isBrowser)return{top:0,left:0};let t=this._document,r=this._getWindow(),o=t.documentElement,i=o.getBoundingClientRect(),s=-i.top||t.body?.scrollTop||r.scrollY||o.scrollTop||0,a=-i.left||t.body?.scrollLeft||r.scrollX||o.scrollLeft||0;return{top:s,left:a}}change(t=qA){return t>0?this._change.pipe(js(t)):this._change}_getWindow(){return this._document.defaultView||window}_updateViewportSize(){let t=this._getWindow();this._viewportSize=this._platform.isBrowser?{width:t.innerWidth,height:t.innerHeight}:{width:0,height:0}}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();var Xq=new y("CDK_VIRTUAL_SCROLL_VIEWPORT");var sp=(()=>{class e{static \u0275fac=function(r){return new(r||e)};static \u0275mod=Z({type:e});static \u0275inj=G({})}return e})(),ap=(()=>{class e{static \u0275fac=function(r){return new(r||e)};static \u0275mod=Z({type:e});static \u0275inj=G({imports:[ln,sp,ln,sp]})}return e})();var es=class{_attachedHost=null;attach(n){return this._attachedHost=n,n.attach(this)}detach(){let n=this._attachedHost;n!=null&&(this._attachedHost=null,n.detach())}get isAttached(){return this._attachedHost!=null}setAttachedHost(n){this._attachedHost=n}},cp=class extends es{component;viewContainerRef;injector;projectableNodes;bindings;constructor(n,t,r,o,i){super(),this.component=n,this.viewContainerRef=t,this.injector=r,this.projectableNodes=o,this.bindings=i||null}},Io=class extends es{templateRef;viewContainerRef;context;injector;constructor(n,t,r,o){super(),this.templateRef=n,this.viewContainerRef=t,this.context=r,this.injector=o}get origin(){return this.templateRef.elementRef}attach(n,t=this.context){return this.context=t,super.attach(n)}detach(){return this.context=void 0,super.detach()}},lp=class extends es{element;constructor(n){super(),this.element=n instanceof z?n.nativeElement:n}},Jc=class{_attachedPortal=null;_disposeFn=null;_isDisposed=!1;hasAttached(){return!!this._attachedPortal}attach(n){if(n instanceof cp)return this._attachedPortal=n,this.attachComponentPortal(n);if(n instanceof Io)return this._attachedPortal=n,this.attachTemplatePortal(n);if(this.attachDomPortal&&n instanceof lp)return this._attachedPortal=n,this.attachDomPortal(n)}attachDomPortal=null;detach(){this._attachedPortal&&(this._attachedPortal.setAttachedHost(null),this._attachedPortal=null),this._invokeDisposeFn()}dispose(){this.hasAttached()&&this.detach(),this._invokeDisposeFn(),this._isDisposed=!0}setDisposeFn(n){this._disposeFn=n}_invokeDisposeFn(){this._disposeFn&&(this._disposeFn(),this._disposeFn=null)}},el=class extends Jc{outletElement;_appRef;_defaultInjector;constructor(n,t,r){super(),this.outletElement=n,this._appRef=t,this._defaultInjector=r}attachComponentPortal(n){let t;if(n.viewContainerRef){let r=n.injector||n.viewContainerRef.injector,o=r.get(Bt,null,{optional:!0})||void 0;t=n.viewContainerRef.createComponent(n.component,{index:n.viewContainerRef.length,injector:r,ngModuleRef:o,projectableNodes:n.projectableNodes||void 0,bindings:n.bindings||void 0}),this.setDisposeFn(()=>t.destroy())}else{let r=this._appRef,o=n.injector||this._defaultInjector||j.NULL,i=o.get(ce,r.injector);t=gc(n.component,{elementInjector:o,environmentInjector:i,projectableNodes:n.projectableNodes||void 0,bindings:n.bindings||void 0}),r.attachView(t.hostView),this.setDisposeFn(()=>{r.viewCount>0&&r.detachView(t.hostView),t.destroy()})}return this.outletElement.appendChild(this._getComponentRootNode(t)),this._attachedPortal=n,t}attachTemplatePortal(n){let t=n.viewContainerRef,r=t.createEmbeddedView(n.templateRef,n.context,{injector:n.injector});return r.rootNodes.forEach(o=>this.outletElement.appendChild(o)),r.detectChanges(),this.setDisposeFn(()=>{let o=t.indexOf(r);o!==-1&&t.remove(o)}),this._attachedPortal=n,r}attachDomPortal=n=>{let t=n.element;t.parentNode;let r=this.outletElement.ownerDocument.createComment("dom-portal");t.parentNode.insertBefore(r,t),this.outletElement.appendChild(t),this._attachedPortal=n,super.setDisposeFn(()=>{r.parentNode&&r.parentNode.replaceChild(t,r)})};dispose(){super.dispose(),this.outletElement.remove()}_getComponentRootNode(n){return n.hostView.rootNodes[0]}},o6=(()=>{class e extends Io{constructor(){let t=f(Ze),r=f(He);super(t,r)}static \u0275fac=function(r){return new(r||e)};static \u0275dir=O({type:e,selectors:[["","cdkPortal",""]],exportAs:["cdkPortal"],features:[X]})}return e})(),i6=(()=>{class e extends Jc{_moduleRef=f(Bt,{optional:!0});_document=f(F);_viewContainerRef=f(He);_isInitialized=!1;_attachedRef=null;constructor(){super()}get portal(){return this._attachedPortal}set portal(t){this.hasAttached()&&!t&&!this._isInitialized||(this.hasAttached()&&super.detach(),t&&super.attach(t),this._attachedPortal=t||null)}attached=new H;get attachedRef(){return this._attachedRef}ngOnInit(){this._isInitialized=!0}ngOnDestroy(){super.dispose(),this._attachedRef=this._attachedPortal=null}attachComponentPortal(t){t.setAttachedHost(this);let r=t.viewContainerRef!=null?t.viewContainerRef:this._viewContainerRef,o=r.createComponent(t.component,{index:r.length,injector:t.injector||r.injector,projectableNodes:t.projectableNodes||void 0,ngModuleRef:this._moduleRef||void 0,bindings:t.bindings||void 0});return r!==this._viewContainerRef&&this._getRootNode().appendChild(o.hostView.rootNodes[0]),super.setDisposeFn(()=>o.destroy()),this._attachedPortal=t,this._attachedRef=o,this.attached.emit(o),o}attachTemplatePortal(t){t.setAttachedHost(this);let r=this._viewContainerRef.createEmbeddedView(t.templateRef,t.context,{injector:t.injector});return super.setDisposeFn(()=>this._viewContainerRef.clear()),this._attachedPortal=t,this._attachedRef=r,this.attached.emit(r),r}attachDomPortal=t=>{let r=t.element;r.parentNode;let o=this._document.createComment("dom-portal");t.setAttachedHost(this),r.parentNode.insertBefore(o,r),this._getRootNode().appendChild(r),this._attachedPortal=t,super.setDisposeFn(()=>{o.parentNode&&o.parentNode.replaceChild(r,o)})};_getRootNode(){let t=this._viewContainerRef.element.nativeElement;return t.nodeType===t.ELEMENT_NODE?t:t.parentNode}static \u0275fac=function(r){return new(r||e)};static \u0275dir=O({type:e,selectors:[["","cdkPortalOutlet",""]],inputs:{portal:[0,"cdkPortalOutlet","portal"]},outputs:{attached:"attached"},exportAs:["cdkPortalOutlet"],features:[X]})}return e})(),kD=(()=>{class e{static \u0275fac=function(r){return new(r||e)};static \u0275mod=Z({type:e});static \u0275inj=G({})}return e})();var FD=Xc();function UD(e){return new tl(e.get(Co),e.get(F))}var tl=class{_viewportRuler;_previousHTMLStyles={top:"",left:""};_previousScrollPosition;_isEnabled=!1;_document;constructor(n,t){this._viewportRuler=n,this._document=t}attach(){}enable(){if(this._canBeEnabled()){let n=this._document.documentElement;this._previousScrollPosition=this._viewportRuler.getViewportScrollPosition(),this._previousHTMLStyles.left=n.style.left||"",this._previousHTMLStyles.top=n.style.top||"",n.style.left=fe(-this._previousScrollPosition.left),n.style.top=fe(-this._previousScrollPosition.top),n.classList.add("cdk-global-scrollblock"),this._isEnabled=!0}}disable(){if(this._isEnabled){let n=this._document.documentElement,t=this._document.body,r=n.style,o=t.style,i=r.scrollBehavior||"",s=o.scrollBehavior||"";this._isEnabled=!1,r.left=this._previousHTMLStyles.left,r.top=this._previousHTMLStyles.top,n.classList.remove("cdk-global-scrollblock"),FD&&(r.scrollBehavior=o.scrollBehavior="auto"),window.scroll(this._previousScrollPosition.left,this._previousScrollPosition.top),FD&&(r.scrollBehavior=i,o.scrollBehavior=s)}}_canBeEnabled(){if(this._document.documentElement.classList.contains("cdk-global-scrollblock")||this._isEnabled)return!1;let t=this._document.documentElement,r=this._viewportRuler.getViewportSize();return t.scrollHeight>r.height||t.scrollWidth>r.width}};function $D(e,n){return new nl(e.get(Ji),e.get(P),e.get(Co),n)}var nl=class{_scrollDispatcher;_ngZone;_viewportRuler;_config;_scrollSubscription=null;_overlayRef;_initialScrollPosition;constructor(n,t,r,o){this._scrollDispatcher=n,this._ngZone=t,this._viewportRuler=r,this._config=o}attach(n){this._overlayRef,this._overlayRef=n}enable(){if(this._scrollSubscription)return;let n=this._scrollDispatcher.scrolled(0).pipe(Ee(t=>!t||!this._overlayRef.overlayElement.contains(t.getElementRef().nativeElement)));this._config&&this._config.threshold&&this._config.threshold>1?(this._initialScrollPosition=this._viewportRuler.getViewportScrollPosition().top,this._scrollSubscription=n.subscribe(()=>{let t=this._viewportRuler.getViewportScrollPosition().top;Math.abs(t-this._initialScrollPosition)>this._config.threshold?this._detach():this._overlayRef.updatePosition()})):this._scrollSubscription=n.subscribe(this._detach)}disable(){this._scrollSubscription&&(this._scrollSubscription.unsubscribe(),this._scrollSubscription=null)}detach(){this.disable(),this._overlayRef=null}_detach=()=>{this.disable(),this._overlayRef.hasAttached()&&this._ngZone.run(()=>this._overlayRef.detach())}};var ts=class{enable(){}disable(){}attach(){}};function up(e,n){return n.some(t=>{let r=e.bottomt.bottom,i=e.rightt.right;return r||o||i||s})}function PD(e,n){return n.some(t=>{let r=e.topt.bottom,i=e.leftt.right;return r||o||i||s})}function hp(e,n){return new rl(e.get(Ji),e.get(Co),e.get(P),n)}var rl=class{_scrollDispatcher;_viewportRuler;_ngZone;_config;_scrollSubscription=null;_overlayRef;constructor(n,t,r,o){this._scrollDispatcher=n,this._viewportRuler=t,this._ngZone=r,this._config=o}attach(n){this._overlayRef,this._overlayRef=n}enable(){if(!this._scrollSubscription){let n=this._config?this._config.scrollThrottle:0;this._scrollSubscription=this._scrollDispatcher.scrolled(n).subscribe(()=>{if(this._overlayRef.updatePosition(),this._config&&this._config.autoClose){let t=this._overlayRef.overlayElement.getBoundingClientRect(),{width:r,height:o}=this._viewportRuler.getViewportSize();up(t,[{width:r,height:o,bottom:o,right:r,top:0,left:0}])&&(this.disable(),this._ngZone.run(()=>this._overlayRef.detach()))}})}}disable(){this._scrollSubscription&&(this._scrollSubscription.unsubscribe(),this._scrollSubscription=null)}detach(){this.disable(),this._overlayRef=null}},zD=(()=>{class e{_injector=f(j);constructor(){}noop=()=>new ts;close=t=>$D(this._injector,t);block=()=>UD(this._injector);reposition=t=>hp(this._injector,t);static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})(),ns=class{positionStrategy;scrollStrategy=new ts;panelClass="";hasBackdrop=!1;backdropClass="cdk-overlay-dark-backdrop";disableAnimations;width;height;minWidth;minHeight;maxWidth;maxHeight;direction;disposeOnNavigation=!1;usePopover;eventPredicate;constructor(n){if(n){let t=Object.keys(n);for(let r of t)n[r]!==void 0&&(this[r]=n[r])}}};var ol=class{connectionPair;scrollableViewProperties;constructor(n,t){this.connectionPair=n,this.scrollableViewProperties=t}};var GD=(()=>{class e{_attachedOverlays=[];_document=f(F);_isAttached=!1;constructor(){}ngOnDestroy(){this.detach()}add(t){this.remove(t),this._attachedOverlays.push(t)}remove(t){let r=this._attachedOverlays.indexOf(t);r>-1&&this._attachedOverlays.splice(r,1),this._attachedOverlays.length===0&&this.detach()}canReceiveEvent(t,r,o){return o.observers.length<1?!1:t.eventPredicate?t.eventPredicate(r):!0}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})(),WD=(()=>{class e extends GD{_ngZone=f(P);_renderer=f(be).createRenderer(null,null);_cleanupKeydown;add(t){super.add(t),this._isAttached||(this._ngZone.runOutsideAngular(()=>{this._cleanupKeydown=this._renderer.listen("body","keydown",this._keydownListener)}),this._isAttached=!0)}detach(){this._isAttached&&(this._cleanupKeydown?.(),this._isAttached=!1)}_keydownListener=t=>{let r=this._attachedOverlays;for(let o=r.length-1;o>-1;o--){let i=r[o];if(this.canReceiveEvent(i,t,i._keydownEvents)){this._ngZone.run(()=>i._keydownEvents.next(t));break}}};static \u0275fac=(()=>{let t;return function(o){return(t||(t=Ve(e)))(o||e)}})();static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})(),qD=(()=>{class e extends GD{_platform=f(ae);_ngZone=f(P);_renderer=f(be).createRenderer(null,null);_cursorOriginalValue;_cursorStyleIsSet=!1;_pointerDownEventTarget=null;_cleanups;add(t){if(super.add(t),!this._isAttached){let r=this._document.body,o={capture:!0},i=this._renderer;this._cleanups=this._ngZone.runOutsideAngular(()=>[i.listen(r,"pointerdown",this._pointerDownListener,o),i.listen(r,"click",this._clickListener,o),i.listen(r,"auxclick",this._clickListener,o),i.listen(r,"contextmenu",this._clickListener,o)]),this._platform.IOS&&!this._cursorStyleIsSet&&(this._cursorOriginalValue=r.style.cursor,r.style.cursor="pointer",this._cursorStyleIsSet=!0),this._isAttached=!0}}detach(){this._isAttached&&(this._cleanups?.forEach(t=>t()),this._cleanups=void 0,this._platform.IOS&&this._cursorStyleIsSet&&(this._document.body.style.cursor=this._cursorOriginalValue,this._cursorStyleIsSet=!1),this._isAttached=!1)}_pointerDownListener=t=>{this._pointerDownEventTarget=Re(t)};_clickListener=t=>{let r=Re(t),o=t.type==="click"&&this._pointerDownEventTarget?this._pointerDownEventTarget:r;this._pointerDownEventTarget=null;let i=this._attachedOverlays.slice();for(let s=i.length-1;s>-1;s--){let a=i[s],c=a._outsidePointerEvents;if(!(!a.hasAttached()||!this.canReceiveEvent(a,t,c))){if(LD(a.overlayElement,r)||LD(a.overlayElement,o))break;this._ngZone?this._ngZone.run(()=>c.next(t)):c.next(t)}}};static \u0275fac=(()=>{let t;return function(o){return(t||(t=Ve(e)))(o||e)}})();static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();function LD(e,n){let t=typeof ShadowRoot<"u"&&ShadowRoot,r=n;for(;r;){if(r===e)return!0;r=t&&r instanceof ShadowRoot?r.host:r.parentNode}return!1}var YD=(()=>{class e{static \u0275fac=function(r){return new(r||e)};static \u0275cmp=Se({type:e,selectors:[["ng-component"]],hostAttrs:["cdk-overlay-style-loader",""],decls:0,vars:0,template:function(r,o){},styles:[`.cdk-overlay-container,.cdk-global-overlay-wrapper{pointer-events:none;top:0;left:0;height:100%;width:100%}.cdk-overlay-container{position:fixed}@layer cdk-overlay{.cdk-overlay-container{z-index:1000}}.cdk-overlay-container:empty{display:none}.cdk-global-overlay-wrapper{display:flex;position:absolute}@layer cdk-overlay{.cdk-global-overlay-wrapper{z-index:1000}}.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;display:flex;max-width:100%;max-height:100%}@layer cdk-overlay{.cdk-overlay-pane{z-index:1000}}.cdk-overlay-backdrop{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:auto;-webkit-tap-highlight-color:rgba(0,0,0,0);opacity:0;touch-action:manipulation}@layer cdk-overlay{.cdk-overlay-backdrop{z-index:1000;transition:opacity 400ms cubic-bezier(0.25, 0.8, 0.25, 1)}}@media(prefers-reduced-motion){.cdk-overlay-backdrop{transition-duration:1ms}}.cdk-overlay-backdrop-showing{opacity:1}@media(forced-colors: active){.cdk-overlay-backdrop-showing{opacity:.6}}@layer cdk-overlay{.cdk-overlay-dark-backdrop{background:rgba(0,0,0,.32)}}.cdk-overlay-transparent-backdrop{transition:visibility 1ms linear,opacity 1ms linear;visibility:hidden;opacity:1}.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing,.cdk-high-contrast-active .cdk-overlay-transparent-backdrop{opacity:0;visibility:visible}.cdk-overlay-backdrop-noop-animation{transition:none}.cdk-overlay-connected-position-bounding-box{position:absolute;display:flex;flex-direction:column;min-width:1px;min-height:1px}@layer cdk-overlay{.cdk-overlay-connected-position-bounding-box{z-index:1000}}.cdk-global-scrollblock{position:fixed;width:100%;overflow-y:scroll}.cdk-overlay-popover{background:none;border:none;padding:0;outline:0;overflow:visible;position:fixed;pointer-events:none;white-space:normal;color:inherit;text-decoration:none;width:100%;height:100%;inset:auto;top:0;left:0}.cdk-overlay-popover::backdrop{display:none}.cdk-overlay-popover .cdk-overlay-backdrop{position:fixed;z-index:auto} -`],encapsulation:2,changeDetection:0})}return e})(),pp=(()=>{class e{_platform=f(ae);_containerElement;_document=f(F);_styleLoader=f(lt);constructor(){}ngOnDestroy(){this._containerElement?.remove()}getContainerElement(){return this._loadStyles(),this._containerElement||this._createContainer(),this._containerElement}_createContainer(){let t="cdk-overlay-container";if(this._platform.isBrowser||Kh()){let o=this._document.querySelectorAll(`.${t}[platform="server"], .${t}[platform="test"]`);for(let i=0;i{let n=this.element;clearTimeout(this._fallbackTimeout),this._cleanupTransitionEnd?.(),this._cleanupTransitionEnd=this._renderer.listen(n,"transitionend",this.dispose),this._fallbackTimeout=setTimeout(this.dispose,500),n.style.pointerEvents="none",n.classList.remove("cdk-overlay-backdrop-showing")})}dispose=()=>{clearTimeout(this._fallbackTimeout),this._cleanupClick?.(),this._cleanupTransitionEnd?.(),this._cleanupClick=this._cleanupTransitionEnd=this._fallbackTimeout=void 0,this.element.remove()}};function mp(e){return e&&e.nodeType===1}var il=class{_portalOutlet;_host;_pane;_config;_ngZone;_keyboardDispatcher;_document;_location;_outsideClickDispatcher;_animationsDisabled;_injector;_renderer;_backdropClick=new R;_attachments=new R;_detachments=new R;_positionStrategy;_scrollStrategy;_locationChanges=B.EMPTY;_backdropRef=null;_detachContentMutationObserver;_detachContentAfterRenderRef;_disposed=!1;_previousHostParent;_keydownEvents=new R;_outsidePointerEvents=new R;_afterNextRenderRef;constructor(n,t,r,o,i,s,a,c,l,u=!1,d,p){this._portalOutlet=n,this._host=t,this._pane=r,this._config=o,this._ngZone=i,this._keyboardDispatcher=s,this._document=a,this._location=c,this._outsideClickDispatcher=l,this._animationsDisabled=u,this._injector=d,this._renderer=p,o.scrollStrategy&&(this._scrollStrategy=o.scrollStrategy,this._scrollStrategy.attach(this)),this._positionStrategy=o.positionStrategy}get overlayElement(){return this._pane}get backdropElement(){return this._backdropRef?.element||null}get hostElement(){return this._host}get eventPredicate(){return this._config?.eventPredicate||null}attach(n){if(this._disposed)return null;this._attachHost();let t=this._portalOutlet.attach(n);return this._positionStrategy?.attach(this),this._updateStackingOrder(),this._updateElementSize(),this._updateElementDirection(),this._scrollStrategy&&this._scrollStrategy.enable(),this._afterNextRenderRef?.destroy(),this._afterNextRenderRef=An(()=>{this.hasAttached()&&this.updatePosition()},{injector:this._injector}),this._togglePointerEvents(!0),this._config.hasBackdrop&&this._attachBackdrop(),this._config.panelClass&&this._toggleClasses(this._pane,this._config.panelClass,!0),this._attachments.next(),this._completeDetachContent(),this._keyboardDispatcher.add(this),this._config.disposeOnNavigation&&(this._locationChanges=this._location.subscribe(()=>this.dispose())),this._outsideClickDispatcher.add(this),typeof t?.onDestroy=="function"&&t.onDestroy(()=>{this.hasAttached()&&this._ngZone.runOutsideAngular(()=>Promise.resolve().then(()=>this.detach()))}),t}detach(){if(!this.hasAttached())return;this.detachBackdrop(),this._togglePointerEvents(!1),this._positionStrategy&&this._positionStrategy.detach&&this._positionStrategy.detach(),this._scrollStrategy&&this._scrollStrategy.disable();let n=this._portalOutlet.detach();return this._detachments.next(),this._completeDetachContent(),this._keyboardDispatcher.remove(this),this._detachContentWhenEmpty(),this._locationChanges.unsubscribe(),this._outsideClickDispatcher.remove(this),n}dispose(){if(this._disposed)return;let n=this.hasAttached();this._positionStrategy&&this._positionStrategy.dispose(),this._disposeScrollStrategy(),this._backdropRef?.dispose(),this._locationChanges.unsubscribe(),this._keyboardDispatcher.remove(this),this._portalOutlet.dispose(),this._attachments.complete(),this._backdropClick.complete(),this._keydownEvents.complete(),this._outsidePointerEvents.complete(),this._outsideClickDispatcher.remove(this),this._host?.remove(),this._afterNextRenderRef?.destroy(),this._previousHostParent=this._pane=this._host=this._backdropRef=null,n&&this._detachments.next(),this._detachments.complete(),this._completeDetachContent(),this._disposed=!0}hasAttached(){return this._portalOutlet.hasAttached()}backdropClick(){return this._backdropClick}attachments(){return this._attachments}detachments(){return this._detachments}keydownEvents(){return this._keydownEvents}outsidePointerEvents(){return this._outsidePointerEvents}getConfig(){return this._config}updatePosition(){this._positionStrategy&&this._positionStrategy.apply()}updatePositionStrategy(n){n!==this._positionStrategy&&(this._positionStrategy&&this._positionStrategy.dispose(),this._positionStrategy=n,this.hasAttached()&&(n.attach(this),this.updatePosition()))}updateSize(n){this._config=E(E({},this._config),n),this._updateElementSize()}setDirection(n){this._config=V(E({},this._config),{direction:n}),this._updateElementDirection()}addPanelClass(n){this._pane&&this._toggleClasses(this._pane,n,!0)}removePanelClass(n){this._pane&&this._toggleClasses(this._pane,n,!1)}getDirection(){let n=this._config.direction;return n?typeof n=="string"?n:n.value:"ltr"}updateScrollStrategy(n){n!==this._scrollStrategy&&(this._disposeScrollStrategy(),this._scrollStrategy=n,this.hasAttached()&&(n.attach(this),n.enable()))}_updateElementDirection(){this._host.setAttribute("dir",this.getDirection())}_updateElementSize(){if(!this._pane)return;let n=this._pane.style;n.width=fe(this._config.width),n.height=fe(this._config.height),n.minWidth=fe(this._config.minWidth),n.minHeight=fe(this._config.minHeight),n.maxWidth=fe(this._config.maxWidth),n.maxHeight=fe(this._config.maxHeight)}_togglePointerEvents(n){this._pane.style.pointerEvents=n?"":"none"}_attachHost(){if(!this._host.parentElement){let n=this._config.usePopover?this._positionStrategy?.getPopoverInsertionPoint?.():null;mp(n)?n.after(this._host):n?.type==="parent"?n.element.appendChild(this._host):this._previousHostParent?.appendChild(this._host)}if(this._config.usePopover)try{this._host.showPopover()}catch{}}_attachBackdrop(){let n="cdk-overlay-backdrop-showing";this._backdropRef?.dispose(),this._backdropRef=new dp(this._document,this._renderer,this._ngZone,t=>{this._backdropClick.next(t)}),this._animationsDisabled&&this._backdropRef.element.classList.add("cdk-overlay-backdrop-noop-animation"),this._config.backdropClass&&this._toggleClasses(this._backdropRef.element,this._config.backdropClass,!0),this._config.usePopover?this._host.prepend(this._backdropRef.element):this._host.parentElement.insertBefore(this._backdropRef.element,this._host),!this._animationsDisabled&&typeof requestAnimationFrame<"u"?this._ngZone.runOutsideAngular(()=>{requestAnimationFrame(()=>this._backdropRef?.element.classList.add(n))}):this._backdropRef.element.classList.add(n)}_updateStackingOrder(){!this._config.usePopover&&this._host.nextSibling&&this._host.parentNode.appendChild(this._host)}detachBackdrop(){this._animationsDisabled?(this._backdropRef?.dispose(),this._backdropRef=null):this._backdropRef?.detach()}_toggleClasses(n,t,r){let o=Dr(t||[]).filter(i=>!!i);o.length&&(r?n.classList.add(...o):n.classList.remove(...o))}_detachContentWhenEmpty(){let n=!1;try{this._detachContentAfterRenderRef=An(()=>{n=!0,this._detachContent()},{injector:this._injector})}catch(t){if(n)throw t;this._detachContent()}globalThis.MutationObserver&&this._pane&&(this._detachContentMutationObserver||=new globalThis.MutationObserver(()=>{this._detachContent()}),this._detachContentMutationObserver.observe(this._pane,{childList:!0}))}_detachContent(){(!this._pane||!this._host||this._pane.children.length===0)&&(this._pane&&this._config.panelClass&&this._toggleClasses(this._pane,this._config.panelClass,!1),this._host&&this._host.parentElement&&(this._previousHostParent=this._host.parentElement,this._host.remove()),this._completeDetachContent())}_completeDetachContent(){this._detachContentAfterRenderRef?.destroy(),this._detachContentAfterRenderRef=void 0,this._detachContentMutationObserver?.disconnect()}_disposeScrollStrategy(){let n=this._scrollStrategy;n?.disable(),n?.detach?.()}},VD="cdk-overlay-connected-position-bounding-box",YA=/([A-Za-z%]+)$/;function gp(e,n){return new sl(n,e.get(Co),e.get(F),e.get(ae),e.get(pp))}var sl=class{_viewportRuler;_document;_platform;_overlayContainer;_overlayRef;_isInitialRender=!1;_lastBoundingBoxSize={width:0,height:0};_isPushed=!1;_canPush=!0;_growAfterOpen=!1;_hasFlexibleDimensions=!0;_positionLocked=!1;_originRect;_overlayRect;_viewportRect;_containerRect;_viewportMargin=0;_scrollables=[];_preferredPositions=[];_origin;_pane;_isDisposed=!1;_boundingBox=null;_lastPosition=null;_lastScrollVisibility=null;_positionChanges=new R;_resizeSubscription=B.EMPTY;_offsetX=0;_offsetY=0;_transformOriginSelector;_appliedPanelClasses=[];_previousPushAmount=null;_popoverLocation="global";positionChanges=this._positionChanges;get positions(){return this._preferredPositions}constructor(n,t,r,o,i){this._viewportRuler=t,this._document=r,this._platform=o,this._overlayContainer=i,this.setOrigin(n)}attach(n){this._overlayRef&&this._overlayRef,this._validatePositions(),n.hostElement.classList.add(VD),this._overlayRef=n,this._boundingBox=n.hostElement,this._pane=n.overlayElement,this._isDisposed=!1,this._isInitialRender=!0,this._lastPosition=null,this._resizeSubscription.unsubscribe(),this._resizeSubscription=this._viewportRuler.change().subscribe(()=>{this._isInitialRender=!0,this.apply()})}apply(){if(this._isDisposed||!this._platform.isBrowser)return;if(!this._isInitialRender&&this._positionLocked&&this._lastPosition){this.reapplyLastPosition();return}this._clearPanelClasses(),this._resetOverlayElementStyles(),this._resetBoundingBoxStyles(),this._viewportRect=this._getNarrowedViewportRect(),this._originRect=this._getOriginRect(),this._overlayRect=this._pane.getBoundingClientRect(),this._containerRect=this._getContainerRect();let n=this._originRect,t=this._overlayRect,r=this._viewportRect,o=this._containerRect,i=[],s;for(let a of this._preferredPositions){let c=this._getOriginPoint(n,o,a),l=this._getOverlayPoint(c,t,a),u=this._getOverlayFit(l,t,r,a);if(u.isCompletelyWithinViewport){this._isPushed=!1,this._applyPosition(a,c);return}if(this._canFitWithFlexibleDimensions(u,l,r)){i.push({position:a,origin:c,overlayRect:t,boundingBoxRect:this._calculateBoundingBoxRect(c,a)});continue}(!s||s.overlayFit.visibleAreac&&(c=u,a=l)}this._isPushed=!1,this._applyPosition(a.position,a.origin);return}if(this._canPush){this._isPushed=!0,this._applyPosition(s.position,s.originPoint);return}this._applyPosition(s.position,s.originPoint)}detach(){this._clearPanelClasses(),this._lastPosition=null,this._previousPushAmount=null,this._resizeSubscription.unsubscribe()}dispose(){this._isDisposed||(this._boundingBox&&Cr(this._boundingBox.style,{top:"",left:"",right:"",bottom:"",height:"",width:"",alignItems:"",justifyContent:""}),this._pane&&this._resetOverlayElementStyles(),this._overlayRef&&this._overlayRef.hostElement.classList.remove(VD),this.detach(),this._positionChanges.complete(),this._overlayRef=this._boundingBox=null,this._isDisposed=!0)}reapplyLastPosition(){if(this._isDisposed||!this._platform.isBrowser)return;let n=this._lastPosition;n?(this._originRect=this._getOriginRect(),this._overlayRect=this._pane.getBoundingClientRect(),this._viewportRect=this._getNarrowedViewportRect(),this._containerRect=this._getContainerRect(),this._applyPosition(n,this._getOriginPoint(this._originRect,this._containerRect,n))):this.apply()}withScrollableContainers(n){return this._scrollables=n,this}withPositions(n){return this._preferredPositions=n,n.indexOf(this._lastPosition)===-1&&(this._lastPosition=null),this._validatePositions(),this}withViewportMargin(n){return this._viewportMargin=n,this}withFlexibleDimensions(n=!0){return this._hasFlexibleDimensions=n,this}withGrowAfterOpen(n=!0){return this._growAfterOpen=n,this}withPush(n=!0){return this._canPush=n,this}withLockedPosition(n=!0){return this._positionLocked=n,this}setOrigin(n){return this._origin=n,this}withDefaultOffsetX(n){return this._offsetX=n,this}withDefaultOffsetY(n){return this._offsetY=n,this}withTransformOriginOn(n){return this._transformOriginSelector=n,this}withPopoverLocation(n){return this._popoverLocation=n,this}getPopoverInsertionPoint(){return this._popoverLocation==="global"?null:this._popoverLocation!=="inline"?this._popoverLocation:this._origin instanceof z?this._origin.nativeElement:mp(this._origin)?this._origin:null}_getOriginPoint(n,t,r){let o;if(r.originX=="center")o=n.left+n.width/2;else{let s=this._isRtl()?n.right:n.left,a=this._isRtl()?n.left:n.right;o=r.originX=="start"?s:a}t.left<0&&(o-=t.left);let i;return r.originY=="center"?i=n.top+n.height/2:i=r.originY=="top"?n.top:n.bottom,t.top<0&&(i-=t.top),{x:o,y:i}}_getOverlayPoint(n,t,r){let o;r.overlayX=="center"?o=-t.width/2:r.overlayX==="start"?o=this._isRtl()?-t.width:0:o=this._isRtl()?0:-t.width;let i;return r.overlayY=="center"?i=-t.height/2:i=r.overlayY=="top"?0:-t.height,{x:n.x+o,y:n.y+i}}_getOverlayFit(n,t,r,o){let i=BD(t),{x:s,y:a}=n,c=this._getOffset(o,"x"),l=this._getOffset(o,"y");c&&(s+=c),l&&(a+=l);let u=0-s,d=s+i.width-r.width,p=0-a,h=a+i.height-r.height,m=this._subtractOverflows(i.width,u,d),b=this._subtractOverflows(i.height,p,h),_=m*b;return{visibleArea:_,isCompletelyWithinViewport:i.width*i.height===_,fitsInViewportVertically:b===i.height,fitsInViewportHorizontally:m==i.width}}_canFitWithFlexibleDimensions(n,t,r){if(this._hasFlexibleDimensions){let o=r.bottom-t.y,i=r.right-t.x,s=jD(this._overlayRef.getConfig().minHeight),a=jD(this._overlayRef.getConfig().minWidth),c=n.fitsInViewportVertically||s!=null&&s<=o,l=n.fitsInViewportHorizontally||a!=null&&a<=i;return c&&l}return!1}_pushOverlayOnScreen(n,t,r){if(this._previousPushAmount&&this._positionLocked)return{x:n.x+this._previousPushAmount.x,y:n.y+this._previousPushAmount.y};let o=BD(t),i=this._viewportRect,s=Math.max(n.x+o.width-i.width,0),a=Math.max(n.y+o.height-i.height,0),c=Math.max(i.top-r.top-n.y,0),l=Math.max(i.left-r.left-n.x,0),u=0,d=0;return o.width<=i.width?u=l||-s:u=n.xm&&!this._isInitialRender&&!this._growAfterOpen&&(s=n.y-m/2)}let c=t.overlayX==="start"&&!o||t.overlayX==="end"&&o,l=t.overlayX==="end"&&!o||t.overlayX==="start"&&o,u,d,p;if(l)p=r.width-n.x+this._getViewportMarginStart()+this._getViewportMarginEnd(),u=n.x-this._getViewportMarginStart();else if(c)d=n.x,u=r.right-n.x-this._getViewportMarginEnd();else{let h=Math.min(r.right-n.x+r.left,n.x),m=this._lastBoundingBoxSize.width;u=h*2,d=n.x-h,u>m&&!this._isInitialRender&&!this._growAfterOpen&&(d=n.x-m/2)}return{top:s,left:d,bottom:a,right:p,width:u,height:i}}_setBoundingBoxStyles(n,t){let r=this._calculateBoundingBoxRect(n,t);!this._isInitialRender&&!this._growAfterOpen&&(r.height=Math.min(r.height,this._lastBoundingBoxSize.height),r.width=Math.min(r.width,this._lastBoundingBoxSize.width));let o={};if(this._hasExactPosition())o.top=o.left="0",o.bottom=o.right="auto",o.maxHeight=o.maxWidth="",o.width=o.height="100%";else{let i=this._overlayRef.getConfig().maxHeight,s=this._overlayRef.getConfig().maxWidth;o.width=fe(r.width),o.height=fe(r.height),o.top=fe(r.top)||"auto",o.bottom=fe(r.bottom)||"auto",o.left=fe(r.left)||"auto",o.right=fe(r.right)||"auto",t.overlayX==="center"?o.alignItems="center":o.alignItems=t.overlayX==="end"?"flex-end":"flex-start",t.overlayY==="center"?o.justifyContent="center":o.justifyContent=t.overlayY==="bottom"?"flex-end":"flex-start",i&&(o.maxHeight=fe(i)),s&&(o.maxWidth=fe(s))}this._lastBoundingBoxSize=r,Cr(this._boundingBox.style,o)}_resetBoundingBoxStyles(){Cr(this._boundingBox.style,{top:"0",left:"0",right:"0",bottom:"0",height:"",width:"",alignItems:"",justifyContent:""})}_resetOverlayElementStyles(){Cr(this._pane.style,{top:"",left:"",bottom:"",right:"",position:"",transform:""})}_setOverlayElementStyles(n,t){let r={},o=this._hasExactPosition(),i=this._hasFlexibleDimensions,s=this._overlayRef.getConfig();if(o){let u=this._viewportRuler.getViewportScrollPosition();Cr(r,this._getExactOverlayY(t,n,u)),Cr(r,this._getExactOverlayX(t,n,u))}else r.position="static";let a="",c=this._getOffset(t,"x"),l=this._getOffset(t,"y");c&&(a+=`translateX(${c}px) `),l&&(a+=`translateY(${l}px)`),r.transform=a.trim(),s.maxHeight&&(o?r.maxHeight=fe(s.maxHeight):i&&(r.maxHeight="")),s.maxWidth&&(o?r.maxWidth=fe(s.maxWidth):i&&(r.maxWidth="")),Cr(this._pane.style,r)}_getExactOverlayY(n,t,r){let o={top:"",bottom:""},i=this._getOverlayPoint(t,this._overlayRect,n);if(this._isPushed&&(i=this._pushOverlayOnScreen(i,this._overlayRect,r)),n.overlayY==="bottom"){let s=this._document.documentElement.clientHeight;o.bottom=`${s-(i.y+this._overlayRect.height)}px`}else o.top=fe(i.y);return o}_getExactOverlayX(n,t,r){let o={left:"",right:""},i=this._getOverlayPoint(t,this._overlayRect,n);this._isPushed&&(i=this._pushOverlayOnScreen(i,this._overlayRect,r));let s;if(this._isRtl()?s=n.overlayX==="end"?"left":"right":s=n.overlayX==="end"?"right":"left",s==="right"){let a=this._document.documentElement.clientWidth;o.right=`${a-(i.x+this._overlayRect.width)}px`}else o.left=fe(i.x);return o}_getScrollVisibility(){let n=this._getOriginRect(),t=this._pane.getBoundingClientRect(),r=this._scrollables.map(o=>o.getElementRef().nativeElement.getBoundingClientRect());return{isOriginClipped:PD(n,r),isOriginOutsideView:up(n,r),isOverlayClipped:PD(t,r),isOverlayOutsideView:up(t,r)}}_subtractOverflows(n,...t){return t.reduce((r,o)=>r-Math.max(o,0),n)}_getNarrowedViewportRect(){let n=this._document.documentElement.clientWidth,t=this._document.documentElement.clientHeight,r=this._viewportRuler.getViewportScrollPosition();return{top:r.top+this._getViewportMarginTop(),left:r.left+this._getViewportMarginStart(),right:r.left+n-this._getViewportMarginEnd(),bottom:r.top+t-this._getViewportMarginBottom(),width:n-this._getViewportMarginStart()-this._getViewportMarginEnd(),height:t-this._getViewportMarginTop()-this._getViewportMarginBottom()}}_isRtl(){return this._overlayRef.getDirection()==="rtl"}_hasExactPosition(){return!this._hasFlexibleDimensions||this._isPushed}_getOffset(n,t){return t==="x"?n.offsetX==null?this._offsetX:n.offsetX:n.offsetY==null?this._offsetY:n.offsetY}_validatePositions(){}_addPanelClasses(n){this._pane&&Dr(n).forEach(t=>{t!==""&&this._appliedPanelClasses.indexOf(t)===-1&&(this._appliedPanelClasses.push(t),this._pane.classList.add(t))})}_clearPanelClasses(){this._pane&&(this._appliedPanelClasses.forEach(n=>{this._pane.classList.remove(n)}),this._appliedPanelClasses=[])}_getViewportMarginStart(){return typeof this._viewportMargin=="number"?this._viewportMargin:this._viewportMargin?.start??0}_getViewportMarginEnd(){return typeof this._viewportMargin=="number"?this._viewportMargin:this._viewportMargin?.end??0}_getViewportMarginTop(){return typeof this._viewportMargin=="number"?this._viewportMargin:this._viewportMargin?.top??0}_getViewportMarginBottom(){return typeof this._viewportMargin=="number"?this._viewportMargin:this._viewportMargin?.bottom??0}_getOriginRect(){let n=this._origin;if(n instanceof z)return n.nativeElement.getBoundingClientRect();if(n instanceof Element)return n.getBoundingClientRect();let t=n.width||0,r=n.height||0;return{top:n.y,bottom:n.y+r,left:n.x,right:n.x+t,height:r,width:t}}_getContainerRect(){let n=this._overlayRef.getConfig().usePopover&&this._popoverLocation!=="global",t=this._overlayContainer.getContainerElement();n&&(t.style.display="block");let r=t.getBoundingClientRect();return n&&(t.style.display=""),r}};function Cr(e,n){for(let t in n)n.hasOwnProperty(t)&&(e[t]=n[t]);return e}function jD(e){if(typeof e!="number"&&e!=null){let[n,t]=e.split(YA);return!t||t==="px"?parseFloat(n):null}return e||null}function BD(e){return{top:Math.floor(e.top),right:Math.floor(e.right),bottom:Math.floor(e.bottom),left:Math.floor(e.left),width:Math.floor(e.width),height:Math.floor(e.height)}}function ZA(e,n){return e===n?!0:e.isOriginClipped===n.isOriginClipped&&e.isOriginOutsideView===n.isOriginOutsideView&&e.isOverlayClipped===n.isOverlayClipped&&e.isOverlayOutsideView===n.isOverlayOutsideView}var HD="cdk-global-overlay-wrapper";function ZD(e){return new al}var al=class{_overlayRef;_cssPosition="static";_topOffset="";_bottomOffset="";_alignItems="";_xPosition="";_xOffset="";_width="";_height="";_isDisposed=!1;attach(n){let t=n.getConfig();this._overlayRef=n,this._width&&!t.width&&n.updateSize({width:this._width}),this._height&&!t.height&&n.updateSize({height:this._height}),n.hostElement.classList.add(HD),this._isDisposed=!1}top(n=""){return this._bottomOffset="",this._topOffset=n,this._alignItems="flex-start",this}left(n=""){return this._xOffset=n,this._xPosition="left",this}bottom(n=""){return this._topOffset="",this._bottomOffset=n,this._alignItems="flex-end",this}right(n=""){return this._xOffset=n,this._xPosition="right",this}start(n=""){return this._xOffset=n,this._xPosition="start",this}end(n=""){return this._xOffset=n,this._xPosition="end",this}width(n=""){return this._overlayRef?this._overlayRef.updateSize({width:n}):this._width=n,this}height(n=""){return this._overlayRef?this._overlayRef.updateSize({height:n}):this._height=n,this}centerHorizontally(n=""){return this.left(n),this._xPosition="center",this}centerVertically(n=""){return this.top(n),this._alignItems="center",this}apply(){if(!this._overlayRef||!this._overlayRef.hasAttached())return;let n=this._overlayRef.overlayElement.style,t=this._overlayRef.hostElement.style,r=this._overlayRef.getConfig(),{width:o,height:i,maxWidth:s,maxHeight:a}=r,c=(o==="100%"||o==="100vw")&&(!s||s==="100%"||s==="100vw"),l=(i==="100%"||i==="100vh")&&(!a||a==="100%"||a==="100vh"),u=this._xPosition,d=this._xOffset,p=this._overlayRef.getConfig().direction==="rtl",h="",m="",b="";c?b="flex-start":u==="center"?(b="center",p?m=d:h=d):p?u==="left"||u==="end"?(b="flex-end",h=d):(u==="right"||u==="start")&&(b="flex-start",m=d):u==="left"||u==="start"?(b="flex-start",h=d):(u==="right"||u==="end")&&(b="flex-end",m=d),n.position=this._cssPosition,n.marginLeft=c?"0":h,n.marginTop=l?"0":this._topOffset,n.marginBottom=this._bottomOffset,n.marginRight=c?"0":m,t.justifyContent=b,t.alignItems=l?"flex-start":this._alignItems}dispose(){if(this._isDisposed||!this._overlayRef)return;let n=this._overlayRef.overlayElement.style,t=this._overlayRef.hostElement,r=t.style;t.classList.remove(HD),r.justifyContent=r.alignItems=n.marginTop=n.marginBottom=n.marginLeft=n.marginRight=n.position="",this._overlayRef=null,this._isDisposed=!0}},KD=(()=>{class e{_injector=f(j);constructor(){}global(){return ZD()}flexibleConnectedTo(t){return gp(this._injector,t)}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})(),yp=new y("OVERLAY_DEFAULT_CONFIG");function vp(e,n){e.get(lt).load(YD);let t=e.get(pp),r=e.get(F),o=e.get(Zi),i=e.get(Qe),s=e.get(wo),a=e.get(Be,null,{optional:!0})||e.get(be).createRenderer(null,null),c=new ns(n),l=e.get(yp,null,{optional:!0})?.usePopover??!0;c.direction=c.direction||s.value,"showPopover"in r.body?c.usePopover=n?.usePopover??l:c.usePopover=!1;let u=r.createElement("div"),d=r.createElement("div");u.id=o.getId("cdk-overlay-"),u.classList.add("cdk-overlay-pane"),d.appendChild(u),c.usePopover&&(d.setAttribute("popover","manual"),d.classList.add("cdk-overlay-popover"));let p=c.usePopover?c.positionStrategy?.getPopoverInsertionPoint?.():null;return mp(p)?p.after(d):p?.type==="parent"?p.element.appendChild(d):t.getContainerElement().appendChild(d),new il(new el(u,i,e),d,u,c,e.get(P),e.get(WD),r,e.get(bc),e.get(qD),n?.disableAnimations??e.get(Ei,null,{optional:!0})==="NoopAnimations",e.get(ce),a)}var XD=(()=>{class e{scrollStrategies=f(zD);_positionBuilder=f(KD);_injector=f(j);constructor(){}create(t){return vp(this._injector,t)}position(){return this._positionBuilder}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})(),KA=[{originX:"start",originY:"bottom",overlayX:"start",overlayY:"top"},{originX:"start",originY:"top",overlayX:"start",overlayY:"bottom"},{originX:"end",originY:"top",overlayX:"end",overlayY:"bottom"},{originX:"end",originY:"bottom",overlayX:"end",overlayY:"top"}],XA=new y("cdk-connected-overlay-scroll-strategy",{providedIn:"root",factory:()=>{let e=f(j);return()=>hp(e)}}),fp=(()=>{class e{elementRef=f(z);constructor(){}static \u0275fac=function(r){return new(r||e)};static \u0275dir=O({type:e,selectors:[["","cdk-overlay-origin",""],["","overlay-origin",""],["","cdkOverlayOrigin",""]],exportAs:["cdkOverlayOrigin"]})}return e})(),QD=new y("cdk-connected-overlay-default-config"),QA=(()=>{class e{_dir=f(wo,{optional:!0});_injector=f(j);_overlayRef;_templatePortal;_backdropSubscription=B.EMPTY;_attachSubscription=B.EMPTY;_detachSubscription=B.EMPTY;_positionSubscription=B.EMPTY;_offsetX;_offsetY;_position;_scrollStrategyFactory=f(XA);_ngZone=f(P);origin;positions;positionStrategy;get offsetX(){return this._offsetX}set offsetX(t){this._offsetX=t,this._position&&this._updatePositionStrategy(this._position)}get offsetY(){return this._offsetY}set offsetY(t){this._offsetY=t,this._position&&this._updatePositionStrategy(this._position)}width;height;minWidth;minHeight;backdropClass;panelClass;viewportMargin=0;scrollStrategy;open=!1;disableClose=!1;transformOriginSelector;hasBackdrop=!1;lockPosition=!1;flexibleDimensions=!1;growAfterOpen=!1;push=!1;disposeOnNavigation=!1;usePopover;matchWidth=!1;set _config(t){typeof t!="string"&&this._assignConfig(t)}backdropClick=new H;positionChange=new H;attach=new H;detach=new H;overlayKeydown=new H;overlayOutsideClick=new H;constructor(){let t=f(Ze),r=f(He),o=f(QD,{optional:!0}),i=f(yp,{optional:!0});this.usePopover=i?.usePopover===!1?null:"global",this._templatePortal=new Io(t,r),this.scrollStrategy=this._scrollStrategyFactory(),o&&this._assignConfig(o)}get overlayRef(){return this._overlayRef}get dir(){return this._dir?this._dir.value:"ltr"}ngOnDestroy(){this._attachSubscription.unsubscribe(),this._detachSubscription.unsubscribe(),this._backdropSubscription.unsubscribe(),this._positionSubscription.unsubscribe(),this._overlayRef?.dispose()}ngOnChanges(t){this._position&&(this._updatePositionStrategy(this._position),this._overlayRef?.updateSize({width:this._getWidth(),minWidth:this.minWidth,height:this.height,minHeight:this.minHeight}),t.origin&&this.open&&this._position.apply()),t.open&&(this.open?this.attachOverlay():this.detachOverlay())}_createOverlay(){(!this.positions||!this.positions.length)&&(this.positions=KA);let t=this._overlayRef=vp(this._injector,this._buildConfig());this._attachSubscription=t.attachments().subscribe(()=>this.attach.emit()),this._detachSubscription=t.detachments().subscribe(()=>this.detach.emit()),t.keydownEvents().subscribe(r=>{this.overlayKeydown.next(r),r.keyCode===27&&!this.disableClose&&!qc(r)&&(r.preventDefault(),this.detachOverlay())}),this._overlayRef.outsidePointerEvents().subscribe(r=>{let o=this._getOriginElement(),i=Re(r);(!o||o!==i&&!o.contains(i))&&this.overlayOutsideClick.next(r)})}_buildConfig(){let t=this._position=this.positionStrategy||this._createPositionStrategy(),r=new ns({direction:this._dir||"ltr",positionStrategy:t,scrollStrategy:this.scrollStrategy,hasBackdrop:this.hasBackdrop,disposeOnNavigation:this.disposeOnNavigation,usePopover:!!this.usePopover});return(this.height||this.height===0)&&(r.height=this.height),(this.minWidth||this.minWidth===0)&&(r.minWidth=this.minWidth),(this.minHeight||this.minHeight===0)&&(r.minHeight=this.minHeight),this.backdropClass&&(r.backdropClass=this.backdropClass),this.panelClass&&(r.panelClass=this.panelClass),r}_updatePositionStrategy(t){let r=this.positions.map(o=>({originX:o.originX,originY:o.originY,overlayX:o.overlayX,overlayY:o.overlayY,offsetX:o.offsetX||this.offsetX,offsetY:o.offsetY||this.offsetY,panelClass:o.panelClass||void 0}));return t.setOrigin(this._getOrigin()).withPositions(r).withFlexibleDimensions(this.flexibleDimensions).withPush(this.push).withGrowAfterOpen(this.growAfterOpen).withViewportMargin(this.viewportMargin).withLockedPosition(this.lockPosition).withTransformOriginOn(this.transformOriginSelector).withPopoverLocation(this.usePopover===null?"global":this.usePopover)}_createPositionStrategy(){let t=gp(this._injector,this._getOrigin());return this._updatePositionStrategy(t),t}_getOrigin(){return this.origin instanceof fp?this.origin.elementRef:this.origin}_getOriginElement(){return this.origin instanceof fp?this.origin.elementRef.nativeElement:this.origin instanceof z?this.origin.nativeElement:typeof Element<"u"&&this.origin instanceof Element?this.origin:null}_getWidth(){return this.width?this.width:this.matchWidth?this._getOriginElement()?.getBoundingClientRect?.().width:void 0}attachOverlay(){this._overlayRef||this._createOverlay();let t=this._overlayRef;t.getConfig().hasBackdrop=this.hasBackdrop,t.updateSize({width:this._getWidth()}),t.hasAttached()||t.attach(this._templatePortal),this.hasBackdrop?this._backdropSubscription=t.backdropClick().subscribe(r=>this.backdropClick.emit(r)):this._backdropSubscription.unsubscribe(),this._positionSubscription.unsubscribe(),this.positionChange.observers.length>0&&(this._positionSubscription=this._position.positionChanges.pipe(zl(()=>this.positionChange.observers.length>0)).subscribe(r=>{this._ngZone.run(()=>this.positionChange.emit(r)),this.positionChange.observers.length===0&&this._positionSubscription.unsubscribe()})),this.open=!0}detachOverlay(){this._overlayRef?.detach(),this._backdropSubscription.unsubscribe(),this._positionSubscription.unsubscribe(),this.open=!1}_assignConfig(t){this.origin=t.origin??this.origin,this.positions=t.positions??this.positions,this.positionStrategy=t.positionStrategy??this.positionStrategy,this.offsetX=t.offsetX??this.offsetX,this.offsetY=t.offsetY??this.offsetY,this.width=t.width??this.width,this.height=t.height??this.height,this.minWidth=t.minWidth??this.minWidth,this.minHeight=t.minHeight??this.minHeight,this.backdropClass=t.backdropClass??this.backdropClass,this.panelClass=t.panelClass??this.panelClass,this.viewportMargin=t.viewportMargin??this.viewportMargin,this.scrollStrategy=t.scrollStrategy??this.scrollStrategy,this.disableClose=t.disableClose??this.disableClose,this.transformOriginSelector=t.transformOriginSelector??this.transformOriginSelector,this.hasBackdrop=t.hasBackdrop??this.hasBackdrop,this.lockPosition=t.lockPosition??this.lockPosition,this.flexibleDimensions=t.flexibleDimensions??this.flexibleDimensions,this.growAfterOpen=t.growAfterOpen??this.growAfterOpen,this.push=t.push??this.push,this.disposeOnNavigation=t.disposeOnNavigation??this.disposeOnNavigation,this.usePopover=t.usePopover??this.usePopover,this.matchWidth=t.matchWidth??this.matchWidth}static \u0275fac=function(r){return new(r||e)};static \u0275dir=O({type:e,selectors:[["","cdk-connected-overlay",""],["","connected-overlay",""],["","cdkConnectedOverlay",""]],inputs:{origin:[0,"cdkConnectedOverlayOrigin","origin"],positions:[0,"cdkConnectedOverlayPositions","positions"],positionStrategy:[0,"cdkConnectedOverlayPositionStrategy","positionStrategy"],offsetX:[0,"cdkConnectedOverlayOffsetX","offsetX"],offsetY:[0,"cdkConnectedOverlayOffsetY","offsetY"],width:[0,"cdkConnectedOverlayWidth","width"],height:[0,"cdkConnectedOverlayHeight","height"],minWidth:[0,"cdkConnectedOverlayMinWidth","minWidth"],minHeight:[0,"cdkConnectedOverlayMinHeight","minHeight"],backdropClass:[0,"cdkConnectedOverlayBackdropClass","backdropClass"],panelClass:[0,"cdkConnectedOverlayPanelClass","panelClass"],viewportMargin:[0,"cdkConnectedOverlayViewportMargin","viewportMargin"],scrollStrategy:[0,"cdkConnectedOverlayScrollStrategy","scrollStrategy"],open:[0,"cdkConnectedOverlayOpen","open"],disableClose:[0,"cdkConnectedOverlayDisableClose","disableClose"],transformOriginSelector:[0,"cdkConnectedOverlayTransformOriginOn","transformOriginSelector"],hasBackdrop:[2,"cdkConnectedOverlayHasBackdrop","hasBackdrop",de],lockPosition:[2,"cdkConnectedOverlayLockPosition","lockPosition",de],flexibleDimensions:[2,"cdkConnectedOverlayFlexibleDimensions","flexibleDimensions",de],growAfterOpen:[2,"cdkConnectedOverlayGrowAfterOpen","growAfterOpen",de],push:[2,"cdkConnectedOverlayPush","push",de],disposeOnNavigation:[2,"cdkConnectedOverlayDisposeOnNavigation","disposeOnNavigation",de],usePopover:[0,"cdkConnectedOverlayUsePopover","usePopover"],matchWidth:[2,"cdkConnectedOverlayMatchWidth","matchWidth",de],_config:[0,"cdkConnectedOverlay","_config"]},outputs:{backdropClick:"backdropClick",positionChange:"positionChange",attach:"attach",detach:"detach",overlayKeydown:"overlayKeydown",overlayOutsideClick:"overlayOutsideClick"},exportAs:["cdkConnectedOverlay"],features:[Ke]})}return e})(),JA=(()=>{class e{static \u0275fac=function(r){return new(r||e)};static \u0275mod=Z({type:e});static \u0275inj=G({providers:[XD],imports:[ln,kD,ap,ap]})}return e})();var aE=(()=>{class e{_renderer;_elementRef;onChange=t=>{};onTouched=()=>{};constructor(t,r){this._renderer=t,this._elementRef=r}setProperty(t,r){this._renderer.setProperty(this._elementRef.nativeElement,t,r)}registerOnTouched(t){this.onTouched=t}registerOnChange(t){this.onChange=t}setDisabledState(t){this.setProperty("disabled",t)}static \u0275fac=function(r){return new(r||e)(w(Be),w(z))};static \u0275dir=O({type:e})}return e})(),cE=(()=>{class e extends aE{static \u0275fac=(()=>{let t;return function(o){return(t||(t=Ve(e)))(o||e)}})();static \u0275dir=O({type:e,features:[X]})}return e})(),us=new y("");var eN={provide:us,useExisting:ve(()=>lE),multi:!0};function tN(){let e=et()?et().getUserAgent():"";return/android (\d+)/.test(e.toLowerCase())}var nN=new y(""),lE=(()=>{class e extends aE{_compositionMode;_composing=!1;constructor(t,r,o){super(t,r),this._compositionMode=o,this._compositionMode==null&&(this._compositionMode=!tN())}writeValue(t){let r=t??"";this.setProperty("value",r)}_handleInput(t){(!this._compositionMode||this._compositionMode&&!this._composing)&&this.onChange(t)}_compositionStart(){this._composing=!0}_compositionEnd(t){this._composing=!1,this._compositionMode&&this.onChange(t)}static \u0275fac=function(r){return new(r||e)(w(Be),w(z),w(nN,8))};static \u0275dir=O({type:e,selectors:[["input","formControlName","",3,"type","checkbox"],["textarea","formControlName",""],["input","formControl","",3,"type","checkbox"],["textarea","formControl",""],["input","ngModel","",3,"type","checkbox"],["textarea","ngModel",""],["","ngDefaultControl",""]],hostBindings:function(r,o){r&1&&yr("input",function(s){return o._handleInput(s.target.value)})("blur",function(){return o.onTouched()})("compositionstart",function(){return o._compositionStart()})("compositionend",function(s){return o._compositionEnd(s.target.value)})},standalone:!1,features:[Te([eN]),X]})}return e})();function Ep(e){return e==null||wp(e)===0}function wp(e){return e==null?null:Array.isArray(e)||typeof e=="string"?e.length:e instanceof Set?e.size:null}var $t=new y(""),Mr=new y(""),rN=/^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,JD=class{static min(n){return uE(n)}static max(n){return dE(n)}static required(n){return fE(n)}static requiredTrue(n){return oN(n)}static email(n){return iN(n)}static minLength(n){return sN(n)}static maxLength(n){return aN(n)}static pattern(n){return cN(n)}static nullValidator(n){return ll()}static compose(n){return vE(n)}static composeAsync(n){return bE(n)}};function uE(e){return n=>{if(n.value==null||e==null)return null;let t=parseFloat(n.value);return!isNaN(t)&&t{if(n.value==null||e==null)return null;let t=parseFloat(n.value);return!isNaN(t)&&t>e?{max:{max:e,actual:n.value}}:null}}function fE(e){return Ep(e.value)?{required:!0}:null}function oN(e){return e.value===!0?null:{required:!0}}function iN(e){return Ep(e.value)||rN.test(e.value)?null:{email:!0}}function sN(e){return n=>{let t=n.value?.length??wp(n.value);return t===null||t===0?null:t{let t=n.value?.length??wp(n.value);return t!==null&&t>e?{maxlength:{requiredLength:e,actualLength:t}}:null}}function cN(e){if(!e)return ll;let n,t;return typeof e=="string"?(t="",e.charAt(0)!=="^"&&(t+="^"),t+=e,e.charAt(e.length-1)!=="$"&&(t+="$"),n=new RegExp(t)):(t=e.toString(),n=e),r=>{if(Ep(r.value))return null;let o=r.value;return n.test(o)?null:{pattern:{requiredPattern:t,actualValue:o}}}}function ll(e){return null}function hE(e){return e!=null}function pE(e){return gr(e)?tt(e):e}function mE(e){let n={};return e.forEach(t=>{n=t!=null?E(E({},n),t):n}),Object.keys(n).length===0?null:n}function gE(e,n){return n.map(t=>t(e))}function lN(e){return!e.validate}function yE(e){return e.map(n=>lN(n)?n:t=>n.validate(t))}function vE(e){if(!e)return null;let n=e.filter(hE);return n.length==0?null:function(t){return mE(gE(t,n))}}function Cp(e){return e!=null?vE(yE(e)):null}function bE(e){if(!e)return null;let n=e.filter(hE);return n.length==0?null:function(t){let r=gE(t,n).map(pE);return Pl(r).pipe(re(mE))}}function Ip(e){return e!=null?bE(yE(e)):null}function eE(e,n){return e===null?[n]:Array.isArray(e)?[...e,n]:[e,n]}function _E(e){return e._rawValidators}function DE(e){return e._rawAsyncValidators}function bp(e){return e?Array.isArray(e)?e:[e]:[]}function ul(e,n){return Array.isArray(e)?e.includes(n):e===n}function tE(e,n){let t=bp(n);return bp(e).forEach(o=>{ul(t,o)||t.push(o)}),t}function nE(e,n){return bp(n).filter(t=>!ul(e,t))}var dl=class{get value(){return this.control?this.control.value:null}get valid(){return this.control?this.control.valid:null}get invalid(){return this.control?this.control.invalid:null}get pending(){return this.control?this.control.pending:null}get disabled(){return this.control?this.control.disabled:null}get enabled(){return this.control?this.control.enabled:null}get errors(){return this.control?this.control.errors:null}get pristine(){return this.control?this.control.pristine:null}get dirty(){return this.control?this.control.dirty:null}get touched(){return this.control?this.control.touched:null}get status(){return this.control?this.control.status:null}get untouched(){return this.control?this.control.untouched:null}get statusChanges(){return this.control?this.control.statusChanges:null}get valueChanges(){return this.control?this.control.valueChanges:null}get path(){return null}_composedValidatorFn;_composedAsyncValidatorFn;_rawValidators=[];_rawAsyncValidators=[];_setValidators(n){this._rawValidators=n||[],this._composedValidatorFn=Cp(this._rawValidators)}_setAsyncValidators(n){this._rawAsyncValidators=n||[],this._composedAsyncValidatorFn=Ip(this._rawAsyncValidators)}get validator(){return this._composedValidatorFn||null}get asyncValidator(){return this._composedAsyncValidatorFn||null}_onDestroyCallbacks=[];_registerOnDestroy(n){this._onDestroyCallbacks.push(n)}_invokeOnDestroyCallbacks(){this._onDestroyCallbacks.forEach(n=>n()),this._onDestroyCallbacks=[]}reset(n=void 0){this.control?.reset(n)}hasError(n,t){return this.control?this.control.hasError(n,t):!1}getError(n,t){return this.control?this.control.getError(n,t):null}},Oe=class extends dl{name;get formDirective(){return null}get path(){return null}},un=class extends dl{_parent=null;name=null;valueAccessor=null},fl=class{_cd;constructor(n){this._cd=n}get isTouched(){return this._cd?.control?._touched?.(),!!this._cd?.control?.touched}get isUntouched(){return!!this._cd?.control?.untouched}get isPristine(){return this._cd?.control?._pristine?.(),!!this._cd?.control?.pristine}get isDirty(){return!!this._cd?.control?.dirty}get isValid(){return this._cd?.control?._status?.(),!!this._cd?.control?.valid}get isInvalid(){return!!this._cd?.control?.invalid}get isPending(){return!!this._cd?.control?.pending}get isSubmitted(){return this._cd?._submitted?.(),!!this._cd?.submitted}};var f9=(()=>{class e extends fl{constructor(t){super(t)}static \u0275fac=function(r){return new(r||e)(w(un,2))};static \u0275dir=O({type:e,selectors:[["","formControlName",""],["","ngModel",""],["","formControl",""]],hostVars:14,hostBindings:function(r,o){r&2&&Ue("ng-untouched",o.isUntouched)("ng-touched",o.isTouched)("ng-pristine",o.isPristine)("ng-dirty",o.isDirty)("ng-valid",o.isValid)("ng-invalid",o.isInvalid)("ng-pending",o.isPending)},standalone:!1,features:[X]})}return e})(),h9=(()=>{class e extends fl{constructor(t){super(t)}static \u0275fac=function(r){return new(r||e)(w(Oe,10))};static \u0275dir=O({type:e,selectors:[["","formGroupName",""],["","formArrayName",""],["","ngModelGroup",""],["","formGroup",""],["","formArray",""],["form",3,"ngNoForm",""],["","ngForm",""]],hostVars:16,hostBindings:function(r,o){r&2&&Ue("ng-untouched",o.isUntouched)("ng-touched",o.isTouched)("ng-pristine",o.isPristine)("ng-dirty",o.isDirty)("ng-valid",o.isValid)("ng-invalid",o.isInvalid)("ng-pending",o.isPending)("ng-submitted",o.isSubmitted)},standalone:!1,features:[X]})}return e})();var rs="VALID",cl="INVALID",Mo="PENDING",os="DISABLED",Pn=class{},hl=class extends Pn{value;source;constructor(n,t){super(),this.value=n,this.source=t}},ss=class extends Pn{pristine;source;constructor(n,t){super(),this.pristine=n,this.source=t}},as=class extends Pn{touched;source;constructor(n,t){super(),this.touched=n,this.source=t}},So=class extends Pn{status;source;constructor(n,t){super(),this.status=n,this.source=t}},pl=class extends Pn{source;constructor(n){super(),this.source=n}},cs=class extends Pn{source;constructor(n){super(),this.source=n}};function Mp(e){return(vl(e)?e.validators:e)||null}function uN(e){return Array.isArray(e)?Cp(e):e||null}function Sp(e,n){return(vl(n)?n.asyncValidators:e)||null}function dN(e){return Array.isArray(e)?Ip(e):e||null}function vl(e){return e!=null&&!Array.isArray(e)&&typeof e=="object"}function EE(e,n,t){let r=e.controls;if(!(n?Object.keys(r):r).length)throw new v(1e3,"");if(!r[t])throw new v(1001,"")}function wE(e,n,t){e._forEachChild((r,o)=>{if(t[o]===void 0)throw new v(1002,"")})}var xo=class{_pendingDirty=!1;_hasOwnPendingAsyncValidator=null;_pendingTouched=!1;_onCollectionChange=()=>{};_updateOn;_parent=null;_asyncValidationSubscription;_composedValidatorFn;_composedAsyncValidatorFn;_rawValidators;_rawAsyncValidators;value;constructor(n,t){this._assignValidators(n),this._assignAsyncValidators(t)}get validator(){return this._composedValidatorFn}set validator(n){this._rawValidators=this._composedValidatorFn=n}get asyncValidator(){return this._composedAsyncValidatorFn}set asyncValidator(n){this._rawAsyncValidators=this._composedAsyncValidatorFn=n}get parent(){return this._parent}get status(){return $e(this.statusReactive)}set status(n){$e(()=>this.statusReactive.set(n))}_status=vr(()=>this.statusReactive());statusReactive=Me(void 0);get valid(){return this.status===rs}get invalid(){return this.status===cl}get pending(){return this.status==Mo}get disabled(){return this.status===os}get enabled(){return this.status!==os}errors;get pristine(){return $e(this.pristineReactive)}set pristine(n){$e(()=>this.pristineReactive.set(n))}_pristine=vr(()=>this.pristineReactive());pristineReactive=Me(!0);get dirty(){return!this.pristine}get touched(){return $e(this.touchedReactive)}set touched(n){$e(()=>this.touchedReactive.set(n))}_touched=vr(()=>this.touchedReactive());touchedReactive=Me(!1);get untouched(){return!this.touched}_events=new R;events=this._events.asObservable();valueChanges;statusChanges;get updateOn(){return this._updateOn?this._updateOn:this.parent?this.parent.updateOn:"change"}setValidators(n){this._assignValidators(n)}setAsyncValidators(n){this._assignAsyncValidators(n)}addValidators(n){this.setValidators(tE(n,this._rawValidators))}addAsyncValidators(n){this.setAsyncValidators(tE(n,this._rawAsyncValidators))}removeValidators(n){this.setValidators(nE(n,this._rawValidators))}removeAsyncValidators(n){this.setAsyncValidators(nE(n,this._rawAsyncValidators))}hasValidator(n){return ul(this._rawValidators,n)}hasAsyncValidator(n){return ul(this._rawAsyncValidators,n)}clearValidators(){this.validator=null}clearAsyncValidators(){this.asyncValidator=null}markAsTouched(n={}){let t=this.touched===!1;this.touched=!0;let r=n.sourceControl??this;n.onlySelf||this._parent?.markAsTouched(V(E({},n),{sourceControl:r})),t&&n.emitEvent!==!1&&this._events.next(new as(!0,r))}markAllAsDirty(n={}){this.markAsDirty({onlySelf:!0,emitEvent:n.emitEvent,sourceControl:this}),this._forEachChild(t=>t.markAllAsDirty(n))}markAllAsTouched(n={}){this.markAsTouched({onlySelf:!0,emitEvent:n.emitEvent,sourceControl:this}),this._forEachChild(t=>t.markAllAsTouched(n))}markAsUntouched(n={}){let t=this.touched===!0;this.touched=!1,this._pendingTouched=!1;let r=n.sourceControl??this;this._forEachChild(o=>{o.markAsUntouched({onlySelf:!0,emitEvent:n.emitEvent,sourceControl:r})}),n.onlySelf||this._parent?._updateTouched(n,r),t&&n.emitEvent!==!1&&this._events.next(new as(!1,r))}markAsDirty(n={}){let t=this.pristine===!0;this.pristine=!1;let r=n.sourceControl??this;n.onlySelf||this._parent?.markAsDirty(V(E({},n),{sourceControl:r})),t&&n.emitEvent!==!1&&this._events.next(new ss(!1,r))}markAsPristine(n={}){let t=this.pristine===!1;this.pristine=!0,this._pendingDirty=!1;let r=n.sourceControl??this;this._forEachChild(o=>{o.markAsPristine({onlySelf:!0,emitEvent:n.emitEvent})}),n.onlySelf||this._parent?._updatePristine(n,r),t&&n.emitEvent!==!1&&this._events.next(new ss(!0,r))}markAsPending(n={}){this.status=Mo;let t=n.sourceControl??this;n.emitEvent!==!1&&(this._events.next(new So(this.status,t)),this.statusChanges.emit(this.status)),n.onlySelf||this._parent?.markAsPending(V(E({},n),{sourceControl:t}))}disable(n={}){let t=this._parentMarkedDirty(n.onlySelf);this.status=os,this.errors=null,this._forEachChild(o=>{o.disable(V(E({},n),{onlySelf:!0}))}),this._updateValue();let r=n.sourceControl??this;n.emitEvent!==!1&&(this._events.next(new hl(this.value,r)),this._events.next(new So(this.status,r)),this.valueChanges.emit(this.value),this.statusChanges.emit(this.status)),this._updateAncestors(V(E({},n),{skipPristineCheck:t}),this),this._onDisabledChange.forEach(o=>o(!0))}enable(n={}){let t=this._parentMarkedDirty(n.onlySelf);this.status=rs,this._forEachChild(r=>{r.enable(V(E({},n),{onlySelf:!0}))}),this.updateValueAndValidity({onlySelf:!0,emitEvent:n.emitEvent}),this._updateAncestors(V(E({},n),{skipPristineCheck:t}),this),this._onDisabledChange.forEach(r=>r(!1))}_updateAncestors(n,t){n.onlySelf||(this._parent?.updateValueAndValidity(n),n.skipPristineCheck||this._parent?._updatePristine({},t),this._parent?._updateTouched({},t))}setParent(n){this._parent=n}getRawValue(){return this.value}updateValueAndValidity(n={}){if(this._setInitialStatus(),this._updateValue(),this.enabled){let r=this._cancelExistingSubscription();this.errors=this._runValidator(),this.status=this._calculateStatus(),(this.status===rs||this.status===Mo)&&this._runAsyncValidator(r,n.emitEvent)}let t=n.sourceControl??this;n.emitEvent!==!1&&(this._events.next(new hl(this.value,t)),this._events.next(new So(this.status,t)),this.valueChanges.emit(this.value),this.statusChanges.emit(this.status)),n.onlySelf||this._parent?.updateValueAndValidity(V(E({},n),{sourceControl:t}))}_updateTreeValidity(n={emitEvent:!0}){this._forEachChild(t=>t._updateTreeValidity(n)),this.updateValueAndValidity({onlySelf:!0,emitEvent:n.emitEvent})}_setInitialStatus(){this.status=this._allControlsDisabled()?os:rs}_runValidator(){return this.validator?this.validator(this):null}_runAsyncValidator(n,t){if(this.asyncValidator){this.status=Mo,this._hasOwnPendingAsyncValidator={emitEvent:t!==!1,shouldHaveEmitted:n!==!1};let r=pE(this.asyncValidator(this));this._asyncValidationSubscription=r.subscribe(o=>{this._hasOwnPendingAsyncValidator=null,this.setErrors(o,{emitEvent:t,shouldHaveEmitted:n})})}}_cancelExistingSubscription(){if(this._asyncValidationSubscription){this._asyncValidationSubscription.unsubscribe();let n=(this._hasOwnPendingAsyncValidator?.emitEvent||this._hasOwnPendingAsyncValidator?.shouldHaveEmitted)??!1;return this._hasOwnPendingAsyncValidator=null,n}return!1}setErrors(n,t={}){this.errors=n,this._updateControlsErrors(t.emitEvent!==!1,this,t.shouldHaveEmitted)}get(n){let t=n;return t==null||(Array.isArray(t)||(t=t.split(".")),t.length===0)?null:t.reduce((r,o)=>r&&r._find(o),this)}getError(n,t){let r=t?this.get(t):this;return r?.errors?r.errors[n]:null}hasError(n,t){return!!this.getError(n,t)}get root(){let n=this;for(;n._parent;)n=n._parent;return n}_updateControlsErrors(n,t,r){this.status=this._calculateStatus(),n&&this.statusChanges.emit(this.status),(n||r)&&this._events.next(new So(this.status,t)),this._parent&&this._parent._updateControlsErrors(n,t,r)}_initObservables(){this.valueChanges=new H,this.statusChanges=new H}_calculateStatus(){return this._allControlsDisabled()?os:this.errors?cl:this._hasOwnPendingAsyncValidator||this._anyControlsHaveStatus(Mo)?Mo:this._anyControlsHaveStatus(cl)?cl:rs}_anyControlsHaveStatus(n){return this._anyControls(t=>t.status===n)}_anyControlsDirty(){return this._anyControls(n=>n.dirty)}_anyControlsTouched(){return this._anyControls(n=>n.touched)}_updatePristine(n,t){let r=!this._anyControlsDirty(),o=this.pristine!==r;this.pristine=r,n.onlySelf||this._parent?._updatePristine(n,t),o&&this._events.next(new ss(this.pristine,t))}_updateTouched(n={},t){this.touched=this._anyControlsTouched(),this._events.next(new as(this.touched,t)),n.onlySelf||this._parent?._updateTouched(n,t)}_onDisabledChange=[];_registerOnCollectionChange(n){this._onCollectionChange=n}_setUpdateStrategy(n){vl(n)&&n.updateOn!=null&&(this._updateOn=n.updateOn)}_parentMarkedDirty(n){return!n&&!!this._parent?.dirty&&!this._parent._anyControlsDirty()}_find(n){return null}_assignValidators(n){this._rawValidators=Array.isArray(n)?n.slice():n,this._composedValidatorFn=uN(this._rawValidators)}_assignAsyncValidators(n){this._rawAsyncValidators=Array.isArray(n)?n.slice():n,this._composedAsyncValidatorFn=dN(this._rawAsyncValidators)}},Ir=class extends xo{constructor(n,t,r){super(Mp(t),Sp(r,t)),this.controls=n,this._initObservables(),this._setUpdateStrategy(t),this._setUpControls(),this.updateValueAndValidity({onlySelf:!0,emitEvent:!!this.asyncValidator})}controls;registerControl(n,t){return this.controls[n]?this.controls[n]:(this.controls[n]=t,t.setParent(this),t._registerOnCollectionChange(this._onCollectionChange),t)}addControl(n,t,r={}){this.registerControl(n,t),this.updateValueAndValidity({emitEvent:r.emitEvent}),this._onCollectionChange()}removeControl(n,t={}){this.controls[n]&&this.controls[n]._registerOnCollectionChange(()=>{}),delete this.controls[n],this.updateValueAndValidity({emitEvent:t.emitEvent}),this._onCollectionChange()}setControl(n,t,r={}){this.controls[n]&&this.controls[n]._registerOnCollectionChange(()=>{}),delete this.controls[n],t&&this.registerControl(n,t),this.updateValueAndValidity({emitEvent:r.emitEvent}),this._onCollectionChange()}contains(n){return this.controls.hasOwnProperty(n)&&this.controls[n].enabled}setValue(n,t={}){wE(this,!0,n),Object.keys(n).forEach(r=>{EE(this,!0,r),this.controls[r].setValue(n[r],{onlySelf:!0,emitEvent:t.emitEvent})}),this.updateValueAndValidity(t)}patchValue(n,t={}){n!=null&&(Object.keys(n).forEach(r=>{let o=this.controls[r];o&&o.patchValue(n[r],{onlySelf:!0,emitEvent:t.emitEvent})}),this.updateValueAndValidity(t))}reset(n={},t={}){this._forEachChild((r,o)=>{r.reset(n?n[o]:null,V(E({},t),{onlySelf:!0}))}),this._updatePristine(t,this),this._updateTouched(t,this),this.updateValueAndValidity(t),t?.emitEvent!==!1&&this._events.next(new cs(this))}getRawValue(){return this._reduceChildren({},(n,t,r)=>(n[r]=t.getRawValue(),n))}_syncPendingControls(){let n=this._reduceChildren(!1,(t,r)=>r._syncPendingControls()?!0:t);return n&&this.updateValueAndValidity({onlySelf:!0}),n}_forEachChild(n){Object.keys(this.controls).forEach(t=>{let r=this.controls[t];r&&n(r,t)})}_setUpControls(){this._forEachChild(n=>{n.setParent(this),n._registerOnCollectionChange(this._onCollectionChange)})}_updateValue(){this.value=this._reduceValue()}_anyControls(n){for(let[t,r]of Object.entries(this.controls))if(this.contains(t)&&n(r))return!0;return!1}_reduceValue(){let n={};return this._reduceChildren(n,(t,r,o)=>((r.enabled||this.disabled)&&(t[o]=r.value),t))}_reduceChildren(n,t){let r=n;return this._forEachChild((o,i)=>{r=t(r,o,i)}),r}_allControlsDisabled(){for(let n of Object.keys(this.controls))if(this.controls[n].enabled)return!1;return Object.keys(this.controls).length>0||this.disabled}_find(n){return this.controls.hasOwnProperty(n)?this.controls[n]:null}};var p9=Ir;var _p=class extends Ir{};var Ao=new y("",{factory:()=>bl}),bl="always";function _l(e,n){return[...n.path,e]}function ls(e,n,t=bl){Tp(e,n),n.valueAccessor.writeValue(e.value),(e.disabled||t==="always")&&n.valueAccessor.setDisabledState?.(e.disabled),hN(e,n),mN(e,n),pN(e,n),fN(e,n)}function ml(e,n,t=!0){let r=()=>{};n?.valueAccessor?.registerOnChange(r),n?.valueAccessor?.registerOnTouched(r),yl(e,n),e&&(n._invokeOnDestroyCallbacks(),e._registerOnCollectionChange(()=>{}))}function gl(e,n){e.forEach(t=>{t.registerOnValidatorChange&&t.registerOnValidatorChange(n)})}function fN(e,n){if(n.valueAccessor.setDisabledState){let t=r=>{n.valueAccessor.setDisabledState(r)};e.registerOnDisabledChange(t),n._registerOnDestroy(()=>{e._unregisterOnDisabledChange(t)})}}function Tp(e,n){let t=_E(e);n.validator!==null?e.setValidators(eE(t,n.validator)):typeof t=="function"&&e.setValidators([t]);let r=DE(e);n.asyncValidator!==null?e.setAsyncValidators(eE(r,n.asyncValidator)):typeof r=="function"&&e.setAsyncValidators([r]);let o=()=>e.updateValueAndValidity();gl(n._rawValidators,o),gl(n._rawAsyncValidators,o)}function yl(e,n){let t=!1;if(e!==null){if(n.validator!==null){let o=_E(e);if(Array.isArray(o)&&o.length>0){let i=o.filter(s=>s!==n.validator);i.length!==o.length&&(t=!0,e.setValidators(i))}}if(n.asyncValidator!==null){let o=DE(e);if(Array.isArray(o)&&o.length>0){let i=o.filter(s=>s!==n.asyncValidator);i.length!==o.length&&(t=!0,e.setAsyncValidators(i))}}}let r=()=>{};return gl(n._rawValidators,r),gl(n._rawAsyncValidators,r),t}function hN(e,n){n.valueAccessor.registerOnChange(t=>{e._pendingValue=t,e._pendingChange=!0,e._pendingDirty=!0,e.updateOn==="change"&&CE(e,n)})}function pN(e,n){n.valueAccessor.registerOnTouched(()=>{e._pendingTouched=!0,e.updateOn==="blur"&&e._pendingChange&&CE(e,n),e.updateOn!=="submit"&&e.markAsTouched()})}function CE(e,n){e._pendingDirty&&e.markAsDirty(),e.setValue(e._pendingValue,{emitModelToViewChange:!1}),n.viewToModelUpdate(e._pendingValue),e._pendingChange=!1}function mN(e,n){let t=(r,o)=>{n.valueAccessor.writeValue(r),o&&n.viewToModelUpdate(r)};e.registerOnChange(t),n._registerOnDestroy(()=>{e._unregisterOnChange(t)})}function IE(e,n){e==null,Tp(e,n)}function gN(e,n){return yl(e,n)}function xp(e,n){if(!e.hasOwnProperty("model"))return!1;let t=e.model;return t.isFirstChange()?!0:!Object.is(n,t.currentValue)}function yN(e){return Object.getPrototypeOf(e.constructor)===cE}function ME(e,n){e._syncPendingControls(),n.forEach(t=>{let r=t.control;r.updateOn==="submit"&&r._pendingChange&&(t.viewToModelUpdate(r._pendingValue),r._pendingChange=!1)})}function Ap(e,n){if(!n)return null;Array.isArray(n);let t,r,o;return n.forEach(i=>{i.constructor===lE?t=i:yN(i)?r=i:o=i}),o||r||t||null}function vN(e,n){let t=e.indexOf(n);t>-1&&e.splice(t,1)}var bN={provide:Oe,useExisting:ve(()=>_N)},is=Promise.resolve(),_N=(()=>{class e extends Oe{callSetDisabledState;get submitted(){return $e(this.submittedReactive)}_submitted=vr(()=>this.submittedReactive());submittedReactive=Me(!1);_directives=new Set;form;ngSubmit=new H;options;constructor(t,r,o){super(),this.callSetDisabledState=o,this.form=new Ir({},Cp(t),Ip(r))}ngAfterViewInit(){this._setUpdateStrategy()}get formDirective(){return this}get control(){return this.form}get path(){return[]}get controls(){return this.form.controls}addControl(t){is.then(()=>{let r=this._findContainer(t.path);t.control=r.registerControl(t.name,t.control),ls(t.control,t,this.callSetDisabledState),t.control.updateValueAndValidity({emitEvent:!1}),this._directives.add(t)})}getControl(t){return this.form.get(t.path)}removeControl(t){is.then(()=>{this._findContainer(t.path)?.removeControl(t.name),this._directives.delete(t)})}addFormGroup(t){is.then(()=>{let r=this._findContainer(t.path),o=new Ir({});IE(o,t),r.registerControl(t.name,o),o.updateValueAndValidity({emitEvent:!1})})}removeFormGroup(t){is.then(()=>{this._findContainer(t.path)?.removeControl?.(t.name)})}getFormGroup(t){return this.form.get(t.path)}updateModel(t,r){is.then(()=>{this.form.get(t.path).setValue(r)})}setValue(t){this.control.setValue(t)}onSubmit(t){return this.submittedReactive.set(!0),ME(this.form,this._directives),this.ngSubmit.emit(t),this.form._events.next(new pl(this.control)),t?.target?.method==="dialog"}onReset(){this.resetForm()}resetForm(t=void 0){this.form.reset(t),this.submittedReactive.set(!1)}_setUpdateStrategy(){this.options&&this.options.updateOn!=null&&(this.form._updateOn=this.options.updateOn)}_findContainer(t){return t.pop(),t.length?this.form.get(t):this.form}static \u0275fac=function(r){return new(r||e)(w($t,10),w(Mr,10),w(Ao,8))};static \u0275dir=O({type:e,selectors:[["form",3,"ngNoForm","",3,"formGroup","",3,"formArray",""],["ng-form"],["","ngForm",""]],hostBindings:function(r,o){r&1&&yr("submit",function(s){return o.onSubmit(s)})("reset",function(){return o.onReset()})},inputs:{options:[0,"ngFormOptions","options"]},outputs:{ngSubmit:"ngSubmit"},exportAs:["ngForm"],standalone:!1,features:[Te([bN]),X]})}return e})();function rE(e,n){let t=e.indexOf(n);t>-1&&e.splice(t,1)}function oE(e){return typeof e=="object"&&e!==null&&Object.keys(e).length===2&&"value"in e&&"disabled"in e}var To=class extends xo{defaultValue=null;_onChange=[];_pendingValue;_pendingChange=!1;constructor(n=null,t,r){super(Mp(t),Sp(r,t)),this._applyFormState(n),this._setUpdateStrategy(t),this._initObservables(),this.updateValueAndValidity({onlySelf:!0,emitEvent:!!this.asyncValidator}),vl(t)&&(t.nonNullable||t.initialValueIsDefault)&&(oE(n)?this.defaultValue=n.value:this.defaultValue=n)}setValue(n,t={}){this.value=this._pendingValue=n,this._onChange.length&&t.emitModelToViewChange!==!1&&this._onChange.forEach(r=>r(this.value,t.emitViewToModelChange!==!1)),this.updateValueAndValidity(t)}patchValue(n,t={}){this.setValue(n,t)}reset(n=this.defaultValue,t={}){this._applyFormState(n),this.markAsPristine(t),this.markAsUntouched(t),this.setValue(this.value,t),t.overwriteDefaultValue&&(this.defaultValue=this.value),this._pendingChange=!1,t?.emitEvent!==!1&&this._events.next(new cs(this))}_updateValue(){}_anyControls(n){return!1}_allControlsDisabled(){return this.disabled}registerOnChange(n){this._onChange.push(n)}_unregisterOnChange(n){rE(this._onChange,n)}registerOnDisabledChange(n){this._onDisabledChange.push(n)}_unregisterOnDisabledChange(n){rE(this._onDisabledChange,n)}_forEachChild(n){}_syncPendingControls(){return this.updateOn==="submit"&&(this._pendingDirty&&this.markAsDirty(),this._pendingTouched&&this.markAsTouched(),this._pendingChange)?(this.setValue(this._pendingValue,{onlySelf:!0,emitModelToViewChange:!1}),!0):!1}_applyFormState(n){oE(n)?(this.value=this._pendingValue=n.value,n.disabled?this.disable({onlySelf:!0,emitEvent:!1}):this.enable({onlySelf:!0,emitEvent:!1})):this.value=this._pendingValue=n}},g9=To,DN=e=>e instanceof To,EN=(()=>{class e extends Oe{_parent;ngOnInit(){this._checkParentType(),this.formDirective.addFormGroup(this)}ngOnDestroy(){this.formDirective?.removeFormGroup(this)}get control(){return this.formDirective.getFormGroup(this)}get path(){return _l(this.name==null?this.name:this.name.toString(),this._parent)}get formDirective(){return this._parent?this._parent.formDirective:null}_checkParentType(){}static \u0275fac=(()=>{let t;return function(o){return(t||(t=Ve(e)))(o||e)}})();static \u0275dir=O({type:e,standalone:!1,features:[X]})}return e})();var wN={provide:un,useExisting:ve(()=>CN)},iE=Promise.resolve(),CN=(()=>{class e extends un{_changeDetectorRef;callSetDisabledState;control=new To;static ngAcceptInputType_isDisabled;_registered=!1;viewModel;name="";isDisabled;model;options;update=new H;constructor(t,r,o,i,s,a){super(),this._changeDetectorRef=s,this.callSetDisabledState=a,this._parent=t,this._setValidators(r),this._setAsyncValidators(o),this.valueAccessor=Ap(this,i)}ngOnChanges(t){if(this._checkForErrors(),!this._registered||"name"in t){if(this._registered&&(this._checkName(),this.formDirective)){let r=t.name.previousValue;this.formDirective.removeControl({name:r,path:this._getPath(r)})}this._setUpControl()}"isDisabled"in t&&this._updateDisabled(t),xp(t,this.viewModel)&&(this._updateValue(this.model),this.viewModel=this.model)}ngOnDestroy(){this.formDirective?.removeControl(this)}get path(){return this._getPath(this.name)}get formDirective(){return this._parent?this._parent.formDirective:null}viewToModelUpdate(t){this.viewModel=t,this.update.emit(t)}_setUpControl(){this._setUpdateStrategy(),this._isStandalone()?this._setUpStandalone():this.formDirective.addControl(this),this._registered=!0}_setUpdateStrategy(){this.options&&this.options.updateOn!=null&&(this.control._updateOn=this.options.updateOn)}_isStandalone(){return!this._parent||!!(this.options&&this.options.standalone)}_setUpStandalone(){ls(this.control,this,this.callSetDisabledState),this.control.updateValueAndValidity({emitEvent:!1})}_checkForErrors(){this._checkName()}_checkName(){this.options&&this.options.name&&(this.name=this.options.name),!this._isStandalone()&&this.name}_updateValue(t){iE.then(()=>{this.control.setValue(t,{emitViewToModelChange:!1}),this._changeDetectorRef?.markForCheck()})}_updateDisabled(t){let r=t.isDisabled.currentValue,o=r!==0&&de(r);iE.then(()=>{o&&!this.control.disabled?this.control.disable():!o&&this.control.disabled&&this.control.enable(),this._changeDetectorRef?.markForCheck()})}_getPath(t){return this._parent?_l(t,this._parent):[t]}static \u0275fac=function(r){return new(r||e)(w(Oe,9),w($t,10),w(Mr,10),w(us,10),w(ho,8),w(Ao,8))};static \u0275dir=O({type:e,selectors:[["","ngModel","",3,"formControlName","",3,"formControl",""]],inputs:{name:"name",isDisabled:[0,"disabled","isDisabled"],model:[0,"ngModel","model"],options:[0,"ngModelOptions","options"]},outputs:{update:"ngModelChange"},exportAs:["ngModel"],standalone:!1,features:[Te([wN]),X,Ke]})}return e})();var y9=(()=>{class e{static \u0275fac=function(r){return new(r||e)};static \u0275dir=O({type:e,selectors:[["form",3,"ngNoForm","",3,"ngNativeValidate",""]],hostAttrs:["novalidate",""],standalone:!1})}return e})(),IN={provide:us,useExisting:ve(()=>MN),multi:!0},MN=(()=>{class e extends cE{writeValue(t){let r=t??"";this.setProperty("value",r)}registerOnChange(t){this.onChange=r=>{t(r==""?null:parseFloat(r))}}static \u0275fac=(()=>{let t;return function(o){return(t||(t=Ve(e)))(o||e)}})();static \u0275dir=O({type:e,selectors:[["input","type","number","formControlName",""],["input","type","number","formControl",""],["input","type","number","ngModel",""]],hostBindings:function(r,o){r&1&&yr("input",function(s){return o.onChange(s.target.value)})("blur",function(){return o.onTouched()})},standalone:!1,features:[Te([IN]),X]})}return e})();var Dp=class extends xo{constructor(n,t,r){super(Mp(t),Sp(r,t)),this.controls=n,this._initObservables(),this._setUpdateStrategy(t),this._setUpControls(),this.updateValueAndValidity({onlySelf:!0,emitEvent:!!this.asyncValidator})}controls;at(n){return this.controls[this._adjustIndex(n)]}push(n,t={}){Array.isArray(n)?n.forEach(r=>{this.controls.push(r),this._registerControl(r)}):(this.controls.push(n),this._registerControl(n)),this.updateValueAndValidity({emitEvent:t.emitEvent}),this._onCollectionChange()}insert(n,t,r={}){this.controls.splice(n,0,t),this._registerControl(t),this.updateValueAndValidity({emitEvent:r.emitEvent})}removeAt(n,t={}){let r=this._adjustIndex(n);r<0&&(r=0),this.controls[r]&&this.controls[r]._registerOnCollectionChange(()=>{}),this.controls.splice(r,1),this.updateValueAndValidity({emitEvent:t.emitEvent})}setControl(n,t,r={}){let o=this._adjustIndex(n);o<0&&(o=0),this.controls[o]&&this.controls[o]._registerOnCollectionChange(()=>{}),this.controls.splice(o,1),t&&(this.controls.splice(o,0,t),this._registerControl(t)),this.updateValueAndValidity({emitEvent:r.emitEvent}),this._onCollectionChange()}get length(){return this.controls.length}setValue(n,t={}){wE(this,!1,n),n.forEach((r,o)=>{EE(this,!1,o),this.at(o).setValue(r,{onlySelf:!0,emitEvent:t.emitEvent})}),this.updateValueAndValidity(t)}patchValue(n,t={}){n!=null&&(n.forEach((r,o)=>{this.at(o)&&this.at(o).patchValue(r,{onlySelf:!0,emitEvent:t.emitEvent})}),this.updateValueAndValidity(t))}reset(n=[],t={}){this._forEachChild((r,o)=>{r.reset(n[o],V(E({},t),{onlySelf:!0}))}),this._updatePristine(t,this),this._updateTouched(t,this),this.updateValueAndValidity(t),t?.emitEvent!==!1&&this._events.next(new cs(this))}getRawValue(){return this.controls.map(n=>n.getRawValue())}clear(n={}){this.controls.length<1||(this._forEachChild(t=>t._registerOnCollectionChange(()=>{})),this.controls.splice(0),this.updateValueAndValidity({emitEvent:n.emitEvent}))}_adjustIndex(n){return n<0?n+this.length:n}_syncPendingControls(){let n=this.controls.reduce((t,r)=>r._syncPendingControls()?!0:t,!1);return n&&this.updateValueAndValidity({onlySelf:!0}),n}_forEachChild(n){this.controls.forEach((t,r)=>{n(t,r)})}_updateValue(){this.value=this.controls.filter(n=>n.enabled||this.disabled).map(n=>n.value)}_anyControls(n){return this.controls.some(t=>t.enabled&&n(t))}_setUpControls(){this._forEachChild(n=>this._registerControl(n))}_allControlsDisabled(){for(let n of this.controls)if(n.enabled)return!1;return this.controls.length>0||this.disabled}_registerControl(n){n.setParent(this),n._registerOnCollectionChange(this._onCollectionChange)}_find(n){return this.at(n)??null}};var SE=(()=>{class e extends Oe{callSetDisabledState;get submitted(){return $e(this._submittedReactive)}set submitted(t){this._submittedReactive.set(t)}_submitted=vr(()=>this._submittedReactive());_submittedReactive=Me(!1);_oldForm;_onCollectionChange=()=>this._updateDomValue();directives=[];constructor(t,r,o){super(),this.callSetDisabledState=o,this._setValidators(t),this._setAsyncValidators(r)}ngOnChanges(t){this.onChanges(t)}ngOnDestroy(){this.onDestroy()}onChanges(t){this._checkFormPresent(),t.hasOwnProperty("form")&&(this._updateValidators(),this._updateDomValue(),this._updateRegistrations(),this._oldForm=this.form)}onDestroy(){this.form&&(yl(this.form,this),this.form._onCollectionChange===this._onCollectionChange&&this.form._registerOnCollectionChange(()=>{}))}get formDirective(){return this}get path(){return[]}addControl(t){let r=this.form.get(t.path);return ls(r,t,this.callSetDisabledState),r.updateValueAndValidity({emitEvent:!1}),this.directives.push(t),r}getControl(t){return this.form.get(t.path)}removeControl(t){ml(t.control||null,t,!1),vN(this.directives,t)}addFormGroup(t){this._setUpFormContainer(t)}removeFormGroup(t){this._cleanUpFormContainer(t)}getFormGroup(t){return this.form.get(t.path)}getFormArray(t){return this.form.get(t.path)}addFormArray(t){this._setUpFormContainer(t)}removeFormArray(t){this._cleanUpFormContainer(t)}updateModel(t,r){this.form.get(t.path).setValue(r)}onReset(){this.resetForm()}resetForm(t=void 0,r={}){this.form.reset(t,r),this._submittedReactive.set(!1)}onSubmit(t){return this.submitted=!0,ME(this.form,this.directives),this.ngSubmit.emit(t),this.form._events.next(new pl(this.control)),t?.target?.method==="dialog"}_updateDomValue(){this.directives.forEach(t=>{let r=t.control,o=this.form.get(t.path);r!==o&&(ml(r||null,t),DN(o)&&(ls(o,t,this.callSetDisabledState),t.control=o))}),this.form._updateTreeValidity({emitEvent:!1})}_setUpFormContainer(t){let r=this.form.get(t.path);IE(r,t),r.updateValueAndValidity({emitEvent:!1})}_cleanUpFormContainer(t){let r=this.form?.get(t.path);r&&gN(r,t)&&r.updateValueAndValidity({emitEvent:!1})}_updateRegistrations(){this.form._registerOnCollectionChange(this._onCollectionChange),this._oldForm?._registerOnCollectionChange(()=>{})}_updateValidators(){Tp(this.form,this),this._oldForm&&yl(this._oldForm,this)}_checkFormPresent(){this.form}static \u0275fac=function(r){return new(r||e)(w($t,10),w(Mr,10),w(Ao,8))};static \u0275dir=O({type:e,features:[X,Ke]})}return e})();var Np=new y(""),SN={provide:un,useExisting:ve(()=>TN)},TN=(()=>{class e extends un{_ngModelWarningConfig;callSetDisabledState;viewModel;form;set isDisabled(t){}model;update=new H;static _ngModelWarningSentOnce=!1;_ngModelWarningSent=!1;constructor(t,r,o,i,s){super(),this._ngModelWarningConfig=i,this.callSetDisabledState=s,this._setValidators(t),this._setAsyncValidators(r),this.valueAccessor=Ap(this,o)}ngOnChanges(t){if(this._isControlChanged(t)){let r=t.form.previousValue;r&&ml(r,this,!1),ls(this.form,this,this.callSetDisabledState),this.form.updateValueAndValidity({emitEvent:!1})}xp(t,this.viewModel)&&(this.form.setValue(this.model),this.viewModel=this.model)}ngOnDestroy(){this.form&&ml(this.form,this,!1)}get path(){return[]}get control(){return this.form}viewToModelUpdate(t){this.viewModel=t,this.update.emit(t)}_isControlChanged(t){return t.hasOwnProperty("form")}static \u0275fac=function(r){return new(r||e)(w($t,10),w(Mr,10),w(us,10),w(Np,8),w(Ao,8))};static \u0275dir=O({type:e,selectors:[["","formControl",""]],inputs:{form:[0,"formControl","form"],isDisabled:[0,"disabled","isDisabled"],model:[0,"ngModel","model"]},outputs:{update:"ngModelChange"},exportAs:["ngForm"],standalone:!1,features:[Te([SN]),X,Ke]})}return e})(),xN={provide:Oe,useExisting:ve(()=>TE)},TE=(()=>{class e extends EN{name=null;constructor(t,r,o){super(),this._parent=t,this._setValidators(r),this._setAsyncValidators(o)}_checkParentType(){AE(this._parent)}static \u0275fac=function(r){return new(r||e)(w(Oe,13),w($t,10),w(Mr,10))};static \u0275dir=O({type:e,selectors:[["","formGroupName",""]],inputs:{name:[0,"formGroupName","name"]},standalone:!1,features:[Te([xN]),X]})}return e})(),AN={provide:Oe,useExisting:ve(()=>xE)},xE=(()=>{class e extends Oe{_parent;name=null;constructor(t,r,o){super(),this._parent=t,this._setValidators(r),this._setAsyncValidators(o)}ngOnInit(){AE(this._parent),this.formDirective.addFormArray(this)}ngOnDestroy(){this.formDirective?.removeFormArray(this)}get control(){return this.formDirective.getFormArray(this)}get formDirective(){return this._parent?this._parent.formDirective:null}get path(){return _l(this.name==null?this.name:this.name.toString(),this._parent)}static \u0275fac=function(r){return new(r||e)(w(Oe,13),w($t,10),w(Mr,10))};static \u0275dir=O({type:e,selectors:[["","formArrayName",""]],inputs:{name:[0,"formArrayName","name"]},standalone:!1,features:[Te([AN]),X]})}return e})();function AE(e){return!(e instanceof TE)&&!(e instanceof SE)&&!(e instanceof xE)}var NN={provide:un,useExisting:ve(()=>RN)},RN=(()=>{class e extends un{_ngModelWarningConfig;_added=!1;viewModel;control;name=null;set isDisabled(t){}model;update=new H;static _ngModelWarningSentOnce=!1;_ngModelWarningSent=!1;constructor(t,r,o,i,s){super(),this._ngModelWarningConfig=s,this._parent=t,this._setValidators(r),this._setAsyncValidators(o),this.valueAccessor=Ap(this,i)}ngOnChanges(t){this._added||this._setUpControl(),xp(t,this.viewModel)&&(this.viewModel=this.model,this.formDirective.updateModel(this,this.model))}ngOnDestroy(){this.formDirective?.removeControl(this)}viewToModelUpdate(t){this.viewModel=t,this.update.emit(t)}get path(){return _l(this.name==null?this.name:this.name.toString(),this._parent)}get formDirective(){return this._parent?this._parent.formDirective:null}_setUpControl(){this.control=this.formDirective.addControl(this),this._added=!0}static \u0275fac=function(r){return new(r||e)(w(Oe,13),w($t,10),w(Mr,10),w(us,10),w(Np,8))};static \u0275dir=O({type:e,selectors:[["","formControlName",""]],inputs:{name:[0,"formControlName","name"],isDisabled:[0,"disabled","isDisabled"],model:[0,"ngModel","model"]},outputs:{update:"ngModelChange"},standalone:!1,features:[Te([NN]),X,Ke]})}return e})();var ON={provide:Oe,useExisting:ve(()=>kN)},kN=(()=>{class e extends SE{form=null;ngSubmit=new H;get control(){return this.form}static \u0275fac=(()=>{let t;return function(o){return(t||(t=Ve(e)))(o||e)}})();static \u0275dir=O({type:e,selectors:[["","formGroup",""]],hostBindings:function(r,o){r&1&&yr("submit",function(s){return o.onSubmit(s)})("reset",function(){return o.onReset()})},inputs:{form:[0,"formGroup","form"]},outputs:{ngSubmit:"ngSubmit"},exportAs:["ngForm"],standalone:!1,features:[Te([ON]),X]})}return e})();function NE(e){return typeof e=="number"?e:parseFloat(e)}var Rp=(()=>{class e{_validator=ll;_onChange;_enabled;ngOnChanges(t){if(this.inputName in t){let r=this.normalizeInput(t[this.inputName].currentValue);this._enabled=this.enabled(r),this._validator=this._enabled?this.createValidator(r):ll,this._onChange?.()}}validate(t){return this._validator(t)}registerOnValidatorChange(t){this._onChange=t}enabled(t){return t!=null}static \u0275fac=function(r){return new(r||e)};static \u0275dir=O({type:e,features:[Ke]})}return e})(),FN={provide:$t,useExisting:ve(()=>PN),multi:!0},PN=(()=>{class e extends Rp{max;inputName="max";normalizeInput=t=>NE(t);createValidator=t=>dE(t);static \u0275fac=(()=>{let t;return function(o){return(t||(t=Ve(e)))(o||e)}})();static \u0275dir=O({type:e,selectors:[["input","type","number","max","","formControlName",""],["input","type","number","max","","formControl",""],["input","type","number","max","","ngModel",""]],hostVars:1,hostBindings:function(r,o){r&2&&rn("max",o._enabled?o.max:null)},inputs:{max:"max"},standalone:!1,features:[Te([FN]),X]})}return e})(),LN={provide:$t,useExisting:ve(()=>VN),multi:!0},VN=(()=>{class e extends Rp{min;inputName="min";normalizeInput=t=>NE(t);createValidator=t=>uE(t);static \u0275fac=(()=>{let t;return function(o){return(t||(t=Ve(e)))(o||e)}})();static \u0275dir=O({type:e,selectors:[["input","type","number","min","","formControlName",""],["input","type","number","min","","formControl",""],["input","type","number","min","","ngModel",""]],hostVars:1,hostBindings:function(r,o){r&2&&rn("min",o._enabled?o.min:null)},inputs:{min:"min"},standalone:!1,features:[Te([LN]),X]})}return e})(),jN={provide:$t,useExisting:ve(()=>BN),multi:!0};var BN=(()=>{class e extends Rp{required;inputName="required";normalizeInput=de;createValidator=t=>fE;enabled(t){return t}static \u0275fac=(()=>{let t;return function(o){return(t||(t=Ve(e)))(o||e)}})();static \u0275dir=O({type:e,selectors:[["","required","","formControlName","",3,"type","checkbox"],["","required","","formControl","",3,"type","checkbox"],["","required","","ngModel","",3,"type","checkbox"]],hostVars:1,hostBindings:function(r,o){r&2&&rn("required",o._enabled?"":null)},inputs:{required:"required"},standalone:!1,features:[Te([jN]),X]})}return e})();var RE=(()=>{class e{static \u0275fac=function(r){return new(r||e)};static \u0275mod=Z({type:e});static \u0275inj=G({})}return e})();function sE(e){return!!e&&(e.asyncValidators!==void 0||e.validators!==void 0||e.updateOn!==void 0)}var HN=(()=>{class e{useNonNullable=!1;get nonNullable(){let t=new e;return t.useNonNullable=!0,t}group(t,r=null){let o=this._reduceControls(t),i={};return sE(r)?i=r:r!==null&&(i.validators=r.validator,i.asyncValidators=r.asyncValidator),new Ir(o,i)}record(t,r=null){let o=this._reduceControls(t);return new _p(o,r)}control(t,r,o){let i={};return this.useNonNullable?(sE(r)?i=r:(i.validators=r,i.asyncValidators=o),new To(t,V(E({},i),{nonNullable:!0}))):new To(t,r,o)}array(t,r,o){let i=t.map(s=>this._createControl(s));return new Dp(i,r,o)}_reduceControls(t){let r={};return Object.keys(t).forEach(o=>{r[o]=this._createControl(t[o])}),r}_createControl(t){if(t instanceof To)return t;if(t instanceof xo)return t;if(Array.isArray(t)){let r=t[0],o=t.length>1?t[1]:null,i=t.length>2?t[2]:null;return this.control(r,o,i)}else return this.control(t)}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();var v9=(()=>{class e extends HN{group(t,r=null){return super.group(t,r)}control(t,r,o){return super.control(t,r,o)}array(t,r,o){return super.array(t,r,o)}static \u0275fac=(()=>{let t;return function(o){return(t||(t=Ve(e)))(o||e)}})();static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})(),b9=(()=>{class e{static withConfig(t){return{ngModule:e,providers:[{provide:Ao,useValue:t.callSetDisabledState??bl}]}}static \u0275fac=function(r){return new(r||e)};static \u0275mod=Z({type:e});static \u0275inj=G({imports:[RE]})}return e})(),_9=(()=>{class e{static withConfig(t){return{ngModule:e,providers:[{provide:Np,useValue:t.warnOnNgModelWithFormControl??"always"},{provide:Ao,useValue:t.callSetDisabledState??bl}]}}static \u0275fac=function(r){return new(r||e)};static \u0275mod=Z({type:e});static \u0275inj=G({imports:[RE]})}return e})();var Dl=class e extends Error{originalError;constructor(n){super(n)}static fromError(n,t){let r=new e(n);return r.originalError=t,r}},UN=(()=>{class e{handleError(t){let r=t;return t.name==="HttpErrorResponse"&&t.status===0?r=Dl.fromError("Controller is unreachable",t):t.error?.message&&(r=Dl.fromError(t.error.message,t)),kl(()=>r)}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac})}return e})(),T9=(()=>{class e{http;errorHandler;requestsNotificationEmitter=new H;constructor(t,r){this.http=t,this.errorHandler=r}get(t,r,o){o=this.getJsonOptions(o);let i=this.getOptionsForController(t,r,o);return this.requestsNotificationEmitter.emit(`GET ${i.url}`),this.http.get(i.url,i.options).pipe(Fe(this.errorHandler.handleError))}getText(t,r,o){o=this.getTextOptions(o);let i=this.getOptionsForController(t,r,o);return this.requestsNotificationEmitter.emit(`GET ${i.url}`),this.http.get(i.url,i.options).pipe(Fe(this.errorHandler.handleError))}getBlob(t,r,o){o=this.getBlobOptions(o);let i=this.getOptionsForController(t,r,o);return this.requestsNotificationEmitter.emit(`GET ${i.url}`),this.http.get(i.url,i.options).pipe(Fe(this.errorHandler.handleError))}post(t,r,o,i){i=this.getJsonOptions(i);let s=this.getOptionsForController(t,r,i);return this.requestsNotificationEmitter.emit(`POST ${s.url}`),this.http.post(s.url,o,s.options).pipe(Fe(this.errorHandler.handleError))}postBlob(t,r,o){let i={responseType:"blob",headers:{}},s=this.getOptionsForController(t,r,i);return this.requestsNotificationEmitter.emit(`POST ${s.url}`),this.http.post(s.url,o,s.options).pipe(Fe(this.errorHandler.handleError))}put(t,r,o,i){i=this.getJsonOptions(i);let s=this.getOptionsForController(t,r,i);return this.requestsNotificationEmitter.emit(`PUT ${s.url}`),this.http.put(s.url,o,s.options).pipe(Fe(this.errorHandler.handleError))}delete(t,r,o){o=this.getJsonOptions(o);let i=this.getOptionsForController(t,r,o);return this.requestsNotificationEmitter.emit(`DELETE ${i.url}`),this.http.delete(i.url,i.options).pipe(Fe(this.errorHandler.handleError))}patch(t,r,o,i){i=this.getJsonOptions(i);let s=this.getOptionsForController(t,r,i);return this.http.patch(s.url,o,s.options).pipe(Fe(this.errorHandler.handleError))}head(t,r,o){o=this.getJsonOptions(o);let i=this.getOptionsForController(t,r,o);return this.http.head(i.url,i.options).pipe(Fe(this.errorHandler.handleError))}options(t,r,o){o=this.getJsonOptions(o);let i=this.getOptionsForController(t,r,o);return this.http.options(i.url,i.options).pipe(Fe(this.errorHandler.handleError))}getJsonOptions(t){return t||{responseType:"json"}}getTextOptions(t){return t||{responseType:"text"}}getBlobOptions(t){return t||{responseType:"blob"}}getOptionsForController(t,r,o){return t&&t.host&&t.port?(t.protocol||(t.protocol=location.protocol),r=`${t.protocol}//${t.host}:${t.port}/${rp.current_version}${r}`):r=`/${rp.current_version}${r}`,o.headers||(o.headers={}),t&&t.authToken&&!t.tokenExpired&&(o.headers.Authorization=`Bearer ${t.authToken}`),{url:r,options:o}}static \u0275fac=function(r){return new(r||e)(I(jc),I(UN))};static \u0275prov=g({token:e,factory:e.\u0275fac})}return e})();var Op=class{_multiple;_emitChanges;compareWith;_selection=new Set;_deselectedToEmit=[];_selectedToEmit=[];_selected=null;get selected(){return this._selected||(this._selected=Array.from(this._selection.values())),this._selected}changed=new R;constructor(n=!1,t,r=!0,o){this._multiple=n,this._emitChanges=r,this.compareWith=o,t&&t.length&&(n?t.forEach(i=>this._markSelected(i)):this._markSelected(t[0]),this._selectedToEmit.length=0)}select(...n){this._verifyValueAssignment(n),n.forEach(r=>this._markSelected(r));let t=this._hasQueuedChanges();return this._emitChangeEvent(),t}deselect(...n){this._verifyValueAssignment(n),n.forEach(r=>this._unmarkSelected(r));let t=this._hasQueuedChanges();return this._emitChangeEvent(),t}setSelection(...n){this._verifyValueAssignment(n);let t=this.selected,r=new Set(n.map(i=>this._getConcreteValue(i)));n.forEach(i=>this._markSelected(i)),t.filter(i=>!r.has(this._getConcreteValue(i,r))).forEach(i=>this._unmarkSelected(i));let o=this._hasQueuedChanges();return this._emitChangeEvent(),o}toggle(n){return this.isSelected(n)?this.deselect(n):this.select(n)}clear(n=!0){this._unmarkAll();let t=this._hasQueuedChanges();return n&&this._emitChangeEvent(),t}isSelected(n){return this._selection.has(this._getConcreteValue(n))}isEmpty(){return this._selection.size===0}hasValue(){return!this.isEmpty()}sort(n){this._multiple&&this.selected&&this._selected.sort(n)}isMultipleSelection(){return this._multiple}_emitChangeEvent(){this._selected=null,(this._selectedToEmit.length||this._deselectedToEmit.length)&&(this.changed.next({source:this,added:this._selectedToEmit,removed:this._deselectedToEmit}),this._deselectedToEmit=[],this._selectedToEmit=[])}_markSelected(n){n=this._getConcreteValue(n),this.isSelected(n)||(this._multiple||this._unmarkAll(),this.isSelected(n)||this._selection.add(n),this._emitChanges&&this._selectedToEmit.push(n))}_unmarkSelected(n){n=this._getConcreteValue(n),this.isSelected(n)&&(this._selection.delete(n),this._emitChanges&&this._deselectedToEmit.push(n))}_unmarkAll(){this.isEmpty()||this._selection.forEach(n=>this._unmarkSelected(n))}_verifyValueAssignment(n){n.length>1&&this._multiple}_hasQueuedChanges(){return!!(this._deselectedToEmit.length||this._selectedToEmit.length)}_getConcreteValue(n,t){if(this.compareWith){t=t??this._selection;for(let r of t)if(this.compareWith(n,r))return r;return n}else return n}};var $N=(()=>{class e{_listeners=[];notify(t,r){for(let o of this._listeners)o(t,r)}listen(t){return this._listeners.push(t),()=>{this._listeners=this._listeners.filter(r=>t!==r)}}ngOnDestroy(){this._listeners=[]}static \u0275fac=function(r){return new(r||e)};static \u0275prov=g({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();var OE=class{applyChanges(n,t,r,o,i){n.forEachOperation((s,a,c)=>{let l,u;if(s.previousIndex==null){let d=r(s,a,c);l=t.createEmbeddedView(d.templateRef,d.context,d.index),u=Ut.INSERTED}else c==null?(t.remove(a),u=Ut.REMOVED):(l=t.get(a),t.move(l,c),u=Ut.MOVED);i&&i({context:l?.context,operation:u,record:s})})}detach(){}};var U9=(()=>{class e{_animationsDisabled=Fn();state="unchecked";disabled=!1;appearance="full";constructor(){}static \u0275fac=function(r){return new(r||e)};static \u0275cmp=Se({type:e,selectors:[["mat-pseudo-checkbox"]],hostAttrs:[1,"mat-pseudo-checkbox"],hostVars:12,hostBindings:function(r,o){r&2&&Ue("mat-pseudo-checkbox-indeterminate",o.state==="indeterminate")("mat-pseudo-checkbox-checked",o.state==="checked")("mat-pseudo-checkbox-disabled",o.disabled)("mat-pseudo-checkbox-minimal",o.appearance==="minimal")("mat-pseudo-checkbox-full",o.appearance==="full")("_mat-animation-noopable",o._animationsDisabled)},inputs:{state:"state",disabled:"disabled",appearance:"appearance"},decls:0,vars:0,template:function(r,o){},styles:[`.mat-pseudo-checkbox{border-radius:2px;cursor:pointer;display:inline-block;vertical-align:middle;box-sizing:border-box;position:relative;flex-shrink:0;transition:border-color 90ms cubic-bezier(0, 0, 0.2, 0.1),background-color 90ms cubic-bezier(0, 0, 0.2, 0.1)}.mat-pseudo-checkbox::after{position:absolute;opacity:0;content:"";border-bottom:2px solid currentColor;transition:opacity 90ms cubic-bezier(0, 0, 0.2, 0.1)}.mat-pseudo-checkbox._mat-animation-noopable{transition:none !important;animation:none !important}.mat-pseudo-checkbox._mat-animation-noopable::after{transition:none}.mat-pseudo-checkbox-disabled{cursor:default}.mat-pseudo-checkbox-indeterminate::after{left:1px;opacity:1;border-radius:2px}.mat-pseudo-checkbox-checked::after{left:1px;border-left:2px solid currentColor;transform:rotate(-45deg);opacity:1;box-sizing:content-box}.mat-pseudo-checkbox-minimal.mat-pseudo-checkbox-checked::after,.mat-pseudo-checkbox-minimal.mat-pseudo-checkbox-indeterminate::after{color:var(--mat-pseudo-checkbox-minimal-selected-checkmark-color, var(--mat-sys-primary))}.mat-pseudo-checkbox-minimal.mat-pseudo-checkbox-checked.mat-pseudo-checkbox-disabled::after,.mat-pseudo-checkbox-minimal.mat-pseudo-checkbox-indeterminate.mat-pseudo-checkbox-disabled::after{color:var(--mat-pseudo-checkbox-minimal-disabled-selected-checkmark-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent))}.mat-pseudo-checkbox-full{border-color:var(--mat-pseudo-checkbox-full-unselected-icon-color, var(--mat-sys-on-surface-variant));border-width:2px;border-style:solid}.mat-pseudo-checkbox-full.mat-pseudo-checkbox-disabled{border-color:var(--mat-pseudo-checkbox-full-disabled-unselected-icon-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent))}.mat-pseudo-checkbox-full.mat-pseudo-checkbox-checked,.mat-pseudo-checkbox-full.mat-pseudo-checkbox-indeterminate{background-color:var(--mat-pseudo-checkbox-full-selected-icon-color, var(--mat-sys-primary));border-color:rgba(0,0,0,0)}.mat-pseudo-checkbox-full.mat-pseudo-checkbox-checked::after,.mat-pseudo-checkbox-full.mat-pseudo-checkbox-indeterminate::after{color:var(--mat-pseudo-checkbox-full-selected-checkmark-color, var(--mat-sys-on-primary))}.mat-pseudo-checkbox-full.mat-pseudo-checkbox-checked.mat-pseudo-checkbox-disabled,.mat-pseudo-checkbox-full.mat-pseudo-checkbox-indeterminate.mat-pseudo-checkbox-disabled{background-color:var(--mat-pseudo-checkbox-full-disabled-selected-icon-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent))}.mat-pseudo-checkbox-full.mat-pseudo-checkbox-checked.mat-pseudo-checkbox-disabled::after,.mat-pseudo-checkbox-full.mat-pseudo-checkbox-indeterminate.mat-pseudo-checkbox-disabled::after{color:var(--mat-pseudo-checkbox-full-disabled-selected-checkmark-color, var(--mat-sys-surface))}.mat-pseudo-checkbox{width:18px;height:18px}.mat-pseudo-checkbox-minimal.mat-pseudo-checkbox-checked::after{width:14px;height:6px;transform-origin:center;top:-4.2426406871px;left:0;bottom:0;right:0;margin:auto}.mat-pseudo-checkbox-minimal.mat-pseudo-checkbox-indeterminate::after{top:8px;width:16px}.mat-pseudo-checkbox-full.mat-pseudo-checkbox-checked::after{width:10px;height:4px;transform-origin:center;top:-2.8284271247px;left:0;bottom:0;right:0;margin:auto}.mat-pseudo-checkbox-full.mat-pseudo-checkbox-indeterminate::after{top:6px;width:12px} -`],encapsulation:2,changeDetection:0})}return e})();export{E as a,V as b,BE as c,zN as d,GN as e,WN as f,B as g,GE as h,k as i,R as j,zn as k,Fo as l,Xp as m,Jp as n,Gn as o,tt as p,ke as q,kl as r,pn as s,Vo as t,iw as u,re as v,Fl as w,ht as x,jo as y,mn as z,mw as A,Pl as B,Ll as C,Wn as D,Ew as E,ww as F,Ee as G,js as H,Fe as I,Vl as J,jl as K,Cw as L,qn as M,pt as N,Bl as O,Iw as P,Bs as Q,Hs as R,Tw as S,xw as T,gm as U,$l as V,ym as W,Bo as X,Us as Y,$s as Z,$n as _,Gl as $,v as aa,Ot as ba,ve as ca,g as da,G as ea,Bw as fa,y as ga,I as ha,f as ia,Um as ja,ce as ka,Ur as la,tg as ma,ng as na,pg as oa,mg as pa,j as qa,F as ra,Pe as sa,ur as ta,H as ua,P as va,nt as wa,Qt as xa,Me as ya,li as za,Ke as Aa,Ve as Ba,ef as Ca,z as Da,Jt as Ea,mr as Fa,Ei as Ga,Oy as Ha,je as Ia,MI as Ja,qy as Ka,Yy as La,AI as Ma,NI as Na,RI as Oa,ev as Pa,nn as Qa,An as Ra,Ze as Sa,be as Ta,Be as Ua,w as Va,YM as Wa,He as Xa,Yv as Ya,Zv as Za,Se as _a,Z as $a,O as ab,xi as bb,kS as cb,X as db,eb,tb as fb,nb as gb,co as hb,gr as ib,WS as jb,ib as kb,Qe as lb,cb as mb,rn as nb,YS as ob,KS as pb,Vf as qb,XS as rb,QS as sb,JS as tb,eT as ub,tT as vb,lb as wb,za as xb,jf as yb,ub as zb,lo as Ab,uo as Bb,on as Cb,Bf as Db,Hf as Eb,fb as Fb,aT as Gb,hb as Hb,yr as Ib,mb as Jb,fT as Kb,fo as Lb,Nn as Mb,yb as Nb,Uf as Ob,vb as Pb,bb as Qb,_b as Rb,Db as Sb,mT as Tb,gT as Ub,$f as Vb,Ue as Wb,zf as Xb,LT as Yb,Ab as Zb,Gf as _b,Nb as $b,Rb as ac,Ob as bc,HT as cc,kb as dc,UT as ec,$T as fc,Te as gc,qT as hc,YT as ic,ZT as jc,KT as kc,XT as lc,JT as mc,t0 as nc,n0 as oc,r0 as pc,o0 as qc,i0 as rc,s0 as sc,$e as tc,vr as uc,l0 as vc,Ub as wc,h8 as xc,p8 as yc,m8 as zc,g8 as Ac,y8 as Bc,v8 as Cc,ho as Dc,mc as Ec,de as Fc,ah as Gc,_8 as Hc,D8 as Ic,k0 as Jc,po as Kc,c_ as Lc,bc as Mc,V0 as Nc,X0 as Oc,C_ as Pc,Q0 as Qc,J0 as Rc,ex as Sc,rx as Tc,ix as Uc,cx as Vc,gh as Wc,S_ as Xc,BG as Yc,Ih as Zc,Ex as _c,xx as $c,On as ad,cn as bd,mo as cd,_r as dd,go as ed,W_ as fd,jc as gd,Xx as hd,t3 as id,n3 as jd,Fh as kd,Lh as ld,nA as md,Re as nd,lt as od,Gi as pd,Wi as qd,Bc as rd,J_ as sd,ct as td,ae as ud,wo as vd,ln as wd,Qi as xd,zA as yd,op as zd,Ut as Ad,ip as Bd,Ji as Cd,WA as Dd,Co as Ed,Xq as Fd,sp as Gd,ap as Hd,Zi as Id,Dr as Jd,cp as Kd,Io as Ld,Jc as Md,o6 as Nd,i6 as Od,kD as Pd,qc as Qd,UD as Rd,hp as Sd,ns as Td,pp as Ud,il as Vd,gp as Wd,ZD as Xd,yp as Yd,vp as Zd,fp as _d,QA as $d,JA as ae,OE as be,us as ce,lE as de,$t as ee,JD as fe,Oe as ge,un as he,f9 as ie,h9 as je,Ir as ke,p9 as le,_N as me,To as ne,g9 as oe,CN as pe,y9 as qe,MN as re,TN as se,TE as te,xE as ue,RN as ve,kN as we,PN as xe,VN as ye,BN as ze,HN as Ae,v9 as Be,b9 as Ce,_9 as De,Uc as Ee,rA as Fe,Gc as Ge,iA as He,Wc as Ie,Bh as Je,o4 as Ke,uD as Le,lA as Me,_A as Ne,EA as Oe,wA as Pe,zh as Qe,Gh as Re,Wh as Se,e5 as Te,IA as Ue,MA as Ve,u5 as We,R5 as Xe,_5 as Ye,I5 as Ze,TA as _e,Fn as $e,Xi as af,Jh as bf,q5 as cf,TD as df,xD as ef,LA as ff,RD as gf,Iq as hf,Mq as if,rp as jf,UN as kf,T9 as lf,$N as mf,Op as nf,U9 as of}; diff --git a/gns3server/static/web-ui/chunk-TYGV4UPE.js b/gns3server/static/web-ui/chunk-TYGV4UPE.js new file mode 100644 index 000000000..feb876c34 --- /dev/null +++ b/gns3server/static/web-ui/chunk-TYGV4UPE.js @@ -0,0 +1,1049 @@ +var bS=Object.create;var ka=Object.defineProperty,_S=Object.defineProperties,DS=Object.getOwnPropertyDescriptor,ES=Object.getOwnPropertyDescriptors,wS=Object.getOwnPropertyNames,Oa=Object.getOwnPropertySymbols,CS=Object.getPrototypeOf,Sd=Object.prototype.hasOwnProperty,Cv=Object.prototype.propertyIsEnumerable;var wv=(t,n,e)=>n in t?ka(t,n,{enumerable:!0,configurable:!0,writable:!0,value:e}):t[n]=e,g=(t,n)=>{for(var e in n||={})Sd.call(n,e)&&wv(t,e,n[e]);if(Oa)for(var e of Oa(n))Cv.call(n,e)&&wv(t,e,n[e]);return t},F=(t,n)=>_S(t,ES(n));var IS=(t,n)=>{var e={};for(var r in t)Sd.call(t,r)&&n.indexOf(r)<0&&(e[r]=t[r]);if(t!=null&&Oa)for(var r of Oa(t))n.indexOf(r)<0&&Cv.call(t,r)&&(e[r]=t[r]);return e};var CL=(t,n)=>()=>{try{return n||t((n={exports:{}}).exports,n),n.exports}catch(e){throw n=0,e}},IL=(t,n)=>{for(var e in n)ka(t,e,{get:n[e],enumerable:!0})},SS=(t,n,e,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let o of wS(n))!Sd.call(t,o)&&o!==e&&ka(t,o,{get:()=>n[o],enumerable:!(r=DS(n,o))||r.enumerable});return t};var SL=(t,n,e)=>(e=t!=null?bS(CS(t)):{},SS(n||!t||!t.__esModule?ka(e,"default",{value:t,enumerable:!0}):e,t));function R(t){return typeof t=="function"}function Zn(t){let e=t(r=>{Error.call(r),r.stack=new Error().stack});return e.prototype=Object.create(Error.prototype),e.prototype.constructor=e,e}var Fa=Zn(t=>function(e){t(this),this.message=e?`${e.length} errors occurred during unsubscription: +${e.map((r,o)=>`${o+1}) ${r.toString()}`).join(` + `)}`:"",this.name="UnsubscriptionError",this.errors=e});function wr(t,n){if(t){let e=t.indexOf(n);0<=e&&t.splice(e,1)}}var G=class t{constructor(n){this.initialTeardown=n,this.closed=!1,this._parentage=null,this._finalizers=null}unsubscribe(){let n;if(!this.closed){this.closed=!0;let{_parentage:e}=this;if(e)if(this._parentage=null,Array.isArray(e))for(let i of e)i.remove(this);else e.remove(this);let{initialTeardown:r}=this;if(R(r))try{r()}catch(i){n=i instanceof Fa?i.errors:[i]}let{_finalizers:o}=this;if(o){this._finalizers=null;for(let i of o)try{Iv(i)}catch(s){n=n??[],s instanceof Fa?n=[...n,...s.errors]:n.push(s)}}if(n)throw new Fa(n)}}add(n){var e;if(n&&n!==this)if(this.closed)Iv(n);else{if(n instanceof t){if(n.closed||n._hasParent(this))return;n._addParent(this)}(this._finalizers=(e=this._finalizers)!==null&&e!==void 0?e:[]).push(n)}}_hasParent(n){let{_parentage:e}=this;return e===n||Array.isArray(e)&&e.includes(n)}_addParent(n){let{_parentage:e}=this;this._parentage=Array.isArray(e)?(e.push(n),e):e?[e,n]:n}_removeParent(n){let{_parentage:e}=this;e===n?this._parentage=null:Array.isArray(e)&&wr(e,n)}remove(n){let{_finalizers:e}=this;e&&wr(e,n),n instanceof t&&n._removeParent(this)}};G.EMPTY=(()=>{let t=new G;return t.closed=!0,t})();var Md=G.EMPTY;function Pa(t){return t instanceof G||t&&"closed"in t&&R(t.remove)&&R(t.add)&&R(t.unsubscribe)}function Iv(t){R(t)?t():t.unsubscribe()}var Nt={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var Do={setTimeout(t,n,...e){let{delegate:r}=Do;return r?.setTimeout?r.setTimeout(t,n,...e):setTimeout(t,n,...e)},clearTimeout(t){let{delegate:n}=Do;return(n?.clearTimeout||clearTimeout)(t)},delegate:void 0};function La(t){Do.setTimeout(()=>{let{onUnhandledError:n}=Nt;if(n)n(t);else throw t})}function Cr(){}var Sv=Td("C",void 0,void 0);function Mv(t){return Td("E",void 0,t)}function Tv(t){return Td("N",t,void 0)}function Td(t,n,e){return{kind:t,value:n,error:e}}var Ir=null;function Eo(t){if(Nt.useDeprecatedSynchronousErrorHandling){let n=!Ir;if(n&&(Ir={errorThrown:!1,error:null}),t(),n){let{errorThrown:e,error:r}=Ir;if(Ir=null,e)throw r}}else t()}function xv(t){Nt.useDeprecatedSynchronousErrorHandling&&Ir&&(Ir.errorThrown=!0,Ir.error=t)}var Sr=class extends G{constructor(n){super(),this.isStopped=!1,n?(this.destination=n,Pa(n)&&n.add(this)):this.destination=xS}static create(n,e,r){return new Ot(n,e,r)}next(n){this.isStopped?Ad(Tv(n),this):this._next(n)}error(n){this.isStopped?Ad(Mv(n),this):(this.isStopped=!0,this._error(n))}complete(){this.isStopped?Ad(Sv,this):(this.isStopped=!0,this._complete())}unsubscribe(){this.closed||(this.isStopped=!0,super.unsubscribe(),this.destination=null)}_next(n){this.destination.next(n)}_error(n){try{this.destination.error(n)}finally{this.unsubscribe()}}_complete(){try{this.destination.complete()}finally{this.unsubscribe()}}},MS=Function.prototype.bind;function xd(t,n){return MS.call(t,n)}var Rd=class{constructor(n){this.partialObserver=n}next(n){let{partialObserver:e}=this;if(e.next)try{e.next(n)}catch(r){ja(r)}}error(n){let{partialObserver:e}=this;if(e.error)try{e.error(n)}catch(r){ja(r)}else ja(n)}complete(){let{partialObserver:n}=this;if(n.complete)try{n.complete()}catch(e){ja(e)}}},Ot=class extends Sr{constructor(n,e,r){super();let o;if(R(n)||!n)o={next:n??void 0,error:e??void 0,complete:r??void 0};else{let i;this&&Nt.useDeprecatedNextContext?(i=Object.create(n),i.unsubscribe=()=>this.unsubscribe(),o={next:n.next&&xd(n.next,i),error:n.error&&xd(n.error,i),complete:n.complete&&xd(n.complete,i)}):o=n}this.destination=new Rd(o)}};function ja(t){Nt.useDeprecatedSynchronousErrorHandling?xv(t):La(t)}function TS(t){throw t}function Ad(t,n){let{onStoppedNotification:e}=Nt;e&&Do.setTimeout(()=>e(t,n))}var xS={closed:!0,next:Cr,error:TS,complete:Cr};var wo=typeof Symbol=="function"&&Symbol.observable||"@@observable";function st(t){return t}function Nd(...t){return Od(t)}function Od(t){return t.length===0?st:t.length===1?t[0]:function(e){return t.reduce((r,o)=>o(r),e)}}var O=(()=>{class t{constructor(e){e&&(this._subscribe=e)}lift(e){let r=new t;return r.source=this,r.operator=e,r}subscribe(e,r,o){let i=RS(e)?e:new Ot(e,r,o);return Eo(()=>{let{operator:s,source:a}=this;i.add(s?s.call(i,a):a?this._subscribe(i):this._trySubscribe(i))}),i}_trySubscribe(e){try{return this._subscribe(e)}catch(r){e.error(r)}}forEach(e,r){return r=Av(r),new r((o,i)=>{let s=new Ot({next:a=>{try{e(a)}catch(c){i(c),s.unsubscribe()}},error:i,complete:o});this.subscribe(s)})}_subscribe(e){var r;return(r=this.source)===null||r===void 0?void 0:r.subscribe(e)}[wo](){return this}pipe(...e){return Od(e)(this)}toPromise(e){return e=Av(e),new e((r,o)=>{let i;this.subscribe(s=>i=s,s=>o(s),()=>r(i))})}}return t.create=n=>new t(n),t})();function Av(t){var n;return(n=t??Nt.Promise)!==null&&n!==void 0?n:Promise}function AS(t){return t&&R(t.next)&&R(t.error)&&R(t.complete)}function RS(t){return t&&t instanceof Sr||AS(t)&&Pa(t)}var Rv=Zn(t=>function(){t(this),this.name="ObjectUnsubscribedError",this.message="object unsubscribed"});var S=(()=>{class t extends O{constructor(){super(),this.closed=!1,this.currentObservers=null,this.observers=[],this.isStopped=!1,this.hasError=!1,this.thrownError=null}lift(e){let r=new Va(this,this);return r.operator=e,r}_throwIfClosed(){if(this.closed)throw new Rv}next(e){Eo(()=>{if(this._throwIfClosed(),!this.isStopped){this.currentObservers||(this.currentObservers=Array.from(this.observers));for(let r of this.currentObservers)r.next(e)}})}error(e){Eo(()=>{if(this._throwIfClosed(),!this.isStopped){this.hasError=this.isStopped=!0,this.thrownError=e;let{observers:r}=this;for(;r.length;)r.shift().error(e)}})}complete(){Eo(()=>{if(this._throwIfClosed(),!this.isStopped){this.isStopped=!0;let{observers:e}=this;for(;e.length;)e.shift().complete()}})}unsubscribe(){this.isStopped=this.closed=!0,this.observers=this.currentObservers=null}get observed(){var e;return((e=this.observers)===null||e===void 0?void 0:e.length)>0}_trySubscribe(e){return this._throwIfClosed(),super._trySubscribe(e)}_subscribe(e){return this._throwIfClosed(),this._checkFinalizedStatuses(e),this._innerSubscribe(e)}_innerSubscribe(e){let{hasError:r,isStopped:o,observers:i}=this;return r||o?Md:(this.currentObservers=null,i.push(e),new G(()=>{this.currentObservers=null,wr(i,e)}))}_checkFinalizedStatuses(e){let{hasError:r,thrownError:o,isStopped:i}=this;r?e.error(o):i&&e.complete()}asObservable(){let e=new O;return e.source=this,e}}return t.create=(n,e)=>new Va(n,e),t})(),Va=class extends S{constructor(n,e){super(),this.destination=n,this.source=e}next(n){var e,r;(r=(e=this.destination)===null||e===void 0?void 0:e.next)===null||r===void 0||r.call(e,n)}error(n){var e,r;(r=(e=this.destination)===null||e===void 0?void 0:e.error)===null||r===void 0||r.call(e,n)}complete(){var n,e;(e=(n=this.destination)===null||n===void 0?void 0:n.complete)===null||e===void 0||e.call(n)}_subscribe(n){var e,r;return(r=(e=this.source)===null||e===void 0?void 0:e.subscribe(n))!==null&&r!==void 0?r:Md}};function kd(t){return R(t?.lift)}function k(t){return n=>{if(kd(n))return n.lift(function(e){try{return t(e,this)}catch(r){this.error(r)}});throw new TypeError("Unable to lift unknown Observable type")}}function N(t,n,e,r,o){return new Fd(t,n,e,r,o)}var Fd=class extends Sr{constructor(n,e,r,o,i,s){super(n),this.onFinalize=i,this.shouldUnsubscribe=s,this._next=e?function(a){try{e(a)}catch(c){n.error(c)}}:super._next,this._error=o?function(a){try{o(a)}catch(c){n.error(c)}finally{this.unsubscribe()}}:super._error,this._complete=r?function(){try{r()}catch(a){n.error(a)}finally{this.unsubscribe()}}:super._complete}unsubscribe(){var n;if(!this.shouldUnsubscribe||this.shouldUnsubscribe()){let{closed:e}=this;super.unsubscribe(),!e&&((n=this.onFinalize)===null||n===void 0||n.call(this))}}};function Ov(t,n,e,r){function o(i){return i instanceof e?i:new e(function(s){s(i)})}return new(e||(e=Promise))(function(i,s){function a(u){try{l(r.next(u))}catch(d){s(d)}}function c(u){try{l(r.throw(u))}catch(d){s(d)}}function l(u){u.done?i(u.value):o(u.value).then(a,c)}l((r=r.apply(t,n||[])).next())})}function Nv(t){var n=typeof Symbol=="function"&&Symbol.iterator,e=n&&t[n],r=0;if(e)return e.call(t);if(t&&typeof t.length=="number")return{next:function(){return t&&r>=t.length&&(t=void 0),{value:t&&t[r++],done:!t}}};throw new TypeError(n?"Object is not iterable.":"Symbol.iterator is not defined.")}function Mr(t){return this instanceof Mr?(this.v=t,this):new Mr(t)}function kv(t,n,e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var r=e.apply(t,n||[]),o,i=[];return o=Object.create((typeof AsyncIterator=="function"?AsyncIterator:Object).prototype),a("next"),a("throw"),a("return",s),o[Symbol.asyncIterator]=function(){return this},o;function s(p){return function(m){return Promise.resolve(m).then(p,d)}}function a(p,m){r[p]&&(o[p]=function(_){return new Promise(function(E,I){i.push([p,_,E,I])>1||c(p,_)})},m&&(o[p]=m(o[p])))}function c(p,m){try{l(r[p](m))}catch(_){h(i[0][3],_)}}function l(p){p.value instanceof Mr?Promise.resolve(p.value.v).then(u,d):h(i[0][2],p)}function u(p){c("next",p)}function d(p){c("throw",p)}function h(p,m){p(m),i.shift(),i.length&&c(i[0][0],i[0][1])}}function Fv(t){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var n=t[Symbol.asyncIterator],e;return n?n.call(t):(t=typeof Nv=="function"?Nv(t):t[Symbol.iterator](),e={},r("next"),r("throw"),r("return"),e[Symbol.asyncIterator]=function(){return this},e);function r(i){e[i]=t[i]&&function(s){return new Promise(function(a,c){s=t[i](s),o(a,c,s.done,s.value)})}}function o(i,s,a,c){Promise.resolve(c).then(function(l){i({value:l,done:a})},s)}}var Co=t=>t&&typeof t.length=="number"&&typeof t!="function";function Ba(t){return R(t?.then)}function Ua(t){return R(t[wo])}function Ha(t){return Symbol.asyncIterator&&R(t?.[Symbol.asyncIterator])}function $a(t){return new TypeError(`You provided ${t!==null&&typeof t=="object"?"an invalid object":`'${t}'`} where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`)}function NS(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var za=NS();function Ga(t){return R(t?.[za])}function Wa(t){return kv(this,arguments,function*(){let e=t.getReader();try{for(;;){let{value:r,done:o}=yield Mr(e.read());if(o)return yield Mr(void 0);yield yield Mr(r)}}finally{e.releaseLock()}})}function qa(t){return R(t?.getReader)}function Y(t){if(t instanceof O)return t;if(t!=null){if(Ua(t))return OS(t);if(Co(t))return kS(t);if(Ba(t))return FS(t);if(Ha(t))return Pv(t);if(Ga(t))return PS(t);if(qa(t))return LS(t)}throw $a(t)}function OS(t){return new O(n=>{let e=t[wo]();if(R(e.subscribe))return e.subscribe(n);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function kS(t){return new O(n=>{for(let e=0;e{t.then(e=>{n.closed||(n.next(e),n.complete())},e=>n.error(e)).then(null,La)})}function PS(t){return new O(n=>{for(let e of t)if(n.next(e),n.closed)return;n.complete()})}function Pv(t){return new O(n=>{jS(t,n).catch(e=>n.error(e))})}function LS(t){return Pv(Wa(t))}function jS(t,n){var e,r,o,i;return Ov(this,void 0,void 0,function*(){try{for(e=Fv(t);r=yield e.next(),!r.done;){let s=r.value;if(n.next(s),n.closed)return}}catch(s){o={error:s}}finally{try{r&&!r.done&&(i=e.return)&&(yield i.call(e))}finally{if(o)throw o.error}}n.complete()})}function at(t){return k((n,e)=>{Y(t).subscribe(N(e,()=>e.complete(),Cr)),!e.closed&&n.subscribe(e)})}function Lv(){return k((t,n)=>{let e=null;t._refCount++;let r=N(n,void 0,void 0,void 0,()=>{if(!t||t._refCount<=0||0<--t._refCount){e=null;return}let o=t._connection,i=e;e=null,o&&(!i||o===i)&&o.unsubscribe(),n.unsubscribe()});t.subscribe(r),r.closed||(e=t.connect())})}var ki=class extends O{constructor(n,e){super(),this.source=n,this.subjectFactory=e,this._subject=null,this._refCount=0,this._connection=null,kd(n)&&(this.lift=n.lift)}_subscribe(n){return this.getSubject().subscribe(n)}getSubject(){let n=this._subject;return(!n||n.isStopped)&&(this._subject=this.subjectFactory()),this._subject}_teardown(){this._refCount=0;let{_connection:n}=this;this._subject=this._connection=null,n?.unsubscribe()}connect(){let n=this._connection;if(!n){n=this._connection=new G;let e=this.getSubject();n.add(this.source.subscribe(N(e,void 0,()=>{this._teardown(),e.complete()},r=>{this._teardown(),e.error(r)},()=>this._teardown()))),n.closed&&(this._connection=null,n=G.EMPTY)}return n}refCount(){return Lv()(this)}};var Io={schedule(t){let n=requestAnimationFrame,e=cancelAnimationFrame,{delegate:r}=Io;r&&(n=r.requestAnimationFrame,e=r.cancelAnimationFrame);let o=n(i=>{e=void 0,t(i)});return new G(()=>e?.(o))},requestAnimationFrame(...t){let{delegate:n}=Io;return(n?.requestAnimationFrame||requestAnimationFrame)(...t)},cancelAnimationFrame(...t){let{delegate:n}=Io;return(n?.cancelAnimationFrame||cancelAnimationFrame)(...t)},delegate:void 0};var Ie=class extends S{constructor(n){super(),this._value=n}get value(){return this.getValue()}_subscribe(n){let e=super._subscribe(n);return!e.closed&&n.next(this._value),e}getValue(){let{hasError:n,thrownError:e,_value:r}=this;if(n)throw e;return this._throwIfClosed(),r}next(n){super.next(this._value=n)}};var Fi={now(){return(Fi.delegate||Date).now()},delegate:void 0};var Pi=class extends S{constructor(n=1/0,e=1/0,r=Fi){super(),this._bufferSize=n,this._windowTime=e,this._timestampProvider=r,this._buffer=[],this._infiniteTimeWindow=!0,this._infiniteTimeWindow=e===1/0,this._bufferSize=Math.max(1,n),this._windowTime=Math.max(1,e)}next(n){let{isStopped:e,_buffer:r,_infiniteTimeWindow:o,_timestampProvider:i,_windowTime:s}=this;e||(r.push(n),!o&&r.push(i.now()+s)),this._trimBuffer(),super.next(n)}_subscribe(n){this._throwIfClosed(),this._trimBuffer();let e=this._innerSubscribe(n),{_infiniteTimeWindow:r,_buffer:o}=this,i=o.slice();for(let s=0;sjv(n)&&t()),n},clearImmediate(t){jv(t)}};var{setImmediate:BS,clearImmediate:US}=Vv,ji={setImmediate(...t){let{delegate:n}=ji;return(n?.setImmediate||BS)(...t)},clearImmediate(t){let{delegate:n}=ji;return(n?.clearImmediate||US)(t)},delegate:void 0};var Za=class extends Kn{constructor(n,e){super(n,e),this.scheduler=n,this.work=e}requestAsyncId(n,e,r=0){return r!==null&&r>0?super.requestAsyncId(n,e,r):(n.actions.push(this),n._scheduled||(n._scheduled=ji.setImmediate(n.flush.bind(n,void 0))))}recycleAsyncId(n,e,r=0){var o;if(r!=null?r>0:this.delay>0)return super.recycleAsyncId(n,e,r);let{actions:i}=n;e!=null&&((o=i[i.length-1])===null||o===void 0?void 0:o.id)!==e&&(ji.clearImmediate(e),n._scheduled===e&&(n._scheduled=void 0))}};var So=class t{constructor(n,e=t.now){this.schedulerActionCtor=n,this.now=e}schedule(n,e=0,r){return new this.schedulerActionCtor(this,n).schedule(r,e)}};So.now=Fi.now;var Qn=class extends So{constructor(n,e=So.now){super(n,e),this.actions=[],this._active=!1}flush(n){let{actions:e}=this;if(this._active){e.push(n);return}let r;this._active=!0;do if(r=n.execute(n.state,n.delay))break;while(n=e.shift());if(this._active=!1,r){for(;n=e.shift();)n.unsubscribe();throw r}}};var Ka=class extends Qn{flush(n){this._active=!0;let e=this._scheduled;this._scheduled=void 0;let{actions:r}=this,o;n=n||r.shift();do if(o=n.execute(n.state,n.delay))break;while((n=r[0])&&n.id===e&&r.shift());if(this._active=!1,o){for(;(n=r[0])&&n.id===e&&r.shift();)n.unsubscribe();throw o}}};var jd=new Ka(Za);var kt=new Qn(Kn),Bv=kt;var Qa=class extends Kn{constructor(n,e){super(n,e),this.scheduler=n,this.work=e}requestAsyncId(n,e,r=0){return r!==null&&r>0?super.requestAsyncId(n,e,r):(n.actions.push(this),n._scheduled||(n._scheduled=Io.requestAnimationFrame(()=>n.flush(void 0))))}recycleAsyncId(n,e,r=0){var o;if(r!=null?r>0:this.delay>0)return super.recycleAsyncId(n,e,r);let{actions:i}=n;e!=null&&e===n._scheduled&&((o=i[i.length-1])===null||o===void 0?void 0:o.id)!==e&&(Io.cancelAnimationFrame(e),n._scheduled=void 0)}};var Xa=class extends Qn{flush(n){this._active=!0;let e;n?e=n.id:(e=this._scheduled,this._scheduled=void 0);let{actions:r}=this,o;n=n||r.shift();do if(o=n.execute(n.state,n.delay))break;while((n=r[0])&&n.id===e&&r.shift());if(this._active=!1,o){for(;(n=r[0])&&n.id===e&&r.shift();)n.unsubscribe();throw o}}};var Vd=new Xa(Qa);var Se=new O(t=>t.complete());function Ja(t){return t&&R(t.schedule)}function Bd(t){return t[t.length-1]}function ec(t){return R(Bd(t))?t.pop():void 0}function nn(t){return Ja(Bd(t))?t.pop():void 0}function Uv(t,n){return typeof Bd(t)=="number"?t.pop():n}function $e(t,n,e,r=0,o=!1){let i=n.schedule(function(){e(),o?t.add(this.schedule(null,r)):this.unsubscribe()},r);if(t.add(i),!o)return i}function tc(t,n=0){return k((e,r)=>{e.subscribe(N(r,o=>$e(r,t,()=>r.next(o),n),()=>$e(r,t,()=>r.complete(),n),o=>$e(r,t,()=>r.error(o),n)))})}function nc(t,n=0){return k((e,r)=>{r.add(t.schedule(()=>e.subscribe(r),n))})}function Hv(t,n){return Y(t).pipe(nc(n),tc(n))}function $v(t,n){return Y(t).pipe(nc(n),tc(n))}function zv(t,n){return new O(e=>{let r=0;return n.schedule(function(){r===t.length?e.complete():(e.next(t[r++]),e.closed||this.schedule())})})}function Gv(t,n){return new O(e=>{let r;return $e(e,n,()=>{r=t[za](),$e(e,n,()=>{let o,i;try{({value:o,done:i}=r.next())}catch(s){e.error(s);return}i?e.complete():e.next(o)},0,!0)}),()=>R(r?.return)&&r.return()})}function rc(t,n){if(!t)throw new Error("Iterable cannot be null");return new O(e=>{$e(e,n,()=>{let r=t[Symbol.asyncIterator]();$e(e,n,()=>{r.next().then(o=>{o.done?e.complete():e.next(o.value)})},0,!0)})})}function Wv(t,n){return rc(Wa(t),n)}function qv(t,n){if(t!=null){if(Ua(t))return Hv(t,n);if(Co(t))return zv(t,n);if(Ba(t))return $v(t,n);if(Ha(t))return rc(t,n);if(Ga(t))return Gv(t,n);if(qa(t))return Wv(t,n)}throw $a(t)}function se(t,n){return n?qv(t,n):Y(t)}function T(...t){let n=nn(t);return se(t,n)}function Tr(t,n){let e=R(t)?t:()=>t,r=o=>o.error(e());return new O(n?o=>n.schedule(r,0,o):r)}function Ft(t){return!!t&&(t instanceof O||R(t.lift)&&R(t.subscribe))}var In=Zn(t=>function(){t(this),this.name="EmptyError",this.message="no elements in sequence"});function HS(t,n){let e=typeof n=="object";return new Promise((r,o)=>{let i=new Ot({next:s=>{r(s),i.unsubscribe()},error:o,complete:()=>{e?r(n.defaultValue):o(new In)}});t.subscribe(i)})}function oc(t){return t instanceof Date&&!isNaN(t)}var $S=Zn(t=>function(e=null){t(this),this.message="Timeout has occurred",this.name="TimeoutError",this.info=e});function zS(t,n){let{first:e,each:r,with:o=GS,scheduler:i=n??kt,meta:s=null}=oc(t)?{first:t}:typeof t=="number"?{each:t}:t;if(e==null&&r==null)throw new TypeError("No timeout provided.");return k((a,c)=>{let l,u,d=null,h=0,p=m=>{u=$e(c,i,()=>{try{l.unsubscribe(),Y(o({meta:s,lastValue:d,seen:h})).subscribe(c)}catch(_){c.error(_)}},m)};l=a.subscribe(N(c,m=>{u?.unsubscribe(),h++,c.next(d=m),r>0&&p(r)},void 0,void 0,()=>{u?.closed||u?.unsubscribe(),d=null})),!h&&p(e!=null?typeof e=="number"?e:+e-i.now():r)})}function GS(t){throw new $S(t)}function H(t,n){return k((e,r)=>{let o=0;e.subscribe(N(r,i=>{r.next(t.call(n,i,o++))}))})}var{isArray:WS}=Array;function qS(t,n){return WS(n)?t(...n):t(n)}function Mo(t){return H(n=>qS(t,n))}var{isArray:YS}=Array,{getPrototypeOf:ZS,prototype:KS,keys:QS}=Object;function ic(t){if(t.length===1){let n=t[0];if(YS(n))return{args:n,keys:null};if(XS(n)){let e=QS(n);return{args:e.map(r=>n[r]),keys:e}}}return{args:t,keys:null}}function XS(t){return t&&typeof t=="object"&&ZS(t)===KS}function sc(t,n){return t.reduce((e,r,o)=>(e[r]=n[o],e),{})}function To(...t){let n=nn(t),e=ec(t),{args:r,keys:o}=ic(t);if(r.length===0)return se([],n);let i=new O(JS(r,n,o?s=>sc(o,s):st));return e?i.pipe(Mo(e)):i}function JS(t,n,e=st){return r=>{Yv(n,()=>{let{length:o}=t,i=new Array(o),s=o,a=o;for(let c=0;c{let l=se(t[c],n),u=!1;l.subscribe(N(r,d=>{i[c]=d,u||(u=!0,a--),a||r.next(e(i.slice()))},()=>{--s||r.complete()}))},r)},r)}}function Yv(t,n,e){t?$e(e,t,n):n()}function Zv(t,n,e,r,o,i,s,a){let c=[],l=0,u=0,d=!1,h=()=>{d&&!c.length&&!l&&n.complete()},p=_=>l{i&&n.next(_),l++;let E=!1;Y(e(_,u++)).subscribe(N(n,I=>{o?.(I),i?p(I):n.next(I)},()=>{E=!0},void 0,()=>{if(E)try{for(l--;c.length&&lm(I)):m(I)}h()}catch(I){n.error(I)}}))};return t.subscribe(N(n,p,()=>{d=!0,h()})),()=>{a?.()}}function ve(t,n,e=1/0){return R(n)?ve((r,o)=>H((i,s)=>n(r,i,o,s))(Y(t(r,o))),e):(typeof n=="number"&&(e=n),k((r,o)=>Zv(r,o,t,e)))}function rn(t=1/0){return ve(st,t)}function Kv(){return rn(1)}function on(...t){return Kv()(se(t,nn(t)))}function Vi(t){return new O(n=>{Y(t()).subscribe(n)})}function Ud(...t){let n=ec(t),{args:e,keys:r}=ic(t),o=new O(i=>{let{length:s}=e;if(!s){i.complete();return}let a=new Array(s),c=s,l=s;for(let u=0;u{d||(d=!0,l--),a[u]=h},()=>c--,void 0,()=>{(!c||!d)&&(l||i.next(r?sc(r,a):a),i.complete())}))}});return n?o.pipe(Mo(n)):o}var eM=["addListener","removeListener"],tM=["addEventListener","removeEventListener"],nM=["on","off"];function Hd(t,n,e,r){if(R(e)&&(r=e,e=void 0),r)return Hd(t,n,e).pipe(Mo(r));let[o,i]=iM(t)?tM.map(s=>a=>t[s](n,a,e)):rM(t)?eM.map(Qv(t,n)):oM(t)?nM.map(Qv(t,n)):[];if(!o&&Co(t))return ve(s=>Hd(s,n,e))(Y(t));if(!o)throw new TypeError("Invalid event target");return new O(s=>{let a=(...c)=>s.next(1i(a)})}function Qv(t,n){return e=>r=>t[e](n,r)}function rM(t){return R(t.addListener)&&R(t.removeListener)}function oM(t){return R(t.on)&&R(t.off)}function iM(t){return R(t.addEventListener)&&R(t.removeEventListener)}function xr(t=0,n,e=Bv){let r=-1;return n!=null&&(Ja(n)?e=n:r=n),new O(o=>{let i=oc(t)?+t-e.now():t;i<0&&(i=0);let s=0;return e.schedule(function(){o.closed||(o.next(s++),0<=r?this.schedule(void 0,r):o.complete())},i)})}function sM(t=0,n=kt){return t<0&&(t=0),xr(t,t,n)}function aM(...t){let n=nn(t),e=Uv(t,1/0),r=t;return r.length?r.length===1?Y(r[0]):rn(e)(se(r,n)):Se}function fe(t,n){return k((e,r)=>{let o=0;e.subscribe(N(r,i=>t.call(n,i,o++)&&r.next(i)))})}function Xv(t){return k((n,e)=>{let r=!1,o=null,i=null,s=!1,a=()=>{if(i?.unsubscribe(),i=null,r){r=!1;let l=o;o=null,e.next(l)}s&&e.complete()},c=()=>{i=null,s&&e.complete()};n.subscribe(N(e,l=>{r=!0,o=l,i||Y(t(l)).subscribe(i=N(e,a,c))},()=>{s=!0,(!r||!i||i.closed)&&e.complete()}))})}function Bi(t,n=kt){return Xv(()=>xr(t,n))}function sn(t){return k((n,e)=>{let r=null,o=!1,i;r=n.subscribe(N(e,void 0,void 0,s=>{i=Y(t(s,sn(t)(n))),r?(r.unsubscribe(),r=null,i.subscribe(e)):o=!0})),o&&(r.unsubscribe(),r=null,i.subscribe(e))})}function Jv(t,n,e,r,o){return(i,s)=>{let a=e,c=n,l=0;i.subscribe(N(s,u=>{let d=l++;c=a?t(c,u,d):(a=!0,u),r&&s.next(c)},o&&(()=>{a&&s.next(c),s.complete()})))}}function $d(t,n){return k(Jv(t,n,arguments.length>=2,!1,!0))}function Xn(t,n){return R(n)?ve(t,n,1):ve(t,1)}function cM(t){return $d((n,e,r)=>!t||t(e,r)?n+1:n,0)}function Ar(t,n=kt){return k((e,r)=>{let o=null,i=null,s=null,a=()=>{if(o){o.unsubscribe(),o=null;let l=i;i=null,r.next(l)}};function c(){let l=s+t,u=n.now();if(u{i=l,s=n.now(),o||(o=n.schedule(c,t),r.add(o))},()=>{a(),r.complete()},void 0,()=>{i=o=null}))})}function ey(t){return k((n,e)=>{let r=!1;n.subscribe(N(e,o=>{r=!0,e.next(o)},()=>{r||e.next(t),e.complete()}))})}function Ue(t){return t<=0?()=>Se:k((n,e)=>{let r=0;n.subscribe(N(e,o=>{++r<=t&&(e.next(o),t<=r&&e.complete())}))})}function ty(){return k((t,n)=>{t.subscribe(N(n,Cr))})}function zd(t){return H(()=>t)}function Gd(t,n){return n?e=>on(n.pipe(Ue(1),ty()),e.pipe(Gd(t))):ve((e,r)=>Y(t(e,r)).pipe(Ue(1),zd(e)))}function lM(t,n=kt){let e=xr(t,n);return Gd(()=>e)}function xo(t,n=st){return t=t??uM,k((e,r)=>{let o,i=!0;e.subscribe(N(r,s=>{let a=n(s);(i||!t(o,a))&&(i=!1,o=a,r.next(s))}))})}function uM(t,n){return t===n}function ny(t=dM){return k((n,e)=>{let r=!1;n.subscribe(N(e,o=>{r=!0,e.next(o)},()=>r?e.complete():e.error(t())))})}function dM(){return new In}function Rr(t){return k((n,e)=>{try{n.subscribe(e)}finally{e.add(t)}})}function Sn(t,n){let e=arguments.length>=2;return r=>r.pipe(t?fe((o,i)=>t(o,i,r)):st,Ue(1),e?ey(n):ny(()=>new In))}function ac(t){return t<=0?()=>Se:k((n,e)=>{let r=[];n.subscribe(N(e,o=>{r.push(o),t{for(let o of r)e.next(o);e.complete()},void 0,()=>{r=null}))})}function ry(){return k((t,n)=>{let e,r=!1;t.subscribe(N(n,o=>{let i=e;e=o,r&&n.next([i,o]),r=!0}))})}function qd(t={}){let{connector:n=()=>new S,resetOnError:e=!0,resetOnComplete:r=!0,resetOnRefCountZero:o=!0}=t;return i=>{let s,a,c,l=0,u=!1,d=!1,h=()=>{a?.unsubscribe(),a=void 0},p=()=>{h(),s=c=void 0,u=d=!1},m=()=>{let _=s;p(),_?.unsubscribe()};return k((_,E)=>{l++,!d&&!u&&h();let I=c=c??n();E.add(()=>{l--,l===0&&!d&&!u&&(a=Wd(m,o))}),I.subscribe(E),!s&&l>0&&(s=new Ot({next:ee=>I.next(ee),error:ee=>{d=!0,h(),a=Wd(p,e,ee),I.error(ee)},complete:()=>{u=!0,h(),a=Wd(p,r),I.complete()}}),Y(_).subscribe(s))})(i)}}function Wd(t,n,...e){if(n===!0){t();return}if(n===!1)return;let r=new Ot({next:()=>{r.unsubscribe(),t()}});return Y(n(...e)).subscribe(r)}function oy(t,n,e){let r,o=!1;return t&&typeof t=="object"?{bufferSize:r=1/0,windowTime:n=1/0,refCount:o=!1,scheduler:e}=t:r=t??1/0,qd({connector:()=>new Pi(r,n,e),resetOnError:!0,resetOnComplete:!1,resetOnRefCountZero:o})}function Ui(t){return fe((n,e)=>t<=e)}function Nr(...t){let n=nn(t);return k((e,r)=>{(n?on(t,e,n):on(t,e)).subscribe(r)})}function He(t,n){return k((e,r)=>{let o=null,i=0,s=!1,a=()=>s&&!o&&r.complete();e.subscribe(N(r,c=>{o?.unsubscribe();let l=0,u=i++;Y(t(c,u)).subscribe(o=N(r,d=>r.next(n?n(c,d,u,l++):d),()=>{o=null,a()}))},()=>{s=!0,a()}))})}function Yd(t,n=!1){return k((e,r)=>{let o=0;e.subscribe(N(r,i=>{let s=t(i,o++);(s||n)&&r.next(i),!s&&r.complete()}))})}function nt(t,n,e){let r=R(t)||n||e?{next:t,error:n,complete:e}:t;return r?k((o,i)=>{var s;(s=r.subscribe)===null||s===void 0||s.call(r);let a=!0;o.subscribe(N(i,c=>{var l;(l=r.next)===null||l===void 0||l.call(r,c),i.next(c)},()=>{var c;a=!1,(c=r.complete)===null||c===void 0||c.call(r),i.complete()},c=>{var l;a=!1,(l=r.error)===null||l===void 0||l.call(r,c),i.error(c)},()=>{var c,l;a&&((c=r.unsubscribe)===null||c===void 0||c.call(r)),(l=r.finalize)===null||l===void 0||l.call(r)}))}):st}var ze=null,cc=!1,Zd=1,fM=null,pe=Symbol("SIGNAL");function x(t){let n=ze;return ze=t,n}function lc(){return ze}var Jn={version:0,lastCleanEpoch:0,dirty:!1,producers:void 0,producersTail:void 0,consumers:void 0,consumersTail:void 0,recomputing:!1,consumerAllowSignalWrites:!1,consumerIsAlwaysLive:!1,kind:"unknown",producerMustRecompute:()=>!1,producerRecomputeValue:()=>{},consumerMarkedDirty:()=>{},consumerOnSignalRead:()=>{}};function Mn(t){if(cc)throw new Error("");if(ze===null)return;ze.consumerOnSignalRead(t);let n=ze.producersTail;if(n!==void 0&&n.producer===t)return;let e,r=ze.recomputing;if(r&&(e=n!==void 0?n.nextProducer:ze.producers,e!==void 0&&e.producer===t)){ze.producersTail=e,e.lastReadVersion=t.version;return}let o=t.consumersTail;if(o!==void 0&&o.consumer===ze&&(!r||pM(o,ze)))return;let i=No(ze),s={producer:t,consumer:ze,nextProducer:e,prevConsumer:o,lastReadVersion:t.version,nextConsumer:void 0};ze.producersTail=s,n!==void 0?n.nextProducer=s:ze.producers=s,i&&cy(t,s)}function iy(){Zd++}function Fr(t){if(!(No(t)&&!t.dirty)&&!(!t.dirty&&t.lastCleanEpoch===Zd)){if(!t.producerMustRecompute(t)&&!Ro(t)){Ao(t);return}t.producerRecomputeValue(t),Ao(t)}}function Kd(t){if(t.consumers===void 0)return;let n=cc;cc=!0;try{for(let e=t.consumers;e!==void 0;e=e.nextConsumer){let r=e.consumer;r.dirty||hM(r)}}finally{cc=n}}function Qd(){return ze?.consumerAllowSignalWrites!==!1}function hM(t){t.dirty=!0,Kd(t),t.consumerMarkedDirty?.(t)}function Ao(t){t.dirty=!1,t.lastCleanEpoch=Zd}function Tn(t){return t&&sy(t),x(t)}function sy(t){t.producersTail=void 0,t.recomputing=!0}function er(t,n){x(n),t&&ay(t)}function ay(t){t.recomputing=!1;let n=t.producersTail,e=n!==void 0?n.nextProducer:t.producers;if(e!==void 0){if(No(t))do e=Xd(e);while(e!==void 0);n!==void 0?n.nextProducer=void 0:t.producers=void 0}}function Ro(t){for(let n=t.producers;n!==void 0;n=n.nextProducer){let e=n.producer,r=n.lastReadVersion;if(r!==e.version||(Fr(e),r!==e.version))return!0}return!1}function tr(t){if(No(t)){let n=t.producers;for(;n!==void 0;)n=Xd(n)}t.producers=void 0,t.producersTail=void 0,t.consumers=void 0,t.consumersTail=void 0}function cy(t,n){let e=t.consumersTail,r=No(t);if(e!==void 0?(n.nextConsumer=e.nextConsumer,e.nextConsumer=n):(n.nextConsumer=void 0,t.consumers=n),n.prevConsumer=e,t.consumersTail=n,!r)for(let o=t.producers;o!==void 0;o=o.nextProducer)cy(o.producer,o)}function Xd(t){let n=t.producer,e=t.nextProducer,r=t.nextConsumer,o=t.prevConsumer;if(t.nextConsumer=void 0,t.prevConsumer=void 0,r!==void 0?r.prevConsumer=o:n.consumersTail=o,o!==void 0)o.nextConsumer=r;else if(n.consumers=r,!No(n)){let i=n.producers;for(;i!==void 0;)i=Xd(i)}return e}function No(t){return t.consumerIsAlwaysLive||t.consumers!==void 0}function Hi(t){fM?.(t)}function pM(t,n){let e=n.producersTail;if(e!==void 0){let r=n.producers;do{if(r===t)return!0;if(r===e)break;r=r.nextProducer}while(r!==void 0)}return!1}function $i(t,n){return Object.is(t,n)}function zi(t,n){let e=Object.create(mM);e.computation=t,n!==void 0&&(e.equal=n);let r=()=>{if(Fr(e),Mn(e),e.value===an)throw e.error;return e.value};return r[pe]=e,Hi(e),r}var Or=Symbol("UNSET"),kr=Symbol("COMPUTING"),an=Symbol("ERRORED"),mM=F(g({},Jn),{value:Or,dirty:!0,error:null,equal:$i,kind:"computed",producerMustRecompute(t){return t.value===Or||t.value===kr},producerRecomputeValue(t){if(t.value===kr)throw new Error("");let n=t.value;t.value=kr;let e=Tn(t),r,o=!1;try{r=t.computation(),x(null),o=n!==Or&&n!==an&&r!==an&&t.equal(n,r)}catch(i){r=an,t.error=i}finally{er(t,e)}if(o){t.value=n;return}t.value=r,t.version++}});function gM(){throw new Error}var ly=gM;function uy(t){ly(t)}function Jd(t){ly=t}var vM=null;function ef(t,n){let e=Object.create(Gi);e.value=t,n!==void 0&&(e.equal=n);let r=()=>dy(e);return r[pe]=e,Hi(e),[r,s=>nr(e,s),s=>uc(e,s)]}function dy(t){return Mn(t),t.value}function nr(t,n){Qd()||uy(t),t.equal(t.value,n)||(t.value=n,yM(t))}function uc(t,n){Qd()||uy(t),nr(t,n(t.value))}var Gi=F(g({},Jn),{equal:$i,value:void 0,kind:"signal"});function yM(t){t.version++,iy(),Kd(t),vM?.(t)}var tf=F(g({},Jn),{consumerIsAlwaysLive:!0,consumerAllowSignalWrites:!0,dirty:!0,kind:"effect"});function nf(t){if(t.dirty=!1,t.version>0&&!Ro(t))return;t.version++;let n=Tn(t);try{t.cleanup(),t.fn()}finally{er(t,n)}}var rf;function dc(){return rf}function cn(t){let n=rf;return rf=t,n}var fy=Symbol("NotFound");function Oo(t){return t===fy||t?.name==="\u0275NotFound"}function of(t,n,e){let r=Object.create(bM);r.source=t,r.computation=n,e!=null&&(r.equal=e);let i=()=>{if(Fr(r),Mn(r),r.value===an)throw r.error;return r.value};return i[pe]=r,Hi(r),i}function hy(t,n){Fr(t),nr(t,n),Ao(t)}function py(t,n){if(Fr(t),t.value===an)throw t.error;uc(t,n),Ao(t)}var bM=F(g({},Jn),{value:Or,dirty:!0,error:null,equal:$i,kind:"linkedSignal",producerMustRecompute(t){return t.value===Or||t.value===kr},producerRecomputeValue(t){if(t.value===kr)throw new Error("");let n=t.value;t.value=kr;let e=Tn(t),r,o=!1;try{let i=t.source(),s=n!==Or&&n!==an,a=s?{source:t.sourceValue,value:n}:void 0;r=t.computation(i,a),t.sourceValue=i,x(null),o=s&&r!==an&&t.equal(n,r)}catch(i){r=an,t.error=i}finally{er(t,e)}if(o){t.value=n;return}t.value=r,t.version++}});function my(t){let n=x(null);try{return t()}finally{x(n)}}var yc="https://angular.dev/best-practices/security#preventing-cross-site-scripting-xss",b=class extends Error{code;constructor(n,e){super(Dt(n,e)),this.code=n}};function _M(t){return`NG0${Math.abs(t)}`}function Dt(t,n){return`${_M(t)}${n?": "+n:""}`}var ye=globalThis;function ne(t){for(let n in t)if(t[n]===ne)return n;throw Error("")}function _y(t,n){for(let e in n)n.hasOwnProperty(e)&&!t.hasOwnProperty(e)&&(t[e]=n[e])}function Xi(t){if(typeof t=="string")return t;if(Array.isArray(t))return`[${t.map(Xi).join(", ")}]`;if(t==null)return""+t;let n=t.overriddenName||t.name;if(n)return`${n}`;let e=t.toString();if(e==null)return""+e;let r=e.indexOf(` +`);return r>=0?e.slice(0,r):e}function bc(t,n){return t?n?`${t} ${n}`:t:n||""}var DM=ne({__forward_ref__:ne});function be(t){return t.__forward_ref__=be,t}function Me(t){return yf(t)?t():t}function yf(t){return typeof t=="function"&&t.hasOwnProperty(DM)&&t.__forward_ref__===be}function v(t){return{token:t.token,providedIn:t.providedIn||null,factory:t.factory,value:void 0}}function Z(t){return{providers:t.providers||[],imports:t.imports||[]}}function Ji(t){return EM(t,_c)}function bf(t){return Ji(t)!==null}function EM(t,n){return t.hasOwnProperty(n)&&t[n]||null}function wM(t){let n=t?.[_c]??null;return n||null}function af(t){return t&&t.hasOwnProperty(hc)?t[hc]:null}var _c=ne({\u0275prov:ne}),hc=ne({\u0275inj:ne}),y=class{_desc;ngMetadataName="InjectionToken";\u0275prov;constructor(n,e){this._desc=n,this.\u0275prov=void 0,typeof e=="number"?this.__NG_ELEMENT_ID__=e:e!==void 0&&(this.\u0275prov=v({token:this,providedIn:e.providedIn||"root",factory:e.factory}))}get multi(){return this}toString(){return`InjectionToken ${this._desc}`}};function _f(t){return t&&!!t.\u0275providers}var es=ne({\u0275cmp:ne}),ts=ne({\u0275dir:ne}),Df=ne({\u0275pipe:ne}),Ef=ne({\u0275mod:ne}),qi=ne({\u0275fac:ne}),Br=ne({__NG_ELEMENT_ID__:ne}),gy=ne({__NG_ENV_ID__:ne});function wf(t){return Ec(t,"@NgModule"),t[Ef]||null}function un(t){return Ec(t,"@Component"),t[es]||null}function Dc(t){return Ec(t,"@Directive"),t[ts]||null}function Dy(t){return Ec(t,"@Pipe"),t[Df]||null}function Ec(t,n){if(t==null)throw new b(-919,!1)}function dn(t){return typeof t=="string"?t:t==null?"":String(t)}var Ey=ne({ngErrorCode:ne}),CM=ne({ngErrorMessage:ne}),IM=ne({ngTokenPath:ne});function Cf(t,n){return wy("",-200,n)}function wc(t,n){throw new b(-201,!1)}function wy(t,n,e){let r=new b(n,t);return r[Ey]=n,r[CM]=t,e&&(r[IM]=e),r}function SM(t){return t[Ey]}var cf;function Cy(){return cf}function Ke(t){let n=cf;return cf=t,n}function If(t,n,e){let r=Ji(t);if(r&&r.providedIn=="root")return r.value===void 0?r.value=r.factory():r.value;if(e&8)return null;if(n!==void 0)return n;wc(t,"")}var MM={},Pr=MM,TM="__NG_DI_FLAG__",lf=class{injector;constructor(n){this.injector=n}retrieve(n,e){let r=Lr(e)||0;try{return this.injector.get(n,r&8?null:Pr,r)}catch(o){if(Oo(o))return o;throw o}}};function xM(t,n=0){let e=dc();if(e===void 0)throw new b(-203,!1);if(e===null)return If(t,void 0,n);{let r=AM(n),o=e.retrieve(t,r);if(Oo(o)){if(r.optional)return null;throw o}return o}}function w(t,n=0){return(Cy()||xM)(Me(t),n)}function f(t,n){return w(t,Lr(n))}function Lr(t){return typeof t>"u"||typeof t=="number"?t:0|(t.optional&&8)|(t.host&&1)|(t.self&&2)|(t.skipSelf&&4)}function AM(t){return{optional:!!(t&8),host:!!(t&1),self:!!(t&2),skipSelf:!!(t&4)}}function uf(t){let n=[];for(let e=0;eArray.isArray(e)?Cc(e,n):n(e))}function Sf(t,n,e){n>=t.length?t.push(e):t.splice(n,0,e)}function ns(t,n){return n>=t.length-1?t.pop():t.splice(n,1)[0]}function My(t,n){let e=[];for(let r=0;rn;){let i=o-2;t[o]=t[i],o--}t[n]=e,t[n+1]=r}}function Ic(t,n,e){let r=Fo(t,n);return r>=0?t[r|1]=e:(r=~r,Ty(t,r,n,e)),r}function Sc(t,n){let e=Fo(t,n);if(e>=0)return t[e|1]}function Fo(t,n){return NM(t,n,1)}function NM(t,n,e){let r=0,o=t.length>>e;for(;o!==r;){let i=r+(o-r>>1),s=t[i<n?o=i:r=i+1}return~(o<{e.push(s)};return Cc(n,s=>{let a=s;pc(a,i,[],r)&&(o||=[],o.push(a))}),o!==void 0&&Ay(o,i),e}function Ay(t,n){for(let e=0;e{n(i,r)})}}function pc(t,n,e,r){if(t=Me(t),!t)return!1;let o=null,i=af(t),s=!i&&un(t);if(!i&&!s){let c=t.ngModule;if(i=af(c),i)o=c;else return!1}else{if(s&&!s.standalone)return!1;o=t}let a=r.has(o);if(s){if(a)return!1;if(r.add(o),s.dependencies){let c=typeof s.dependencies=="function"?s.dependencies():s.dependencies;for(let l of c)pc(l,n,e,r)}}else if(i){if(i.imports!=null&&!a){r.add(o);let l;Cc(i.imports,u=>{pc(u,n,e,r)&&(l||=[],l.push(u))}),l!==void 0&&Ay(l,n)}if(!a){let l=rr(o)||(()=>new o);n({provide:o,useFactory:l,deps:Ge},o),n({provide:Tf,useValue:o,multi:!0},o),n({provide:Ur,useValue:()=>w(o),multi:!0},o)}let c=i.providers;if(c!=null&&!a){let l=t;Af(c,u=>{n(u,l)})}}else return!1;return o!==t&&t.providers!==void 0}function Af(t,n){for(let e of t)_f(e)&&(e=e.\u0275providers),Array.isArray(e)?Af(e,n):n(e)}var OM=ne({provide:String,useValue:ne});function Ry(t){return t!==null&&typeof t=="object"&&OM in t}function kM(t){return!!(t&&t.useExisting)}function FM(t){return!!(t&&t.useFactory)}function jr(t){return typeof t=="function"}function Ny(t){return!!t.useClass}var rs=new y(""),fc={},vy={},sf;function Po(){return sf===void 0&&(sf=new Yi),sf}var re=class{},Vr=class extends re{parent;source;scopes;records=new Map;_ngOnDestroyHooks=new Set;_onDestroyHooks=[];get destroyed(){return this._destroyed}_destroyed=!1;injectorDefTypes;constructor(n,e,r,o){super(),this.parent=e,this.source=r,this.scopes=o,ff(n,s=>this.processProvider(s)),this.records.set(Mf,ko(void 0,this)),o.has("environment")&&this.records.set(re,ko(void 0,this));let i=this.records.get(rs);i!=null&&typeof i.value=="string"&&this.scopes.add(i.value),this.injectorDefTypes=new Set(this.get(Tf,Ge,{self:!0}))}retrieve(n,e){let r=Lr(e)||0;try{return this.get(n,Pr,r)}catch(o){if(Oo(o))return o;throw o}}destroy(){Wi(this),this._destroyed=!0;let n=x(null);try{for(let r of this._ngOnDestroyHooks)r.ngOnDestroy();let e=this._onDestroyHooks;this._onDestroyHooks=[];for(let r of e)r()}finally{this.records.clear(),this._ngOnDestroyHooks.clear(),this.injectorDefTypes.clear(),x(n)}}onDestroy(n){return Wi(this),this._onDestroyHooks.push(n),()=>this.removeOnDestroy(n)}runInContext(n){Wi(this);let e=cn(this),r=Ke(void 0),o;try{return n()}finally{cn(e),Ke(r)}}get(n,e=Pr,r){if(Wi(this),n.hasOwnProperty(gy))return n[gy](this);let o=Lr(r),i,s=cn(this),a=Ke(void 0);try{if(!(o&4)){let l=this.records.get(n);if(l===void 0){let u=BM(n)&&Ji(n);u&&this.injectableDefInScope(u)?l=ko(df(n),fc):l=null,this.records.set(n,l)}if(l!=null)return this.hydrate(n,l,o)}let c=o&2?Po():this.parent;return e=o&8&&e===Pr?null:e,c.get(n,e)}catch(c){let l=SM(c);throw l===-200||l===-201?new b(l,null):c}finally{Ke(a),cn(s)}}resolveInjectorInitializers(){let n=x(null),e=cn(this),r=Ke(void 0),o;try{let i=this.get(Ur,Ge,{self:!0});for(let s of i)s()}finally{cn(e),Ke(r),x(n)}}toString(){return"R3Injector[...]"}processProvider(n){n=Me(n);let e=jr(n)?n:Me(n&&n.provide),r=LM(n);if(!jr(n)&&n.multi===!0){let o=this.records.get(e);o||(o=ko(void 0,fc,!0),o.factory=()=>uf(o.multi),this.records.set(e,o)),e=n,o.multi.push(n)}this.records.set(e,r)}hydrate(n,e,r){let o=x(null);try{if(e.value===vy)throw Cf("");return e.value===fc&&(e.value=vy,e.value=e.factory(void 0,r)),typeof e.value=="object"&&e.value&&VM(e.value)&&this._ngOnDestroyHooks.add(e.value),e.value}finally{x(o)}}injectableDefInScope(n){if(!n.providedIn)return!1;let e=Me(n.providedIn);return typeof e=="string"?e==="any"||this.scopes.has(e):this.injectorDefTypes.has(e)}removeOnDestroy(n){let e=this._onDestroyHooks.indexOf(n);e!==-1&&this._onDestroyHooks.splice(e,1)}};function df(t){let n=Ji(t),e=n!==null?n.factory:rr(t);if(e!==null)return e;if(t instanceof y)throw new b(-204,!1);if(t instanceof Function)return PM(t);throw new b(-204,!1)}function PM(t){if(t.length>0)throw new b(-204,!1);let e=wM(t);return e!==null?()=>e.factory(t):()=>new t}function LM(t){if(Ry(t))return ko(void 0,t.useValue);{let n=Rf(t);return ko(n,fc)}}function Rf(t,n,e){let r;if(jr(t)){let o=Me(t);return rr(o)||df(o)}else if(Ry(t))r=()=>Me(t.useValue);else if(FM(t))r=()=>t.useFactory(...uf(t.deps||[]));else if(kM(t))r=(o,i)=>w(Me(t.useExisting),i!==void 0&&i&8?8:void 0);else{let o=Me(t&&(t.useClass||t.provide));if(jM(t))r=()=>new o(...uf(t.deps));else return rr(o)||df(o)}return r}function Wi(t){if(t.destroyed)throw new b(-205,!1)}function ko(t,n,e=!1){return{factory:t,value:n,multi:e?[]:void 0}}function jM(t){return!!t.deps}function VM(t){return t!==null&&typeof t=="object"&&typeof t.ngOnDestroy=="function"}function BM(t){return typeof t=="function"||typeof t=="object"&&t.ngMetadataName==="InjectionToken"}function ff(t,n){for(let e of t)Array.isArray(e)?ff(e,n):e&&_f(e)?ff(e.\u0275providers,n):n(e)}function xe(t,n){let e;t instanceof Vr?(Wi(t),e=t):e=new lf(t);let r,o=cn(e),i=Ke(void 0);try{return n()}finally{cn(o),Ke(i)}}function Nf(){return Cy()!==void 0||dc()!=null}var Lt=0,A=1,P=2,Te=3,Et=4,Qe=5,Hr=6,Lo=7,me=8,An=9,jt=10,ie=11,jo=12,Of=13,$r=14,We=15,ar=16,zr=17,fn=18,Rn=19,kf=20,xn=21,Mc=22,or=23,ct=24,Gr=25,cr=26,le=27,Oy=1,Ff=6,lr=7,os=8,Wr=9,ge=10;function Nn(t){return Array.isArray(t)&&typeof t[Oy]=="object"}function Vt(t){return Array.isArray(t)&&t[Oy]===!0}function Pf(t){return(t.flags&4)!==0}function hn(t){return t.componentOffset>-1}function Vo(t){return(t.flags&1)===1}function Bt(t){return!!t.template}function Bo(t){return(t[P]&512)!==0}function qr(t){return(t[P]&256)===256}var Lf="svg",ky="math";function wt(t){for(;Array.isArray(t);)t=t[Lt];return t}function jf(t,n){return wt(n[t])}function Ct(t,n){return wt(n[t.index])}function Tc(t,n){return t.data[n]}function is(t,n){return t[n]}function Vf(t,n,e,r){e>=t.data.length&&(t.data[e]=null,t.blueprint[e]=null),n[e]=r}function It(t,n){let e=n[t];return Nn(e)?e:e[Lt]}function Fy(t){return(t[P]&4)===4}function xc(t){return(t[P]&128)===128}function Py(t){return Vt(t[Te])}function lt(t,n){return n==null?null:t[n]}function Bf(t){t[zr]=0}function Uf(t){t[P]&1024||(t[P]|=1024,xc(t)&&Yr(t))}function Ly(t,n){for(;t>0;)n=n[$r],t--;return n}function ss(t){return!!(t[P]&9216||t[ct]?.dirty)}function Ac(t){t[jt].changeDetectionScheduler?.notify(8),t[P]&64&&(t[P]|=1024),ss(t)&&Yr(t)}function Yr(t){t[jt].changeDetectionScheduler?.notify(0);let n=ir(t);for(;n!==null&&!(n[P]&8192||(n[P]|=8192,!xc(n)));)n=ir(n)}function Hf(t,n){if(qr(t))throw new b(911,!1);t[xn]===null&&(t[xn]=[]),t[xn].push(n)}function jy(t,n){if(t[xn]===null)return;let e=t[xn].indexOf(n);e!==-1&&t[xn].splice(e,1)}function ir(t){let n=t[Te];return Vt(n)?n[Te]:n}function $f(t){return t[Lo]??=[]}function zf(t){return t.cleanup??=[]}function Vy(t,n,e,r){let o=$f(n);o.push(e),t.firstCreatePass&&zf(t).push(r,o.length-1)}var B={lFrame:Xy(null),bindingsEnabled:!0,skipHydrationRootTNode:null};var hf=!1;function By(){return B.lFrame.elementDepthCount}function Uy(){B.lFrame.elementDepthCount++}function Gf(){B.lFrame.elementDepthCount--}function Rc(){return B.bindingsEnabled}function Wf(){return B.skipHydrationRootTNode!==null}function qf(t){return B.skipHydrationRootTNode===t}function Yf(){B.skipHydrationRootTNode=null}function C(){return B.lFrame.lView}function ce(){return B.lFrame.tView}function Hy(t){return B.lFrame.contextLView=t,t[me]}function $y(t){return B.lFrame.contextLView=null,t}function Ee(){let t=Zf();for(;t!==null&&t.type===64;)t=t.parent;return t}function Zf(){return B.lFrame.currentTNode}function zy(){let t=B.lFrame,n=t.currentTNode;return t.isParent?n:n.parent}function Uo(t,n){let e=B.lFrame;e.currentTNode=t,e.isParent=n}function Kf(){return B.lFrame.isParent}function Qf(){B.lFrame.isParent=!1}function Gy(){return B.lFrame.contextLView}function Xf(){return hf}function Zi(t){let n=hf;return hf=t,n}function pn(){let t=B.lFrame,n=t.bindingRootIndex;return n===-1&&(n=t.bindingRootIndex=t.tView.bindingStartIndex),n}function Jf(){return B.lFrame.bindingIndex}function Wy(t){return B.lFrame.bindingIndex=t}function On(){return B.lFrame.bindingIndex++}function as(t){let n=B.lFrame,e=n.bindingIndex;return n.bindingIndex=n.bindingIndex+t,e}function qy(){return B.lFrame.inI18n}function Yy(t,n){let e=B.lFrame;e.bindingIndex=e.bindingRootIndex=t,Nc(n)}function Zy(){return B.lFrame.currentDirectiveIndex}function Nc(t){B.lFrame.currentDirectiveIndex=t}function Ky(t){let n=B.lFrame.currentDirectiveIndex;return n===-1?null:t[n]}function Oc(){return B.lFrame.currentQueryIndex}function cs(t){B.lFrame.currentQueryIndex=t}function UM(t){let n=t[A];return n.type===2?n.declTNode:n.type===1?t[Qe]:null}function eh(t,n,e){if(e&4){let o=n,i=t;for(;o=o.parent,o===null&&!(e&1);)if(o=UM(i),o===null||(i=i[$r],o.type&10))break;if(o===null)return!1;n=o,t=i}let r=B.lFrame=Qy();return r.currentTNode=n,r.lView=t,!0}function kc(t){let n=Qy(),e=t[A];B.lFrame=n,n.currentTNode=e.firstChild,n.lView=t,n.tView=e,n.contextLView=t,n.bindingIndex=e.bindingStartIndex,n.inI18n=!1}function Qy(){let t=B.lFrame,n=t===null?null:t.child;return n===null?Xy(t):n}function Xy(t){let n={currentTNode:null,isParent:!0,lView:null,tView:null,selectedIndex:-1,contextLView:null,elementDepthCount:0,currentNamespace:null,currentDirectiveIndex:-1,bindingRootIndex:-1,bindingIndex:-1,currentQueryIndex:0,parent:t,child:null,inI18n:!1};return t!==null&&(t.child=n),n}function Jy(){let t=B.lFrame;return B.lFrame=t.parent,t.currentTNode=null,t.lView=null,t}var th=Jy;function Fc(){let t=Jy();t.isParent=!0,t.tView=null,t.selectedIndex=-1,t.contextLView=null,t.elementDepthCount=0,t.currentDirectiveIndex=-1,t.currentNamespace=null,t.bindingRootIndex=-1,t.bindingIndex=-1,t.currentQueryIndex=0}function eb(t){return(B.lFrame.contextLView=Ly(t,B.lFrame.contextLView))[me]}function Ut(){return B.lFrame.selectedIndex}function ur(t){B.lFrame.selectedIndex=t}function Ho(){let t=B.lFrame;return Tc(t.tView,t.selectedIndex)}function tb(){B.lFrame.currentNamespace=Lf}function nb(){HM()}function HM(){B.lFrame.currentNamespace=null}function nh(){return B.lFrame.currentNamespace}var rb=!0;function Pc(){return rb}function ls(t){rb=t}function pf(t,n=null,e=null,r){let o=rh(t,n,e,r);return o.resolveInjectorInitializers(),o}function rh(t,n=null,e=null,r,o=new Set){let i=[e||Ge,xy(t)],s;return new Vr(i,n||Po(),s||null,o)}var $=class t{static THROW_IF_NOT_FOUND=Pr;static NULL=new Yi;static create(n,e){if(Array.isArray(n))return pf({name:""},e,n,"");{let r=n.name??"";return pf({name:r},n.parent,n.providers,r)}}static \u0275prov=v({token:t,providedIn:"any",factory:()=>w(Mf)});static __NG_ELEMENT_ID__=-1},L=new y(""),Ae=(()=>{class t{static __NG_ELEMENT_ID__=$M;static __NG_ENV_ID__=e=>e}return t})(),mc=class extends Ae{_lView;constructor(n){super(),this._lView=n}get destroyed(){return qr(this._lView)}onDestroy(n){let e=this._lView;return Hf(e,n),()=>jy(e,n)}};function $M(){return new mc(C())}var ob=!1,ib=new y(""),kn=(()=>{class t{taskId=0;pendingTasks=new Set;destroyed=!1;pendingTask=new Ie(!1);debugTaskTracker=f(ib,{optional:!0});get hasPendingTasks(){return this.destroyed?!1:this.pendingTask.value}get hasPendingTasksObservable(){return this.destroyed?new O(e=>{e.next(!1),e.complete()}):this.pendingTask}add(){!this.hasPendingTasks&&!this.destroyed&&this.pendingTask.next(!0);let e=this.taskId++;return this.pendingTasks.add(e),this.debugTaskTracker?.add(e),e}has(e){return this.pendingTasks.has(e)}remove(e){this.pendingTasks.delete(e),this.debugTaskTracker?.remove(e),this.pendingTasks.size===0&&this.hasPendingTasks&&this.pendingTask.next(!1)}ngOnDestroy(){this.pendingTasks.clear(),this.hasPendingTasks&&this.pendingTask.next(!1),this.destroyed=!0,this.pendingTask.unsubscribe()}static \u0275prov=v({token:t,providedIn:"root",factory:()=>new t})}return t})(),mf=class extends S{__isAsync;destroyRef=void 0;pendingTasks=void 0;constructor(n=!1){super(),this.__isAsync=n,Nf()&&(this.destroyRef=f(Ae,{optional:!0})??void 0,this.pendingTasks=f(kn,{optional:!0})??void 0)}emit(n){let e=x(null);try{super.next(n)}finally{x(e)}}subscribe(n,e,r){let o=n,i=e||(()=>null),s=r;if(n&&typeof n=="object"){let c=n;o=c.next?.bind(c),i=c.error?.bind(c),s=c.complete?.bind(c)}this.__isAsync&&(i=this.wrapInTimeout(i),o&&(o=this.wrapInTimeout(o)),s&&(s=this.wrapInTimeout(s)));let a=super.subscribe({next:o,error:i,complete:s});return n instanceof G&&n.add(a),a}wrapInTimeout(n){return e=>{let r=this.pendingTasks?.add();setTimeout(()=>{try{n(e)}finally{r!==void 0&&this.pendingTasks?.remove(r)}})}}},U=mf;function gc(...t){}function oh(t){let n,e;function r(){t=gc;try{e!==void 0&&typeof cancelAnimationFrame=="function"&&cancelAnimationFrame(e),n!==void 0&&clearTimeout(n)}catch{}}return n=setTimeout(()=>{t(),r()}),typeof requestAnimationFrame=="function"&&(e=requestAnimationFrame(()=>{t(),r()})),()=>r()}function sb(t){return queueMicrotask(()=>t()),()=>{t=gc}}var ih="isAngularZone",Ki=ih+"_ID",zM=0,j=class t{hasPendingMacrotasks=!1;hasPendingMicrotasks=!1;isStable=!0;onUnstable=new U(!1);onMicrotaskEmpty=new U(!1);onStable=new U(!1);onError=new U(!1);constructor(n){let{enableLongStackTrace:e=!1,shouldCoalesceEventChangeDetection:r=!1,shouldCoalesceRunChangeDetection:o=!1,scheduleInRootZone:i=ob}=n;if(typeof Zone>"u")throw new b(908,!1);Zone.assertZonePatched();let s=this;s._nesting=0,s._outer=s._inner=Zone.current,Zone.TaskTrackingZoneSpec&&(s._inner=s._inner.fork(new Zone.TaskTrackingZoneSpec)),e&&Zone.longStackTraceZoneSpec&&(s._inner=s._inner.fork(Zone.longStackTraceZoneSpec)),s.shouldCoalesceEventChangeDetection=!o&&r,s.shouldCoalesceRunChangeDetection=o,s.callbackScheduled=!1,s.scheduleInRootZone=i,qM(s)}static isInAngularZone(){return typeof Zone<"u"&&Zone.current.get(ih)===!0}static assertInAngularZone(){if(!t.isInAngularZone())throw new b(909,!1)}static assertNotInAngularZone(){if(t.isInAngularZone())throw new b(909,!1)}run(n,e,r){return this._inner.run(n,e,r)}runTask(n,e,r,o){let i=this._inner,s=i.scheduleEventTask("NgZoneEvent: "+o,n,GM,gc,gc);try{return i.runTask(s,e,r)}finally{i.cancelTask(s)}}runGuarded(n,e,r){return this._inner.runGuarded(n,e,r)}runOutsideAngular(n){return this._outer.run(n)}},GM={};function sh(t){if(t._nesting==0&&!t.hasPendingMicrotasks&&!t.isStable)try{t._nesting++,t.onMicrotaskEmpty.emit(null)}finally{if(t._nesting--,!t.hasPendingMicrotasks)try{t.runOutsideAngular(()=>t.onStable.emit(null))}finally{t.isStable=!0}}}function WM(t){if(t.isCheckStableRunning||t.callbackScheduled)return;t.callbackScheduled=!0;function n(){oh(()=>{t.callbackScheduled=!1,gf(t),t.isCheckStableRunning=!0,sh(t),t.isCheckStableRunning=!1})}t.scheduleInRootZone?Zone.root.run(()=>{n()}):t._outer.run(()=>{n()}),gf(t)}function qM(t){let n=()=>{WM(t)},e=zM++;t._inner=t._inner.fork({name:"angular",properties:{[ih]:!0,[Ki]:e,[Ki+e]:!0},onInvokeTask:(r,o,i,s,a,c)=>{if(YM(c))return r.invokeTask(i,s,a,c);try{return yy(t),r.invokeTask(i,s,a,c)}finally{(t.shouldCoalesceEventChangeDetection&&s.type==="eventTask"||t.shouldCoalesceRunChangeDetection)&&n(),by(t)}},onInvoke:(r,o,i,s,a,c,l)=>{try{return yy(t),r.invoke(i,s,a,c,l)}finally{t.shouldCoalesceRunChangeDetection&&!t.callbackScheduled&&!ZM(c)&&n(),by(t)}},onHasTask:(r,o,i,s)=>{r.hasTask(i,s),o===i&&(s.change=="microTask"?(t._hasPendingMicrotasks=s.microTask,gf(t),sh(t)):s.change=="macroTask"&&(t.hasPendingMacrotasks=s.macroTask))},onHandleError:(r,o,i,s)=>(r.handleError(i,s),t.runOutsideAngular(()=>t.onError.emit(s)),!1)})}function gf(t){t._hasPendingMicrotasks||(t.shouldCoalesceEventChangeDetection||t.shouldCoalesceRunChangeDetection)&&t.callbackScheduled===!0?t.hasPendingMicrotasks=!0:t.hasPendingMicrotasks=!1}function yy(t){t._nesting++,t.isStable&&(t.isStable=!1,t.onUnstable.emit(null))}function by(t){t._nesting--,sh(t)}var Qi=class{hasPendingMicrotasks=!1;hasPendingMacrotasks=!1;isStable=!0;onUnstable=new U;onMicrotaskEmpty=new U;onStable=new U;onError=new U;run(n,e,r){return n.apply(e,r)}runGuarded(n,e,r){return n.apply(e,r)}runOutsideAngular(n){return n()}runTask(n,e,r,o){return n.apply(e,r)}};function YM(t){return ab(t,"__ignore_ng_zone__")}function ZM(t){return ab(t,"__scheduler_tick__")}function ab(t,n){return!Array.isArray(t)||t.length!==1?!1:t[0]?.data?.[n]===!0}var _t=class{_console=console;handleError(n){this._console.error("ERROR",n)}},ut=new y("",{factory:()=>{let t=f(j),n=f(re),e;return r=>{t.runOutsideAngular(()=>{n.destroyed&&!e?setTimeout(()=>{throw r}):(e??=n.get(_t),e.handleError(r))})}}}),cb={provide:Ur,useValue:()=>{let t=f(_t,{optional:!0})},multi:!0};function W(t,n){let[e,r,o]=ef(t,n?.equal),i=e,s=i[pe];return i.set=r,i.update=o,i.asReadonly=us.bind(i),i}function us(){let t=this[pe];if(t.readonlyFn===void 0){let n=()=>this();n[pe]=t,t.readonlyFn=n}return t.readonlyFn}var $o=(()=>{class t{view;node;constructor(e,r){this.view=e,this.node=r}static __NG_ELEMENT_ID__=KM}return t})();function KM(){return new $o(C(),Ee())}var ln=class{},ds=new y("",{factory:()=>!0});var ah=new y(""),zo=(()=>{class t{internalPendingTasks=f(kn);scheduler=f(ln);errorHandler=f(ut);add(){let e=this.internalPendingTasks.add();return()=>{this.internalPendingTasks.has(e)&&(this.scheduler.notify(11),this.internalPendingTasks.remove(e))}}run(e){let r=this.add();e().catch(this.errorHandler).finally(r)}static \u0275prov=v({token:t,providedIn:"root",factory:()=>new t})}return t})(),Lc=(()=>{class t{static \u0275prov=v({token:t,providedIn:"root",factory:()=>new vf})}return t})(),vf=class{dirtyEffectCount=0;queues=new Map;add(n){this.enqueue(n),this.schedule(n)}schedule(n){n.dirty&&this.dirtyEffectCount++}remove(n){let e=n.zone,r=this.queues.get(e);r.has(n)&&(r.delete(n),n.dirty&&this.dirtyEffectCount--)}enqueue(n){let e=n.zone;this.queues.has(e)||this.queues.set(e,new Set);let r=this.queues.get(e);r.has(n)||r.add(n)}flush(){for(;this.dirtyEffectCount>0;){let n=!1;for(let[e,r]of this.queues)e===null?n||=this.flushQueue(r):n||=e.run(()=>this.flushQueue(r));n||(this.dirtyEffectCount=0)}}flushQueue(n){let e=!1;for(let r of n)r.dirty&&(this.dirtyEffectCount--,e=!0,r.run());return e}},vc=class{[pe];constructor(n){this[pe]=n}destroy(){this[pe].destroy()}};function Go(t,n){let e=n?.injector??f($),r=n?.manualCleanup!==!0?e.get(Ae):null,o,i=e.get($o,null,{optional:!0}),s=e.get(ln);return i!==null?(o=JM(i.view,s,t),r instanceof mc&&r._lView===i.view&&(r=null)):o=eT(t,e.get(Lc),s),o.injector=e,r!==null&&(o.onDestroyFns=[r.onDestroy(()=>o.destroy())]),new vc(o)}var lb=F(g({},tf),{cleanupFns:void 0,zone:null,onDestroyFns:null,run(){let t=Zi(!1);try{nf(this)}finally{Zi(t)}},cleanup(){if(!this.cleanupFns?.length)return;let t=x(null);try{for(;this.cleanupFns.length;)this.cleanupFns.pop()()}finally{this.cleanupFns=[],x(t)}}}),QM=F(g({},lb),{consumerMarkedDirty(){this.scheduler.schedule(this),this.notifier.notify(12)},destroy(){if(tr(this),this.onDestroyFns!==null)for(let t of this.onDestroyFns)t();this.cleanup(),this.scheduler.remove(this)}}),XM=F(g({},lb),{consumerMarkedDirty(){this.view[P]|=8192,Yr(this.view),this.notifier.notify(13)},destroy(){if(tr(this),this.onDestroyFns!==null)for(let t of this.onDestroyFns)t();this.cleanup(),this.view[or]?.delete(this)}});function JM(t,n,e){let r=Object.create(XM);return r.view=t,r.zone=typeof Zone<"u"?Zone.current:null,r.notifier=n,r.fn=ub(r,e),t[or]??=new Set,t[or].add(r),r.consumerMarkedDirty(r),r}function eT(t,n,e){let r=Object.create(QM);return r.fn=ub(r,t),r.scheduler=n,r.notifier=e,r.zone=typeof Zone<"u"?Zone.current:null,r.scheduler.add(r),r.notifier.notify(12),r}function ub(t,n){return()=>{n(e=>(t.cleanupFns??=[]).push(e))}}function ws(t){return{toString:t}.toString()}function aT(t){return typeof t=="function"}function Yb(t,n,e,r){n!==null?n.applyValueToInputSignal(n,r):t[e]=r}var qc=class{previousValue;currentValue;firstChange;constructor(n,e,r){this.previousValue=n,this.currentValue=e,this.firstChange=r}isFirstChange(){return this.firstChange}},Re=(()=>{let t=()=>Zb;return t.ngInherit=!0,t})();function Zb(t){return t.type.prototype.ngOnChanges&&(t.setInput=lT),cT}function cT(){let t=Qb(this),n=t?.current;if(n){let e=t.previous;if(e===Pt)t.previous=n;else for(let r in n)e[r]=n[r];t.current=null,this.ngOnChanges(n)}}function lT(t,n,e,r,o){let i=this.declaredInputs[r],s=Qb(t)||uT(t,{previous:Pt,current:null}),a=s.current||(s.current={}),c=s.previous,l=c[i];a[i]=new qc(l&&l.currentValue,e,c===Pt),Yb(t,n,o,e)}var Kb="__ngSimpleChanges__";function Qb(t){return t[Kb]||null}function uT(t,n){return t[Kb]=n}var db=[];var oe=function(t,n=null,e){for(let r=0;r=r)break}else n[c]<0&&(t[zr]+=65536),(a>14>16&&(t[P]&3)===n&&(t[P]+=16384,fb(a,i)):fb(a,i)}var qo=-1,Kr=class{factory;name;injectImpl;resolving=!1;canSeeViewProviders;multi;componentProviders;index;providerFactory;constructor(n,e,r,o){this.factory=n,this.name=o,this.canSeeViewProviders=e,this.injectImpl=r}};function hT(t){return(t.flags&8)!==0}function pT(t){return(t.flags&16)!==0}function mT(t,n,e){let r=0;for(;rn){s=i-1;break}}}for(;i>16}function Zc(t,n){let e=vT(t),r=n;for(;e>0;)r=r[$r],e--;return r}var bh=!0;function Kc(t){let n=bh;return bh=t,n}var yT=256,n_=yT-1,r_=5,bT=0,mn={};function _T(t,n,e){let r;typeof e=="string"?r=e.charCodeAt(0)||0:e.hasOwnProperty(Br)&&(r=e[Br]),r==null&&(r=e[Br]=bT++);let o=r&n_,i=1<>r_)]|=i}function Qc(t,n){let e=o_(t,n);if(e!==-1)return e;let r=n[A];r.firstCreatePass&&(t.injectorIndex=n.length,lh(r.data,t),lh(n,null),lh(r.blueprint,null));let o=rp(t,n),i=t.injectorIndex;if(t_(o)){let s=Yc(o),a=Zc(o,n),c=a[A].data;for(let l=0;l<8;l++)n[i+l]=a[s+l]|c[s+l]}return n[i+8]=o,i}function lh(t,n){t.push(0,0,0,0,0,0,0,0,n)}function o_(t,n){return t.injectorIndex===-1||t.parent&&t.parent.injectorIndex===t.injectorIndex||n[t.injectorIndex+8]===null?-1:t.injectorIndex}function rp(t,n){if(t.parent&&t.parent.injectorIndex!==-1)return t.parent.injectorIndex;let e=0,r=null,o=n;for(;o!==null;){if(r=l_(o),r===null)return qo;if(e++,o=o[$r],r.injectorIndex!==-1)return r.injectorIndex|e<<16}return qo}function _h(t,n,e){_T(t,n,e)}function DT(t,n){if(n==="class")return t.classes;if(n==="style")return t.styles;let e=t.attrs;if(e){let r=e.length,o=0;for(;o>20,d=r?a:a+u,h=o?a+u:l;for(let p=d;p=c&&m.type===e)return p}if(o){let p=s[c];if(p&&Bt(p)&&p.type===e)return c}return null}function gs(t,n,e,r,o){let i=t[e],s=n.data;if(i instanceof Kr){let a=i;if(a.resolving)throw Cf("");let c=Kc(a.canSeeViewProviders);a.resolving=!0;let l=s[e].type||s[e],u,d=a.injectImpl?Ke(a.injectImpl):null,h=eh(t,r,0);try{i=t[e]=a.factory(void 0,o,s,t,r),n.firstCreatePass&&e>=r.directiveStart&&dT(e,s[e],n)}finally{d!==null&&Ke(d),Kc(c),a.resolving=!1,th()}}return i}function wT(t){if(typeof t=="string")return t.charCodeAt(0)||0;let n=t.hasOwnProperty(Br)?t[Br]:void 0;return typeof n=="number"?n>=0?n&n_:CT:n}function pb(t,n,e){let r=1<>r_)]&r)}function mb(t,n){return!(t&2)&&!(t&1&&n)}var Zr=class{_tNode;_lView;constructor(n,e){this._tNode=n,this._lView=e}get(n,e,r){return a_(this._tNode,this._lView,n,Lr(r),e)}};function CT(){return new Zr(Ee(),C())}function Ne(t){return ws(()=>{let n=t.prototype.constructor,e=n[qi]||Dh(n),r=Object.prototype,o=Object.getPrototypeOf(t.prototype).constructor;for(;o&&o!==r;){let i=o[qi]||Dh(o);if(i&&i!==e)return i;o=Object.getPrototypeOf(o)}return i=>new i})}function Dh(t){return yf(t)?()=>{let n=Dh(Me(t));return n&&n()}:rr(t)}function IT(t,n,e,r,o){let i=t,s=n;for(;i!==null&&s!==null&&s[P]&2048&&!Bo(s);){let a=c_(i,s,e,r|2,mn);if(a!==mn)return a;let c=i.parent;if(!c){let l=s[kf];if(l){let u=l.get(e,mn,r&-5);if(u!==mn)return u}c=l_(s),s=s[$r]}i=c}return o}function l_(t){let n=t[A],e=n.type;return e===2?n.declTNode:e===1?t[Qe]:null}function Cs(t){return DT(Ee(),t)}function ST(){return Jo(Ee(),C())}function Jo(t,n){return new z(Ct(t,n))}var z=(()=>{class t{nativeElement;constructor(e){this.nativeElement=e}static __NG_ELEMENT_ID__=ST}return t})();function u_(t){return t instanceof z?t.nativeElement:t}function MT(){return this._results[Symbol.iterator]()}var Fn=class{_emitDistinctChangesOnly;dirty=!0;_onDirty=void 0;_results=[];_changesDetected=!1;_changes=void 0;length=0;first=void 0;last=void 0;get changes(){return this._changes??=new S}constructor(n=!1){this._emitDistinctChangesOnly=n}get(n){return this._results[n]}map(n){return this._results.map(n)}filter(n){return this._results.filter(n)}find(n){return this._results.find(n)}reduce(n,e){return this._results.reduce(n,e)}forEach(n){this._results.forEach(n)}some(n){return this._results.some(n)}toArray(){return this._results.slice()}toString(){return this._results.toString()}reset(n,e){this.dirty=!1;let r=Sy(n);(this._changesDetected=!Iy(this._results,r,e))&&(this._results=r,this.length=r.length,this.last=r[this.length-1],this.first=r[0])}notifyOnChanges(){this._changes!==void 0&&(this._changesDetected||!this._emitDistinctChangesOnly)&&this._changes.next(this)}onDirty(n){this._onDirty=n}setDirty(){this.dirty=!0,this._onDirty?.()}destroy(){this._changes!==void 0&&(this._changes.complete(),this._changes.unsubscribe())}[Symbol.iterator]=MT};function d_(t){return(t.flags&128)===128}var op=(function(t){return t[t.OnPush=0]="OnPush",t[t.Eager=1]="Eager",t[t.Default=1]="Default",t})(op||{}),f_=new Map,TT=0;function xT(){return TT++}function AT(t){f_.set(t[Rn],t)}function Eh(t){f_.delete(t[Rn])}var gb="__ngContext__";function Zo(t,n){Nn(n)?(t[gb]=n[Rn],AT(n)):t[gb]=n}function h_(t){return m_(t[jo])}function p_(t){return m_(t[Et])}function m_(t){for(;t!==null&&!Vt(t);)t=t[Et];return t}var wh;function ip(t){wh=t}function g_(){if(wh!==void 0)return wh;if(typeof document<"u")return document;throw new b(210,!1)}var hr=new y("",{factory:()=>RT}),RT="ng";var dl=new y(""),Jr=new y("",{providedIn:"platform",factory:()=>"unknown"}),Is=new y(""),eo=new y("",{factory:()=>f(L).body?.querySelector("[ngCspNonce]")?.getAttribute("ngCspNonce")||null});var v_="r";var y_="di";var sp=new y(""),b_=!1,__=new y("",{factory:()=>b_});var fl=new y("");var vb=new WeakMap;function NT(t,n){if(t==null||typeof t!="object")return;let e=vb.get(t);e||(e=new WeakSet,vb.set(t,e)),e.add(n)}var OT=(t,n,e,r)=>{};function kT(t,n,e,r){OT(t,n,e,r)}function hl(t){return(t.flags&32)===32}var FT=()=>null;function D_(t,n,e=!1){return FT(t,n,e)}function E_(t,n){let e=t.contentQueries;if(e!==null){let r=x(null);try{for(let o=0;ot,createScript:t=>t,createScriptURL:t=>t})}catch{}return jc}function pl(t){return PT()?.createHTML(t)||t}var Vc;function w_(){if(Vc===void 0&&(Vc=null,ye.trustedTypes))try{Vc=ye.trustedTypes.createPolicy("angular#unsafe-bypass",{createHTML:t=>t,createScript:t=>t,createScriptURL:t=>t})}catch{}return Vc}function yb(t){return w_()?.createHTML(t)||t}function bb(t){return w_()?.createScriptURL(t)||t}var Pn=class{changingThisBreaksApplicationSecurity;constructor(n){this.changingThisBreaksApplicationSecurity=n}toString(){return`SafeValue must use [property]=binding: ${this.changingThisBreaksApplicationSecurity} (see ${yc})`}},Ih=class extends Pn{getTypeName(){return"HTML"}},Sh=class extends Pn{getTypeName(){return"Style"}},Mh=class extends Pn{getTypeName(){return"Script"}},Th=class extends Pn{getTypeName(){return"URL"}},xh=class extends Pn{getTypeName(){return"ResourceURL"}};function ft(t){return t instanceof Pn?t.changingThisBreaksApplicationSecurity:t}function vn(t,n){let e=C_(t);if(e!=null&&e!==n){if(e==="ResourceURL"&&n==="URL")return!0;throw new Error(`Required a safe ${n}, got a ${e} (see ${yc})`)}return e===n}function C_(t){return t instanceof Pn&&t.getTypeName()||null}function cp(t){return new Ih(t)}function lp(t){return new Sh(t)}function up(t){return new Mh(t)}function dp(t){return new Th(t)}function fp(t){return new xh(t)}function LT(t){let n=new Rh(t);return jT()?new Ah(n):n}var Ah=class{inertDocumentHelper;constructor(n){this.inertDocumentHelper=n}getInertBodyElement(n){n=""+n;try{let e=new window.DOMParser().parseFromString(pl(n),"text/html").body;return e===null?this.inertDocumentHelper.getInertBodyElement(n):(e.firstChild?.remove(),e)}catch{return null}}},Rh=class{defaultDoc;inertDocument;constructor(n){this.defaultDoc=n,this.inertDocument=this.defaultDoc.implementation.createHTMLDocument("sanitization-inert")}getInertBodyElement(n){let e=this.inertDocument.createElement("template");return e.innerHTML=pl(n),e}};function jT(){try{return!!new window.DOMParser().parseFromString(pl(""),"text/html")}catch{return!1}}var VT=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:\/?#]*(?:[\/?#]|$))/i;function Ss(t){return t=String(t),t.match(VT)?t:"unsafe:"+t}function Ln(t){let n={};for(let e of t.split(","))n[e]=!0;return n}function Ms(...t){let n={};for(let e of t)for(let r in e)e.hasOwnProperty(r)&&(n[r]=!0);return n}var I_=Ln("area,br,col,hr,img,wbr"),S_=Ln("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),M_=Ln("rp,rt"),BT=Ms(M_,S_),UT=Ms(S_,Ln("address,article,aside,blockquote,caption,center,del,details,dialog,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,main,map,menu,nav,ol,pre,section,summary,table,ul")),HT=Ms(M_,Ln("a,abbr,acronym,audio,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,picture,q,ruby,rp,rt,s,samp,small,source,span,strike,strong,sub,sup,time,track,tt,u,var,video")),_b=Ms(I_,UT,HT,BT),T_=Ln("background,cite,href,itemtype,longdesc,poster,src,xlink:href"),$T=Ln("abbr,accesskey,align,alt,autoplay,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,controls,coords,datetime,default,dir,download,face,headers,height,hidden,hreflang,hspace,ismap,itemscope,itemprop,kind,label,lang,language,loop,media,muted,nohref,nowrap,open,preload,rel,rev,role,rows,rowspan,rules,scope,scrolling,shape,size,sizes,span,srclang,srcset,start,summary,tabindex,target,title,translate,type,usemap,valign,value,vspace,width"),zT=Ln("aria-activedescendant,aria-atomic,aria-autocomplete,aria-busy,aria-checked,aria-colcount,aria-colindex,aria-colspan,aria-controls,aria-current,aria-describedby,aria-details,aria-disabled,aria-dropeffect,aria-errormessage,aria-expanded,aria-flowto,aria-grabbed,aria-haspopup,aria-hidden,aria-invalid,aria-keyshortcuts,aria-label,aria-labelledby,aria-level,aria-live,aria-modal,aria-multiline,aria-multiselectable,aria-orientation,aria-owns,aria-placeholder,aria-posinset,aria-pressed,aria-readonly,aria-relevant,aria-required,aria-roledescription,aria-rowcount,aria-rowindex,aria-rowspan,aria-selected,aria-setsize,aria-sort,aria-valuemax,aria-valuemin,aria-valuenow,aria-valuetext"),GT=Ms(T_,$T,zT),WT=Ln("script,style,template"),Nh=class{sanitizedSomething=!1;buf=[];sanitizeChildren(n){let e=n.firstChild,r=!0,o=[];for(;e;){if(e.nodeType===Node.ELEMENT_NODE?r=this.startElement(e):e.nodeType===Node.TEXT_NODE?this.chars(e.nodeValue):this.sanitizedSomething=!0,r&&e.firstChild){o.push(e),e=ZT(e);continue}for(;e;){e.nodeType===Node.ELEMENT_NODE&&this.endElement(e);let i=YT(e);if(i){e=i;break}e=o.pop()}}return this.buf.join("")}startElement(n){let e=Db(n).toLowerCase();if(!_b.hasOwnProperty(e))return this.sanitizedSomething=!0,!WT.hasOwnProperty(e);this.buf.push("<"),this.buf.push(e);let r=n.attributes;for(let o=0;o"),!0}endElement(n){let e=Db(n).toLowerCase();_b.hasOwnProperty(e)&&!I_.hasOwnProperty(e)&&(this.buf.push(""))}chars(n){this.buf.push(Eb(n))}};function qT(t,n){return(t.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_CONTAINED_BY)!==Node.DOCUMENT_POSITION_CONTAINED_BY}function YT(t){let n=t.nextSibling;if(n&&t!==n.previousSibling)throw x_(n);return n}function ZT(t){let n=t.firstChild;if(n&&qT(t,n))throw x_(n);return n}function Db(t){let n=t.nodeName;return typeof n=="string"?n:"FORM"}function x_(t){return new Error(`Failed to sanitize html because the element is clobbered: ${t.outerHTML}`)}var KT=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,QT=/([^\#-~ |!])/g;function Eb(t){return t.replace(/&/g,"&").replace(KT,function(n){let e=n.charCodeAt(0),r=n.charCodeAt(1);return"&#"+((e-55296)*1024+(r-56320)+65536)+";"}).replace(QT,function(n){return"&#"+n.charCodeAt(0)+";"}).replace(//g,">")}var Bc;function ml(t,n){let e=null;try{Bc=Bc||LT(t);let r=n?String(n):"";e=Bc.getInertBodyElement(r);let o=5,i=r;do{if(o===0)throw new Error("Failed to sanitize html because the input is unstable");o--,r=i,i=e.innerHTML,e=Bc.getInertBodyElement(r)}while(r!==i);let a=new Nh().sanitizeChildren(wb(e)||e);return pl(a)}finally{if(e){let r=wb(e)||e;for(;r.firstChild;)r.firstChild.remove()}}}function wb(t){return"content"in t&&XT(t)?t.content:null}function XT(t){return t.nodeType===Node.ELEMENT_NODE&&t.nodeName==="TEMPLATE"}var JT=/^>|^->||--!>|)/g,t0="\u200B$1\u200B";function n0(t){return t.replace(JT,n=>n.replace(e0,t0))}function r0(t,n){return t.createText(n)}function o0(t,n,e){t.setValue(n,e)}function i0(t,n){return t.createComment(n0(n))}function A_(t,n,e){return t.createElement(n,e)}function Xc(t,n,e,r,o){t.insertBefore(n,e,r,o)}function R_(t,n,e){t.appendChild(n,e)}function Cb(t,n,e,r,o){r!==null?Xc(t,n,e,r,o):R_(t,n,e)}function N_(t,n,e,r){t.removeChild(null,n,e,r)}function s0(t,n,e){t.setAttribute(n,"style",e)}function a0(t,n,e){e===""?t.removeAttribute(n,"class"):t.setAttribute(n,"class",e)}function O_(t,n,e){let{mergedAttrs:r,classes:o,styles:i}=e;r!==null&&mT(t,n,r),o!==null&&a0(t,n,o),i!==null&&s0(t,n,i)}var ot=(function(t){return t[t.NONE=0]="NONE",t[t.HTML=1]="HTML",t[t.STYLE=2]="STYLE",t[t.SCRIPT=3]="SCRIPT",t[t.URL=4]="URL",t[t.RESOURCE_URL=5]="RESOURCE_URL",t[t.ATTRIBUTE_NO_BINDING=6]="ATTRIBUTE_NO_BINDING",t})(ot||{});function c0(t){let n=pp();return n?yb(n.sanitize(ot.HTML,t)||""):vn(t,"HTML")?yb(ft(t)):ml(g_(),dn(t))}function k_(t){let n=pp();return n?n.sanitize(ot.URL,t)||"":vn(t,"URL")?ft(t):Ss(dn(t))}function F_(t){let n=pp();if(n)return bb(n.sanitize(ot.RESOURCE_URL,t)||"");if(vn(t,"ResourceURL"))return bb(ft(t));throw new b(904,!1)}var l0={embed:{src:!0},frame:{src:!0},iframe:{src:!0},media:{src:!0},base:{href:!0},link:{href:!0},object:{data:!0,codebase:!0}};function u0(t,n){return l0[t.toLowerCase()]?.[n.toLowerCase()]===!0?F_:k_}function hp(t,n,e){return u0(n,e)(t)}function pp(){let t=C();return t&&t[jt].sanitizer}function d0(t){return t.ownerDocument.defaultView}function f0(t){return t.ownerDocument}function P_(t){return t instanceof Function?t():t}function h0(t,n,e){let r=t.length;for(;;){let o=t.indexOf(n,e);if(o===-1)return o;if(o===0||t.charCodeAt(o-1)<=32){let i=n.length;if(o+i===r||t.charCodeAt(o+i)<=32)return o}e=o+1}}var L_="ng-template";function p0(t,n,e,r){let o=0;if(r){for(;o-1){let i;for(;++oi?d="":d=o[u+1].toLowerCase(),r&2&&l!==d){if(Ht(r))return!1;s=!0}}}}return Ht(r)||s}function Ht(t){return(t&1)===0}function v0(t,n,e,r){if(n===null)return-1;let o=0;if(r||!e){let i=!1;for(;o-1)for(e++;e0?'="'+a+'"':"")+"]"}else r&8?o+="."+s:r&4&&(o+=" "+s);else o!==""&&!Ht(s)&&(n+=Ib(i,o),o=""),r=s,i=i||!Ht(r);e++}return o!==""&&(n+=Ib(i,o)),n}function w0(t){return t.map(E0).join(",")}function C0(t){let n=[],e=[],r=1,o=2;for(;r=0;i--){let s=e[i],a=s.parentNode;s===n?(e.splice(i,1),hs.add(s),s.dispatchEvent(new CustomEvent("animationend",{detail:{cancel:!0}}))):(o&&s===o||a&&r&&a!==r)&&(e.splice(i,1),s.dispatchEvent(new CustomEvent("animationend",{detail:{cancel:!0}})),s.parentNode?.removeChild(s))}}function A0(t,n){let e=kh.get(t);e?e.includes(n)||e.push(n):kh.set(t,[n])}var Qr=new Set,vl=(function(t){return t[t.CHANGE_DETECTION=0]="CHANGE_DETECTION",t[t.AFTER_NEXT_RENDER=1]="AFTER_NEXT_RENDER",t})(vl||{}),Wt=new y(""),Sb=new Set;function qt(t){Sb.has(t)||(Sb.add(t),performance?.mark?.("mark_feature_usage",{detail:{feature:t}}))}var yl=(()=>{class t{impl=null;execute(){this.impl?.execute()}static \u0275prov=v({token:t,providedIn:"root",factory:()=>new t})}return t})(),Dp=[0,1,2,3],Ep=(()=>{class t{ngZone=f(j);scheduler=f(ln);errorHandler=f(_t,{optional:!0});sequences=new Set;deferredRegistrations=new Set;executing=!1;constructor(){f(Wt,{optional:!0})}execute(){let e=this.sequences.size>0;e&&oe(K.AfterRenderHooksStart),this.executing=!0;for(let r of Dp)for(let o of this.sequences)if(!(o.erroredOrDestroyed||!o.hooks[r]))try{o.pipelinedValue=this.ngZone.runOutsideAngular(()=>this.maybeTrace(()=>{let i=o.hooks[r];return i(o.pipelinedValue)},o.snapshot))}catch(i){o.erroredOrDestroyed=!0,this.errorHandler?.handleError(i)}this.executing=!1;for(let r of this.sequences)r.afterRun(),r.once&&(this.sequences.delete(r),r.destroy());for(let r of this.deferredRegistrations)this.sequences.add(r);this.deferredRegistrations.size>0&&this.scheduler.notify(7),this.deferredRegistrations.clear(),e&&oe(K.AfterRenderHooksEnd)}register(e){let{view:r}=e;r!==void 0?((r[Gr]??=[]).push(e),Yr(r),r[P]|=8192):this.executing?this.deferredRegistrations.add(e):this.addSequence(e)}addSequence(e){this.sequences.add(e),this.scheduler.notify(7)}unregister(e){this.executing&&this.sequences.has(e)?(e.erroredOrDestroyed=!0,e.pipelinedValue=void 0,e.once=!0):(this.sequences.delete(e),this.deferredRegistrations.delete(e))}maybeTrace(e,r){return r?r.run(vl.AFTER_NEXT_RENDER,e):e()}static \u0275prov=v({token:t,providedIn:"root",factory:()=>new t})}return t})(),vs=class{impl;hooks;view;once;snapshot;erroredOrDestroyed=!1;pipelinedValue=void 0;unregisterOnDestroy;constructor(n,e,r,o,i,s=null){this.impl=n,this.hooks=e,this.view=r,this.once=o,this.snapshot=s,this.unregisterOnDestroy=i?.onDestroy(()=>this.destroy())}afterRun(){this.erroredOrDestroyed=!1,this.pipelinedValue=void 0,this.snapshot?.dispose(),this.snapshot=null}destroy(){this.impl.unregister(this),this.unregisterOnDestroy?.();let n=this.view?.[Gr];n&&(this.view[Gr]=n.filter(e=>e!==this))}};function ht(t,n){let e=n?.injector??f($);return qt("NgAfterNextRender"),N0(t,e,n,!0)}function R0(t){return t instanceof Function?[void 0,void 0,t,void 0]:[t.earlyRead,t.write,t.mixedReadWrite,t.read]}function N0(t,n,e,r){let o=n.get(yl);o.impl??=n.get(Ep);let i=n.get(Wt,null,{optional:!0}),s=e?.manualCleanup!==!0?n.get(Ae):null,a=n.get($o,null,{optional:!0}),c=new vs(o.impl,R0(t),a?.view,r,s,i?.snapshot(null));return o.impl.register(c),c}var H_=new y("",{factory:()=>({queue:new Set,isScheduled:!1,scheduler:null,injector:f(re)})});function $_(t,n,e){let r=t.get(H_);if(Array.isArray(n))for(let o of n)r.queue.add(o),e?.detachedLeaveAnimationFns?.push(o);else r.queue.add(n),e?.detachedLeaveAnimationFns?.push(n);r.scheduler&&r.scheduler(t)}function O0(t,n){let e=t.get(H_);if(n.detachedLeaveAnimationFns){for(let r of n.detachedLeaveAnimationFns)e.queue.delete(r);n.detachedLeaveAnimationFns=void 0}}function k0(t,n){for(let[e,r]of n)$_(t,r.animateFns)}function Mb(t,n,e,r){let o=t?.[cr]?.enter;n!==null&&o&&o.has(e.index)&&k0(r,o)}function Wo(t,n,e,r,o,i,s,a){if(o!=null){let c,l=!1;Vt(o)?c=o:Nn(o)&&(l=!0,o=o[Lt]);let u=wt(o);t===0&&r!==null?(Mb(a,r,i,e),s==null?R_(n,r,u):Xc(n,r,u,s||null,!0)):t===1&&r!==null?(Mb(a,r,i,e),Xc(n,r,u,s||null,!0),x0(i,u)):t===2?(a?.[cr]?.leave?.has(i.index)&&A0(i,u),hs.delete(u),Tb(a,i,e,d=>{if(hs.has(u)){hs.delete(u);return}N_(n,u,l,d)})):t===3&&(hs.delete(u),Tb(a,i,e,()=>{n.destroyNode(u)})),c!=null&&G0(n,t,e,c,i,r,s)}}function F0(t,n){z_(t,n),n[Lt]=null,n[Qe]=null}function P0(t,n,e,r,o,i){r[Lt]=o,r[Qe]=n,_l(t,r,e,1,o,i)}function z_(t,n){n[jt].changeDetectionScheduler?.notify(9),_l(t,n,n[ie],2,null,null)}function L0(t){let n=t[jo];if(!n)return uh(t[A],t);for(;n;){let e=null;if(Nn(n))e=n[jo];else{let r=n[ge];r&&(e=r)}if(!e){for(;n&&!n[Et]&&n!==t;)Nn(n)&&uh(n[A],n),n=n[Te];n===null&&(n=t),Nn(n)&&uh(n[A],n),e=n&&n[Et]}n=e}}function wp(t,n){let e=t[Wr],r=e.indexOf(n);e.splice(r,1)}function bl(t,n){if(qr(n))return;let e=n[ie];e.destroyNode&&_l(t,n,e,3,null,null),L0(n)}function uh(t,n){if(qr(n))return;let e=x(null);try{n[P]&=-129,n[P]|=256,n[ct]&&tr(n[ct]),B0(t,n),V0(t,n),n[A].type===1&&n[ie].destroy();let r=n[ar];if(r!==null&&Vt(n[Te])){r!==n[Te]&&wp(r,n);let o=n[fn];o!==null&&o.detachView(t)}Eh(n)}finally{x(e)}}function Tb(t,n,e,r){let o=t?.[cr];if(o==null||o.leave==null||!o.leave.has(n.index))return r(!1);t&&Qr.add(t[Rn]),$_(e,()=>{if(o.leave&&o.leave.has(n.index)){let s=o.leave.get(n.index),a=[];if(s){for(let c=0;c{t[cr].running=void 0,Qr.delete(t[Rn]),n(!0)});return}n(!1)}function V0(t,n){let e=t.cleanup,r=n[Lo];if(e!==null)for(let s=0;s=0?r[a]():r[-a].unsubscribe(),s+=2}else{let a=r[e[s+1]];e[s].call(a)}r!==null&&(n[Lo]=null);let o=n[xn];if(o!==null){n[xn]=null;for(let s=0;sle&&U_(t,n,le,!1);let a=s?K.TemplateUpdateStart:K.TemplateCreateStart;oe(a,o,e),e(r,o)}finally{ur(i);let a=s?K.TemplateUpdateEnd:K.TemplateCreateEnd;oe(a,o,e)}}function Dl(t,n,e){Q0(t,n,e),(e.flags&64)===64&&X0(t,n,e)}function Ts(t,n,e=Ct){let r=n.localNames;if(r!==null){let o=n.index+1;for(let i=0;inull;function K0(t){return t==="class"?"className":t==="for"?"htmlFor":t==="formaction"?"formAction":t==="innerHtml"?"innerHTML":t==="readonly"?"readOnly":t==="tabindex"?"tabIndex":t}function K_(t,n,e,r,o,i){let s=n[A];if(El(t,s,n,e,r)){hn(t)&&X_(n,t.index);return}t.type&3&&(e=K0(e)),Q_(t,n,e,r,o,i)}function Q_(t,n,e,r,o,i){if(t.type&3){let s=Ct(t,n);r=i!=null?i(r,t.value||"",e):r,o.setProperty(s,e,r)}else t.type&12}function X_(t,n){let e=It(n,t);e[P]&16||(e[P]|=64)}function Q0(t,n,e){let r=e.directiveStart,o=e.directiveEnd;hn(e)&&M0(n,e,t.data[r+e.componentOffset]),t.firstCreatePass||Qc(e,n);let i=e.initialInputs;for(let s=r;s{Yr(t.lView)},consumerOnSignalRead(){this.lView[ct]=this}});function ux(t){let n=t[ct]??Object.create(dx);return n.lView=t,n}var dx=F(g({},Jn),{consumerIsAlwaysLive:!0,kind:"template",consumerMarkedDirty:t=>{let n=ir(t.lView);for(;n&&!rD(n[A]);)n=ir(n);n&&Uf(n)},consumerOnSignalRead(){this.lView[ct]=this}});function rD(t){return t.type!==2}function oD(t){if(t[or]===null)return;let n=!0;for(;n;){let e=!1;for(let r of t[or])r.dirty&&(e=!0,r.zone===null||Zone.current===r.zone?r.run():r.zone.run(()=>r.run()));n=e&&!!(t[P]&8192)}}var fx=100;function iD(t,n=0){let r=t[jt].rendererFactory,o=!1;o||r.begin?.();try{hx(t,n)}finally{o||r.end?.()}}function hx(t,n){let e=Xf();try{Zi(!0),Ph(t,n);let r=0;for(;ss(t);){if(r===fx)throw new b(103,!1);r++,Ph(t,1)}}finally{Zi(e)}}function px(t,n,e,r){if(qr(n))return;let o=n[P],i=!1,s=!1;kc(n);let a=!0,c=null,l=null;i||(rD(t)?(l=sx(n),c=Tn(l)):lc()===null?(a=!1,l=ux(n),c=Tn(l)):n[ct]&&(tr(n[ct]),n[ct]=null));try{Bf(n),Wy(t.bindingStartIndex),e!==null&&Z_(t,n,e,2,r);let u=(o&3)===3;if(!i)if(u){let p=t.preOrderCheckHooks;p!==null&&Hc(n,p,null)}else{let p=t.preOrderHooks;p!==null&&$c(n,p,0,null),ch(n,0)}if(s||mx(n),oD(n),sD(n,0),t.contentQueries!==null&&E_(t,n),!i)if(u){let p=t.contentCheckHooks;p!==null&&Hc(n,p)}else{let p=t.contentHooks;p!==null&&$c(n,p,1),ch(n,1)}vx(t,n);let d=t.components;d!==null&&cD(n,d,0);let h=t.viewQuery;if(h!==null&&Ch(2,h,r),!i)if(u){let p=t.viewCheckHooks;p!==null&&Hc(n,p)}else{let p=t.viewHooks;p!==null&&$c(n,p,2),ch(n,2)}if(t.firstUpdatePass===!0&&(t.firstUpdatePass=!1),n[Mc]){for(let p of n[Mc])p();n[Mc]=null}i||(tD(n),n[P]&=-73)}catch(u){throw i||Yr(n),u}finally{l!==null&&(er(l,c),a&&cx(l)),Fc()}}function sD(t,n){for(let e=h_(t);e!==null;e=p_(e))for(let r=ge;r0&&(t[e-1][Et]=r[Et]);let i=ns(t,ge+n);F0(r[A],r);let s=i[fn];s!==null&&s.detachView(i[A]),r[Te]=null,r[Et]=null,r[P]&=-129}return r}function yx(t,n,e,r){let o=ge+r,i=e.length;r>0&&(e[o-1][Et]=n),r-1&&(bs(n,r),ns(e,r))}this._attachedToViewContainer=!1}bl(this._lView[A],this._lView)}onDestroy(n){Hf(this._lView,n)}markForCheck(){Ap(this._cdRefInjectingView||this._lView,4)}detach(){this._lView[P]&=-129}reattach(){Ac(this._lView),this._lView[P]|=128}detectChanges(){this._lView[P]|=1024,iD(this._lView)}checkNoChanges(){}attachToViewContainerRef(){if(this._appRef)throw new b(902,!1);this._attachedToViewContainer=!0}detachFromAppRef(){this._appRef=null;let n=Bo(this._lView),e=this._lView[ar];e!==null&&!n&&wp(e,this._lView),z_(this._lView[A],this._lView)}attachToAppRef(n){if(this._attachedToViewContainer)throw new b(902,!1);this._appRef=n;let e=Bo(this._lView),r=this._lView[ar];r!==null&&!e&&fD(r,this._lView),Ac(this._lView)}};var dt=(()=>{class t{_declarationLView;_declarationTContainer;elementRef;static __NG_ELEMENT_ID__=bx;constructor(e,r,o){this._declarationLView=e,this._declarationTContainer=r,this.elementRef=o}get ssrId(){return this._declarationTContainer.tView?.ssrId||null}createEmbeddedView(e,r){return this.createEmbeddedViewImpl(e,r)}createEmbeddedViewImpl(e,r,o){let i=xs(this._declarationLView,this._declarationTContainer,e,{embeddedViewInjector:r,dehydratedView:o});return new dr(i)}}return t})();function bx(){return wl(Ee(),C())}function wl(t,n){return t.type&4?new dt(n,t,Jo(t,n)):null}function ei(t,n,e,r,o){let i=t.data[n];if(i===null)i=_x(t,n,e,r,o),qy()&&(i.flags|=32);else if(i.type&64){i.type=e,i.value=r,i.attrs=o;let s=zy();i.injectorIndex=s===null?-1:s.injectorIndex}return Uo(i,!0),i}function _x(t,n,e,r,o){let i=Zf(),s=Kf(),a=s?i:i&&i.parent,c=t.data[n]=Ex(t,a,e,n,r,o);return Dx(t,c,i,s),c}function Dx(t,n,e,r){t.firstChild===null&&(t.firstChild=n),e!==null&&(r?e.child==null&&n.parent!==null&&(e.child=n):e.next===null&&(e.next=n,n.prev=e))}function Ex(t,n,e,r,o,i){let s=n?n.injectorIndex:-1,a=0;return Wf()&&(a|=128),{type:e,index:r,insertBeforeIndex:null,injectorIndex:s,directiveStart:-1,directiveEnd:-1,directiveStylingLast:-1,componentOffset:-1,controlDirectiveIndex:-1,customControlIndex:-1,propertyBindings:null,flags:a,providerIndexes:0,value:o,namespace:nh(),attrs:i,mergedAttrs:null,localNames:null,initialInputs:null,inputs:null,hostDirectiveInputs:null,outputs:null,hostDirectiveOutputs:null,directiveToIndex:null,tView:null,next:null,prev:null,projectionNext:null,child:null,parent:n,projection:null,styles:null,stylesWithoutHost:null,residualStyles:void 0,classes:null,classesWithoutHost:null,residualClasses:void 0,classBindings:0,styleBindings:0}}function wx(t){let n=t[Ff]??[],r=t[Te][ie],o=[];for(let i of n)i.data[y_]!==void 0?o.push(i):Cx(i,r);t[Ff]=o}function Cx(t,n){let e=0,r=t.firstChild;if(r){let o=t.data[v_];for(;enull,Sx=()=>null;function Jc(t,n){return Ix(t,n)}function hD(t,n,e){return Sx(t,n,e)}var pD=class{},Cl=class{},Lh=class{resolveComponentFactory(n){throw new b(917,!1)}},Rs=class{static NULL=new Lh},je=class{},Oe=(()=>{class t{destroyNode=null;static __NG_ELEMENT_ID__=()=>Mx()}return t})();function Mx(){let t=C(),n=Ee(),e=It(n.index,t);return(Nn(e)?e:t)[ie]}var mD=(()=>{class t{static \u0275prov=v({token:t,providedIn:"root",factory:()=>null})}return t})();var Gc={},jh=class{injector;parentInjector;constructor(n,e){this.injector=n,this.parentInjector=e}get(n,e,r){let o=this.injector.get(n,Gc,r);return o!==Gc||e===Gc?o:this.parentInjector.get(n,e,r)}};function el(t,n,e){let r=e?t.styles:null,o=e?t.classes:null,i=0;if(n!==null)for(let s=0;s0&&(e.directiveToIndex=new Map);for(let h=0;h0;){let e=t[--n];if(typeof e=="number"&&e<0)return e}return 0}function Fx(t,n,e){if(e){if(n.exportAs)for(let r=0;rr(wt(_[t.index])):t.index;ED(m,n,e,i,a,p,!1)}}return l}function Vx(t){return t.startsWith("animation")||t.startsWith("transition")}function Bx(t,n,e,r){let o=t.cleanup;if(o!=null)for(let i=0;ic?a[c]:null}typeof s=="string"&&(i+=2)}return null}function ED(t,n,e,r,o,i,s){let a=n.firstCreatePass?zf(n):null,c=$f(e),l=c.length;c.push(o,i),a&&a.push(r,t,l,(l+1)*(s?-1:1))}function kb(t,n,e,r,o,i){let s=n[e],a=n[A],l=a.data[e].outputs[r],d=s[l].subscribe(i);ED(t.index,a,n,o,i,d,!0)}var Vh=Symbol("BINDING");function wD(t){return t.debugInfo?.className||t.type.name||null}var tl=class extends Rs{ngModule;constructor(n){super(),this.ngModule=n}resolveComponentFactory(n){let e=un(n);return new fr(e,this.ngModule)}};function Ux(t){return Object.keys(t).map(n=>{let[e,r,o]=t[n],i={propName:e,templateName:n,isSignal:(r&gl.SignalBased)!==0};return o&&(i.transform=o),i})}function Hx(t){return Object.keys(t).map(n=>({propName:t[n],templateName:n}))}function $x(t,n,e){let r=n instanceof re?n:n?.injector;return r&&t.getStandaloneInjector!==null&&(r=t.getStandaloneInjector(r)||r),r?new jh(e,r):e}function zx(t){let n=t.get(je,null);if(n===null)throw new b(407,!1);let e=t.get(mD,null),r=t.get(ln,null),o=t.get(Wt,null,{optional:!0});return{rendererFactory:n,sanitizer:e,changeDetectionScheduler:r,ngReflect:!1,tracingService:o}}function Gx(t,n){let e=CD(t);return A_(n,e,e==="svg"?Lf:e==="math"?ky:null)}function Wx(t){if(t?.toLowerCase()==="script")throw new b(905,!1)}function CD(t){return(t.selectors[0][0]||"div").toLowerCase()}var fr=class extends Cl{componentDef;ngModule;selector;componentType;ngContentSelectors;isBoundToModule;cachedInputs=null;cachedOutputs=null;get inputs(){return this.cachedInputs??=Ux(this.componentDef.inputs),this.cachedInputs}get outputs(){return this.cachedOutputs??=Hx(this.componentDef.outputs),this.cachedOutputs}constructor(n,e){super(),this.componentDef=n,this.ngModule=e,this.componentType=n.type,this.selector=w0(n.selectors),this.ngContentSelectors=n.ngContentSelectors??[],this.isBoundToModule=!!e}create(n,e,r,o,i,s){oe(K.DynamicComponentStart);let a=x(null);try{let c=this.componentDef,l=$x(c,o||this.ngModule,n),u=zx(l),d=u.tracingService;return d&&d.componentCreate?d.componentCreate(wD(c),()=>this.createComponentRef(u,l,e,r,i,s)):this.createComponentRef(u,l,e,r,i,s)}finally{x(a)}}createComponentRef(n,e,r,o,i,s){let a=this.componentDef,c=qx(o,a,s,i),l=n.rendererFactory.createRenderer(null,a),u=o?q0(l,o,a.encapsulation,e):Gx(a,l);Wx(u?.tagName);let d=s?.some(Fb)||i?.some(m=>typeof m!="function"&&m.bindings.some(Fb)),h=vp(null,c,null,512|V_(a),null,null,n,l,e,null,D_(u,e,!0));h[le]=u,kc(h);let p=null;try{let m=Np(le,h,2,"#host",()=>c.directiveRegistry,!0,0);O_(l,u,m),Zo(u,h),Dl(c,h,m),ap(c,m,h),Op(c,m),r!==void 0&&Zx(m,this.ngContentSelectors,r),p=It(m.index,h),h[me]=p[me],xp(c,h,null)}catch(m){throw p!==null&&Eh(p),Eh(h),m}finally{oe(K.DynamicComponentEnd),Fc()}return new nl(this.componentType,h,!!d)}};function qx(t,n,e,r){let o=t?["ng-version","21.2.18"]:C0(n.selectors[0]),i=null,s=null,a=0;if(e)for(let u of e)a+=u[Vh].requiredVars,u.create&&(u.targetIdx=0,(i??=[]).push(u)),u.update&&(u.targetIdx=0,(s??=[]).push(u));if(r)for(let u=0;u{if(e&1&&t)for(let r of t)r.create();if(e&2&&n)for(let r of n)r.update()}}function Fb(t){let n=t[Vh].kind;return n==="input"||n==="twoWay"}var nl=class extends pD{_rootLView;_hasInputBindings;instance;hostView;changeDetectorRef;componentType;location;previousInputValues=null;_tNode;constructor(n,e,r){super(),this._rootLView=e,this._hasInputBindings=r,this._tNode=Tc(e[A],le),this.location=Jo(this._tNode,e),this.instance=It(this._tNode.index,e)[me],this.hostView=this.changeDetectorRef=new dr(e,void 0),this.componentType=n}setInput(n,e){this._hasInputBindings;let r=this._tNode;if(this.previousInputValues??=new Map,this.previousInputValues.has(n)&&Object.is(this.previousInputValues.get(n),e))return;let o=this._rootLView,i=El(r,o[A],o,n,e);this.previousInputValues.set(n,e);let s=It(r.index,o);Ap(s,1)}get injector(){return new Zr(this._tNode,this._rootLView)}destroy(){this.hostView.destroy()}onDestroy(n){this.hostView.onDestroy(n)}};function Zx(t,n,e){let r=t.projection=[];for(let o=0;o{class t{static __NG_ELEMENT_ID__=Kx}return t})();function Kx(){let t=Ee();return ID(t,C())}var Bh=class t extends qe{_lContainer;_hostTNode;_hostLView;constructor(n,e,r){super(),this._lContainer=n,this._hostTNode=e,this._hostLView=r}get element(){return Jo(this._hostTNode,this._hostLView)}get injector(){return new Zr(this._hostTNode,this._hostLView)}get parentInjector(){let n=rp(this._hostTNode,this._hostLView);if(t_(n)){let e=Zc(n,this._hostLView),r=Yc(n),o=e[A].data[r+8];return new Zr(o,e)}else return new Zr(null,this._hostLView)}clear(){for(;this.length>0;)this.remove(this.length-1)}get(n){let e=Pb(this._lContainer);return e!==null&&e[n]||null}get length(){return this._lContainer.length-ge}createEmbeddedView(n,e,r){let o,i;typeof r=="number"?o=r:r!=null&&(o=r.index,i=r.injector);let s=Jc(this._lContainer,n.ssrId),a=n.createEmbeddedViewImpl(e||{},i,s);return this.insertImpl(a,o,Ko(this._hostTNode,s)),a}createComponent(n,e,r,o,i,s,a){let c=n&&!aT(n),l;if(c)l=e;else{let E=e||{};l=E.index,r=E.injector,o=E.projectableNodes,i=E.environmentInjector||E.ngModuleRef,s=E.directives,a=E.bindings}let u=c?n:new fr(un(n)),d=r||this.parentInjector;if(!i&&u.ngModule==null){let I=(c?d:this.parentInjector).get(re,null);I&&(i=I)}let h=un(u.componentType??{}),p=Jc(this._lContainer,h?.id??null),m=p?.firstChild??null,_=u.create(d,o,m,i,s,a);return this.insertImpl(_.hostView,l,Ko(this._hostTNode,p)),_}insert(n,e){return this.insertImpl(n,e,!0)}insertImpl(n,e,r){let o=n._lView;if(Py(o)){let a=this.indexOf(n);if(a!==-1)this.detach(a);else{let c=o[Te],l=new t(c,c[Qe],c[Te]);l.detach(l.indexOf(n))}}let i=this._adjustIndex(e),s=this._lContainer;return As(s,o,i,r),n.attachToViewContainerRef(),Sf(dh(s),i,n),n}move(n,e){return this.insert(n,e)}indexOf(n){let e=Pb(this._lContainer);return e!==null?e.indexOf(n):-1}remove(n){let e=this._adjustIndex(n,-1),r=bs(this._lContainer,e);r&&(ns(dh(this._lContainer),e),bl(r[A],r))}detach(n){let e=this._adjustIndex(n,-1),r=bs(this._lContainer,e);return r&&ns(dh(this._lContainer),e)!=null?new dr(r):null}_adjustIndex(n,e=0){return n??this.length+e}};function Pb(t){return t[os]}function dh(t){return t[os]||(t[os]=[])}function ID(t,n){let e,r=n[t.index];return Vt(r)?e=r:(e=lD(r,n,null,t),n[t.index]=e,yp(n,e)),Xx(e,n,t,r),new Bh(e,t,n)}function Qx(t,n){let e=t[ie],r=e.createComment(""),o=Ct(n,t),i=e.parentNode(o);return Xc(e,i,r,e.nextSibling(o),!1),r}var Xx=tA,Jx=()=>!1;function eA(t,n,e){return Jx(t,n,e)}function tA(t,n,e,r){if(t[lr])return;let o;e.type&8?o=wt(r):o=Qx(n,e),t[lr]=o}var Uh=class t{queryList;matches=null;constructor(n){this.queryList=n}clone(){return new t(this.queryList)}setDirty(){this.queryList.setDirty()}},Hh=class t{queries;constructor(n=[]){this.queries=n}createEmbeddedView(n){let e=n.queries;if(e!==null){let r=n.contentQueries!==null?n.contentQueries[0]:e.length,o=[];for(let i=0;i0)r.push(s[a/2]);else{let l=i[a+1],u=n[-c];for(let d=ge;dn.trim())}function AD(t,n,e){t.queries===null&&(t.queries=new $h),t.queries.track(new zh(n,e))}function aA(t,n){let e=t.contentQueries||(t.contentQueries=[]),r=e.length?e[e.length-1]:-1;n!==r&&e.push(t.queries.length-1,n)}function Pp(t,n){return t.queries.getByIndex(n)}function RD(t,n){let e=t[A],r=Pp(e,n);return r.crossesNgTemplate?Gh(e,t,n,[]):SD(e,t,r,n)}function Lp(t,n,e){let r,o=zi(()=>{r._dirtyCounter();let i=cA(r,t);if(n&&i===void 0)throw new b(-951,!1);return i});return r=o[pe],r._dirtyCounter=W(0),r._flatValue=void 0,o}function jp(t){return Lp(!0,!1,t)}function Vp(t){return Lp(!0,!0,t)}function ND(t){return Lp(!1,!1,t)}function OD(t,n){let e=t[pe];e._lView=C(),e._queryIndex=n,e._queryList=Fp(e._lView,n),e._queryList.onDirty(()=>e._dirtyCounter.update(r=>r+1))}function cA(t,n){let e=t._lView,r=t._queryIndex;if(e===void 0||r===void 0||e[P]&4)return n?void 0:Ge;let o=Fp(e,r),i=RD(e,r);return o.reset(i,u_),n?o.first:o._changesDetected||t._flatValue===void 0?t._flatValue=o.toArray():t._flatValue}var gn=class{},Sl=class{};var ol=class extends gn{ngModuleType;_parent;_bootstrapComponents=[];_r3Injector;instance;destroyCbs=[];componentFactoryResolver=new tl(this);constructor(n,e,r,o=!0){super(),this.ngModuleType=n,this._parent=e;let i=wf(n);this._bootstrapComponents=P_(i.bootstrap),this._r3Injector=rh(n,e,[{provide:gn,useValue:this},{provide:Rs,useValue:this.componentFactoryResolver},...r],Xi(n),new Set(["environment"])),o&&this.resolveInjectorInitializers()}resolveInjectorInitializers(){this._r3Injector.resolveInjectorInitializers(),this.instance=this._r3Injector.get(this.ngModuleType)}get injector(){return this._r3Injector}destroy(){let n=this._r3Injector;!n.destroyed&&n.destroy(),this.destroyCbs.forEach(e=>e()),this.destroyCbs=null}onDestroy(n){this.destroyCbs.push(n)}},il=class extends Sl{moduleType;constructor(n){super(),this.moduleType=n}create(n){return new ol(this.moduleType,n,[])}};var Ds=class extends gn{injector;componentFactoryResolver=new tl(this);instance=null;constructor(n){super();let e=new Vr([...n.providers,{provide:gn,useValue:this},{provide:Rs,useValue:this.componentFactoryResolver}],n.parent||Po(),n.debugName,new Set(["environment"]));this.injector=e,n.runEnvironmentInitializers&&e.resolveInjectorInitializers()}destroy(){this.injector.destroy()}onDestroy(n){this.injector.onDestroy(n)}};function ni(t,n,e=null){return new Ds({providers:t,parent:n,debugName:e,runEnvironmentInitializers:!0}).injector}var lA=(()=>{class t{_injector;cachedInjectors=new Map;constructor(e){this._injector=e}getOrCreateStandaloneInjector(e){if(!e.standalone)return null;if(!this.cachedInjectors.has(e)){let r=xf(!1,e.type),o=r.length>0?ni([r],this._injector,""):null;this.cachedInjectors.set(e,o)}return this.cachedInjectors.get(e)}ngOnDestroy(){try{for(let e of this.cachedInjectors.values())e!==null&&e.destroy()}finally{this.cachedInjectors.clear()}}static \u0275prov=v({token:t,providedIn:"environment",factory:()=>new t(w(re))})}return t})();function ke(t){return ws(()=>{let n=kD(t),e=F(g({},n),{decls:t.decls,vars:t.vars,template:t.template,consts:t.consts||null,ngContentSelectors:t.ngContentSelectors,onPush:t.changeDetection===op.OnPush,directiveDefs:null,pipeDefs:null,dependencies:n.standalone&&t.dependencies||null,getStandaloneInjector:n.standalone?o=>o.get(lA).getOrCreateStandaloneInjector(e):null,getExternalStyles:null,signals:t.signals??!1,data:t.data||{},encapsulation:t.encapsulation||zt.Emulated,styles:t.styles||Ge,_:null,schemas:t.schemas||null,tView:null,id:""});n.standalone&&qt("NgStandalone"),FD(e);let r=t.dependencies;return e.directiveDefs=Lb(r,uA),e.pipeDefs=Lb(r,Dy),e.id=hA(e),e})}function uA(t){return un(t)||Dc(t)}function X(t){return ws(()=>({type:t.type,bootstrap:t.bootstrap||Ge,declarations:t.declarations||Ge,imports:t.imports||Ge,exports:t.exports||Ge,transitiveCompileScopes:null,schemas:t.schemas||null,id:t.id||null}))}function dA(t,n){if(t==null)return Pt;let e={};for(let r in t)if(t.hasOwnProperty(r)){let o=t[r],i,s,a,c;Array.isArray(o)?(a=o[0],i=o[1],s=o[2]??i,c=o[3]||null):(i=o,s=o,a=gl.None,c=null),e[i]=[r,a,c],n[i]=s}return e}function fA(t){if(t==null)return Pt;let n={};for(let e in t)t.hasOwnProperty(e)&&(n[t[e]]=e);return n}function M(t){return ws(()=>{let n=kD(t);return FD(n),n})}function Ns(t){return{type:t.type,name:t.name,factory:null,pure:t.pure!==!1,standalone:t.standalone??!0,onDestroy:t.type.prototype.ngOnDestroy||null}}function kD(t){let n={};return{type:t.type,providersResolver:null,viewProvidersResolver:null,factory:null,hostBindings:t.hostBindings||null,hostVars:t.hostVars||0,hostAttrs:t.hostAttrs||null,contentQueries:t.contentQueries||null,declaredInputs:n,inputConfig:t.inputs||Pt,exportAs:t.exportAs||null,standalone:t.standalone??!0,signals:t.signals===!0,selectors:t.selectors||Ge,viewQuery:t.viewQuery||null,features:t.features||null,setInput:null,resolveHostDirectives:null,hostDirectives:null,controlDef:null,inputs:dA(t.inputs,n),outputs:fA(t.outputs),debugInfo:null}}function FD(t){t.features?.forEach(n=>n(t))}function Lb(t,n){return t?()=>{let e=typeof t=="function"?t():t,r=[];for(let o of e){let i=n(o);i!==null&&r.push(i)}return r}:null}function hA(t){let n=0,e=typeof t.consts=="function"?"":t.consts,r=[t.selectors,t.ngContentSelectors,t.hostVars,t.hostAttrs,e,t.vars,t.decls,t.encapsulation,t.standalone,t.signals,t.exportAs,JSON.stringify(t.inputs),JSON.stringify(t.outputs),Object.getOwnPropertyNames(t.type.prototype),!!t.contentQueries,!!t.viewQuery];for(let i of r.join("|"))n=Math.imul(31,n)+i.charCodeAt(0)<<0;return n+=2147483648,"c"+n}function pA(t){let n=e=>{let r=Array.isArray(t);e.hostDirectives===null?(e.resolveHostDirectives=mA,e.hostDirectives=r?t.map(Wh):[t]):r?e.hostDirectives.unshift(...t.map(Wh)):e.hostDirectives.unshift(t)};return n.ngInherit=!0,n}function mA(t){let n=[],e=!1,r=null,o=null;for(let i=0;i=0;r--){let o=t[r];o.hostVars=n+=o.hostVars,o.hostAttrs=Yo(o.hostAttrs,e=Yo(e,o.hostAttrs))}}function fh(t){return t===Pt?{}:t===Ge?[]:t}function _A(t,n){let e=t.viewQuery;e?t.viewQuery=(r,o)=>{n(r,o),e(r,o)}:t.viewQuery=n}function DA(t,n){let e=t.contentQueries;e?t.contentQueries=(r,o,i)=>{n(r,o,i),e(r,o,i)}:t.contentQueries=n}function EA(t,n){let e=t.hostBindings;e?t.hostBindings=(r,o)=>{n(r,o),e(r,o)}:t.hostBindings=n}function LD(t,n,e,r,o,i,s,a){if(e.firstCreatePass){t.mergedAttrs=Yo(t.mergedAttrs,t.attrs);let u=t.tView=gp(2,t,o,i,s,e.directiveRegistry,e.pipeRegistry,null,e.schemas,e.consts,null);e.queries!==null&&(e.queries.template(e,t),u.queries=e.queries.embeddedTView(t))}a&&(t.flags|=a),Uo(t,!1);let c=CA(e,n,t,r);Pc()&&Cp(e,n,c,t),Zo(c,n);let l=lD(c,n,c,t);n[r+le]=l,yp(n,l),eA(l,t,n)}function wA(t,n,e,r,o,i,s,a,c,l,u){let d=e+le,h;return n.firstCreatePass?(h=ei(n,d,4,s||null,a||null),Rc()&&gD(n,t,h,lt(n.consts,l),Sp),Xb(n,h)):h=n.data[d],LD(h,t,n,e,r,o,i,c),Vo(h)&&Dl(n,t,h),l!=null&&Ts(t,h,u),h}function Qo(t,n,e,r,o,i,s,a,c,l,u){let d=e+le,h;if(n.firstCreatePass){if(h=ei(n,d,4,s||null,a||null),l!=null){let p=lt(n.consts,l);h.localNames=[];for(let m=0;m{class t{log(e){console.log(e)}warn(e){console.warn(e)}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"platform"})}return t})();function ri(t){return typeof t=="function"&&t[pe]!==void 0}function Bp(t){return ri(t)&&typeof t.set=="function"}var Tl=new y(""),xl=new y(""),Os=(()=>{class t{_ngZone;registry;_isZoneStable=!0;_callbacks=[];_taskTrackingZone=null;_destroyRef;constructor(e,r,o){this._ngZone=e,this.registry=r,Nf()&&(this._destroyRef=f(Ae,{optional:!0})??void 0),Up||(UD(o),o.addToWindow(r)),this._watchAngularEvents(),e.run(()=>{this._taskTrackingZone=typeof Zone>"u"?null:Zone.current.get("TaskTrackingZone")})}_watchAngularEvents(){let e=this._ngZone.onUnstable.subscribe({next:()=>{this._isZoneStable=!1}}),r=this._ngZone.runOutsideAngular(()=>this._ngZone.onStable.subscribe({next:()=>{j.assertNotInAngularZone(),queueMicrotask(()=>{this._isZoneStable=!0,this._runCallbacksIfReady()})}}));this._destroyRef?.onDestroy(()=>{e.unsubscribe(),r.unsubscribe()})}isStable(){return this._isZoneStable&&!this._ngZone.hasPendingMacrotasks}_runCallbacksIfReady(){if(this.isStable())queueMicrotask(()=>{for(;this._callbacks.length!==0;){let e=this._callbacks.pop();clearTimeout(e.timeoutId),e.doneCb()}});else{let e=this.getPendingTasks();this._callbacks=this._callbacks.filter(r=>r.updateCb&&r.updateCb(e)?(clearTimeout(r.timeoutId),!1):!0)}}getPendingTasks(){return this._taskTrackingZone?this._taskTrackingZone.macroTasks.map(e=>({source:e.source,creationLocation:e.creationLocation,data:e.data})):[]}addCallback(e,r,o){let i=-1;r&&r>0&&(i=setTimeout(()=>{this._callbacks=this._callbacks.filter(s=>s.timeoutId!==i),e()},r)),this._callbacks.push({doneCb:e,timeoutId:i,updateCb:o})}whenStable(e,r,o){if(o&&!this._taskTrackingZone)throw new Error('Task tracking zone is required when passing an update callback to whenStable(). Is "zone.js/plugins/task-tracking" loaded?');this.addCallback(e,r,o),this._runCallbacksIfReady()}registerApplication(e){this.registry.registerApplication(e,this)}unregisterApplication(e){this.registry.unregisterApplication(e)}findProviders(e,r,o){return[]}static \u0275fac=function(r){return new(r||t)(w(j),w(BD),w(xl))};static \u0275prov=v({token:t,factory:t.\u0275fac})}return t})(),BD=(()=>{class t{_applications=new Map;registerApplication(e,r){this._applications.set(e,r)}unregisterApplication(e){this._applications.delete(e)}unregisterAllApplications(){this._applications.clear()}getTestability(e){return this._applications.get(e)||null}getAllTestabilities(){return Array.from(this._applications.values())}getAllRootElements(){return Array.from(this._applications.keys())}findTestabilityInTree(e,r=!0){return Up?.findTestabilityInTree(this,e,r)??null}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"platform"})}return t})();function UD(t){Up=t}var Up;function jn(t){return!!t&&typeof t.then=="function"}function Al(t){return!!t&&typeof t.subscribe=="function"}var Hp=new y("");function Rl(t){return sr([{provide:Hp,multi:!0,useValue:t}])}var $p=(()=>{class t{resolve;reject;initialized=!1;done=!1;donePromise=new Promise((e,r)=>{this.resolve=e,this.reject=r});appInits=f(Hp,{optional:!0})??[];injector=f($);constructor(){}runInitializers(){if(this.initialized)return;let e=[];for(let o of this.appInits){let i=xe(this.injector,o);if(jn(i))e.push(i);else if(Al(i)){let s=new Promise((a,c)=>{i.subscribe({complete:a,error:c})});e.push(s)}}let r=()=>{this.done=!0,this.resolve()};Promise.all(e).then(()=>{r()}).catch(o=>{this.reject(o)}),e.length===0&&r(),this.initialized=!0}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})(),Nl=new y("");function HD(){Jd(()=>{let t="";throw new b(600,t)})}function $D(t){return t.isBoundToModule}var SA=10;var Be=(()=>{class t{_runningTick=!1;_destroyed=!1;_destroyListeners=[];_views=[];internalErrorHandler=f(ut);afterRenderManager=f(yl);zonelessEnabled=f(ds);rootEffectScheduler=f(Lc);dirtyFlags=0;tracingSnapshot=null;allTestViews=new Set;autoDetectTestViews=new Set;includeAllTestViews=!1;afterTick=new S;get allViews(){return[...(this.includeAllTestViews?this.allTestViews:this.autoDetectTestViews).keys(),...this._views]}get destroyed(){return this._destroyed}componentTypes=[];components=[];internalPendingTask=f(kn);get isStable(){return this.internalPendingTask.hasPendingTasksObservable.pipe(H(e=>!e))}constructor(){f(Wt,{optional:!0})}whenStable(){let e;return new Promise(r=>{e=this.isStable.subscribe({next:o=>{o&&r()}})}).finally(()=>{e.unsubscribe()})}_injector=f(re);_rendererFactory=null;get injector(){return this._injector}bootstrap(e,r){return this.bootstrapImpl(e,r)}bootstrapImpl(e,r,o=$.NULL){return this._injector.get(j).run(()=>{oe(K.BootstrapComponentStart);let s=e instanceof Cl;if(!this._injector.get($p).done){let m="";throw new b(405,m)}let c;s?c=e:c=this._injector.get(Rs).resolveComponentFactory(e),this.componentTypes.push(c.componentType);let l=$D(c)?void 0:this._injector.get(gn),u=r||c.selector,d=c.create(o,[],u,l),h=d.location.nativeElement,p=d.injector.get(Tl,null);return p?.registerApplication(h),d.onDestroy(()=>{this.detachView(d.hostView),ms(this.components,d),p?.unregisterApplication(h)}),this._loadComponent(d),oe(K.BootstrapComponentEnd,d),d})}tick(){this.zonelessEnabled||(this.dirtyFlags|=1),this._tick()}_tick(){oe(K.ChangeDetectionStart),this.tracingSnapshot!==null?this.tracingSnapshot.run(vl.CHANGE_DETECTION,this.tickImpl):this.tickImpl()}tickImpl=()=>{if(this._runningTick)throw oe(K.ChangeDetectionEnd),new b(101,!1);let e=x(null);try{this._runningTick=!0,this.synchronize()}finally{this._runningTick=!1,this.tracingSnapshot?.dispose(),this.tracingSnapshot=null,x(e),this.afterTick.next(),oe(K.ChangeDetectionEnd)}};synchronize(){this._rendererFactory===null&&!this._injector.destroyed&&(this._rendererFactory=this._injector.get(je,null,{optional:!0}));let e=0;for(;this.dirtyFlags!==0&&e++ss(e))){this.dirtyFlags|=2;return}else this.dirtyFlags&=-8}attachView(e){let r=e;this._views.push(r),r.attachToAppRef(this)}detachView(e){let r=e;ms(this._views,r),r.detachFromAppRef()}_loadComponent(e){this.attachView(e.hostView);try{this.tick()}catch(o){this.internalErrorHandler(o)}this.components.push(e),this._injector.get(Nl,[]).forEach(o=>o(e))}ngOnDestroy(){if(!this._destroyed)try{this._destroyListeners.forEach(e=>e()),this._views.slice().forEach(e=>e.destroy())}finally{this._destroyed=!0,this._views=[],this._destroyListeners=[]}}onDestroy(e){return this._destroyListeners.push(e),()=>ms(this._destroyListeners,e)}destroy(){if(this._destroyed)throw new b(406,!1);let e=this._injector;e.destroy&&!e.destroyed&&e.destroy()}get viewCount(){return this._views.length}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();function ms(t,n){let e=t.indexOf(n);e>-1&&t.splice(e,1)}function zD(t,n){let e=C(),r=On();if(rt(e,r,n)){let o=ce(),i=Ho();if(El(i,o,e,t,n))hn(i)&&X_(e,i.index);else{let a=Ct(i,e);J_(e[ie],a,null,i.value,t,n,null)}}return zD}function Yt(t,n,e,r){let o=C(),i=On();if(rt(o,i,n)){let s=ce(),a=Ho();ex(a,o,t,n,e,r)}return Yt}function MA(){return C()[We][me]}var qh=class{destroy(n){}updateValue(n,e){}swap(n,e){let r=Math.min(n,e),o=Math.max(n,e),i=this.detach(o);if(o-r>1){let s=this.detach(r);this.attach(r,i),this.attach(o,s)}else this.attach(r,i)}move(n,e){this.attach(e,this.detach(n))}};function hh(t,n,e,r,o){return t===e&&Object.is(n,r)?1:Object.is(o(t,n),o(e,r))?-1:0}function TA(t,n,e,r){let o,i,s=0,a=t.length-1,c=void 0;if(Array.isArray(n)){x(r);let l=n.length-1;for(x(null);s<=a&&s<=l;){let u=t.at(s),d=n[s],h=hh(s,u,s,d,e);if(h!==0){h<0&&t.updateValue(s,d),s++;continue}let p=t.at(a),m=n[l],_=hh(a,p,l,m,e);if(_!==0){_<0&&t.updateValue(a,m),a--,l--;continue}let E=e(s,u),I=e(a,p),ee=e(s,d);if(Object.is(ee,I)){let Pe=e(l,m);Object.is(Pe,E)?(t.swap(s,a),t.updateValue(a,m),l--,a--):t.move(a,s),t.updateValue(s,d),s++;continue}if(o??=new sl,i??=Ub(t,s,a,e),Yh(t,o,s,ee))t.updateValue(s,d),s++,a++;else if(i.has(ee))o.set(E,t.detach(s)),a--;else{let Pe=t.create(s,n[s]);t.attach(s,Pe),s++,a++}}for(;s<=l;)Bb(t,o,e,s,n[s]),s++}else if(n!=null){x(r);let l=n[Symbol.iterator]();x(null);let u=l.next();for(;!u.done&&s<=a;){let d=t.at(s),h=u.value,p=hh(s,d,s,h,e);if(p!==0)p<0&&t.updateValue(s,h),s++,u=l.next();else{o??=new sl,i??=Ub(t,s,a,e);let m=e(s,h);if(Yh(t,o,s,m))t.updateValue(s,h),s++,a++,u=l.next();else if(!i.has(m))t.attach(s,t.create(s,h)),s++,a++,u=l.next();else{let _=e(s,d);o.set(_,t.detach(s)),a--}}}for(;!u.done;)Bb(t,o,e,t.length,u.value),u=l.next()}for(;s<=a;)t.destroy(t.detach(a--));o?.forEach(l=>{t.destroy(l)})}function Yh(t,n,e,r){return n!==void 0&&n.has(r)?(t.attach(e,n.get(r)),n.delete(r),!0):!1}function Bb(t,n,e,r,o){if(Yh(t,n,r,e(r,o)))t.updateValue(r,o);else{let i=t.create(r,o);t.attach(r,i)}}function Ub(t,n,e,r){let o=new Set;for(let i=n;i<=e;i++)o.add(r(i,t.at(i)));return o}var sl=class{kvMap=new Map;_vMap=void 0;has(n){return this.kvMap.has(n)}delete(n){if(!this.has(n))return!1;let e=this.kvMap.get(n);return this._vMap!==void 0&&this._vMap.has(e)?(this.kvMap.set(n,this._vMap.get(e)),this._vMap.delete(e)):this.kvMap.delete(n),!0}get(n){return this.kvMap.get(n)}set(n,e){if(this.kvMap.has(n)){let r=this.kvMap.get(n);this._vMap===void 0&&(this._vMap=new Map);let o=this._vMap;for(;o.has(r);)r=o.get(r);o.set(r,e)}else this.kvMap.set(n,e)}forEach(n){for(let[e,r]of this.kvMap)if(n(r,e),this._vMap!==void 0){let o=this._vMap;for(;o.has(r);)r=o.get(r),n(r,e)}}};function xA(t,n,e,r,o,i,s,a){qt("NgControlFlow");let c=C(),l=ce(),u=lt(l.consts,i);return Qo(c,l,t,n,e,r,o,u,256,s,a),zp}function zp(t,n,e,r,o,i,s,a){qt("NgControlFlow");let c=C(),l=ce(),u=lt(l.consts,i);return Qo(c,l,t,n,e,r,o,u,512,s,a),zp}function AA(t,n){qt("NgControlFlow");let e=C(),r=On(),o=e[r]!==Ve?e[r]:-1,i=o!==-1?al(e,le+o):void 0,s=0;if(rt(e,r,t)){let a=x(null);try{if(i!==void 0&&dD(i,s),t!==-1){let c=le+t,l=al(e,c),u=Xh(e[A],c),d=hD(l,u,e),h=xs(e,u,n,{dehydratedView:d});As(l,h,s,Ko(u,d))}}finally{x(a)}}else if(i!==void 0){let a=uD(i,s);a!==void 0&&(a[me]=n)}}var Zh=class{lContainer;$implicit;$index;constructor(n,e,r){this.lContainer=n,this.$implicit=e,this.$index=r}get $count(){return this.lContainer.length-ge}};function RA(t){return t}function NA(t,n){return n}var Kh=class{hasEmptyBlock;trackByFn;liveCollection;constructor(n,e,r){this.hasEmptyBlock=n,this.trackByFn=e,this.liveCollection=r}};function OA(t,n,e,r,o,i,s,a,c,l,u,d,h){qt("NgControlFlow");let p=C(),m=ce(),_=c!==void 0,E=C(),I=a?s.bind(E[We][me]):s,ee=new Kh(_,I);E[le+t]=ee,Qo(p,m,t+1,n,e,r,o,lt(m.consts,i),256),_&&Qo(p,m,t+2,c,l,u,d,lt(m.consts,h),512)}var Qh=class extends qh{lContainer;hostLView;templateTNode;operationsCounter=void 0;needsIndexUpdate=!1;constructor(n,e,r){super(),this.lContainer=n,this.hostLView=e,this.templateTNode=r}get length(){return this.lContainer.length-ge}at(n){return this.getLView(n)[me].$implicit}attach(n,e){let r=e[Hr];this.needsIndexUpdate||=n!==this.length,As(this.lContainer,e,n,Ko(this.templateTNode,r)),FA(this.lContainer,n)}detach(n){return this.needsIndexUpdate||=n!==this.length-1,PA(this.lContainer,n),LA(this.lContainer,n)}create(n,e){let r=Jc(this.lContainer,this.templateTNode.tView.ssrId);return xs(this.hostLView,this.templateTNode,new Zh(this.lContainer,e,n),{dehydratedView:r})}destroy(n){bl(n[A],n)}updateValue(n,e){this.getLView(n)[me].$implicit=e}reset(){this.needsIndexUpdate=!1}updateIndexes(){if(this.needsIndexUpdate)for(let n=0;n0){let i=r[An];O0(i,o),Qr.delete(r[Rn]),o.detachedLeaveAnimationFns=void 0}}function PA(t,n){if(t.length<=ge)return;let e=ge+n,r=t[e],o=r?r[cr]:void 0;o&&o.leave&&o.leave.size>0&&(o.detachedLeaveAnimationFns=[])}function LA(t,n){return bs(t,n)}function jA(t,n){return uD(t,n)}function Xh(t,n){return Tc(t,n)}function GD(t,n,e){let r=C(),o=On();if(rt(r,o,n)){let i=ce(),s=Ho();K_(s,r,t,n,r[ie],e)}return GD}function Jh(t,n,e,r,o){El(n,t,e,o?"class":"style",r)}function cl(t,n,e,r){let o=C(),i=o[A],s=t+le,a=i.firstCreatePass?Np(s,o,2,n,Sp,Rc(),e,r):i.data[s];if(hn(a)){let c=o[jt].tracingService;if(c&&c.componentCreate){let l=i.data[a.directiveStart+a.componentOffset];return c.componentCreate(wD(l),()=>(Hb(t,n,o,a,r),cl))}}return Hb(t,n,o,a,r),cl}function Hb(t,n,e,r,o){if(Mp(r,e,t,n,WD),Vo(r)){let i=e[A];Dl(i,e,r),ap(i,r,e)}o!=null&&Ts(e,r)}function Gp(){let t=ce(),n=Ee(),e=Tp(n);return t.firstCreatePass&&Op(t,e),qf(e)&&Yf(),Gf(),e.classesWithoutHost!=null&&hT(e)&&Jh(t,e,C(),e.classesWithoutHost,!0),e.stylesWithoutHost!=null&&pT(e)&&Jh(t,e,C(),e.stylesWithoutHost,!1),Gp}function Ol(t,n,e,r){return cl(t,n,e,r),Gp(),Ol}function to(t,n,e,r){let o=C(),i=o[A],s=t+le,a=i.firstCreatePass?Lx(s,i,2,n,e,r):i.data[s];return Mp(a,o,t,n,WD),r!=null&&Ts(o,a),to}function no(){let t=Ee(),n=Tp(t);return qf(n)&&Yf(),Gf(),no}function yn(t,n,e,r){return to(t,n,e,r),no(),yn}var WD=(t,n,e,r,o)=>(ls(!0),A_(n[ie],r,nh()));function Wp(t,n,e){let r=C(),o=r[A],i=t+le,s=o.firstCreatePass?Np(i,r,8,"ng-container",Sp,Rc(),n,e):o.data[i];if(Mp(s,r,t,"ng-container",VA),Vo(s)){let a=r[A];Dl(a,r,s),ap(a,s,r)}return e!=null&&Ts(r,s),Wp}function qp(){let t=ce(),n=Ee(),e=Tp(n);return t.firstCreatePass&&Op(t,e),qp}function qD(t,n,e){return Wp(t,n,e),qp(),qD}var VA=(t,n,e,r,o)=>(ls(!0),i0(n[ie],""));function BA(){return C()}function YD(t,n,e){let r=C(),o=On();if(rt(r,o,n)){let i=ce(),s=Ho();Q_(s,r,t,n,r[ie],e)}return YD}var fs=void 0;function UA(t){let n=Math.floor(Math.abs(t)),e=t.toString().replace(/^[^.]*\.?/,"").length;return n===1&&e===0?1:5}var HA=["en",[["a","p"],["AM","PM"]],[["AM","PM"]],[["S","M","T","W","T","F","S"],["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],["Su","Mo","Tu","We","Th","Fr","Sa"]],fs,[["J","F","M","A","M","J","J","A","S","O","N","D"],["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],["January","February","March","April","May","June","July","August","September","October","November","December"]],fs,[["B","A"],["BC","AD"],["Before Christ","Anno Domini"]],0,[6,0],["M/d/yy","MMM d, y","MMMM d, y","EEEE, MMMM d, y"],["h:mm\u202Fa","h:mm:ss\u202Fa","h:mm:ss\u202Fa z","h:mm:ss\u202Fa zzzz"],["{1}, {0}",fs,fs,fs],[".",",",";","%","+","-","E","\xD7","\u2030","\u221E","NaN",":"],["#,##0.###","#,##0%","\xA4#,##0.00","#E0"],"USD","$","US Dollar",{},"ltr",UA],ph=Object.create(null);function pt(t){let n=$A(t),e=$b(n);if(e)return e;let r=n.split("-")[0];if(e=$b(r),e)return e;if(r==="en")return HA;throw new b(701,!1)}function $b(t){if(!(t in ph)){let n=ye.ng&&ye.ng.common&&ye.ng.common.locales&&ye.ng.common.locales[t];return n!==void 0&&(ph[t]=n),n}return ph[t]}var _e=(function(t){return t[t.LocaleId=0]="LocaleId",t[t.DayPeriodsFormat=1]="DayPeriodsFormat",t[t.DayPeriodsStandalone=2]="DayPeriodsStandalone",t[t.DaysFormat=3]="DaysFormat",t[t.DaysStandalone=4]="DaysStandalone",t[t.MonthsFormat=5]="MonthsFormat",t[t.MonthsStandalone=6]="MonthsStandalone",t[t.Eras=7]="Eras",t[t.FirstDayOfWeek=8]="FirstDayOfWeek",t[t.WeekendRange=9]="WeekendRange",t[t.DateFormat=10]="DateFormat",t[t.TimeFormat=11]="TimeFormat",t[t.DateTimeFormat=12]="DateTimeFormat",t[t.NumberSymbols=13]="NumberSymbols",t[t.NumberFormats=14]="NumberFormats",t[t.CurrencyCode=15]="CurrencyCode",t[t.CurrencySymbol=16]="CurrencySymbol",t[t.CurrencyName=17]="CurrencyName",t[t.Currencies=18]="Currencies",t[t.Directionality=19]="Directionality",t[t.PluralCase=20]="PluralCase",t[t.ExtraData=21]="ExtraData",t})(_e||{});function $A(t){return t.toLowerCase().replace(/_/g,"-")}var ks="en-US";var zA=ks;function ZD(t){typeof t=="string"&&(zA=t.toLowerCase().replace(/_/g,"-"))}function Zt(t,n,e){let r=C(),o=ce(),i=Ee();return QD(o,r,r[ie],i,t,n,e),Zt}function KD(t,n,e){let r=C(),o=ce(),i=Ee();return(i.type&3||e)&&DD(i,o,r,e,r[ie],t,n,Wc(i,r,n)),KD}function QD(t,n,e,r,o,i,s){let a=!0,c=null;if((r.type&3||s)&&(c??=Wc(r,n,i),DD(r,t,n,s,e,o,i,c)&&(a=!1)),a){let l=r.outputs?.[o],u=r.hostDirectiveOutputs?.[o];if(u&&u.length)for(let d=0;d>17&32767}function KA(t){return(t&2)==2}function QA(t,n){return t&131071|n<<17}function ep(t){return t|2}function Xo(t){return(t&131068)>>2}function mh(t,n){return t&-131069|n<<2}function XA(t){return(t&1)===1}function tp(t){return t|1}function JA(t,n,e,r,o,i){let s=i?n.classBindings:n.styleBindings,a=Xr(s),c=Xo(s);t[r]=e;let l=!1,u;if(Array.isArray(e)){let d=e;u=d[1],(u===null||Fo(d,u)>0)&&(l=!0)}else u=e;if(o)if(c!==0){let h=Xr(t[a+1]);t[r+1]=Uc(h,a),h!==0&&(t[h+1]=mh(t[h+1],r)),t[a+1]=QA(t[a+1],r)}else t[r+1]=Uc(a,0),a!==0&&(t[a+1]=mh(t[a+1],r)),a=r;else t[r+1]=Uc(c,0),a===0?a=r:t[c+1]=mh(t[c+1],r),c=r;l&&(t[r+1]=ep(t[r+1])),zb(t,u,r,!0),zb(t,u,r,!1),eR(n,u,t,r,i),s=Uc(a,c),i?n.classBindings=s:n.styleBindings=s}function eR(t,n,e,r,o){let i=o?t.residualClasses:t.residualStyles;i!=null&&typeof n=="string"&&Fo(i,n)>=0&&(e[r+1]=tp(e[r+1]))}function zb(t,n,e,r){let o=t[e+1],i=n===null,s=r?Xr(o):Xo(o),a=!1;for(;s!==0&&(a===!1||i);){let c=t[s],l=t[s+1];tR(c,n)&&(a=!0,t[s+1]=r?tp(l):ep(l)),s=r?Xr(l):Xo(l)}a&&(t[e+1]=r?ep(o):tp(o))}function tR(t,n){return t===null||n==null||(Array.isArray(t)?t[1]:t)===n?!0:Array.isArray(t)&&typeof n=="string"?Fo(t,n)>=0:!1}var $t={textEnd:0,key:0,keyEnd:0,value:0,valueEnd:0};function nR(t){return t.substring($t.key,$t.keyEnd)}function rR(t){return oR(t),eE(t,tE(t,0,$t.textEnd))}function eE(t,n){let e=$t.textEnd;return e===n?-1:(n=$t.keyEnd=iR(t,$t.key=n,e),tE(t,n,e))}function oR(t){$t.key=0,$t.keyEnd=0,$t.value=0,$t.valueEnd=0,$t.textEnd=t.length}function tE(t,n,e){for(;n32;)n++;return n}function Pl(t,n,e){return nE(t,n,e,!1),Pl}function Xe(t,n){return nE(t,n,null,!0),Xe}function Yp(t){aR(hR,sR,t,!0)}function sR(t,n){for(let e=rR(n);e>=0;e=eE(n,e))Ic(t,nR(n),!0)}function nE(t,n,e,r){let o=C(),i=ce(),s=as(2);if(i.firstUpdatePass&&oE(i,t,s,r),n!==Ve&&rt(o,s,n)){let a=i.data[Ut()];iE(i,a,o,o[ie],t,o[s+1]=mR(n,e),r,s)}}function aR(t,n,e,r){let o=ce(),i=as(2);o.firstUpdatePass&&oE(o,null,i,r);let s=C();if(e!==Ve&&rt(s,i,e)){let a=o.data[Ut()];if(sE(a,r)&&!rE(o,i)){let c=r?a.classesWithoutHost:a.stylesWithoutHost;c!==null&&(e=bc(c,e||"")),Jh(o,a,s,e,r)}else pR(o,a,s,s[ie],s[i+1],s[i+1]=fR(t,n,e),r,i)}}function rE(t,n){return n>=t.expandoStartIndex}function oE(t,n,e,r){let o=t.data;if(o[e+1]===null){let i=o[Ut()],s=rE(t,e);sE(i,r)&&n===null&&!s&&(n=!1),n=cR(o,i,n,r),JA(o,i,n,e,s,r)}}function cR(t,n,e,r){let o=Ky(t),i=r?n.residualClasses:n.residualStyles;if(o===null)(r?n.classBindings:n.styleBindings)===0&&(e=gh(null,t,n,e,r),e=Es(e,n.attrs,r),i=null);else{let s=n.directiveStylingLast;if(s===-1||t[s]!==o)if(e=gh(o,t,n,e,r),i===null){let c=lR(t,n,r);c!==void 0&&Array.isArray(c)&&(c=gh(null,t,n,c[1],r),c=Es(c,n.attrs,r),uR(t,n,r,c))}else i=dR(t,n,r)}return i!==void 0&&(r?n.residualClasses=i:n.residualStyles=i),e}function lR(t,n,e){let r=e?n.classBindings:n.styleBindings;if(Xo(r)!==0)return t[Xr(r)]}function uR(t,n,e,r){let o=e?n.classBindings:n.styleBindings;t[Xr(o)]=r}function dR(t,n,e){let r,o=n.directiveEnd;for(let i=1+n.directiveStylingLast;i0;){let c=t[o],l=Array.isArray(c),u=l?c[1]:c,d=u===null,h=e[o+1];h===Ve&&(h=d?Ge:void 0);let p=d?Sc(h,r):u===r?h:void 0;if(l&&!ll(p)&&(p=Sc(c,r)),ll(p)&&(a=p,s))return a;let m=t[o+1];o=s?Xr(m):Xo(m)}if(n!==null){let c=i?n.residualClasses:n.residualStyles;c!=null&&(a=Sc(c,r))}return a}function ll(t){return t!==void 0}function mR(t,n){return t==null||t===""||(typeof n=="string"?t=t+n:typeof t=="object"&&(t=Xi(ft(t)))),t}function sE(t,n){return(t.flags&(n?8:16))!==0}function gR(t,n=""){let e=C(),r=ce(),o=t+le,i=r.firstCreatePass?ei(r,o,1,n,null):r.data[o],s=vR(r,e,i,n);e[o]=s,Pc()&&Cp(r,e,s,i),Uo(i,!1)}var vR=(t,n,e,r)=>(ls(!0),r0(n[ie],r));function aE(t,n,e,r=""){return rt(t,On(),e)?n+dn(e)+r:Ve}function yR(t,n,e,r,o,i=""){let s=Jf(),a=_s(t,s,e,o);return as(2),a?n+dn(e)+r+dn(o)+i:Ve}function bR(t,n,e,r,o,i,s,a=""){let c=Jf(),l=_D(t,c,e,o,s);return as(3),l?n+dn(e)+r+dn(o)+i+dn(s)+a:Ve}function cE(t){return Zp("",t),cE}function Zp(t,n,e){let r=C(),o=aE(r,t,n,e);return o!==Ve&&Kp(r,Ut(),o),Zp}function lE(t,n,e,r,o){let i=C(),s=yR(i,t,n,e,r,o);return s!==Ve&&Kp(i,Ut(),s),lE}function uE(t,n,e,r,o,i,s){let a=C(),c=bR(a,t,n,e,r,o,i,s);return c!==Ve&&Kp(a,Ut(),c),uE}function Kp(t,n,e){let r=jf(n,t);o0(t[ie],r,e)}function dE(t,n,e){Bp(n)&&(n=n());let r=C(),o=On();if(rt(r,o,n)){let i=ce(),s=Ho();K_(s,r,t,n,r[ie],e)}return dE}function _R(t,n){let e=Bp(t);return e&&t.set(n),e}function fE(t,n){let e=C(),r=ce(),o=Ee();return QD(r,e,e[ie],o,t,n),fE}function DR(t,n,e=""){return aE(C(),t,n,e)}function ER(t,n,e){let r=pn()+t,o=C();return o[r]===Ve?ti(o,r,n(e,o)):bD(o,r)}function Wb(t,n,e){let r=ce();r.firstCreatePass&&hE(n,r.data,r.blueprint,Bt(t),e)}function hE(t,n,e,r,o){if(t=Me(t),Array.isArray(t))for(let i=0;i>20;if(jr(t)||!t.multi){let p=new Kr(l,o,D,null),m=yh(c,n,o?u:u+h,d);m===-1?(_h(Qc(a,s),i,c),vh(i,t,n.length),n.push(c),a.directiveStart++,a.directiveEnd++,o&&(a.providerIndexes+=1048576),e.push(p),s.push(p)):(e[m]=p,s[m]=p)}else{let p=yh(c,n,u+h,d),m=yh(c,n,u,u+h),_=p>=0&&e[p],E=m>=0&&e[m];if(o&&!E||!o&&!_){_h(Qc(a,s),i,c);let I=IR(o?CR:wR,e.length,o,r,l,t);!o&&E&&(e[m].providerFactory=I),vh(i,t,n.length,0),n.push(c),a.directiveStart++,a.directiveEnd++,o&&(a.providerIndexes+=1048576),e.push(I),s.push(I)}else{let I=pE(e[o?m:p],l,!o&&r);vh(i,t,p>-1?p:m,I)}!o&&r&&E&&e[m].componentProviders++}}}function vh(t,n,e,r){let o=jr(n),i=Ny(n);if(o||i){let c=(i?Me(n.useClass):n).prototype.ngOnDestroy;if(c){let l=t.destroyHooks||(t.destroyHooks=[]);if(!o&&n.multi){let u=l.indexOf(e);u===-1?l.push(e,[r,c]):l[u+1].push(r,c)}else l.push(e,c)}}}function pE(t,n,e){return e&&t.componentProviders++,t.multi.push(n)-1}function yh(t,n,e,r){for(let o=e;o{e.providersResolver=(r,o)=>Wb(r,o?o(t):t,!1),n&&(e.viewProvidersResolver=(r,o)=>Wb(r,o?o(n):n,!0))}}function SR(t,n){let e=pn()+t,r=C();return r[e]===Ve?ti(r,e,n()):bD(r,e)}function MR(t,n,e){return mE(C(),pn(),t,n,e)}function TR(t,n,e,r){return gE(C(),pn(),t,n,e,r)}function xR(t,n,e,r,o){return vE(C(),pn(),t,n,e,r,o)}function AR(t,n,e,r,o,i,s){return RR(C(),pn(),t,n,e,r,o,i)}function Ll(t,n){let e=t[n];return e===Ve?void 0:e}function mE(t,n,e,r,o,i){let s=n+e;return rt(t,s,o)?ti(t,s+1,i?r.call(i,o):r(o)):Ll(t,s+1)}function gE(t,n,e,r,o,i,s){let a=n+e;return _s(t,a,o,i)?ti(t,a+2,s?r.call(s,o,i):r(o,i)):Ll(t,a+2)}function vE(t,n,e,r,o,i,s,a){let c=n+e;return _D(t,c,o,i,s)?ti(t,c+3,a?r.call(a,o,i,s):r(o,i,s)):Ll(t,c+3)}function RR(t,n,e,r,o,i,s,a,c){let l=n+e;return jx(t,l,o,i,s,a)?ti(t,l+4,c?r.call(c,o,i,s,a):r(o,i,s,a)):Ll(t,l+4)}function NR(t,n){let e=ce(),r,o=t+le;e.firstCreatePass?(r=OR(n,e.pipeRegistry),e.data[o]=r,r.onDestroy&&(e.destroyHooks??=[]).push(o,r.onDestroy)):r=e.data[o];let i=r.factory||(r.factory=rr(r.type,!0)),s,a=Ke(D);try{let c=Kc(!1),l=i();return Kc(c),Vf(e,C(),o,l),l}finally{Ke(a)}}function OR(t,n){if(n)for(let e=n.length-1;e>=0;e--){let r=n[e];if(t===r.name)return r}}function kR(t,n,e){let r=t+le,o=C(),i=is(o,r);return Qp(o,r)?mE(o,pn(),n,i.transform,e,i):i.transform(e)}function FR(t,n,e,r){let o=t+le,i=C(),s=is(i,o);return Qp(i,o)?gE(i,pn(),n,s.transform,e,r,s):s.transform(e,r)}function PR(t,n,e,r,o){let i=t+le,s=C(),a=is(s,i);return Qp(s,i)?vE(s,pn(),n,a.transform,e,r,o,a):a.transform(e,r,o)}function Qp(t,n){return t[A].data[n].pure}function LR(t,n){return wl(t,n)}var ul=class{ngModuleFactory;componentFactories;constructor(n,e){this.ngModuleFactory=n,this.componentFactories=e}},Xp=(()=>{class t{compileModuleSync(e){return new il(e)}compileModuleAsync(e){return Promise.resolve(this.compileModuleSync(e))}compileModuleAndAllComponentsSync(e){let r=this.compileModuleSync(e),o=wf(e),i=P_(o.declarations).reduce((s,a)=>{let c=un(a);return c&&s.push(new fr(c)),s},[]);return new ul(r,i)}compileModuleAndAllComponentsAsync(e){return Promise.resolve(this.compileModuleAndAllComponentsSync(e))}clearCache(){}clearCacheFor(e){}getModuleId(e){}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();var yE=(()=>{class t{applicationErrorHandler=f(ut);appRef=f(Be);taskService=f(kn);ngZone=f(j);zonelessEnabled=f(ds);tracing=f(Wt,{optional:!0});zoneIsDefined=typeof Zone<"u"&&!!Zone.root.run;schedulerTickApplyArgs=[{data:{__scheduler_tick__:!0}}];subscriptions=new G;angularZoneId=this.zoneIsDefined?this.ngZone._inner?.get(Ki):null;scheduleInRootZone=!this.zonelessEnabled&&this.zoneIsDefined&&(f(ah,{optional:!0})??!1);cancelScheduledCallback=null;useMicrotaskScheduler=!1;runningTick=!1;pendingRenderTaskId=null;constructor(){this.subscriptions.add(this.appRef.afterTick.subscribe(()=>{let e=this.taskService.add();if(!this.runningTick&&(this.cleanup(),!this.zonelessEnabled||this.appRef.includeAllTestViews)){this.taskService.remove(e);return}this.switchToMicrotaskScheduler(),this.taskService.remove(e)})),this.subscriptions.add(this.ngZone.onUnstable.subscribe(()=>{this.runningTick||this.cleanup()}))}switchToMicrotaskScheduler(){this.ngZone.runOutsideAngular(()=>{let e=this.taskService.add();this.useMicrotaskScheduler=!0,queueMicrotask(()=>{this.useMicrotaskScheduler=!1,this.taskService.remove(e)})})}notify(e){if(!this.zonelessEnabled&&e===5)return;switch(e){case 0:{this.appRef.dirtyFlags|=2;break}case 3:case 2:case 4:case 5:case 1:{this.appRef.dirtyFlags|=4;break}case 6:{this.appRef.dirtyFlags|=2;break}case 12:{this.appRef.dirtyFlags|=16;break}case 13:{this.appRef.dirtyFlags|=2;break}case 11:break;default:this.appRef.dirtyFlags|=8}if(this.appRef.tracingSnapshot=this.tracing?.snapshot(this.appRef.tracingSnapshot)??null,!this.shouldScheduleTick())return;let r=this.useMicrotaskScheduler?sb:oh;this.pendingRenderTaskId=this.taskService.add(),this.scheduleInRootZone?this.cancelScheduledCallback=Zone.root.run(()=>r(()=>this.tick())):this.cancelScheduledCallback=this.ngZone.runOutsideAngular(()=>r(()=>this.tick()))}shouldScheduleTick(){return!(this.appRef.destroyed||this.pendingRenderTaskId!==null||this.runningTick||this.appRef._runningTick||!this.zonelessEnabled&&this.zoneIsDefined&&Zone.current.get(Ki+this.angularZoneId))}tick(){if(this.runningTick||this.appRef.destroyed)return;if(this.appRef.dirtyFlags===0){this.cleanup();return}!this.zonelessEnabled&&this.appRef.dirtyFlags&7&&(this.appRef.dirtyFlags|=1);let e=this.taskService.add();try{this.ngZone.run(()=>{this.runningTick=!0,this.appRef._tick()},void 0,this.schedulerTickApplyArgs)}catch(r){this.applicationErrorHandler(r)}finally{this.taskService.remove(e),this.cleanup()}}ngOnDestroy(){this.subscriptions.unsubscribe(),this.cleanup()}cleanup(){if(this.runningTick=!1,this.cancelScheduledCallback?.(),this.cancelScheduledCallback=null,this.pendingRenderTaskId!==null){let e=this.pendingRenderTaskId;this.pendingRenderTaskId=null,this.taskService.remove(e)}}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();function jR(){return qt("NgZoneless"),sr([...Jp(),[]])}function Jp(){return[{provide:ln,useExisting:yE},{provide:j,useClass:Qi},{provide:ds,useValue:!0}]}function VR(){return typeof $localize<"u"&&$localize.locale||ks}var Ls=new y("",{factory:()=>f(Ls,{optional:!0,skipSelf:!0})||VR()});var js=class{destroyed=!1;listeners=null;errorHandler=f(_t,{optional:!0});destroyRef=f(Ae);constructor(){this.destroyRef.onDestroy(()=>{this.destroyed=!0,this.listeners=null})}subscribe(n){if(this.destroyed)throw new b(953,!1);return(this.listeners??=[]).push(n),{unsubscribe:()=>{let e=this.listeners?.indexOf(n);e!==void 0&&e!==-1&&this.listeners?.splice(e,1)}}}emit(n){if(this.destroyed){console.warn(Dt(953,!1));return}if(this.listeners===null)return;let e=x(null);try{for(let r of this.listeners)try{r(n)}catch(o){this.errorHandler?.handleError(o)}}finally{x(e)}}};function q(t){return my(t)}function Kt(t,n){return zi(t,n?.equal)}var BR=t=>t;function em(t,n){if(typeof t=="function"){let e=of(t,BR,n?.equal);return bE(e,n?.debugName)}else{let e=of(t.source,t.computation,t.equal);return bE(e,t.debugName)}}function bE(t,n){let e=t[pe],r=t;return r.set=o=>hy(e,o),r.update=o=>py(e,o),r.asReadonly=us.bind(t),r}var Ul=Symbol("InputSignalNode#UNSET"),xE=F(g({},Gi),{transformFn:void 0,applyValueToInputSignal(t,n){nr(t,n)}});function AE(t,n){let e=Object.create(xE);e.value=t,e.transformFn=n?.transform;function r(){if(Mn(e),e.value===Ul){let o=null;throw new b(-950,o)}return e.value}return r[pe]=e,r}var Vl=class{attributeName;constructor(n){this.attributeName=n}__NG_ELEMENT_ID__=()=>Cs(this.attributeName);toString(){return`HostAttributeToken ${this.attributeName}`}},Zq=(()=>{let t=new y("");return t.__NG_ELEMENT_ID__=n=>{let e=Ee();if(e===null)throw new b(-204,!1);if(e.type&2)return e.value;if(n&8)return null;throw new b(-204,!1)},t})();function Kq(t){return new js}function _E(t,n){return AE(t,n)}function QR(t){return AE(Ul,t)}var RE=(_E.required=QR,_E);function DE(t,n){return jp(n)}function XR(t,n){return Vp(n)}var Qq=(DE.required=XR,DE);function Xq(t,n){return ND(n)}function EE(t,n){return jp(n)}function JR(t,n){return Vp(n)}var Jq=(EE.required=JR,EE);function NE(t,n){let e=Object.create(xE),r=new js;e.value=t;function o(){return Mn(e),wE(e.value),e.value}return o[pe]=e,o.asReadonly=us.bind(o),o.set=i=>{e.equal(e.value,i)||(nr(e,i),r.emit(i))},o.update=i=>{wE(e.value),o.set(i(e.value))},o.subscribe=r.subscribe.bind(r),o.destroyRef=r.destroyRef,o}function wE(t){if(t===Ul)throw new b(952,!1)}function CE(t,n){return NE(t,n)}function eN(t){return NE(Ul,t)}var e5=(CE.required=eN,CE);var nm=new y(""),tN=new y("");function Vs(t){return!t.moduleRef}function nN(t){let n=Vs(t)?t.r3Injector:t.moduleRef.injector,e=n.get(j);return e.run(()=>{Vs(t)?t.r3Injector.resolveInjectorInitializers():t.moduleRef.resolveInjectorInitializers();let r=n.get(ut),o;if(e.runOutsideAngular(()=>{o=e.onError.subscribe({next:r})}),Vs(t)){let i=()=>n.destroy(),s=t.platformInjector.get(nm);s.add(i),n.onDestroy(()=>{o.unsubscribe(),s.delete(i)})}else{let i=()=>t.moduleRef.destroy(),s=t.platformInjector.get(nm);s.add(i),t.moduleRef.onDestroy(()=>{ms(t.allPlatformModules,t.moduleRef),o.unsubscribe(),s.delete(i)})}return oN(r,e,()=>{let i=n.get(kn),s=i.add(),a=n.get($p);return a.runInitializers(),a.donePromise.then(()=>{let c=n.get(Ls,ks);if(ZD(c||ks),!n.get(tN,!0))return Vs(t)?n.get(Be):(t.allPlatformModules.push(t.moduleRef),t.moduleRef);if(Vs(t)){let u=n.get(Be);return t.rootComponent!==void 0&&u.bootstrap(t.rootComponent),u}else return rN?.(t.moduleRef,t.allPlatformModules),t.moduleRef}).finally(()=>{i.remove(s)})})})}var rN;function oN(t,n,e){try{let r=e();return jn(r)?r.catch(o=>{throw n.runOutsideAngular(()=>t(o)),o}):r}catch(r){throw n.runOutsideAngular(()=>t(r)),r}}var jl=null;function iN(t=[],n){return $.create({name:n,providers:[{provide:rs,useValue:"platform"},{provide:nm,useValue:new Set([()=>jl=null])},...t]})}function sN(t=[]){if(jl)return jl;let n=iN(t);return jl=n,HD(),aN(n),n}function aN(t){let n=t.get(dl,null);xe(t,()=>{n?.forEach(e=>e())})}var cN=1e4;var t5=cN-1e3;var St=(()=>{class t{static __NG_ELEMENT_ID__=lN}return t})();function lN(t){return uN(Ee(),C(),(t&16)===16)}function uN(t,n,e){if(hn(t)&&!e){let r=It(t.index,n);return new dr(r,r)}else if(t.type&175){let r=n[We];return new dr(r,n)}return null}var rm=class{supports(n){return kp(n)}create(n){return new om(n)}},dN=(t,n)=>n,om=class{length=0;collection;_linkedRecords=null;_unlinkedRecords=null;_previousItHead=null;_itHead=null;_itTail=null;_additionsHead=null;_additionsTail=null;_movesHead=null;_movesTail=null;_removalsHead=null;_removalsTail=null;_identityChangesHead=null;_identityChangesTail=null;_trackByFn;constructor(n){this._trackByFn=n||dN}forEachItem(n){let e;for(e=this._itHead;e!==null;e=e._next)n(e)}forEachOperation(n){let e=this._itHead,r=this._removalsHead,o=0,i=null;for(;e||r;){let s=!r||e&&e.currentIndex{s=this._trackByFn(o,a),e===null||!Object.is(e.trackById,s)?(e=this._mismatch(e,a,s,o),r=!0):(r&&(e=this._verifyReinsertion(e,a,s,o)),Object.is(e.item,a)||this._addIdentityChange(e,a)),e=e._next,o++}),this.length=o;return this._truncate(e),this.collection=n,this.isDirty}get isDirty(){return this._additionsHead!==null||this._movesHead!==null||this._removalsHead!==null||this._identityChangesHead!==null}_reset(){if(this.isDirty){let n;for(n=this._previousItHead=this._itHead;n!==null;n=n._next)n._nextPrevious=n._next;for(n=this._additionsHead;n!==null;n=n._nextAdded)n.previousIndex=n.currentIndex;for(this._additionsHead=this._additionsTail=null,n=this._movesHead;n!==null;n=n._nextMoved)n.previousIndex=n.currentIndex;this._movesHead=this._movesTail=null,this._removalsHead=this._removalsTail=null,this._identityChangesHead=this._identityChangesTail=null}}_mismatch(n,e,r,o){let i;return n===null?i=this._itTail:(i=n._prev,this._remove(n)),n=this._unlinkedRecords===null?null:this._unlinkedRecords.get(r,null),n!==null?(Object.is(n.item,e)||this._addIdentityChange(n,e),this._reinsertAfter(n,i,o)):(n=this._linkedRecords===null?null:this._linkedRecords.get(r,o),n!==null?(Object.is(n.item,e)||this._addIdentityChange(n,e),this._moveAfter(n,i,o)):n=this._addAfter(new im(e,r),i,o)),n}_verifyReinsertion(n,e,r,o){let i=this._unlinkedRecords===null?null:this._unlinkedRecords.get(r,null);return i!==null?n=this._reinsertAfter(i,n._prev,o):n.currentIndex!=o&&(n.currentIndex=o,this._addToMoves(n,o)),n}_truncate(n){for(;n!==null;){let e=n._next;this._addToRemovals(this._unlink(n)),n=e}this._unlinkedRecords!==null&&this._unlinkedRecords.clear(),this._additionsTail!==null&&(this._additionsTail._nextAdded=null),this._movesTail!==null&&(this._movesTail._nextMoved=null),this._itTail!==null&&(this._itTail._next=null),this._removalsTail!==null&&(this._removalsTail._nextRemoved=null),this._identityChangesTail!==null&&(this._identityChangesTail._nextIdentityChange=null)}_reinsertAfter(n,e,r){this._unlinkedRecords!==null&&this._unlinkedRecords.remove(n);let o=n._prevRemoved,i=n._nextRemoved;return o===null?this._removalsHead=i:o._nextRemoved=i,i===null?this._removalsTail=o:i._prevRemoved=o,this._insertAfter(n,e,r),this._addToMoves(n,r),n}_moveAfter(n,e,r){return this._unlink(n),this._insertAfter(n,e,r),this._addToMoves(n,r),n}_addAfter(n,e,r){return this._insertAfter(n,e,r),this._additionsTail===null?this._additionsTail=this._additionsHead=n:this._additionsTail=this._additionsTail._nextAdded=n,n}_insertAfter(n,e,r){let o=e===null?this._itHead:e._next;return n._next=o,n._prev=e,o===null?this._itTail=n:o._prev=n,e===null?this._itHead=n:e._next=n,this._linkedRecords===null&&(this._linkedRecords=new Bl),this._linkedRecords.put(n),n.currentIndex=r,n}_remove(n){return this._addToRemovals(this._unlink(n))}_unlink(n){this._linkedRecords!==null&&this._linkedRecords.remove(n);let e=n._prev,r=n._next;return e===null?this._itHead=r:e._next=r,r===null?this._itTail=e:r._prev=e,n}_addToMoves(n,e){return n.previousIndex===e||(this._movesTail===null?this._movesTail=this._movesHead=n:this._movesTail=this._movesTail._nextMoved=n),n}_addToRemovals(n){return this._unlinkedRecords===null&&(this._unlinkedRecords=new Bl),this._unlinkedRecords.put(n),n.currentIndex=null,n._nextRemoved=null,this._removalsTail===null?(this._removalsTail=this._removalsHead=n,n._prevRemoved=null):(n._prevRemoved=this._removalsTail,this._removalsTail=this._removalsTail._nextRemoved=n),n}_addIdentityChange(n,e){return n.item=e,this._identityChangesTail===null?this._identityChangesTail=this._identityChangesHead=n:this._identityChangesTail=this._identityChangesTail._nextIdentityChange=n,n}},im=class{item;trackById;currentIndex=null;previousIndex=null;_nextPrevious=null;_prev=null;_next=null;_prevDup=null;_nextDup=null;_prevRemoved=null;_nextRemoved=null;_nextAdded=null;_nextMoved=null;_nextIdentityChange=null;constructor(n,e){this.item=n,this.trackById=e}},sm=class{_head=null;_tail=null;add(n){this._head===null?(this._head=this._tail=n,n._nextDup=null,n._prevDup=null):(this._tail._nextDup=n,n._prevDup=this._tail,n._nextDup=null,this._tail=n)}get(n,e){let r;for(r=this._head;r!==null;r=r._nextDup)if((e===null||e<=r.currentIndex)&&Object.is(r.trackById,n))return r;return null}remove(n){let e=n._prevDup,r=n._nextDup;return e===null?this._head=r:e._nextDup=r,r===null?this._tail=e:r._prevDup=e,this._head===null}},Bl=class{map=new Map;put(n){let e=n.trackById,r=this.map.get(e);r||(r=new sm,this.map.set(e,r)),r.add(n)}get(n,e){let r=n,o=this.map.get(r);return o?o.get(n,e):null}remove(n){let e=n.trackById;return this.map.get(e).remove(n)&&this.map.delete(e),n}get isEmpty(){return this.map.size===0}clear(){this.map.clear()}};function IE(t,n,e){let r=t.previousIndex;if(r===null)return r;let o=0;return e&&r{if(e&&e.key===o)this._maybeAddToChanges(e,r),this._appendAfter=e,e=e._next;else{let i=this._getOrCreateRecordForKey(o,r);e=this._insertBeforeOrAppend(e,i)}}),e){e._prev&&(e._prev._next=null),this._removalsHead=e;for(let r=e;r!==null;r=r._nextRemoved)r===this._mapHead&&(this._mapHead=null),this._records.delete(r.key),r._nextRemoved=r._next,r.previousValue=r.currentValue,r.currentValue=null,r._prev=null,r._next=null}return this._changesTail&&(this._changesTail._nextChanged=null),this._additionsTail&&(this._additionsTail._nextAdded=null),this.isDirty}_insertBeforeOrAppend(n,e){if(n){let r=n._prev;return e._next=n,e._prev=r,n._prev=e,r&&(r._next=e),n===this._mapHead&&(this._mapHead=e),this._appendAfter=n,n}return this._appendAfter?(this._appendAfter._next=e,e._prev=this._appendAfter):this._mapHead=e,this._appendAfter=e,null}_getOrCreateRecordForKey(n,e){if(this._records.has(n)){let o=this._records.get(n);this._maybeAddToChanges(o,e);let i=o._prev,s=o._next;return i&&(i._next=s),s&&(s._prev=i),o._next=null,o._prev=null,o}let r=new lm(n);return this._records.set(n,r),r.currentValue=e,this._addToAdditions(r),r}_reset(){if(this.isDirty){let n;for(this._previousMapHead=this._mapHead,n=this._previousMapHead;n!==null;n=n._next)n._nextPrevious=n._next;for(n=this._changesHead;n!==null;n=n._nextChanged)n.previousValue=n.currentValue;for(n=this._additionsHead;n!=null;n=n._nextAdded)n.previousValue=n.currentValue;this._changesHead=this._changesTail=null,this._additionsHead=this._additionsTail=null,this._removalsHead=null}}_maybeAddToChanges(n,e){Object.is(e,n.currentValue)||(n.previousValue=n.currentValue,n.currentValue=e,this._addToChanges(n))}_addToAdditions(n){this._additionsHead===null?this._additionsHead=this._additionsTail=n:(this._additionsTail._nextAdded=n,this._additionsTail=n)}_addToChanges(n){this._changesHead===null?this._changesHead=this._changesTail=n:(this._changesTail._nextChanged=n,this._changesTail=n)}_forEach(n,e){n instanceof Map?n.forEach(e):Object.keys(n).forEach(r=>e(n[r],r))}},lm=class{key;previousValue=null;currentValue=null;_nextPrevious=null;_next=null;_prev=null;_nextAdded=null;_nextRemoved=null;_nextChanged=null;constructor(n){this.key=n}};function SE(){return new Hl([new rm])}var Hl=(()=>{class t{factories;static \u0275prov=v({token:t,providedIn:"root",factory:SE});constructor(e){this.factories=e}static create(e,r){if(r!=null){let o=r.factories.slice();e=e.concat(o)}return new t(e)}static extend(e){return{provide:t,useFactory:()=>{let r=f(t,{optional:!0,skipSelf:!0});return t.create(e,r||SE())}}}find(e){let r=this.factories.find(o=>o.supports(e));if(r!=null)return r;throw new b(901,!1)}}return t})();function ME(){return new fm([new am])}var fm=(()=>{class t{static \u0275prov=v({token:t,providedIn:"root",factory:ME});factories;constructor(e){this.factories=e}static create(e,r){if(r){let o=r.factories.slice();e=e.concat(o)}return new t(e)}static extend(e){return{provide:t,useFactory:()=>{let r=f(t,{optional:!0,skipSelf:!0});return t.create(e,r||ME())}}}find(e){let r=this.factories.find(o=>o.supports(e));if(r)return r;throw new b(901,!1)}}return t})();var OE=(()=>{class t{constructor(e){}static \u0275fac=function(r){return new(r||t)(w(Be))};static \u0275mod=X({type:t});static \u0275inj=Z({})}return t})();function kE(t){let{rootComponent:n,appProviders:e,platformProviders:r,platformRef:o}=t;oe(K.BootstrapApplicationStart);try{let i=o?.injector??sN(r),s=[Jp(),cb,...e||[]],a=new Ds({providers:s,parent:i,debugName:"",runEnvironmentInitializers:!1});return nN({r3Injector:a.injector,platformInjector:i,rootComponent:n})}catch(i){return Promise.reject(i)}finally{oe(K.BootstrapApplicationEnd)}}function ue(t){return typeof t=="boolean"?t:t!=null&&t!=="false"}function hm(t,n=NaN){return!isNaN(parseFloat(t))&&!isNaN(Number(t))?Number(t):n}var tm=Symbol("NOT_SET"),FE=new Set,fN=F(g({},Gi),{kind:"afterRenderEffectPhase",consumerIsAlwaysLive:!0,consumerAllowSignalWrites:!0,value:tm,cleanup:null,consumerMarkedDirty(){if(this.sequence.impl.executing){if(this.sequence.lastPhase===null||this.sequence.lastPhase(Mn(l),l.value),l.signal[pe]=l,l.registerCleanupFn=u=>(l.cleanup??=new Set).add(u),this.nodes[a]=l,this.hooks[a]=u=>l.phaseFn(u)}}afterRun(){super.afterRun(),this.lastPhase=null}destroy(){if(this.onDestroyFns!==null)for(let n of this.onDestroyFns)n();super.destroy();for(let n of this.nodes)if(n)try{for(let e of n.cleanup??FE)e()}finally{tr(n)}}};function n5(t,n){let e=n?.injector??f($),r=e.get(ln),o=e.get(yl),i=e.get(Wt,null,{optional:!0});o.impl??=e.get(Ep);let s=t;typeof s=="function"&&(s={mixedReadWrite:t});let a=e.get($o,null,{optional:!0}),c=new um(o.impl,[s.earlyRead,s.write,s.mixedReadWrite,s.read],a?.view,r,e,i?.snapshot(null));return o.impl.register(c),c}function $l(t,n){let e=un(t),r=n.elementInjector||Po();return new fr(e).create(r,n.projectableNodes,n.hostElement,n.environmentInjector,n.directives,n.bindings)}function PE(t){let n=un(t);if(!n)return null;let e=new fr(n);return{get selector(){return e.selector},get type(){return e.componentType},get inputs(){return e.inputs},get outputs(){return e.outputs},get ngContentSelectors(){return e.ngContentSelectors},get isStandalone(){return n.standalone},get isSignal(){return n.signals}}}var LE=null;function mt(){return LE}function pm(t){LE??=t}var Bs=class{},Bn=(()=>{class t{historyGo(e){throw new Error("")}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:()=>f(jE),providedIn:"platform"})}return t})(),mm=new y(""),jE=(()=>{class t extends Bn{_location;_history;_doc=f(L);constructor(){super(),this._location=window.location,this._history=window.history}getBaseHrefFromDOM(){return mt().getBaseHref(this._doc)}onPopState(e){let r=mt().getGlobalEventTarget(this._doc,"window");return r.addEventListener("popstate",e,!1),()=>r.removeEventListener("popstate",e)}onHashChange(e){let r=mt().getGlobalEventTarget(this._doc,"window");return r.addEventListener("hashchange",e,!1),()=>r.removeEventListener("hashchange",e)}get href(){return this._location.href}get protocol(){return this._location.protocol}get hostname(){return this._location.hostname}get port(){return this._location.port}get pathname(){return this._location.pathname}get search(){return this._location.search}get hash(){return this._location.hash}set pathname(e){this._location.pathname=e}pushState(e,r,o){this._history.pushState(e,r,o)}replaceState(e,r,o){this._history.replaceState(e,r,o)}forward(){this._history.forward()}back(){this._history.back()}historyGo(e=0){this._history.go(e)}getState(){return this._history.state}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:()=>new t,providedIn:"platform"})}return t})();function zl(t,n){return t?n?t.endsWith("/")?n.startsWith("/")?t+n.slice(1):t+n:n.startsWith("/")?t+n:`${t}/${n}`:t:n}function VE(t){let n=t.search(/#|\?|$/);return t[n-1]==="/"?t.slice(0,n-1)+t.slice(n):t}function Qt(t){return t&&t[0]!=="?"?`?${t}`:t}var Xt=(()=>{class t{historyGo(e){throw new Error("")}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:()=>f(Wl),providedIn:"root"})}return t})(),Gl=new y(""),Wl=(()=>{class t extends Xt{_platformLocation;_baseHref;_removeListenerFns=[];constructor(e,r){super(),this._platformLocation=e,this._baseHref=r??this._platformLocation.getBaseHrefFromDOM()??f(L).location?.origin??""}ngOnDestroy(){for(;this._removeListenerFns.length;)this._removeListenerFns.pop()()}onPopState(e){this._removeListenerFns.push(this._platformLocation.onPopState(e),this._platformLocation.onHashChange(e))}getBaseHref(){return this._baseHref}prepareExternalUrl(e){return zl(this._baseHref,e)}path(e=!1){let r=this._platformLocation.pathname+Qt(this._platformLocation.search),o=this._platformLocation.hash;return o&&e?`${r}${o}`:r}pushState(e,r,o,i){let s=this.prepareExternalUrl(o+Qt(i));this._platformLocation.pushState(e,r,s)}replaceState(e,r,o,i){let s=this.prepareExternalUrl(o+Qt(i));this._platformLocation.replaceState(e,r,s)}forward(){this._platformLocation.forward()}back(){this._platformLocation.back()}getState(){return this._platformLocation.getState()}historyGo(e=0){this._platformLocation.historyGo?.(e)}static \u0275fac=function(r){return new(r||t)(w(Bn),w(Gl,8))};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();var bn=(()=>{class t{_subject=new S;_basePath;_locationStrategy;_urlChangeListeners=[];_urlChangeSubscription=null;constructor(e){this._locationStrategy=e;let r=this._locationStrategy.getBaseHref();this._basePath=mN(VE(BE(r))),this._locationStrategy.onPopState(o=>{this._subject.next({url:this.path(!0),pop:!0,state:o.state,type:o.type})})}ngOnDestroy(){this._urlChangeSubscription?.unsubscribe(),this._urlChangeListeners=[]}path(e=!1){return this.normalize(this._locationStrategy.path(e))}getState(){return this._locationStrategy.getState()}isCurrentPathEqualTo(e,r=""){return this.path()==this.normalize(e+Qt(r))}normalize(e){return t.stripTrailingSlash(pN(this._basePath,BE(e)))}prepareExternalUrl(e){return e&&e[0]!=="/"&&(e="/"+e),this._locationStrategy.prepareExternalUrl(e)}go(e,r="",o=null){this._locationStrategy.pushState(o,"",e,r),this._notifyUrlChangeListeners(this.prepareExternalUrl(e+Qt(r)),o)}replaceState(e,r="",o=null){this._locationStrategy.replaceState(o,"",e,r),this._notifyUrlChangeListeners(this.prepareExternalUrl(e+Qt(r)),o)}forward(){this._locationStrategy.forward()}back(){this._locationStrategy.back()}historyGo(e=0){this._locationStrategy.historyGo?.(e)}onUrlChange(e){return this._urlChangeListeners.push(e),this._urlChangeSubscription??=this.subscribe(r=>{this._notifyUrlChangeListeners(r.url,r.state)}),()=>{let r=this._urlChangeListeners.indexOf(e);this._urlChangeListeners.splice(r,1),this._urlChangeListeners.length===0&&(this._urlChangeSubscription?.unsubscribe(),this._urlChangeSubscription=null)}}_notifyUrlChangeListeners(e="",r){this._urlChangeListeners.forEach(o=>o(e,r))}subscribe(e,r,o){return this._subject.subscribe({next:e,error:r??void 0,complete:o??void 0})}static normalizeQueryParams=Qt;static joinWithSlash=zl;static stripTrailingSlash=VE;static \u0275fac=function(r){return new(r||t)(w(Xt))};static \u0275prov=v({token:t,factory:()=>hN(),providedIn:"root"})}return t})();function hN(){return new bn(w(Xt))}function pN(t,n){if(!t||!n.startsWith(t))return n;let e=n.substring(t.length);return e===""||["/",";","?","#"].includes(e[0])?e:n}function BE(t){return t.replace(/\/index\.html$/,"")}function mN(t){if(new RegExp("^(https?:)?//").test(t)){let[,e]=t.split(/\/\/[^\/]+/);return e}return t}var Em=(()=>{class t extends Xt{_platformLocation;_baseHref="";_removeListenerFns=[];constructor(e,r){super(),this._platformLocation=e,r!=null&&(this._baseHref=r)}ngOnDestroy(){for(;this._removeListenerFns.length;)this._removeListenerFns.pop()()}onPopState(e){this._removeListenerFns.push(this._platformLocation.onPopState(e),this._platformLocation.onHashChange(e))}getBaseHref(){return this._baseHref}path(e=!1){let r=this._platformLocation.hash??"#";return r.length>0?r.substring(1):r}prepareExternalUrl(e){let r=zl(this._baseHref,e);return r.length>0?"#"+r:r}pushState(e,r,o,i){let s=this.prepareExternalUrl(o+Qt(i))||this._platformLocation.pathname;this._platformLocation.pushState(e,r,s)}replaceState(e,r,o,i){let s=this.prepareExternalUrl(o+Qt(i))||this._platformLocation.pathname;this._platformLocation.replaceState(e,r,s)}forward(){this._platformLocation.forward()}back(){this._platformLocation.back()}getState(){return this._platformLocation.getState()}historyGo(e=0){this._platformLocation.historyGo?.(e)}static \u0275fac=function(r){return new(r||t)(w(Bn),w(Gl,8))};static \u0275prov=v({token:t,factory:t.\u0275fac})}return t})();var Ye=(function(t){return t[t.Format=0]="Format",t[t.Standalone=1]="Standalone",t})(Ye||{}),ae=(function(t){return t[t.Narrow=0]="Narrow",t[t.Abbreviated=1]="Abbreviated",t[t.Wide=2]="Wide",t[t.Short=3]="Short",t})(ae||{}),it=(function(t){return t[t.Short=0]="Short",t[t.Medium=1]="Medium",t[t.Long=2]="Long",t[t.Full=3]="Full",t})(it||{}),Hn={Decimal:0,Group:1,List:2,PercentSign:3,PlusSign:4,MinusSign:5,Exponential:6,SuperscriptingExponent:7,PerMille:8,Infinity:9,NaN:10,TimeSeparator:11,CurrencyDecimal:12,CurrencyGroup:13};function GE(t){return pt(t)[_e.LocaleId]}function WE(t,n,e){let r=pt(t),o=[r[_e.DayPeriodsFormat],r[_e.DayPeriodsStandalone]],i=Mt(o,n);return Mt(i,e)}function qE(t,n,e){let r=pt(t),o=[r[_e.DaysFormat],r[_e.DaysStandalone]],i=Mt(o,n);return Mt(i,e)}function YE(t,n,e){let r=pt(t),o=[r[_e.MonthsFormat],r[_e.MonthsStandalone]],i=Mt(o,n);return Mt(i,e)}function ZE(t,n){let r=pt(t)[_e.Eras];return Mt(r,n)}function Us(t,n){let e=pt(t);return Mt(e[_e.DateFormat],n)}function Hs(t,n){let e=pt(t);return Mt(e[_e.TimeFormat],n)}function $s(t,n){let r=pt(t)[_e.DateTimeFormat];return Mt(r,n)}function zs(t,n){let e=pt(t),r=e[_e.NumberSymbols][n];if(typeof r>"u"){if(n===Hn.CurrencyDecimal)return e[_e.NumberSymbols][Hn.Decimal];if(n===Hn.CurrencyGroup)return e[_e.NumberSymbols][Hn.Group]}return r}function KE(t){if(!t[_e.ExtraData])throw new b(2303,!1)}function QE(t){let n=pt(t);return KE(n),(n[_e.ExtraData][2]||[]).map(r=>typeof r=="string"?gm(r):[gm(r[0]),gm(r[1])])}function XE(t,n,e){let r=pt(t);KE(r);let o=[r[_e.ExtraData][0],r[_e.ExtraData][1]],i=Mt(o,n)||[];return Mt(i,e)||[]}function Mt(t,n){for(let e=n;e>-1;e--)if(typeof t[e]<"u")return t[e];throw new b(2304,!1)}function gm(t){let[n,e]=t.split(":");return{hours:+n,minutes:+e}}var gN=/^(\d{4,})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/,ql={},vN=/((?:[^BEGHLMOSWYZabcdhmswyz']+)|(?:'(?:[^']|'')*')|(?:G{1,5}|y{1,4}|Y{1,4}|M{1,5}|L{1,5}|w{1,2}|W{1}|d{1,2}|E{1,6}|c{1,6}|a{1,5}|b{1,5}|B{1,5}|h{1,2}|H{1,2}|m{1,2}|s{1,2}|S{1,3}|z{1,4}|Z{1,5}|O{1,4}))([\s\S]*)/,yN=256;function JE(t,n,e,r){let o=TN(t);bN(n),n=Un(e,n)||n;let s=[],a;for(;n;)if(a=vN.exec(n),a){s=s.concat(a.slice(1));let u=s.pop();if(!u)break;n=u}else{s.push(n);break}let c=o.getTimezoneOffset();r&&(c=tw(r,c),o=MN(o,r));let l="";return s.forEach(u=>{let d=IN(u);l+=d?d(o,e,c):u==="''"?"'":u.replace(/(^'|'$)/g,"").replace(/''/g,"'")}),l}function bN(t){if(t.length>yN)throw new b(2300,!1)}function Xl(t,n,e){let r=new Date(0);return r.setFullYear(t,n,e),r.setHours(0,0,0),r}function Un(t,n){let e=GE(t);if(ql[e]??={},ql[e][n])return ql[e][n];let r="";switch(n){case"shortDate":r=Us(t,it.Short);break;case"mediumDate":r=Us(t,it.Medium);break;case"longDate":r=Us(t,it.Long);break;case"fullDate":r=Us(t,it.Full);break;case"shortTime":r=Hs(t,it.Short);break;case"mediumTime":r=Hs(t,it.Medium);break;case"longTime":r=Hs(t,it.Long);break;case"fullTime":r=Hs(t,it.Full);break;case"short":let o=Un(t,"shortTime"),i=Un(t,"shortDate");r=Yl($s(t,it.Short),[o,i]);break;case"medium":let s=Un(t,"mediumTime"),a=Un(t,"mediumDate");r=Yl($s(t,it.Medium),[s,a]);break;case"long":let c=Un(t,"longTime"),l=Un(t,"longDate");r=Yl($s(t,it.Long),[c,l]);break;case"full":let u=Un(t,"fullTime"),d=Un(t,"fullDate");r=Yl($s(t,it.Full),[u,d]);break}return r&&(ql[e][n]=r),r}function Yl(t,n){return n&&(t=t.replace(/\{([^}]+)}/g,function(e,r){return n!=null&&r in n?n[r]:e})),t}function Jt(t,n,e="-",r,o){let i="";(t<0||o&&t<=0)&&(o?t=-t+1:(t=-t,i=e));let s=String(t);for(;s.length0||a>-e)&&(a+=e),t===3)a===0&&e===-12&&(a=12);else if(t===6)return _N(a,n);let c=zs(s,Hn.MinusSign);return Jt(a,n,c,r,o)}}function DN(t,n){switch(t){case 0:return n.getFullYear();case 1:return n.getMonth();case 2:return n.getDate();case 3:return n.getHours();case 4:return n.getMinutes();case 5:return n.getSeconds();case 6:return n.getMilliseconds();case 7:return n.getDay();default:throw new b(2301,!1)}}function de(t,n,e=Ye.Format,r=!1){return function(o,i){return EN(o,i,t,n,e,r)}}function EN(t,n,e,r,o,i){switch(e){case 2:return YE(n,o,r)[t.getMonth()];case 1:return qE(n,o,r)[t.getDay()];case 0:let s=t.getHours(),a=t.getMinutes();if(i){let l=QE(n),u=XE(n,o,r),d=l.findIndex(h=>{if(Array.isArray(h)){let[p,m]=h,_=s>=p.hours&&a>=p.minutes,E=s0?Math.floor(o/60):Math.ceil(o/60);switch(t){case 0:return(o>=0?"+":"")+Jt(s,2,i)+Jt(Math.abs(o%60),2,i);case 1:return"GMT"+(o>=0?"+":"")+Jt(s,1,i);case 2:return"GMT"+(o>=0?"+":"")+Jt(s,2,i)+":"+Jt(Math.abs(o%60),2,i);case 3:return r===0?"Z":(o>=0?"+":"")+Jt(s,2,i)+":"+Jt(Math.abs(o%60),2,i);default:throw new b(2310,!1)}}}var wN=0,Ql=4;function CN(t){let n=Xl(t,wN,1).getDay();return Xl(t,0,1+(n<=Ql?Ql:Ql+7)-n)}function ew(t){let n=t.getDay(),e=n===0?-3:Ql-n;return Xl(t.getFullYear(),t.getMonth(),t.getDate()+e)}function vm(t,n=!1){return function(e,r){let o;if(n){let i=new Date(e.getFullYear(),e.getMonth(),1).getDay()-1,s=e.getDate();o=1+Math.floor((s+i)/7)}else{let i=ew(e),s=CN(i.getFullYear()),a=i.getTime()-s.getTime();o=1+Math.round(a/6048e5)}return Jt(o,t,zs(r,Hn.MinusSign))}}function Kl(t,n=!1){return function(e,r){let i=ew(e).getFullYear();return Jt(i,t,zs(r,Hn.MinusSign),n)}}var ym={};function IN(t){if(ym[t])return ym[t];let n;switch(t){case"G":case"GG":case"GGG":n=de(3,ae.Abbreviated);break;case"GGGG":n=de(3,ae.Wide);break;case"GGGGG":n=de(3,ae.Narrow);break;case"y":n=Ce(0,1,0,!1,!0);break;case"yy":n=Ce(0,2,0,!0,!0);break;case"yyy":n=Ce(0,3,0,!1,!0);break;case"yyyy":n=Ce(0,4,0,!1,!0);break;case"Y":n=Kl(1);break;case"YY":n=Kl(2,!0);break;case"YYY":n=Kl(3);break;case"YYYY":n=Kl(4);break;case"M":case"L":n=Ce(1,1,1);break;case"MM":case"LL":n=Ce(1,2,1);break;case"MMM":n=de(2,ae.Abbreviated);break;case"MMMM":n=de(2,ae.Wide);break;case"MMMMM":n=de(2,ae.Narrow);break;case"LLL":n=de(2,ae.Abbreviated,Ye.Standalone);break;case"LLLL":n=de(2,ae.Wide,Ye.Standalone);break;case"LLLLL":n=de(2,ae.Narrow,Ye.Standalone);break;case"w":n=vm(1);break;case"ww":n=vm(2);break;case"W":n=vm(1,!0);break;case"d":n=Ce(2,1);break;case"dd":n=Ce(2,2);break;case"c":case"cc":n=Ce(7,1);break;case"ccc":n=de(1,ae.Abbreviated,Ye.Standalone);break;case"cccc":n=de(1,ae.Wide,Ye.Standalone);break;case"ccccc":n=de(1,ae.Narrow,Ye.Standalone);break;case"cccccc":n=de(1,ae.Short,Ye.Standalone);break;case"E":case"EE":case"EEE":n=de(1,ae.Abbreviated);break;case"EEEE":n=de(1,ae.Wide);break;case"EEEEE":n=de(1,ae.Narrow);break;case"EEEEEE":n=de(1,ae.Short);break;case"a":case"aa":case"aaa":n=de(0,ae.Abbreviated);break;case"aaaa":n=de(0,ae.Wide);break;case"aaaaa":n=de(0,ae.Narrow);break;case"b":case"bb":case"bbb":n=de(0,ae.Abbreviated,Ye.Standalone,!0);break;case"bbbb":n=de(0,ae.Wide,Ye.Standalone,!0);break;case"bbbbb":n=de(0,ae.Narrow,Ye.Standalone,!0);break;case"B":case"BB":case"BBB":n=de(0,ae.Abbreviated,Ye.Format,!0);break;case"BBBB":n=de(0,ae.Wide,Ye.Format,!0);break;case"BBBBB":n=de(0,ae.Narrow,Ye.Format,!0);break;case"h":n=Ce(3,1,-12);break;case"hh":n=Ce(3,2,-12);break;case"H":n=Ce(3,1);break;case"HH":n=Ce(3,2);break;case"m":n=Ce(4,1);break;case"mm":n=Ce(4,2);break;case"s":n=Ce(5,1);break;case"ss":n=Ce(5,2);break;case"S":n=Ce(6,1);break;case"SS":n=Ce(6,2);break;case"SSS":n=Ce(6,3);break;case"Z":case"ZZ":case"ZZZ":n=Zl(0);break;case"ZZZZZ":n=Zl(3);break;case"O":case"OO":case"OOO":case"z":case"zz":case"zzz":n=Zl(1);break;case"OOOO":case"ZZZZ":case"zzzz":n=Zl(2);break;default:return null}return ym[t]=n,n}function tw(t,n){t=t.replace(/:/g,"");let e=Date.parse("Jan 01, 1970 00:00:00 "+t)/6e4;return isNaN(e)?n:e}function SN(t,n){return t=new Date(t.getTime()),t.setMinutes(t.getMinutes()+n),t}function MN(t,n,e){let o=t.getTimezoneOffset(),i=tw(n,o);return SN(t,-1*(i-o))}function TN(t){if(UE(t))return t;if(typeof t=="number"&&!isNaN(t))return new Date(t);if(typeof t=="string"){if(t=t.trim(),/^(\d{4}(-\d{1,2}(-\d{1,2})?)?)$/.test(t)){let[o,i=1,s=1]=t.split("-").map(a=>+a);return Xl(o,i-1,s)}let e=parseFloat(t);if(!isNaN(t-e))return new Date(e);let r;if(r=t.match(gN))return xN(r)}let n=new Date(t);if(!UE(n))throw new b(2311,!1);return n}function xN(t){let n=new Date(0),e=0,r=0,o=t[8]?n.setUTCFullYear:n.setFullYear,i=t[8]?n.setUTCHours:n.setHours;t[9]&&(e=Number(t[9]+t[10]),r=Number(t[9]+t[11])),o.call(n,Number(t[1]),Number(t[2])-1,Number(t[3]));let s=Number(t[4]||0)-e,a=Number(t[5]||0)-r,c=Number(t[6]||0),l=Math.floor(parseFloat("0."+(t[7]||0))*1e3);return i.call(n,s,a,c,l),n}function UE(t){return t instanceof Date&&!isNaN(t.valueOf())}var bm=/\s+/,HE=[],AN=(()=>{class t{_ngEl;_renderer;initialClasses=HE;rawClass;stateMap=new Map;constructor(e,r){this._ngEl=e,this._renderer=r}set klass(e){this.initialClasses=e!=null?e.trim().split(bm):HE}set ngClass(e){this.rawClass=typeof e=="string"?e.trim().split(bm):e}ngDoCheck(){for(let r of this.initialClasses)this._updateState(r,!0);let e=this.rawClass;if(Array.isArray(e)||e instanceof Set)for(let r of e)this._updateState(r,!0);else if(e!=null)for(let r of Object.keys(e))this._updateState(r,!!e[r]);this._applyStateDiff()}_updateState(e,r){let o=this.stateMap.get(e);o!==void 0?(o.enabled!==r&&(o.changed=!0,o.enabled=r),o.touched=!0):this.stateMap.set(e,{enabled:r,changed:!0,touched:!0})}_applyStateDiff(){for(let e of this.stateMap){let r=e[0],o=e[1];o.changed?(this._toggleClass(r,o.enabled),o.changed=!1):o.touched||(o.enabled&&this._toggleClass(r,!1),this.stateMap.delete(r)),o.touched=!1}}_toggleClass(e,r){e=e.trim(),e.length>0&&e.split(bm).forEach(o=>{r?this._renderer.addClass(this._ngEl.nativeElement,o):this._renderer.removeClass(this._ngEl.nativeElement,o)})}static \u0275fac=function(r){return new(r||t)(D(z),D(Oe))};static \u0275dir=M({type:t,selectors:[["","ngClass",""]],inputs:{klass:[0,"class","klass"],ngClass:"ngClass"}})}return t})();var Jl=class{$implicit;ngForOf;index;count;constructor(n,e,r,o){this.$implicit=n,this.ngForOf=e,this.index=r,this.count=o}get first(){return this.index===0}get last(){return this.index===this.count-1}get even(){return this.index%2===0}get odd(){return!this.even}},nw=(()=>{class t{_viewContainer;_template;_differs;set ngForOf(e){this._ngForOf=e,this._ngForOfDirty=!0}set ngForTrackBy(e){this._trackByFn=e}get ngForTrackBy(){return this._trackByFn}_ngForOf=null;_ngForOfDirty=!0;_differ=null;_trackByFn;constructor(e,r,o){this._viewContainer=e,this._template=r,this._differs=o}set ngForTemplate(e){e&&(this._template=e)}ngDoCheck(){if(this._ngForOfDirty){this._ngForOfDirty=!1;let e=this._ngForOf;!this._differ&&e&&(this._differ=this._differs.find(e).create(this.ngForTrackBy))}if(this._differ){let e=this._differ.diff(this._ngForOf);e&&this._applyChanges(e)}}_applyChanges(e){let r=this._viewContainer;e.forEachOperation((o,i,s)=>{if(o.previousIndex==null)r.createEmbeddedView(this._template,new Jl(o.item,this._ngForOf,-1,-1),s===null?void 0:s);else if(s==null)r.remove(i===null?void 0:i);else if(i!==null){let a=r.get(i);r.move(a,s),$E(a,o)}});for(let o=0,i=r.length;o{let i=r.get(o.currentIndex);$E(i,o)})}static ngTemplateContextGuard(e,r){return!0}static \u0275fac=function(r){return new(r||t)(D(qe),D(dt),D(Hl))};static \u0275dir=M({type:t,selectors:[["","ngFor","","ngForOf",""]],inputs:{ngForOf:"ngForOf",ngForTrackBy:"ngForTrackBy",ngForTemplate:"ngForTemplate"}})}return t})();function $E(t,n){t.context.$implicit=n.item}var RN=(()=>{class t{_viewContainer;_context=new eu;_thenTemplateRef=null;_elseTemplateRef=null;_thenViewRef=null;_elseViewRef=null;constructor(e,r){this._viewContainer=e,this._thenTemplateRef=r}set ngIf(e){this._context.$implicit=this._context.ngIf=e,this._updateView()}set ngIfThen(e){zE(e,!1),this._thenTemplateRef=e,this._thenViewRef=null,this._updateView()}set ngIfElse(e){zE(e,!1),this._elseTemplateRef=e,this._elseViewRef=null,this._updateView()}_updateView(){this._context.$implicit?this._thenViewRef||(this._viewContainer.clear(),this._elseViewRef=null,this._thenTemplateRef&&(this._thenViewRef=this._viewContainer.createEmbeddedView(this._thenTemplateRef,this._context))):this._elseViewRef||(this._viewContainer.clear(),this._thenViewRef=null,this._elseTemplateRef&&(this._elseViewRef=this._viewContainer.createEmbeddedView(this._elseTemplateRef,this._context)))}static ngIfUseIfTypeGuard;static ngTemplateGuard_ngIf;static ngTemplateContextGuard(e,r){return!0}static \u0275fac=function(r){return new(r||t)(D(qe),D(dt))};static \u0275dir=M({type:t,selectors:[["","ngIf",""]],inputs:{ngIf:"ngIf",ngIfThen:"ngIfThen",ngIfElse:"ngIfElse"}})}return t})(),eu=class{$implicit=null;ngIf=null};function zE(t,n){if(t&&!t.createEmbeddedView)throw new b(2020,!1)}var NN=(()=>{class t{_ngEl;_differs;_renderer;_ngStyle=null;_differ=null;constructor(e,r,o){this._ngEl=e,this._differs=r,this._renderer=o}set ngStyle(e){this._ngStyle=e,!this._differ&&e&&(this._differ=this._differs.find(e).create())}ngDoCheck(){if(this._differ){let e=this._differ.diff(this._ngStyle);e&&this._applyChanges(e)}}_setStyle(e,r){let[o,i]=e.split("."),s=o.indexOf("-")===-1?void 0:Gt.DashCase;r!=null?this._renderer.setStyle(this._ngEl.nativeElement,o,i?`${r}${i}`:r,s):this._renderer.removeStyle(this._ngEl.nativeElement,o,s)}_applyChanges(e){e.forEachRemovedItem(r=>this._setStyle(r.key,null)),e.forEachAddedItem(r=>this._setStyle(r.key,r.currentValue)),e.forEachChangedItem(r=>this._setStyle(r.key,r.currentValue))}static \u0275fac=function(r){return new(r||t)(D(z),D(fm),D(Oe))};static \u0275dir=M({type:t,selectors:[["","ngStyle",""]],inputs:{ngStyle:"ngStyle"}})}return t})(),ON=(()=>{class t{_viewContainerRef;_viewRef=null;ngTemplateOutletContext=null;ngTemplateOutlet=null;ngTemplateOutletInjector=null;injector=f($);constructor(e){this._viewContainerRef=e}ngOnChanges(e){if(this._shouldRecreateView(e)){let r=this._viewContainerRef;if(this._viewRef&&r.remove(r.indexOf(this._viewRef)),!this.ngTemplateOutlet){this._viewRef=null;return}let o=this._createContextForwardProxy();this._viewRef=r.createEmbeddedView(this.ngTemplateOutlet,o,{injector:this._getInjector()})}}_getInjector(){return this.ngTemplateOutletInjector==="outlet"?this.injector:this.ngTemplateOutletInjector??void 0}_shouldRecreateView(e){return!!e.ngTemplateOutlet||!!e.ngTemplateOutletInjector}_createContextForwardProxy(){return new Proxy({},{set:(e,r,o)=>this.ngTemplateOutletContext?Reflect.set(this.ngTemplateOutletContext,r,o):!1,get:(e,r,o)=>{if(this.ngTemplateOutletContext)return Reflect.get(this.ngTemplateOutletContext,r,o)}})}static \u0275fac=function(r){return new(r||t)(D(qe))};static \u0275dir=M({type:t,selectors:[["","ngTemplateOutlet",""]],inputs:{ngTemplateOutletContext:"ngTemplateOutletContext",ngTemplateOutlet:"ngTemplateOutlet",ngTemplateOutletInjector:"ngTemplateOutletInjector"},features:[Re]})}return t})();function wm(t,n){return new b(2100,!1)}var _m=class{createSubscription(n,e,r){return q(()=>n.subscribe({next:e,error:r}))}dispose(n){q(()=>n.unsubscribe())}},Dm=class{createSubscription(n,e,r){return n.then(o=>e?.(o),o=>r?.(o)),{unsubscribe:()=>{e=null,r=null}}}dispose(n){n.unsubscribe()}},kN=new Dm,FN=new _m,PN=(()=>{class t{_ref;_latestValue=null;markForCheckOnValueUpdate=!0;_subscription=null;_obj=null;_strategy=null;applicationErrorHandler=f(ut);constructor(e){this._ref=e}ngOnDestroy(){this._subscription&&this._dispose(),this._ref=null}transform(e){if(!this._obj){if(e)try{this.markForCheckOnValueUpdate=!1,this._subscribe(e)}finally{this.markForCheckOnValueUpdate=!0}return this._latestValue}return e!==this._obj?(this._dispose(),this.transform(e)):this._latestValue}_subscribe(e){this._obj=e,this._strategy=this._selectStrategy(e),this._subscription=this._strategy.createSubscription(e,r=>this._updateLatestValue(e,r),r=>this.applicationErrorHandler(r))}_selectStrategy(e){if(jn(e))return kN;if(Al(e))return FN;throw wm(t,e)}_dispose(){this._strategy.dispose(this._subscription),this._latestValue=null,this._subscription=null,this._obj=null}_updateLatestValue(e,r){e===this._obj&&(this._latestValue=r,this.markForCheckOnValueUpdate&&this._ref?.markForCheck())}static \u0275fac=function(r){return new(r||t)(D(St,16))};static \u0275pipe=Ns({name:"async",type:t,pure:!1})}return t})();var LN=/(?:[0-9A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0560-\u0588\u05D0-\u05EA\u05EF-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u0870-\u0887\u0889-\u088E\u08A0-\u08C9\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C5D\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D04-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E86-\u0E8A\u0E8C-\u0EA3\u0EA5\u0EA7-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u1711\u171F-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1878\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4C\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1C90-\u1CBA\u1CBD-\u1CBF\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u1CFA\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312F\u3131-\u318E\u31A0-\u31BF\u31F0-\u31FF\u3400-\u4DBF\u4E00-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7CA\uA7D0\uA7D1\uA7D3\uA7D5-\uA7D9\uA7F2-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA8FE\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB69\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF2D-\uDF40\uDF42-\uDF49\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF]|\uD801[\uDC00-\uDC9D\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDD70-\uDD7A\uDD7C-\uDD8A\uDD8C-\uDD92\uDD94\uDD95\uDD97-\uDDA1\uDDA3-\uDDB1\uDDB3-\uDDB9\uDDBB\uDDBC\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67\uDF80-\uDF85\uDF87-\uDFB0\uDFB2-\uDFBA]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE35\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2\uDD00-\uDD23\uDE80-\uDEA9\uDEB0\uDEB1\uDF00-\uDF1C\uDF27\uDF30-\uDF45\uDF70-\uDF81\uDFB0-\uDFC4\uDFE0-\uDFF6]|\uD804[\uDC03-\uDC37\uDC71\uDC72\uDC75\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD44\uDD47\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC5F-\uDC61\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDEB8\uDF00-\uDF1A\uDF40-\uDF46]|\uD806[\uDC00-\uDC2B\uDCA0-\uDCDF\uDCFF-\uDD06\uDD09\uDD0C-\uDD13\uDD15\uDD16\uDD18-\uDD2F\uDD3F\uDD41\uDDA0-\uDDA7\uDDAA-\uDDD0\uDDE1\uDDE3\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE89\uDE9D\uDEB0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46\uDD60-\uDD65\uDD67\uDD68\uDD6A-\uDD89\uDD98\uDEE0-\uDEF2\uDFB0]|\uD808[\uDC00-\uDF99]|\uD809[\uDC80-\uDD43]|\uD80B[\uDF90-\uDFF0]|[\uD80C\uD81C-\uD820\uD822\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879\uD880-\uD883][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE70-\uDEBE\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDE40-\uDE7F\uDF00-\uDF4A\uDF50\uDF93-\uDF9F\uDFE0\uDFE1\uDFE3]|\uD821[\uDC00-\uDFF7]|\uD823[\uDC00-\uDCD5\uDD00-\uDD08]|\uD82B[\uDFF0-\uDFF3\uDFF5-\uDFFB\uDFFD\uDFFE]|\uD82C[\uDC00-\uDD22\uDD50-\uDD52\uDD64-\uDD67\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD837[\uDF00-\uDF1E]|\uD838[\uDD00-\uDD2C\uDD37-\uDD3D\uDD4E\uDE90-\uDEAD\uDEC0-\uDEEB]|\uD839[\uDFE0-\uDFE6\uDFE8-\uDFEB\uDFED\uDFEE\uDFF0-\uDFFE]|\uD83A[\uDC00-\uDCC4\uDD00-\uDD43\uDD4B]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDEDF\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF38\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]|\uD884[\uDC00-\uDF4A])\S*/g,jN=(()=>{class t{transform(e){return e==null?null:(VN(t,e),e.replace(LN,r=>r[0].toUpperCase()+r.slice(1).toLowerCase()))}static \u0275fac=function(r){return new(r||t)};static \u0275pipe=Ns({name:"titlecase",type:t,pure:!0})}return t})();function VN(t,n){if(typeof n!="string")throw wm(t,n)}var BN="mediumDate",rw=new y(""),ow=new y(""),UN=(()=>{class t{locale;defaultTimezone;defaultOptions;constructor(e,r,o){this.locale=e,this.defaultTimezone=r,this.defaultOptions=o}transform(e,r,o,i){if(e==null||e===""||e!==e)return null;try{let s=r??this.defaultOptions?.dateFormat??BN,a=o??this.defaultOptions?.timezone??this.defaultTimezone??void 0;return JE(e,s,i||this.locale,a)}catch(s){throw wm(t,s.message)}}static \u0275fac=function(r){return new(r||t)(D(Ls,16),D(rw,24),D(ow,24))};static \u0275pipe=Ns({name:"date",type:t,pure:!0})}return t})();var Cm=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275mod=X({type:t});static \u0275inj=Z({})}return t})();function Gs(t,n){n=encodeURIComponent(n);for(let e of t.split(";")){let r=e.indexOf("="),[o,i]=r==-1?[e,""]:[e.slice(0,r),e.slice(r+1)];if(o.trim()===n)return decodeURIComponent(i)}return null}var oo=class{};var Sm="browser";function iw(t){return t===Sm}var Mm=(()=>{class t{static \u0275prov=v({token:t,providedIn:"root",factory:()=>new Im(f(L),window)})}return t})(),Im=class{document;window;offset=()=>[0,0];constructor(n,e){this.document=n,this.window=e}setOffset(n){Array.isArray(n)?this.offset=()=>n:this.offset=n}getScrollPosition(){return[this.window.scrollX,this.window.scrollY]}scrollToPosition(n,e){this.window.scrollTo(F(g({},e),{left:n[0],top:n[1]}))}scrollToAnchor(n,e){let r=HN(this.document,n);r&&(this.scrollToElement(r,e),r.focus({preventScroll:!0}))}setHistoryScrollRestoration(n){try{this.window.history.scrollRestoration=n}catch{console.warn(Dt(2400,!1))}}scrollToElement(n,e){let r=n.getBoundingClientRect(),o=r.left+this.window.pageXOffset,i=r.top+this.window.pageYOffset,s=this.offset();this.window.scrollTo(F(g({},e),{left:o-s[0],top:i-s[1]}))}};function HN(t,n){let e=t.getElementById(n)||t.getElementsByName(n)[0];if(e)return e;if(typeof t.createTreeWalker=="function"&&t.body&&typeof t.body.attachShadow=="function"){let r=t.createTreeWalker(t.body,NodeFilter.SHOW_ELEMENT),o=r.currentNode;for(;o;){let i=o.shadowRoot;if(i){let s=i.getElementById(n)||i.querySelector(`[name="${n}"]`);if(s)return s}o=r.nextNode()}}return null}var Ws=class{_doc;constructor(n){this._doc=n}manager},tu=(()=>{class t extends Ws{constructor(e){super(e)}supports(e){return!0}addEventListener(e,r,o,i){return e.addEventListener(r,o,i),()=>this.removeEventListener(e,r,o,i)}removeEventListener(e,r,o,i){return e.removeEventListener(r,o,i)}static \u0275fac=function(r){return new(r||t)(w(L))};static \u0275prov=v({token:t,factory:t.\u0275fac})}return t})(),ou=new y(""),Rm=(()=>{class t{_zone;_plugins;_eventNameToPlugin=new Map;constructor(e,r){this._zone=r,e.forEach(s=>{s.manager=this});let o=e.filter(s=>!(s instanceof tu));this._plugins=o.slice().reverse();let i=e.find(s=>s instanceof tu);i&&this._plugins.push(i)}addEventListener(e,r,o,i){return this._findPluginFor(r).addEventListener(e,r,o,i)}getZone(){return this._zone}_findPluginFor(e){let r=this._eventNameToPlugin.get(e);if(r)return r;if(r=this._plugins.find(i=>i.supports(e)),!r)throw new b(5101,!1);return this._eventNameToPlugin.set(e,r),r}static \u0275fac=function(r){return new(r||t)(w(ou),w(j))};static \u0275prov=v({token:t,factory:t.\u0275fac})}return t})(),Tm="ng-app-id";function sw(t){for(let n of t)n.remove()}function aw(t,n){let e=n.createElement("style");return e.textContent=t,e}function zN(t,n,e,r){let o=t.head?.querySelectorAll(`style[${Tm}="${n}"],link[${Tm}="${n}"]`);if(o)for(let i of o)i.removeAttribute(Tm),i instanceof HTMLLinkElement?r.set(i.href.slice(i.href.lastIndexOf("/")+1),{usage:0,elements:[i]}):i.textContent&&e.set(i.textContent,{usage:0,elements:[i]})}function Am(t,n){let e=n.createElement("link");return e.setAttribute("rel","stylesheet"),e.setAttribute("href",t),e}var Nm=(()=>{class t{doc;appId;nonce;inline=new Map;external=new Map;hosts=new Set;constructor(e,r,o,i={}){this.doc=e,this.appId=r,this.nonce=o,zN(e,r,this.inline,this.external),this.hosts.add(e.head)}addStyles(e,r){for(let o of e)this.addUsage(o,this.inline,aw);r?.forEach(o=>this.addUsage(o,this.external,Am))}removeStyles(e,r){for(let o of e)this.removeUsage(o,this.inline);r?.forEach(o=>this.removeUsage(o,this.external))}addUsage(e,r,o){let i=r.get(e);i?i.usage++:r.set(e,{usage:1,elements:[...this.hosts].map(s=>this.addElement(s,o(e,this.doc)))})}removeUsage(e,r){let o=r.get(e);o&&(o.usage--,o.usage<=0&&(sw(o.elements),r.delete(e)))}ngOnDestroy(){for(let[,{elements:e}]of[...this.inline,...this.external])sw(e);this.hosts.clear()}addHost(e){this.hosts.add(e);for(let[r,{elements:o}]of this.inline)o.push(this.addElement(e,aw(r,this.doc)));for(let[r,{elements:o}]of this.external)o.push(this.addElement(e,Am(r,this.doc)))}removeHost(e){this.hosts.delete(e)}addElement(e,r){return this.nonce&&r.setAttribute("nonce",this.nonce),e.appendChild(r)}static \u0275fac=function(r){return new(r||t)(w(L),w(hr),w(eo,8),w(Jr))};static \u0275prov=v({token:t,factory:t.\u0275fac})}return t})(),xm={svg:"http://www.w3.org/2000/svg",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/",math:"http://www.w3.org/1998/Math/MathML"},Om=/%COMP%/g;var lw="%COMP%",GN=`_nghost-${lw}`,WN=`_ngcontent-${lw}`,qN=!0,YN=new y("",{factory:()=>qN});function ZN(t){return WN.replace(Om,t)}function KN(t){return GN.replace(Om,t)}function uw(t,n){return n.map(e=>e.replace(Om,t))}var km=(()=>{class t{eventManager;sharedStylesHost;appId;removeStylesOnCompDestroy;doc;ngZone;nonce;tracingService;rendererByCompId=new Map;defaultRenderer;constructor(e,r,o,i,s,a,c=null,l=null){this.eventManager=e,this.sharedStylesHost=r,this.appId=o,this.removeStylesOnCompDestroy=i,this.doc=s,this.ngZone=a,this.nonce=c,this.tracingService=l,this.defaultRenderer=new qs(e,s,a,this.tracingService)}createRenderer(e,r){if(!e||!r)return this.defaultRenderer;let o=this.getOrCreateRenderer(e,r);return o instanceof ru?o.applyToHost(e):o instanceof Ys&&o.applyStyles(),o}getOrCreateRenderer(e,r){let o=this.rendererByCompId,i=o.get(r.id);if(!i){let s=this.doc,a=this.ngZone,c=this.eventManager,l=this.sharedStylesHost,u=this.removeStylesOnCompDestroy,d=this.tracingService;switch(r.encapsulation){case zt.Emulated:i=new ru(c,l,r,this.appId,u,s,a,d);break;case zt.ShadowDom:return new nu(c,e,r,s,a,this.nonce,d,l);case zt.ExperimentalIsolatedShadowDom:return new nu(c,e,r,s,a,this.nonce,d);default:i=new Ys(c,l,r,u,s,a,d);break}o.set(r.id,i)}return i}ngOnDestroy(){this.rendererByCompId.clear()}componentReplaced(e){this.rendererByCompId.delete(e)}static \u0275fac=function(r){return new(r||t)(w(Rm),w(Nm),w(hr),w(YN),w(L),w(j),w(eo),w(Wt,8))};static \u0275prov=v({token:t,factory:t.\u0275fac})}return t})(),qs=class{eventManager;doc;ngZone;tracingService;data=Object.create(null);throwOnSyntheticProps=!0;constructor(n,e,r,o){this.eventManager=n,this.doc=e,this.ngZone=r,this.tracingService=o}destroy(){}destroyNode=null;createElement(n,e){return e?this.doc.createElementNS(xm[e]||e,n):this.doc.createElement(n)}createComment(n){return this.doc.createComment(n)}createText(n){return this.doc.createTextNode(n)}appendChild(n,e){(cw(n)?n.content:n).appendChild(e)}insertBefore(n,e,r){n&&(cw(n)?n.content:n).insertBefore(e,r)}removeChild(n,e){e.remove()}selectRootElement(n,e){let r=typeof n=="string"?this.doc.querySelector(n):n;if(!r)throw new b(-5104,!1);return e||(r.textContent=""),r}parentNode(n){return n.parentNode}nextSibling(n){return n.nextSibling}setAttribute(n,e,r,o){if(o){e=o+":"+e;let i=xm[o];i?n.setAttributeNS(i,e,r):n.setAttribute(e,r)}else n.setAttribute(e,r)}removeAttribute(n,e,r){if(r){let o=xm[r];o?n.removeAttributeNS(o,e):n.removeAttribute(`${r}:${e}`)}else n.removeAttribute(e)}addClass(n,e){n.classList.add(e)}removeClass(n,e){n.classList.remove(e)}setStyle(n,e,r,o){o&(Gt.DashCase|Gt.Important)?n.style.setProperty(e,r,o&Gt.Important?"important":""):n.style[e]=r}removeStyle(n,e,r){r&Gt.DashCase?n.style.removeProperty(e):n.style[e]=""}setProperty(n,e,r){n!=null&&(n[e]=r)}setValue(n,e){n.nodeValue=e}listen(n,e,r,o){if(typeof n=="string"&&(n=mt().getGlobalEventTarget(this.doc,n),!n))throw new b(5102,!1);let i=this.decoratePreventDefault(r);return this.tracingService?.wrapEventListener&&(i=this.tracingService.wrapEventListener(n,e,i)),this.eventManager.addEventListener(n,e,i,o)}decoratePreventDefault(n){return e=>{if(e==="__ngUnwrap__")return n;n(e)===!1&&e.preventDefault()}}};function cw(t){return t.tagName==="TEMPLATE"&&t.content!==void 0}var nu=class extends qs{hostEl;sharedStylesHost;shadowRoot;constructor(n,e,r,o,i,s,a,c){super(n,o,i,a),this.hostEl=e,this.sharedStylesHost=c,this.shadowRoot=e.attachShadow({mode:"open"}),this.sharedStylesHost&&this.sharedStylesHost.addHost(this.shadowRoot);let l=r.styles;l=uw(r.id,l);for(let d of l){let h=document.createElement("style");s&&h.setAttribute("nonce",s),h.textContent=d,this.shadowRoot.appendChild(h)}let u=r.getExternalStyles?.();if(u)for(let d of u){let h=Am(d,o);s&&h.setAttribute("nonce",s),this.shadowRoot.appendChild(h)}}nodeOrShadowRoot(n){return n===this.hostEl?this.shadowRoot:n}appendChild(n,e){return super.appendChild(this.nodeOrShadowRoot(n),e)}insertBefore(n,e,r){return super.insertBefore(this.nodeOrShadowRoot(n),e,r)}removeChild(n,e){return super.removeChild(null,e)}parentNode(n){return this.nodeOrShadowRoot(super.parentNode(this.nodeOrShadowRoot(n)))}destroy(){this.sharedStylesHost&&this.sharedStylesHost.removeHost(this.shadowRoot)}},Ys=class extends qs{sharedStylesHost;removeStylesOnCompDestroy;styles;styleUrls;constructor(n,e,r,o,i,s,a,c){super(n,i,s,a),this.sharedStylesHost=e,this.removeStylesOnCompDestroy=o;let l=r.styles;this.styles=c?uw(c,l):l,this.styleUrls=r.getExternalStyles?.(c)}applyStyles(){this.sharedStylesHost.addStyles(this.styles,this.styleUrls)}destroy(){this.removeStylesOnCompDestroy&&Qr.size===0&&this.sharedStylesHost.removeStyles(this.styles,this.styleUrls)}},ru=class extends Ys{contentAttr;hostAttr;constructor(n,e,r,o,i,s,a,c){let l=o+"-"+r.id;super(n,e,r,i,s,a,c,l),this.contentAttr=ZN(l),this.hostAttr=KN(l)}applyToHost(n){this.applyStyles(),this.setAttribute(n,this.hostAttr,"")}createElement(n,e){let r=super.createElement(n,e);return super.setAttribute(r,this.contentAttr,""),r}};var iu=class t extends Bs{supportsDOMEvents=!0;static makeCurrent(){pm(new t)}onAndCancel(n,e,r,o){return n.addEventListener(e,r,o),()=>{n.removeEventListener(e,r,o)}}dispatchEvent(n,e){n.dispatchEvent(e)}remove(n){n.remove()}createElement(n,e){return e=e||this.getDefaultDocument(),e.createElement(n)}createHtmlDocument(){return document.implementation.createHTMLDocument("fakeTitle")}getDefaultDocument(){return document}isElementNode(n){return n.nodeType===Node.ELEMENT_NODE}isShadowRoot(n){return n instanceof DocumentFragment}getGlobalEventTarget(n,e){return e==="window"?window:e==="document"?n:e==="body"?n.body:null}getBaseHref(n){let e=QN();return e==null?null:XN(e)}resetBaseElement(){Zs=null}getUserAgent(){return window.navigator.userAgent}getCookie(n){return Gs(document.cookie,n)}},Zs=null;function QN(){return Zs=Zs||document.head.querySelector("base"),Zs?Zs.getAttribute("href"):null}function XN(t){return new URL(t,document.baseURI).pathname}var su=class{addToWindow(n){ye.getAngularTestability=(r,o=!0)=>{let i=n.findTestabilityInTree(r,o);if(i==null)throw new b(5103,!1);return i},ye.getAllAngularTestabilities=()=>n.getAllTestabilities(),ye.getAllAngularRootElements=()=>n.getAllRootElements();let e=r=>{let o=ye.getAllAngularTestabilities(),i=o.length,s=function(){i--,i==0&&r()};o.forEach(a=>{a.whenStable(s)})};ye.frameworkStabilizers||(ye.frameworkStabilizers=[]),ye.frameworkStabilizers.push(e)}findTestabilityInTree(n,e,r){if(e==null)return null;let o=n.getTestability(e);return o??(r?mt().isShadowRoot(e)?this.findTestabilityInTree(n,e.host,!0):this.findTestabilityInTree(n,e.parentElement,!0):null)}},JN=(()=>{class t{build(){return new XMLHttpRequest}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac})}return t})(),dw=["alt","control","meta","shift"],eO={"\b":"Backspace"," ":"Tab","\x7F":"Delete","\x1B":"Escape",Del:"Delete",Esc:"Escape",Left:"ArrowLeft",Right:"ArrowRight",Up:"ArrowUp",Down:"ArrowDown",Menu:"ContextMenu",Scroll:"ScrollLock",Win:"OS"},tO={alt:t=>t.altKey,control:t=>t.ctrlKey,meta:t=>t.metaKey,shift:t=>t.shiftKey},fw=(()=>{class t extends Ws{constructor(e){super(e)}supports(e){return t.parseEventName(e)!=null}addEventListener(e,r,o,i){let s=t.parseEventName(r),a=t.eventCallback(s.fullKey,o,this.manager.getZone());return this.manager.getZone().runOutsideAngular(()=>mt().onAndCancel(e,s.domEventName,a,i))}static parseEventName(e){let r=e.toLowerCase().split("."),o=r.shift();if(r.length===0||!(o==="keydown"||o==="keyup"))return null;let i=t._normalizeKey(r.pop()),s="",a=r.indexOf("code");if(a>-1&&(r.splice(a,1),s="code."),dw.forEach(l=>{let u=r.indexOf(l);u>-1&&(r.splice(u,1),s+=l+".")}),s+=i,r.length!=0||i.length===0)return null;let c={};return c.domEventName=o,c.fullKey=s,c}static matchEventFullKeyCode(e,r){let o=eO[e.key]||e.key,i="";return r.indexOf("code.")>-1&&(o=e.code,i="code."),o==null||!o?!1:(o=o.toLowerCase(),o===" "?o="space":o==="."&&(o="dot"),dw.forEach(s=>{if(s!==o){let a=tO[s];a(e)&&(i+=s+".")}}),i+=o,i===r)}static eventCallback(e,r,o){return i=>{t.matchEventFullKeyCode(i,e)&&o.runGuarded(()=>r(i))}}static _normalizeKey(e){return e==="esc"?"escape":e}static \u0275fac=function(r){return new(r||t)(w(L))};static \u0275prov=v({token:t,factory:t.\u0275fac})}return t})();async function nO(t,n,e){let r=g({rootComponent:t},rO(n,e));return kE(r)}function rO(t,n){return{platformRef:n?.platformRef,appProviders:[...hw,...t?.providers??[]],platformProviders:aO}}function oO(){iu.makeCurrent()}function iO(){return new _t}function sO(){return ip(document),document}var aO=[{provide:Jr,useValue:Sm},{provide:dl,useValue:oO,multi:!0},{provide:L,useFactory:sO}];var cO=[{provide:xl,useClass:su},{provide:Tl,useClass:Os},{provide:Os,useClass:Os}],hw=[{provide:rs,useValue:"root"},{provide:_t,useFactory:iO},{provide:ou,useClass:tu,multi:!0},{provide:ou,useClass:fw,multi:!0},km,Nm,Rm,{provide:je,useExisting:km},{provide:oo,useClass:JN},[]],lO=(()=>{class t{constructor(){}static \u0275fac=function(r){return new(r||t)};static \u0275mod=X({type:t});static \u0275inj=Z({providers:[...hw,...cO],imports:[Cm,OE]})}return t})();var _n=class t{headers;normalizedNames=new Map;lazyInit;lazyUpdate=null;constructor(n){n?typeof n=="string"?this.lazyInit=()=>{this.headers=new Map,n.split(` +`).forEach(e=>{let r=e.indexOf(":");if(r>0){let o=e.slice(0,r),i=e.slice(r+1).trim();this.addHeaderEntry(o,i)}})}:typeof Headers<"u"&&n instanceof Headers?(this.headers=new Map,n.forEach((e,r)=>{this.addHeaderEntry(r,e)})):this.lazyInit=()=>{this.headers=new Map,Object.entries(n).forEach(([e,r])=>{this.setHeaderEntries(e,r)})}:this.headers=new Map}has(n){return this.init(),this.headers.has(n.toLowerCase())}get(n){this.init();let e=this.headers.get(n.toLowerCase());return e&&e.length>0?e[0]:null}keys(){return this.init(),Array.from(this.normalizedNames.values())}getAll(n){return this.init(),this.headers.get(n.toLowerCase())||null}append(n,e){return this.clone({name:n,value:e,op:"a"})}set(n,e){return this.clone({name:n,value:e,op:"s"})}delete(n,e){return this.clone({name:n,value:e,op:"d"})}maybeSetNormalizedName(n,e){this.normalizedNames.has(e)||this.normalizedNames.set(e,n)}init(){this.lazyInit&&(this.lazyInit instanceof t?this.copyFrom(this.lazyInit):this.lazyInit(),this.lazyInit=null,this.lazyUpdate&&(this.lazyUpdate.forEach(n=>this.applyUpdate(n)),this.lazyUpdate=null))}copyFrom(n){n.init(),Array.from(n.headers.keys()).forEach(e=>{this.headers.set(e,n.headers.get(e)),this.normalizedNames.set(e,n.normalizedNames.get(e))})}clone(n){let e=new t;return e.lazyInit=this.lazyInit&&this.lazyInit instanceof t?this.lazyInit:this,e.lazyUpdate=(this.lazyUpdate||[]).concat([n]),e}applyUpdate(n){let e=n.name.toLowerCase();switch(n.op){case"a":case"s":let r=n.value;if(typeof r=="string"&&(r=[r]),r.length===0)return;this.maybeSetNormalizedName(n.name,e);let o=(n.op==="a"?this.headers.get(e):void 0)||[];o.push(...r),this.headers.set(e,o);break;case"d":let i=n.value;if(!i)this.headers.delete(e),this.normalizedNames.delete(e);else{let s=this.headers.get(e);if(!s)return;s=s.filter(a=>i.indexOf(a)===-1),s.length===0?(this.headers.delete(e),this.normalizedNames.delete(e)):this.headers.set(e,s)}break}}addHeaderEntry(n,e){let r=n.toLowerCase();this.maybeSetNormalizedName(n,r),this.headers.has(r)?this.headers.get(r).push(e):this.headers.set(r,[e])}setHeaderEntries(n,e){let r=(Array.isArray(e)?e:[e]).map(i=>i.toString()),o=n.toLowerCase();this.headers.set(o,r),this.maybeSetNormalizedName(n,o)}forEach(n){this.init(),Array.from(this.normalizedNames.keys()).forEach(e=>n(this.normalizedNames.get(e),this.headers.get(e)))}};var cu=class{map=new Map;set(n,e){return this.map.set(n,e),this}get(n){return this.map.has(n)||this.map.set(n,n.defaultValue()),this.map.get(n)}delete(n){return this.map.delete(n),this}has(n){return this.map.has(n)}keys(){return this.map.keys()}},lu=class{encodeKey(n){return pw(n)}encodeValue(n){return pw(n)}decodeKey(n){return decodeURIComponent(n)}decodeValue(n){return decodeURIComponent(n)}};function uO(t,n){let e=new Map;return t.length>0&&t.replace(/^\?/,"").split("&").forEach(o=>{let i=o.indexOf("="),[s,a]=i==-1?[n.decodeKey(o),""]:[n.decodeKey(o.slice(0,i)),n.decodeValue(o.slice(i+1))],c=e.get(s)||[];c.push(a),e.set(s,c)}),e}var dO=/%(\d[a-f0-9])/gi,fO={40:"@","3A":":",24:"$","2C":",","3B":";","3D":"=","3F":"?","2F":"/"};function pw(t){return encodeURIComponent(t).replace(dO,(n,e)=>fO[e]??n)}function au(t){return`${t}`}var $n=class t{map;encoder;updates=null;cloneFrom=null;constructor(n={}){if(this.encoder=n.encoder||new lu,n.fromString){if(n.fromObject)throw new b(2805,!1);this.map=uO(n.fromString,this.encoder)}else n.fromObject?(this.map=new Map,Object.keys(n.fromObject).forEach(e=>{let r=n.fromObject[e],o=Array.isArray(r)?r.map(au):[au(r)];this.map.set(e,o)})):this.map=null}has(n){return this.init(),this.map.has(n)}get(n){this.init();let e=this.map.get(n);return e?e[0]:null}getAll(n){return this.init(),this.map.get(n)||null}keys(){return this.init(),Array.from(this.map.keys())}append(n,e){return this.clone({param:n,value:e,op:"a"})}appendAll(n){let e=[];return Object.keys(n).forEach(r=>{let o=n[r];Array.isArray(o)?o.forEach(i=>{e.push({param:r,value:i,op:"a"})}):e.push({param:r,value:o,op:"a"})}),this.clone(e)}set(n,e){return this.clone({param:n,value:e,op:"s"})}delete(n,e){return this.clone({param:n,value:e,op:"d"})}toString(){return this.init(),this.keys().map(n=>{let e=this.encoder.encodeKey(n);return this.map.get(n).map(r=>e+"="+this.encoder.encodeValue(r)).join("&")}).filter(n=>n!=="").join("&")}clone(n){let e=new t({encoder:this.encoder});return e.cloneFrom=this.cloneFrom||this,e.updates=(this.updates||[]).concat(n),e}init(){this.map===null&&(this.map=new Map),this.cloneFrom!==null&&(this.cloneFrom.init(),this.cloneFrom.keys().forEach(n=>this.map.set(n,this.cloneFrom.map.get(n))),this.updates.forEach(n=>{switch(n.op){case"a":case"s":let e=(n.op==="a"?this.map.get(n.param):void 0)||[];e.push(au(n.value)),this.map.set(n.param,e);break;case"d":if(n.value!==void 0){let r=this.map.get(n.param)||[],o=r.indexOf(au(n.value));o!==-1&&r.splice(o,1),r.length>0?this.map.set(n.param,r):this.map.delete(n.param)}else{this.map.delete(n.param);break}}}),this.cloneFrom=this.updates=null)}};function hO(t){switch(t){case"DELETE":case"GET":case"HEAD":case"OPTIONS":case"JSONP":return!1;default:return!0}}function mw(t){return typeof ArrayBuffer<"u"&&t instanceof ArrayBuffer}function gw(t){return typeof Blob<"u"&&t instanceof Blob}function vw(t){return typeof FormData<"u"&&t instanceof FormData}function pO(t){return typeof URLSearchParams<"u"&&t instanceof URLSearchParams}var yw="Content-Type",bw="Accept",Dw="text/plain",Ew="application/json",mO=`${Ew}, ${Dw}, */*`,oi=class t{url;body=null;headers;context;reportProgress=!1;withCredentials=!1;credentials;keepalive=!1;cache;priority;mode;redirect;referrer;integrity;referrerPolicy;responseType="json";method;params;urlWithParams;transferCache;timeout;constructor(n,e,r,o){this.url=e,this.method=n.toUpperCase();let i;if(hO(this.method)||o?(this.body=r!==void 0?r:null,i=o):i=r,i){if(this.reportProgress=!!i.reportProgress,this.withCredentials=!!i.withCredentials,this.keepalive=!!i.keepalive,i.responseType&&(this.responseType=i.responseType),i.headers&&(this.headers=i.headers),i.context&&(this.context=i.context),i.params&&(this.params=i.params),i.priority&&(this.priority=i.priority),i.cache&&(this.cache=i.cache),i.credentials&&(this.credentials=i.credentials),typeof i.timeout=="number"){if(i.timeout<1||!Number.isInteger(i.timeout))throw new b(2822,"");this.timeout=i.timeout}i.mode&&(this.mode=i.mode),i.redirect&&(this.redirect=i.redirect),i.integrity&&(this.integrity=i.integrity),i.referrer!==void 0&&(this.referrer=i.referrer),i.referrerPolicy&&(this.referrerPolicy=i.referrerPolicy),this.transferCache=i.transferCache}if(this.headers??=new _n,this.context??=new cu,!this.params)this.params=new $n,this.urlWithParams=e;else{let s=this.params.toString();if(s.length===0)this.urlWithParams=e;else{let a=e.indexOf("?"),c=a===-1?"?":aOi.set(Er,n.setHeaders[Er]),Pe)),n.setParams&&(Le=Object.keys(n.setParams).reduce((Oi,Er)=>Oi.set(Er,n.setParams[Er]),Le)),new t(e,r,E,{params:Le,headers:Pe,context:Ni,reportProgress:ee,responseType:o,withCredentials:I,transferCache:m,keepalive:i,cache:a,priority:s,timeout:_,mode:c,redirect:l,credentials:u,referrer:d,integrity:h,referrerPolicy:p})}},io=(function(t){return t[t.Sent=0]="Sent",t[t.UploadProgress=1]="UploadProgress",t[t.ResponseHeader=2]="ResponseHeader",t[t.DownloadProgress=3]="DownloadProgress",t[t.Response=4]="Response",t[t.User=5]="User",t})(io||{}),si=class{headers;status;statusText;url;ok;type;redirected;responseType;constructor(n,e=200,r="OK"){this.headers=n.headers||new _n,this.status=n.status!==void 0?n.status:e,this.statusText=n.statusText||r,this.url=n.url||null,this.redirected=n.redirected,this.responseType=n.responseType,this.ok=this.status>=200&&this.status<300}},uu=class t extends si{constructor(n={}){super(n)}type=io.ResponseHeader;clone(n={}){return new t({headers:n.headers||this.headers,status:n.status!==void 0?n.status:this.status,statusText:n.statusText||this.statusText,url:n.url||this.url||void 0})}},Ks=class t extends si{body;constructor(n={}){super(n),this.body=n.body!==void 0?n.body:null}type=io.Response;clone(n={}){return new t({body:n.body!==void 0?n.body:this.body,headers:n.headers||this.headers,status:n.status!==void 0?n.status:this.status,statusText:n.statusText||this.statusText,url:n.url||this.url||void 0,redirected:n.redirected??this.redirected,responseType:n.responseType??this.responseType})}},ii=class extends si{name="HttpErrorResponse";message;error;ok=!1;constructor(n){super(n,0,"Unknown Error"),this.status>=200&&this.status<300?this.message=`Http failure during parsing for ${n.url||"(unknown url)"}`:this.message=`Http failure response for ${n.url||"(unknown url)"}: ${n.status} ${n.statusText}`,this.error=n.error||null}},gO=200,vO=204;var yO=new y("");var bO=/^\)\]\}',?\n/;var Pm=(()=>{class t{xhrFactory;tracingService=f(Wt,{optional:!0});constructor(e){this.xhrFactory=e}maybePropagateTrace(e){return this.tracingService?.propagate?this.tracingService.propagate(e):e}handle(e){if(e.method==="JSONP")throw new b(-2800,!1);let r=this.xhrFactory;return T(null).pipe(He(()=>new O(i=>{let s=r.build();if(s.open(e.method,e.urlWithParams),e.withCredentials&&(s.withCredentials=!0),e.headers.forEach((E,I)=>s.setRequestHeader(E,I.join(","))),e.headers.has(bw)||s.setRequestHeader(bw,mO),!e.headers.has(yw)){let E=e.detectContentTypeHeader();E!==null&&s.setRequestHeader(yw,E)}if(e.timeout&&(s.timeout=e.timeout),e.responseType){let E=e.responseType.toLowerCase();s.responseType=E!=="json"?E:"text"}let a=e.serializeBody(),c=null,l=()=>{if(c!==null)return c;let E=s.statusText||"OK",I=new _n(s.getAllResponseHeaders()),ee=s.responseURL||e.url;return c=new uu({headers:I,status:s.status,statusText:E,url:ee}),c},u=this.maybePropagateTrace(()=>{let{headers:E,status:I,statusText:ee,url:Pe}=l(),Le=null;I!==vO&&(Le=typeof s.response>"u"?s.responseText:s.response),I===0&&(I=Le?gO:0);let Ni=I>=200&&I<300;if(e.responseType==="json"&&typeof Le=="string"){let Oi=Le;Le=Le.replace(bO,"");try{Le=Le!==""?JSON.parse(Le):null}catch(Er){Le=Oi,Ni&&(Ni=!1,Le={error:Er,text:Le})}}Ni?(i.next(new Ks({body:Le,headers:E,status:I,statusText:ee,url:Pe||void 0})),i.complete()):i.error(new ii({error:Le,headers:E,status:I,statusText:ee,url:Pe||void 0}))}),d=this.maybePropagateTrace(E=>{let{url:I}=l(),ee=new ii({error:E,status:s.status||0,statusText:s.statusText||"Unknown Error",url:I||void 0});i.error(ee)}),h=d;e.timeout&&(h=this.maybePropagateTrace(E=>{let{url:I}=l(),ee=new ii({error:new DOMException("Request timed out","TimeoutError"),status:s.status||0,statusText:s.statusText||"Request timeout",url:I||void 0});i.error(ee)}));let p=!1,m=this.maybePropagateTrace(E=>{p||(i.next(l()),p=!0);let I={type:io.DownloadProgress,loaded:E.loaded};E.lengthComputable&&(I.total=E.total),e.responseType==="text"&&s.responseText&&(I.partialText=s.responseText),i.next(I)}),_=this.maybePropagateTrace(E=>{let I={type:io.UploadProgress,loaded:E.loaded};E.lengthComputable&&(I.total=E.total),i.next(I)});return s.addEventListener("load",u),s.addEventListener("error",d),s.addEventListener("timeout",h),s.addEventListener("abort",d),e.reportProgress&&(s.addEventListener("progress",m),a!==null&&s.upload&&s.upload.addEventListener("progress",_)),s.send(a),i.next({type:io.Sent}),()=>{s.removeEventListener("error",d),s.removeEventListener("abort",d),s.removeEventListener("load",u),s.removeEventListener("timeout",h),e.reportProgress&&(s.removeEventListener("progress",m),a!==null&&s.upload&&s.upload.removeEventListener("progress",_)),s.readyState!==s.DONE&&s.abort()}})))}static \u0275fac=function(r){return new(r||t)(w(oo))};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();function ww(t,n){return n(t)}function _O(t,n){return(e,r)=>n.intercept(e,{handle:o=>t(o,r)})}function DO(t,n,e){return(r,o)=>xe(e,()=>n(r,i=>t(i,o)))}var Cw=new y(""),Lm=new y("",{factory:()=>[]}),Iw=new y(""),jm=new y("",{factory:()=>!0});function EO(){let t=null;return(n,e)=>{t===null&&(t=(f(Cw,{optional:!0})??[]).reduceRight(_O,ww));let r=f(zo);if(f(jm)){let i=r.add();return t(n,e).pipe(Rr(i))}else return t(n,e)}}var Vm=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:function(r){let o=null;return r?o=new(r||t):o=w(Pm),o},providedIn:"root"})}return t})();var du=(()=>{class t{backend;injector;chain=null;pendingTasks=f(zo);contributeToStability=f(jm);constructor(e,r){this.backend=e,this.injector=r}handle(e){if(this.chain===null){let r=Array.from(new Set([...this.injector.get(Lm),...this.injector.get(Iw,[])]));this.chain=r.reduceRight((o,i)=>DO(o,i,this.injector),ww)}if(this.contributeToStability){let r=this.pendingTasks.add();return this.chain(e,o=>this.backend.handle(o)).pipe(Rr(r))}else return this.chain(e,r=>this.backend.handle(r))}static \u0275fac=function(r){return new(r||t)(w(Vm),w(re))};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})(),Bm=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:function(r){let o=null;return r?o=new(r||t):o=w(du),o},providedIn:"root"})}return t})();function Fm(t,n){return{body:n,headers:t.headers,context:t.context,observe:t.observe,params:t.params,reportProgress:t.reportProgress,responseType:t.responseType,withCredentials:t.withCredentials,credentials:t.credentials,transferCache:t.transferCache,timeout:t.timeout,keepalive:t.keepalive,priority:t.priority,cache:t.cache,mode:t.mode,redirect:t.redirect,integrity:t.integrity,referrer:t.referrer,referrerPolicy:t.referrerPolicy}}var fu=(()=>{class t{handler;constructor(e){this.handler=e}request(e,r,o={}){let i;if(e instanceof oi)i=e;else{let c;o.headers instanceof _n?c=o.headers:c=new _n(o.headers);let l;o.params&&(o.params instanceof $n?l=o.params:l=new $n({fromObject:o.params})),i=new oi(e,r,o.body!==void 0?o.body:null,{headers:c,context:o.context,params:l,reportProgress:o.reportProgress,responseType:o.responseType||"json",withCredentials:o.withCredentials,transferCache:o.transferCache,keepalive:o.keepalive,priority:o.priority,cache:o.cache,mode:o.mode,redirect:o.redirect,credentials:o.credentials,referrer:o.referrer,referrerPolicy:o.referrerPolicy,integrity:o.integrity,timeout:o.timeout})}let s=T(i).pipe(Xn(c=>this.handler.handle(c)));if(e instanceof oi||o.observe==="events")return s;let a=s.pipe(fe(c=>c instanceof Ks));switch(o.observe||"body"){case"body":switch(i.responseType){case"arraybuffer":return a.pipe(H(c=>{if(c.body!==null&&!(c.body instanceof ArrayBuffer))throw new b(2806,!1);return c.body}));case"blob":return a.pipe(H(c=>{if(c.body!==null&&!(c.body instanceof Blob))throw new b(2807,!1);return c.body}));case"text":return a.pipe(H(c=>{if(c.body!==null&&typeof c.body!="string")throw new b(2808,!1);return c.body}));default:return a.pipe(H(c=>c.body))}case"response":return a;default:throw new b(2809,!1)}}delete(e,r={}){return this.request("DELETE",e,r)}get(e,r={}){return this.request("GET",e,r)}head(e,r={}){return this.request("HEAD",e,r)}jsonp(e,r){return this.request("JSONP",e,{params:new $n().append(r,"JSONP_CALLBACK"),observe:"body",responseType:"json"})}options(e,r={}){return this.request("OPTIONS",e,r)}patch(e,r,o={}){return this.request("PATCH",e,Fm(o,r))}post(e,r,o={}){return this.request("POST",e,Fm(o,r))}put(e,r,o={}){return this.request("PUT",e,Fm(o,r))}static \u0275fac=function(r){return new(r||t)(w(Bm))};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();var wO=new y("",{factory:()=>!0}),CO="XSRF-TOKEN",IO=new y("",{factory:()=>CO}),SO="X-XSRF-TOKEN",MO=new y("",{factory:()=>SO}),TO=(()=>{class t{cookieName=f(IO);doc=f(L);lastCookieString="";lastToken=null;parseCount=0;getToken(){let e=this.doc.cookie||"";return e!==this.lastCookieString&&(this.parseCount++,this.lastToken=Gs(e,this.cookieName),this.lastCookieString=e),this.lastToken}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})(),Sw=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:function(r){let o=null;return r?o=new(r||t):o=w(TO),o},providedIn:"root"})}return t})();function xO(t,n){if(!f(wO)||t.method==="GET"||t.method==="HEAD")return n(t);try{let o=f(Bn).href,{origin:i}=new URL(o),{origin:s}=new URL(t.url,i);if(i!==s)return n(t)}catch{return n(t)}let e=f(Sw).getToken(),r=f(MO);return e!=null&&!t.headers.has(r)&&(t=t.clone({headers:t.headers.set(r,e)})),n(t)}var Um=(function(t){return t[t.Interceptors=0]="Interceptors",t[t.LegacyInterceptors=1]="LegacyInterceptors",t[t.CustomXsrfConfiguration=2]="CustomXsrfConfiguration",t[t.NoXsrfProtection=3]="NoXsrfProtection",t[t.JsonpSupport=4]="JsonpSupport",t[t.RequestsMadeViaParent=5]="RequestsMadeViaParent",t[t.Fetch=6]="Fetch",t})(Um||{});function AO(t,n){return{\u0275kind:t,\u0275providers:n}}function Mw(...t){let n=[fu,du,{provide:Bm,useExisting:du},{provide:Vm,useFactory:()=>f(yO,{optional:!0})??f(Pm)},{provide:Lm,useValue:xO,multi:!0}];for(let e of t)n.push(...e.\u0275providers);return sr(n)}var _w=new y("");function Tw(){return AO(Um.LegacyInterceptors,[{provide:_w,useFactory:EO},{provide:Lm,useExisting:_w,multi:!0}])}var RO=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275mod=X({type:t});static \u0275inj=Z({providers:[Mw(Tw())]})}return t})();var xw=(()=>{class t{_doc;constructor(e){this._doc=e}getTitle(){return this._doc.title}setTitle(e){this._doc.title=e||""}static \u0275fac=function(r){return new(r||t)(w(L))};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();function OO(t,n){if(typeof COMPILED>"u"||!COMPILED){let e=ye.ng=ye.ng||{};e[t]=n}}var Hm=class{msPerTick;numTicks;constructor(n,e){this.msPerTick=n,this.numTicks=e}},$m=class{appRef;constructor(n){this.appRef=n.injector.get(Be)}timeChangeDetection(n){let e=n&&n.record,r="Change Detection";e&&"profile"in console&&typeof console.profile=="function"&&console.profile(r);let o=performance.now(),i=0;for(;i<5||performance.now()-o<500;)this.appRef.tick(),i++;let s=performance.now();e&&"profileEnd"in console&&typeof console.profileEnd=="function"&&console.profileEnd(r);let a=(s-o)/i;return console.log(`ran ${i} change detection cycles`),console.log(`${a.toFixed(2)} ms per check`),new Hm(a,i)}},kO="profiler";function k9(t){return OO(kO,new $m(t)),t}var zm=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:function(r){let o=null;return r?o=new(r||t):o=w(FO),o},providedIn:"root"})}return t})(),FO=(()=>{class t extends zm{_doc;constructor(e){super(),this._doc=e}sanitize(e,r){if(r==null)return null;switch(e){case ot.NONE:return r;case ot.HTML:return vn(r,"HTML")?ft(r):ml(this._doc,String(r)).toString();case ot.STYLE:return vn(r,"Style")?ft(r):r;case ot.SCRIPT:if(vn(r,"Script"))return ft(r);throw new b(5200,!1);case ot.URL:return vn(r,"URL")?ft(r):Ss(String(r));case ot.RESOURCE_URL:if(vn(r,"ResourceURL"))return ft(r);throw new b(5201,!1);default:throw new b(5202,!1)}}bypassSecurityTrustHtml(e){return cp(e)}bypassSecurityTrustStyle(e){return lp(e)}bypassSecurityTrustScript(e){return up(e)}bypassSecurityTrustUrl(e){return dp(e)}bypassSecurityTrustResourceUrl(e){return fp(e)}static \u0275fac=function(r){return new(r||t)(w(L))};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();function Qs(t){return t.buttons===0||t.detail===0}function Xs(t){let n=t.touches&&t.touches[0]||t.changedTouches&&t.changedTouches[0];return!!n&&n.identifier===-1&&(n.radiusX==null||n.radiusX===1)&&(n.radiusY==null||n.radiusY===1)}var Gm;function Aw(){if(Gm==null){let t=typeof document<"u"?document.head:null;Gm=!!(t&&(t.createShadowRoot||t.attachShadow))}return Gm}function Wm(t){if(Aw()){let n=t.getRootNode?t.getRootNode():null;if(typeof ShadowRoot<"u"&&ShadowRoot&&n instanceof ShadowRoot)return n}return null}function LO(){let t=typeof document<"u"&&document?document.activeElement:null;for(;t&&t.shadowRoot;){let n=t.shadowRoot.activeElement;if(n===t)break;t=n}return t}function Je(t){return t.composedPath?t.composedPath()[0]:t.target}var qm;try{qm=typeof Intl<"u"&&Intl.v8BreakIterator}catch{qm=!1}var he=(()=>{class t{_platformId=f(Jr);isBrowser=this._platformId?iw(this._platformId):typeof document=="object"&&!!document;EDGE=this.isBrowser&&/(edge)/i.test(navigator.userAgent);TRIDENT=this.isBrowser&&/(msie|trident)/i.test(navigator.userAgent);BLINK=this.isBrowser&&!!(window.chrome||qm)&&typeof CSS<"u"&&!this.EDGE&&!this.TRIDENT;WEBKIT=this.isBrowser&&/AppleWebKit/i.test(navigator.userAgent)&&!this.BLINK&&!this.EDGE&&!this.TRIDENT;IOS=this.isBrowser&&/iPad|iPhone|iPod/.test(navigator.userAgent)&&!("MSStream"in window);FIREFOX=this.isBrowser&&/(firefox|minefield)/i.test(navigator.userAgent);ANDROID=this.isBrowser&&/android/i.test(navigator.userAgent)&&!this.TRIDENT;SAFARI=this.isBrowser&&/safari/i.test(navigator.userAgent)&&this.WEBKIT;constructor(){}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();var Js;function Rw(){if(Js==null&&typeof window<"u")try{window.addEventListener("test",null,Object.defineProperty({},"passive",{get:()=>Js=!0}))}finally{Js=Js||!1}return Js}function ai(t){return Rw()?t:!!t.capture}function so(t,n=0){return Nw(t)?Number(t):arguments.length===2?n:0}function Nw(t){return!isNaN(parseFloat(t))&&!isNaN(Number(t))}function Tt(t){return t instanceof z?t.nativeElement:t}var Ow=new y("cdk-input-modality-detector-options"),kw={ignoreKeys:[18,17,224,91,16]},Fw=650,Ym={passive:!0,capture:!0},Pw=(()=>{class t{_platform=f(he);_listenerCleanups;modalityDetected;modalityChanged;get mostRecentModality(){return this._modality.value}_mostRecentTarget=null;_modality=new Ie(null);_options;_lastTouchMs=0;_onKeydown=e=>{this._options?.ignoreKeys?.some(r=>r===e.keyCode)||(this._modality.next("keyboard"),this._mostRecentTarget=Je(e))};_onMousedown=e=>{Date.now()-this._lastTouchMs{if(Xs(e)){this._modality.next("keyboard");return}this._lastTouchMs=Date.now(),this._modality.next("touch"),this._mostRecentTarget=Je(e)};constructor(){let e=f(j),r=f(L),o=f(Ow,{optional:!0});if(this._options=g(g({},kw),o),this.modalityDetected=this._modality.pipe(Ui(1)),this.modalityChanged=this.modalityDetected.pipe(xo()),this._platform.isBrowser){let i=f(je).createRenderer(null,null);this._listenerCleanups=e.runOutsideAngular(()=>[i.listen(r,"keydown",this._onKeydown,Ym),i.listen(r,"mousedown",this._onMousedown,Ym),i.listen(r,"touchstart",this._onTouchstart,Ym)])}}ngOnDestroy(){this._modality.complete(),this._listenerCleanups?.forEach(e=>e())}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})(),ea=(function(t){return t[t.IMMEDIATE=0]="IMMEDIATE",t[t.EVENTUAL=1]="EVENTUAL",t})(ea||{}),Lw=new y("cdk-focus-monitor-default-options"),hu=ai({passive:!0,capture:!0}),pu=(()=>{class t{_ngZone=f(j);_platform=f(he);_inputModalityDetector=f(Pw);_origin=null;_lastFocusOrigin=null;_windowFocused=!1;_windowFocusTimeoutId;_originTimeoutId;_originFromTouchInteraction=!1;_elementInfo=new Map;_monitoredElementCount=0;_rootNodeFocusListenerCount=new Map;_detectionMode;_windowFocusListener=()=>{this._windowFocused=!0,this._windowFocusTimeoutId=setTimeout(()=>this._windowFocused=!1)};_document=f(L);_stopInputModalityDetector=new S;constructor(){let e=f(Lw,{optional:!0});this._detectionMode=e?.detectionMode||ea.IMMEDIATE}_rootNodeFocusAndBlurListener=e=>{let r=Je(e);for(let o=r;o;o=o.parentElement)e.type==="focus"?this._onFocus(e,o):this._onBlur(e,o)};monitor(e,r=!1){let o=Tt(e);if(!this._platform.isBrowser||o.nodeType!==1)return T();let i=Wm(o)||this._document,s=this._elementInfo.get(o);if(s)return r&&(s.checkChildren=!0),s.subject;let a={checkChildren:r,subject:new S,rootNode:i};return this._elementInfo.set(o,a),this._registerGlobalListeners(a),a.subject}stopMonitoring(e){let r=Tt(e),o=this._elementInfo.get(r);o&&(o.subject.complete(),this._setClasses(r),this._elementInfo.delete(r),this._removeGlobalListeners(o))}focusVia(e,r,o){let i=Tt(e),s=this._document.activeElement;i===s?this._getClosestElementsInfo(i).forEach(([a,c])=>this._originChanged(a,r,c)):(this._setOrigin(r),typeof i.focus=="function"&&i.focus(o))}ngOnDestroy(){this._elementInfo.forEach((e,r)=>this.stopMonitoring(r))}_getWindow(){return this._document.defaultView||window}_getFocusOrigin(e){return this._origin?this._originFromTouchInteraction?this._shouldBeAttributedToTouch(e)?"touch":"program":this._origin:this._windowFocused&&this._lastFocusOrigin?this._lastFocusOrigin:e&&this._isLastInteractionFromInputLabel(e)?"mouse":"program"}_shouldBeAttributedToTouch(e){return this._detectionMode===ea.EVENTUAL||!!e?.contains(this._inputModalityDetector._mostRecentTarget)}_setClasses(e,r){e.classList.toggle("cdk-focused",!!r),e.classList.toggle("cdk-touch-focused",r==="touch"),e.classList.toggle("cdk-keyboard-focused",r==="keyboard"),e.classList.toggle("cdk-mouse-focused",r==="mouse"),e.classList.toggle("cdk-program-focused",r==="program")}_setOrigin(e,r=!1){this._ngZone.runOutsideAngular(()=>{if(this._origin=e,this._originFromTouchInteraction=e==="touch"&&r,this._detectionMode===ea.IMMEDIATE){clearTimeout(this._originTimeoutId);let o=this._originFromTouchInteraction?Fw:1;this._originTimeoutId=setTimeout(()=>this._origin=null,o)}})}_onFocus(e,r){let o=this._elementInfo.get(r),i=Je(e);!o||!o.checkChildren&&r!==i||this._originChanged(r,this._getFocusOrigin(i),o)}_onBlur(e,r){let o=this._elementInfo.get(r);!o||o.checkChildren&&e.relatedTarget instanceof Node&&r.contains(e.relatedTarget)||(this._setClasses(r),this._emitOrigin(o,null))}_emitOrigin(e,r){e.subject.observers.length&&this._ngZone.run(()=>e.subject.next(r))}_registerGlobalListeners(e){if(!this._platform.isBrowser)return;let r=e.rootNode,o=this._rootNodeFocusListenerCount.get(r)||0;o||this._ngZone.runOutsideAngular(()=>{r.addEventListener("focus",this._rootNodeFocusAndBlurListener,hu),r.addEventListener("blur",this._rootNodeFocusAndBlurListener,hu)}),this._rootNodeFocusListenerCount.set(r,o+1),++this._monitoredElementCount===1&&(this._ngZone.runOutsideAngular(()=>{this._getWindow().addEventListener("focus",this._windowFocusListener)}),this._inputModalityDetector.modalityDetected.pipe(at(this._stopInputModalityDetector)).subscribe(i=>{this._setOrigin(i,!0)}))}_removeGlobalListeners(e){let r=e.rootNode;if(this._rootNodeFocusListenerCount.has(r)){let o=this._rootNodeFocusListenerCount.get(r);o>1?this._rootNodeFocusListenerCount.set(r,o-1):(r.removeEventListener("focus",this._rootNodeFocusAndBlurListener,hu),r.removeEventListener("blur",this._rootNodeFocusAndBlurListener,hu),this._rootNodeFocusListenerCount.delete(r))}--this._monitoredElementCount||(this._getWindow().removeEventListener("focus",this._windowFocusListener),this._stopInputModalityDetector.next(),clearTimeout(this._windowFocusTimeoutId),clearTimeout(this._originTimeoutId))}_originChanged(e,r,o){this._setClasses(e,r),this._emitOrigin(o,r),this._lastFocusOrigin=r}_getClosestElementsInfo(e){let r=[];return this._elementInfo.forEach((o,i)=>{(i===e||o.checkChildren&&i.contains(e))&&r.push([i,o])}),r}_isLastInteractionFromInputLabel(e){let{_mostRecentTarget:r,mostRecentModality:o}=this._inputModalityDetector;if(o!=="mouse"||!r||r===e||e.nodeName!=="INPUT"&&e.nodeName!=="TEXTAREA"||e.disabled)return!1;let i=e.labels;if(i){for(let s=0;s{class t{_elementRef=f(z);_focusMonitor=f(pu);_monitorSubscription;_focusOrigin=null;cdkFocusChange=new U;constructor(){}get focusOrigin(){return this._focusOrigin}ngAfterViewInit(){let e=this._elementRef.nativeElement;this._monitorSubscription=this._focusMonitor.monitor(e,e.nodeType===1&&e.hasAttribute("cdkMonitorSubtreeFocus")).subscribe(r=>{this._focusOrigin=r,this.cdkFocusChange.emit(r)})}ngOnDestroy(){this._focusMonitor.stopMonitoring(this._elementRef),this._monitorSubscription?.unsubscribe()}static \u0275fac=function(r){return new(r||t)};static \u0275dir=M({type:t,selectors:[["","cdkMonitorElementFocus",""],["","cdkMonitorSubtreeFocus",""]],outputs:{cdkFocusChange:"cdkFocusChange"},exportAs:["cdkMonitorFocus"]})}return t})();var mu=new WeakMap,xt=(()=>{class t{_appRef;_injector=f($);_environmentInjector=f(re);load(e){let r=this._appRef=this._appRef||this._injector.get(Be),o=mu.get(r);o||(o={loaders:new Set,refs:[]},mu.set(r,o),r.onDestroy(()=>{mu.get(r)?.refs.forEach(i=>i.destroy()),mu.delete(r)})),o.loaders.has(e)||(o.loaders.add(e),o.refs.push($l(e,{environmentInjector:this._environmentInjector})))}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();var vu=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275cmp=ke({type:t,selectors:[["ng-component"]],exportAs:["cdkVisuallyHidden"],decls:0,vars:0,template:function(r,o){},styles:[`.cdk-visually-hidden { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; + white-space: nowrap; + outline: 0; + -webkit-appearance: none; + -moz-appearance: none; + left: 0; +} +[dir=rtl] .cdk-visually-hidden { + left: auto; + right: 0; +} +`],encapsulation:2,changeDetection:0})}return t})(),gu;function VO(){if(gu===void 0&&(gu=null,typeof window<"u")){let t=window;t.trustedTypes!==void 0&&(gu=t.trustedTypes.createPolicy("angular#components",{createHTML:n=>n}))}return gu}function BO(t){return VO()?.createHTML(t)||t}function jw(t,n,e){let r=e.sanitize(ot.HTML,n);t.innerHTML=BO(r||"")}function ao(t){return Array.isArray(t)?t:[t]}var Vw=new Set,co,yu=(()=>{class t{_platform=f(he);_nonce=f(eo,{optional:!0});_matchMedia;constructor(){this._matchMedia=this._platform.isBrowser&&window.matchMedia?window.matchMedia.bind(window):HO}matchMedia(e){return(this._platform.WEBKIT||this._platform.BLINK)&&UO(e,this._nonce),this._matchMedia(e)}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();function UO(t,n){if(!Vw.has(t))try{co||(co=document.createElement("style"),n&&co.setAttribute("nonce",n),co.setAttribute("type","text/css"),document.head.appendChild(co)),co.sheet&&(co.sheet.insertRule(`@media ${t} {body{ }}`,0),Vw.add(t))}catch(e){console.error(e)}}function HO(t){return{matches:t==="all"||t==="",media:t,addListener:()=>{},removeListener:()=>{}}}var Zm=(()=>{class t{_mediaMatcher=f(yu);_zone=f(j);_queries=new Map;_destroySubject=new S;constructor(){}ngOnDestroy(){this._destroySubject.next(),this._destroySubject.complete()}isMatched(e){return Bw(ao(e)).some(o=>this._registerQuery(o).mql.matches)}observe(e){let o=Bw(ao(e)).map(s=>this._registerQuery(s).observable),i=To(o);return i=on(i.pipe(Ue(1)),i.pipe(Ui(1),Ar(0))),i.pipe(H(s=>{let a={matches:!1,breakpoints:{}};return s.forEach(({matches:c,query:l})=>{a.matches=a.matches||c,a.breakpoints[l]=c}),a}))}_registerQuery(e){if(this._queries.has(e))return this._queries.get(e);let r=this._mediaMatcher.matchMedia(e),i={observable:new O(s=>{let a=c=>this._zone.run(()=>s.next(c));return r.addListener(a),()=>{r.removeListener(a)}}).pipe(Nr(r),H(({matches:s})=>({query:e,matches:s})),at(this._destroySubject)),mql:r};return this._queries.set(e,i),i}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();function Bw(t){return t.map(n=>n.split(",")).reduce((n,e)=>n.concat(e)).map(n=>n.trim())}function $O(t){if(t.type==="characterData"&&t.target instanceof Comment)return!0;if(t.type==="childList"){for(let n=0;n{class t{create(e){return typeof MutationObserver>"u"?null:new MutationObserver(e)}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})(),Hw=(()=>{class t{_mutationObserverFactory=f(Uw);_observedElements=new Map;_ngZone=f(j);constructor(){}ngOnDestroy(){this._observedElements.forEach((e,r)=>this._cleanupObserver(r))}observe(e){let r=Tt(e);return new O(o=>{let s=this._observeElement(r).pipe(H(a=>a.filter(c=>!$O(c))),fe(a=>!!a.length)).subscribe(a=>{this._ngZone.run(()=>{o.next(a)})});return()=>{s.unsubscribe(),this._unobserveElement(r)}})}_observeElement(e){return this._ngZone.runOutsideAngular(()=>{if(this._observedElements.has(e))this._observedElements.get(e).count++;else{let r=new S,o=this._mutationObserverFactory.create(i=>r.next(i));o&&o.observe(e,{characterData:!0,childList:!0,subtree:!0}),this._observedElements.set(e,{observer:o,stream:r,count:1})}return this._observedElements.get(e).stream})}_unobserveElement(e){this._observedElements.has(e)&&(this._observedElements.get(e).count--,this._observedElements.get(e).count||this._cleanupObserver(e))}_cleanupObserver(e){if(this._observedElements.has(e)){let{observer:r,stream:o}=this._observedElements.get(e);r&&r.disconnect(),o.complete(),this._observedElements.delete(e)}}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})(),FY=(()=>{class t{_contentObserver=f(Hw);_elementRef=f(z);event=new U;get disabled(){return this._disabled}set disabled(e){this._disabled=e,this._disabled?this._unsubscribe():this._subscribe()}_disabled=!1;get debounce(){return this._debounce}set debounce(e){this._debounce=so(e),this._subscribe()}_debounce;_currentSubscription=null;constructor(){}ngAfterContentInit(){!this._currentSubscription&&!this.disabled&&this._subscribe()}ngOnDestroy(){this._unsubscribe()}_subscribe(){this._unsubscribe();let e=this._contentObserver.observe(this._elementRef);this._currentSubscription=(this.debounce?e.pipe(Ar(this.debounce)):e).subscribe(this.event)}_unsubscribe(){this._currentSubscription?.unsubscribe()}static \u0275fac=function(r){return new(r||t)};static \u0275dir=M({type:t,selectors:[["","cdkObserveContent",""]],inputs:{disabled:[2,"cdkObserveContentDisabled","disabled",ue],debounce:"debounce"},outputs:{event:"cdkObserveContent"},exportAs:["cdkObserveContent"]})}return t})(),$w=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275mod=X({type:t});static \u0275inj=Z({providers:[Uw]})}return t})();var zO=(()=>{class t{_platform=f(he);constructor(){}isDisabled(e){return e.hasAttribute("disabled")}isVisible(e){return WO(e)&&getComputedStyle(e).visibility==="visible"}isTabbable(e){if(!this._platform.isBrowser)return!1;let r=GO(ek(e));if(r&&(zw(r)===-1||!this.isVisible(r)))return!1;let o=e.nodeName.toLowerCase(),i=zw(e);return e.hasAttribute("contenteditable")?i!==-1:o==="iframe"||o==="object"||this._platform.WEBKIT&&this._platform.IOS&&!XO(e)?!1:o==="audio"?e.hasAttribute("controls")?i!==-1:!1:o==="video"?i===-1?!1:i!==null?!0:this._platform.FIREFOX||e.hasAttribute("controls"):e.tabIndex>=0}isFocusable(e,r){return JO(e)&&!this.isDisabled(e)&&(r?.ignoreVisibility||this.isVisible(e))}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();function GO(t){try{return t.frameElement}catch{return null}}function WO(t){return!!(t.offsetWidth||t.offsetHeight||typeof t.getClientRects=="function"&&t.getClientRects().length)}function qO(t){let n=t.nodeName.toLowerCase();return n==="input"||n==="select"||n==="button"||n==="textarea"}function YO(t){return KO(t)&&t.type=="hidden"}function ZO(t){return QO(t)&&t.hasAttribute("href")}function KO(t){return t.nodeName.toLowerCase()=="input"}function QO(t){return t.nodeName.toLowerCase()=="a"}function qw(t){if(!t.hasAttribute("tabindex")||t.tabIndex===void 0)return!1;let n=t.getAttribute("tabindex");return!!(n&&!isNaN(parseInt(n,10)))}function zw(t){if(!qw(t))return null;let n=parseInt(t.getAttribute("tabindex")||"",10);return isNaN(n)?-1:n}function XO(t){let n=t.nodeName.toLowerCase(),e=n==="input"&&t.type;return e==="text"||e==="password"||n==="select"||n==="textarea"}function JO(t){return YO(t)?!1:qO(t)||ZO(t)||t.hasAttribute("contenteditable")||qw(t)}function ek(t){return t.ownerDocument&&t.ownerDocument.defaultView||window}var Qm=class{_element;_checker;_ngZone;_document;_injector;_startAnchor=null;_endAnchor=null;_hasAttached=!1;startAnchorListener=()=>this.focusLastTabbableElement();endAnchorListener=()=>this.focusFirstTabbableElement();get enabled(){return this._enabled}set enabled(n){this._enabled=n,this._startAnchor&&this._endAnchor&&(this._toggleAnchorTabIndex(n,this._startAnchor),this._toggleAnchorTabIndex(n,this._endAnchor))}_enabled=!0;constructor(n,e,r,o,i=!1,s){this._element=n,this._checker=e,this._ngZone=r,this._document=o,this._injector=s,i||this.attachAnchors()}destroy(){let n=this._startAnchor,e=this._endAnchor;n&&(n.removeEventListener("focus",this.startAnchorListener),n.remove()),e&&(e.removeEventListener("focus",this.endAnchorListener),e.remove()),this._startAnchor=this._endAnchor=null,this._hasAttached=!1}attachAnchors(){return this._hasAttached?!0:(this._ngZone.runOutsideAngular(()=>{this._startAnchor||(this._startAnchor=this._createAnchor(),this._startAnchor.addEventListener("focus",this.startAnchorListener)),this._endAnchor||(this._endAnchor=this._createAnchor(),this._endAnchor.addEventListener("focus",this.endAnchorListener))}),this._element.parentNode&&(this._element.parentNode.insertBefore(this._startAnchor,this._element),this._element.parentNode.insertBefore(this._endAnchor,this._element.nextSibling),this._hasAttached=!0),this._hasAttached)}focusInitialElementWhenReady(n){return new Promise(e=>{this._executeOnStable(()=>e(this.focusInitialElement(n)))})}focusFirstTabbableElementWhenReady(n){return new Promise(e=>{this._executeOnStable(()=>e(this.focusFirstTabbableElement(n)))})}focusLastTabbableElementWhenReady(n){return new Promise(e=>{this._executeOnStable(()=>e(this.focusLastTabbableElement(n)))})}_getRegionBoundary(n){let e=this._element.querySelectorAll(`[cdk-focus-region-${n}], [cdkFocusRegion${n}], [cdk-focus-${n}]`);return n=="start"?e.length?e[0]:this._getFirstTabbableElement(this._element):e.length?e[e.length-1]:this._getLastTabbableElement(this._element)}focusInitialElement(n){let e=this._element.querySelector("[cdk-focus-initial], [cdkFocusInitial]");if(e){if(!this._checker.isFocusable(e)){let r=this._getFirstTabbableElement(e);return r?.focus(n),!!r}return e.focus(n),!0}return this.focusFirstTabbableElement(n)}focusFirstTabbableElement(n){let e=this._getRegionBoundary("start");return e&&e.focus(n),!!e}focusLastTabbableElement(n){let e=this._getRegionBoundary("end");return e&&e.focus(n),!!e}hasAttached(){return this._hasAttached}_getFirstTabbableElement(n){if(this._checker.isFocusable(n)&&this._checker.isTabbable(n))return n;let e=n.children;for(let r=0;r=0;r--){let o=e[r].nodeType===this._document.ELEMENT_NODE?this._getLastTabbableElement(e[r]):null;if(o)return o}return null}_createAnchor(){let n=this._document.createElement("div");return this._toggleAnchorTabIndex(this._enabled,n),n.classList.add("cdk-visually-hidden"),n.classList.add("cdk-focus-trap-anchor"),n.setAttribute("aria-hidden","true"),n}_toggleAnchorTabIndex(n,e){n?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")}toggleAnchors(n){this._startAnchor&&this._endAnchor&&(this._toggleAnchorTabIndex(n,this._startAnchor),this._toggleAnchorTabIndex(n,this._endAnchor))}_executeOnStable(n){this._injector?ht(n,{injector:this._injector}):setTimeout(n)}},tk=(()=>{class t{_checker=f(zO);_ngZone=f(j);_document=f(L);_injector=f($);constructor(){f(xt).load(vu)}create(e,r=!1){return new Qm(e,this._checker,this._ngZone,this._document,r,this._injector)}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();var Yw=new y("liveAnnouncerElement",{providedIn:"root",factory:()=>null}),Zw=new y("LIVE_ANNOUNCER_DEFAULT_OPTIONS"),nk=0,rk=(()=>{class t{_ngZone=f(j);_defaultOptions=f(Zw,{optional:!0});_liveElement;_document=f(L);_sanitizer=f(zm);_previousTimeout;_currentPromise;_currentResolve;constructor(){let e=f(Yw,{optional:!0});this._liveElement=e||this._createLiveElement()}announce(e,...r){let o=this._defaultOptions,i,s;return r.length===1&&typeof r[0]=="number"?s=r[0]:[i,s]=r,this.clear(),clearTimeout(this._previousTimeout),i||(i=o&&o.politeness?o.politeness:"polite"),s==null&&o&&(s=o.duration),this._liveElement.setAttribute("aria-live",i),this._liveElement.id&&this._exposeAnnouncerToModals(this._liveElement.id),this._ngZone.runOutsideAngular(()=>(this._currentPromise||(this._currentPromise=new Promise(a=>this._currentResolve=a)),clearTimeout(this._previousTimeout),this._previousTimeout=setTimeout(()=>{!e||typeof e=="string"?this._liveElement.textContent=e:jw(this._liveElement,e,this._sanitizer),typeof s=="number"&&(this._previousTimeout=setTimeout(()=>this.clear(),s)),this._currentResolve?.(),this._currentPromise=this._currentResolve=void 0},100),this._currentPromise))}clear(){this._liveElement&&(this._liveElement.textContent="")}ngOnDestroy(){clearTimeout(this._previousTimeout),this._liveElement?.remove(),this._liveElement=null,this._currentResolve?.(),this._currentPromise=this._currentResolve=void 0}_createLiveElement(){let e="cdk-live-announcer-element",r=this._document.getElementsByClassName(e),o=this._document.createElement("div");for(let i=0;i .cdk-overlay-container [aria-modal="true"]');for(let o=0;o{class t{_platform=f(he);_hasCheckedHighContrastMode=!1;_document=f(L);_breakpointSubscription;constructor(){this._breakpointSubscription=f(Zm).observe("(forced-colors: active)").subscribe(()=>{this._hasCheckedHighContrastMode&&(this._hasCheckedHighContrastMode=!1,this._applyBodyHighContrastModeCssClasses())})}getHighContrastMode(){if(!this._platform.isBrowser)return pr.NONE;let e=this._document.createElement("div");e.style.backgroundColor="rgb(1,2,3)",e.style.position="absolute",this._document.body.appendChild(e);let r=this._document.defaultView||window,o=r&&r.getComputedStyle?r.getComputedStyle(e):null,i=(o&&o.backgroundColor||"").replace(/ /g,"");switch(e.remove(),i){case"rgb(0,0,0)":case"rgb(45,50,54)":case"rgb(32,32,32)":return pr.WHITE_ON_BLACK;case"rgb(255,255,255)":case"rgb(255,250,239)":return pr.BLACK_ON_WHITE}return pr.NONE}ngOnDestroy(){this._breakpointSubscription.unsubscribe()}_applyBodyHighContrastModeCssClasses(){if(!this._hasCheckedHighContrastMode&&this._platform.isBrowser&&this._document.body){let e=this._document.body.classList;e.remove(Km,Gw,Ww),this._hasCheckedHighContrastMode=!0;let r=this.getHighContrastMode();r===pr.BLACK_ON_WHITE?e.add(Km,Gw):r===pr.WHITE_ON_BLACK&&e.add(Km,Ww)}}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})(),ok=(()=>{class t{constructor(){f(Kw)._applyBodyHighContrastModeCssClasses()}static \u0275fac=function(r){return new(r||t)};static \u0275mod=X({type:t});static \u0275inj=Z({imports:[$w]})}return t})();var ik=200,ci=class{_letterKeyStream=new S;_items=[];_selectedItemIndex=-1;_pressedLetters=[];_skipPredicateFn;_selectedItem=new S;selectedItem=this._selectedItem;constructor(n,e){let r=typeof e?.debounceInterval=="number"?e.debounceInterval:ik;e?.skipPredicate&&(this._skipPredicateFn=e.skipPredicate),this.setItems(n),this._setupKeyHandler(r)}destroy(){this._pressedLetters=[],this._letterKeyStream.complete(),this._selectedItem.complete()}setCurrentSelectedItemIndex(n){this._selectedItemIndex=n}setItems(n){this._items=n}handleKey(n){let e=n.keyCode;n.key&&n.key.length===1?this._letterKeyStream.next(n.key.toLocaleUpperCase()):(e>=65&&e<=90||e>=48&&e<=57)&&this._letterKeyStream.next(String.fromCharCode(e))}isTyping(){return this._pressedLetters.length>0}reset(){this._pressedLetters=[]}_setupKeyHandler(n){this._letterKeyStream.pipe(nt(e=>this._pressedLetters.push(e)),Ar(n),fe(()=>this._pressedLetters.length>0),H(()=>this._pressedLetters.join("").toLocaleUpperCase())).subscribe(e=>{for(let r=1;rt[e]):t.altKey||t.shiftKey||t.ctrlKey||t.metaKey}var li=class{_items;_activeItemIndex=W(-1);_activeItem=W(null);_wrap=!1;_typeaheadSubscription=G.EMPTY;_itemChangesSubscription;_vertical=!0;_horizontal=null;_allowedModifierKeys=[];_homeAndEnd=!1;_pageUpAndDown={enabled:!1,delta:10};_effectRef;_typeahead;_skipPredicateFn=n=>n.disabled;constructor(n,e){this._items=n,n instanceof Fn?this._itemChangesSubscription=n.changes.subscribe(r=>this._itemsChanged(r.toArray())):ri(n)&&(this._effectRef=Go(()=>this._itemsChanged(n()),{injector:e}))}tabOut=new S;change=new S;skipPredicate(n){return this._skipPredicateFn=n,this}withWrap(n=!0){return this._wrap=n,this}withVerticalOrientation(n=!0){return this._vertical=n,this}withHorizontalOrientation(n){return this._horizontal=n,this}withAllowedModifierKeys(n){return this._allowedModifierKeys=n,this}withTypeAhead(n=200){this._typeaheadSubscription.unsubscribe();let e=this._getItemsArray();return this._typeahead=new ci(e,{debounceInterval:typeof n=="number"?n:void 0,skipPredicate:r=>this._skipPredicateFn(r)}),this._typeaheadSubscription=this._typeahead.selectedItem.subscribe(r=>{this.setActiveItem(r)}),this}cancelTypeahead(){return this._typeahead?.reset(),this}withHomeAndEnd(n=!0){return this._homeAndEnd=n,this}withPageUpDown(n=!0,e=10){return this._pageUpAndDown={enabled:n,delta:e},this}setActiveItem(n){let e=this._activeItem();this.updateActiveItem(n),this._activeItem()!==e&&this.change.next(this._activeItemIndex())}onKeydown(n){let e=n.keyCode,o=["altKey","ctrlKey","metaKey","shiftKey"].every(i=>!n[i]||this._allowedModifierKeys.indexOf(i)>-1);switch(e){case 9:this.tabOut.next();return;case 40:if(this._vertical&&o){this.setNextItemActive();break}else return;case 38:if(this._vertical&&o){this.setPreviousItemActive();break}else return;case 39:if(this._horizontal&&o){this._horizontal==="rtl"?this.setPreviousItemActive():this.setNextItemActive();break}else return;case 37:if(this._horizontal&&o){this._horizontal==="rtl"?this.setNextItemActive():this.setPreviousItemActive();break}else return;case 36:if(this._homeAndEnd&&o){this.setFirstItemActive();break}else return;case 35:if(this._homeAndEnd&&o){this.setLastItemActive();break}else return;case 33:if(this._pageUpAndDown.enabled&&o){let i=this._activeItemIndex()-this._pageUpAndDown.delta;this._setActiveItemByIndex(i>0?i:0,1);break}else return;case 34:if(this._pageUpAndDown.enabled&&o){let i=this._activeItemIndex()+this._pageUpAndDown.delta,s=this._getItemsArray().length;this._setActiveItemByIndex(i-1&&r!==this._activeItemIndex()&&(this._activeItemIndex.set(r),this._typeahead?.setCurrentSelectedItemIndex(r))}}};var Xm=class extends li{setActiveItem(n){this.activeItem&&this.activeItem.setInactiveStyles(),super.setActiveItem(n),this.activeItem&&this.activeItem.setActiveStyles()}};var Jm=class extends li{_origin="program";setFocusOrigin(n){return this._origin=n,this}setActiveItem(n){super.setActiveItem(n),this.activeItem&&this.activeItem.focus(this._origin)}};function eg(t){return Ft(t)?t:T(t)}var tg=class{_activeItemIndex=-1;_activeItem=null;_shouldActivationFollowFocus=!1;_horizontalOrientation="ltr";_skipPredicateFn=n=>!1;_trackByFn=n=>n;_items=[];_typeahead;_typeaheadSubscription=G.EMPTY;_hasInitialFocused=!1;_initializeFocus(){if(this._hasInitialFocused||this._items.length===0)return;let n=0;for(let r=0;rthis._itemsChanged(r.toArray()))):Ft(n)?n.subscribe(r=>this._itemsChanged(r)):(this._items=n,this._initializeFocus()),typeof e.shouldActivationFollowFocus=="boolean"&&(this._shouldActivationFollowFocus=e.shouldActivationFollowFocus),e.horizontalOrientation&&(this._horizontalOrientation=e.horizontalOrientation),e.skipPredicate&&(this._skipPredicateFn=e.skipPredicate),e.trackBy&&(this._trackByFn=e.trackBy),typeof e.typeAheadDebounceInterval<"u"&&this._setTypeAhead(e.typeAheadDebounceInterval)}change=new S;destroy(){this._typeaheadSubscription.unsubscribe(),this._typeahead?.destroy(),this.change.complete()}onKeydown(n){switch(n.key){case"Tab":return;case"ArrowDown":this._focusNextItem();break;case"ArrowUp":this._focusPreviousItem();break;case"ArrowRight":this._horizontalOrientation==="rtl"?this._collapseCurrentItem():this._expandCurrentItem();break;case"ArrowLeft":this._horizontalOrientation==="rtl"?this._expandCurrentItem():this._collapseCurrentItem();break;case"Home":this._focusFirstItem();break;case"End":this._focusLastItem();break;case"Enter":case" ":this._activateCurrentItem();break;default:if(n.key==="*"){this._expandAllItemsAtCurrentItemLevel();break}this._typeahead?.handleKey(n);return}this._typeahead?.reset(),n.preventDefault()}getActiveItemIndex(){return this._activeItemIndex}getActiveItem(){return this._activeItem}_itemsChanged(n){this._hasInitialFocused&&this._activeItem&&!n.includes(this._activeItem)&&(this._activeItem=null,this._hasInitialFocused=!1),this._items=n,this._typeahead?.setItems(this._items),this._updateActiveItemIndex(this._items),this._initializeFocus()}_focusFirstItem(){this.focusItem(this._findNextAvailableItemIndex(-1))}_focusLastItem(){this.focusItem(this._findPreviousAvailableItemIndex(this._items.length))}_focusNextItem(){this.focusItem(this._findNextAvailableItemIndex(this._activeItemIndex))}_focusPreviousItem(){this.focusItem(this._findPreviousAvailableItemIndex(this._activeItemIndex))}focusItem(n,e={}){e.emitChangeEvent??=!0;let r=typeof n=="number"?n:this._items.findIndex(s=>this._trackByFn(s)===this._trackByFn(n));if(r<0||r>=this._items.length)return;let o=this._items[r];if(this._activeItem!==null&&this._trackByFn(o)===this._trackByFn(this._activeItem))return;let i=this._activeItem;this._activeItem=o??null,this._activeItemIndex=r,this._typeahead?.setCurrentSelectedItemIndex(r),this._activeItem?.focus(),i?.unfocus(),e.emitChangeEvent&&this.change.next(this._activeItem),this._shouldActivationFollowFocus&&this._activateCurrentItem()}_updateActiveItemIndex(n){let e=this._activeItem;if(!e)return;let r=n.findIndex(o=>this._trackByFn(o)===this._trackByFn(e));r>-1&&r!==this._activeItemIndex&&(this._activeItemIndex=r,this._typeahead?.setCurrentSelectedItemIndex(r))}_setTypeAhead(n){this._typeahead=new ci(this._items,{debounceInterval:typeof n=="number"?n:void 0,skipPredicate:e=>this._skipPredicateFn(e)}),this._typeaheadSubscription=this._typeahead.selectedItem.subscribe(e=>{this.focusItem(e)})}_findNextAvailableItemIndex(n){for(let e=n+1;e=0;e--)if(!this._skipPredicateFn(this._items[e]))return e;return n}_collapseCurrentItem(){if(this._activeItem)if(this._isCurrentItemExpanded())this._activeItem.collapse();else{let n=this._activeItem.getParent();if(!n||this._skipPredicateFn(n))return;this.focusItem(n)}}_expandCurrentItem(){this._activeItem&&(this._isCurrentItemExpanded()?eg(this._activeItem.getChildren()).pipe(Ue(1)).subscribe(n=>{let e=n.find(r=>!this._skipPredicateFn(r));e&&this.focusItem(e)}):this._activeItem.expand())}_isCurrentItemExpanded(){return this._activeItem?typeof this._activeItem.isExpanded=="boolean"?this._activeItem.isExpanded:this._activeItem.isExpanded():!1}_isItemDisabled(n){return typeof n.isDisabled=="boolean"?n.isDisabled:n.isDisabled?.()}_expandAllItemsAtCurrentItemLevel(){if(!this._activeItem)return;let n=this._activeItem.getParent(),e;n?e=eg(n.getChildren()):e=T(this._items.filter(r=>r.getParent()===null)),e.pipe(Ue(1)).subscribe(r=>{for(let o of r)o.expand()})}_activateCurrentItem(){this._activeItem?.activate()}},TZ=new y("tree-key-manager",{providedIn:"root",factory:()=>(t,n)=>new tg(t,n)});var ng={},ta=class t{_appId=f(hr);static _infix=`a${Math.floor(Math.random()*1e5).toString()}`;getId(n,e=!1){return this._appId!=="ng"&&(n+=this._appId),ng.hasOwnProperty(n)||(ng[n]=0),`${n}${e?t._infix+"-":""}${ng[n]++}`}static \u0275fac=function(e){return new(e||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})};var Xw=" ";function sk(t,n,e){let r=Du(t,n);e=e.trim(),!r.some(o=>o.trim()===e)&&(r.push(e),t.setAttribute(n,r.join(Xw)))}function ak(t,n,e){let r=Du(t,n);e=e.trim();let o=r.filter(i=>i!==e);o.length?t.setAttribute(n,o.join(Xw)):t.removeAttribute(n)}function Du(t,n){return t.getAttribute(n)?.match(/\S+/g)??[]}var Jw="cdk-describedby-message",_u="cdk-describedby-host",og=0,UZ=(()=>{class t{_platform=f(he);_document=f(L);_messageRegistry=new Map;_messagesContainer=null;_id=`${og++}`;constructor(){f(xt).load(vu),this._id=f(hr)+"-"+og++}describe(e,r,o){if(!this._canBeDescribed(e,r))return;let i=rg(r,o);typeof r!="string"?(Qw(r,this._id),this._messageRegistry.set(i,{messageElement:r,referenceCount:0})):this._messageRegistry.has(i)||this._createMessageElement(r,o),this._isElementDescribedByMessage(e,i)||this._addMessageReference(e,i)}removeDescription(e,r,o){if(!r||!this._isElementNode(e))return;let i=rg(r,o);if(this._isElementDescribedByMessage(e,i)&&this._removeMessageReference(e,i),typeof r=="string"){let s=this._messageRegistry.get(i);s&&s.referenceCount===0&&this._deleteMessageElement(i)}this._messagesContainer?.childNodes.length===0&&(this._messagesContainer.remove(),this._messagesContainer=null)}ngOnDestroy(){let e=this._document.querySelectorAll(`[${_u}="${this._id}"]`);for(let r=0;ro.indexOf(Jw)!=0);e.setAttribute("aria-describedby",r.join(" "))}_addMessageReference(e,r){let o=this._messageRegistry.get(r);sk(e,"aria-describedby",o.messageElement.id),e.setAttribute(_u,this._id),o.referenceCount++}_removeMessageReference(e,r){let o=this._messageRegistry.get(r);o.referenceCount--,ak(e,"aria-describedby",o.messageElement.id),e.removeAttribute(_u)}_isElementDescribedByMessage(e,r){let o=Du(e,"aria-describedby"),i=this._messageRegistry.get(r),s=i&&i.messageElement.id;return!!s&&o.indexOf(s)!=-1}_canBeDescribed(e,r){if(!this._isElementNode(e))return!1;if(r&&typeof r=="object")return!0;let o=r==null?"":`${r}`.trim(),i=e.getAttribute("aria-label");return o?!i||i.trim()!==o:!1}_isElementNode(e){return e.nodeType===this._document.ELEMENT_NODE}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();function rg(t,n){return typeof t=="string"?`${n||""}/${t}`:t}function Qw(t,n){t.id||(t.id=`${Jw}-${n}-${og++}`)}var en=(function(t){return t[t.NORMAL=0]="NORMAL",t[t.NEGATED=1]="NEGATED",t[t.INVERTED=2]="INVERTED",t})(en||{}),Eu,lo;function wu(){if(lo==null){if(typeof document!="object"||!document||typeof Element!="function"||!Element)return lo=!1,lo;if(document.documentElement?.style&&"scrollBehavior"in document.documentElement.style)lo=!0;else{let t=Element.prototype.scrollTo;t?lo=!/\{\s*\[native code\]\s*\}/.test(t.toString()):lo=!1}}return lo}function ui(){if(typeof document!="object"||!document)return en.NORMAL;if(Eu==null){let t=document.createElement("div"),n=t.style;t.dir="rtl",n.width="1px",n.overflow="auto",n.visibility="hidden",n.pointerEvents="none",n.position="absolute";let e=document.createElement("div"),r=e.style;r.width="2px",r.height="1px",t.appendChild(e),document.body.appendChild(t),Eu=en.NORMAL,t.scrollLeft===0&&(t.scrollLeft=1,Eu=t.scrollLeft===0?en.NEGATED:en.INVERTED),t.remove()}return Eu}function ig(){return typeof __karma__<"u"&&!!__karma__||typeof jasmine<"u"&&!!jasmine||typeof jest<"u"&&!!jest||typeof Mocha<"u"&&!!Mocha}var di,eC=["color","button","checkbox","date","datetime-local","email","file","hidden","image","month","number","password","radio","range","reset","search","submit","tel","text","time","url","week"];function QZ(){if(di)return di;if(typeof document!="object"||!document)return di=new Set(eC),di;let t=document.createElement("input");return di=new Set(eC.filter(n=>(t.setAttribute("type",n),t.type===n))),di}var n7={XSmall:"(max-width: 599.98px)",Small:"(min-width: 600px) and (max-width: 959.98px)",Medium:"(min-width: 960px) and (max-width: 1279.98px)",Large:"(min-width: 1280px) and (max-width: 1919.98px)",XLarge:"(min-width: 1920px)",Handset:"(max-width: 599.98px) and (orientation: portrait), (max-width: 959.98px) and (orientation: landscape)",Tablet:"(min-width: 600px) and (max-width: 839.98px) and (orientation: portrait), (min-width: 960px) and (max-width: 1279.98px) and (orientation: landscape)",Web:"(min-width: 840px) and (orientation: portrait), (min-width: 1280px) and (orientation: landscape)",HandsetPortrait:"(max-width: 599.98px) and (orientation: portrait)",TabletPortrait:"(min-width: 600px) and (max-width: 839.98px) and (orientation: portrait)",WebPortrait:"(min-width: 840px) and (orientation: portrait)",HandsetLandscape:"(max-width: 959.98px) and (orientation: landscape)",TabletLandscape:"(min-width: 960px) and (max-width: 1279.98px) and (orientation: landscape)",WebLandscape:"(min-width: 1280px) and (orientation: landscape)"};var ck=new y("MATERIAL_ANIMATIONS"),tC=null;function lk(){return f(ck,{optional:!0})?.animationsDisabled||f(Is,{optional:!0})==="NoopAnimations"?"di-disabled":(tC??=f(yu).matchMedia("(prefers-reduced-motion)").matches,tC?"reduced-motion":"enabled")}function mr(){return lk()!=="enabled"}function De(t){return t==null?"":typeof t=="string"?t:`${t}px`}function l7(t){return t!=null&&`${t}`!="false"}var At=(function(t){return t[t.FADING_IN=0]="FADING_IN",t[t.VISIBLE=1]="VISIBLE",t[t.FADING_OUT=2]="FADING_OUT",t[t.HIDDEN=3]="HIDDEN",t})(At||{}),sg=class{_renderer;element;config;_animationForciblyDisabledThroughCss;state=At.HIDDEN;constructor(n,e,r,o=!1){this._renderer=n,this.element=e,this.config=r,this._animationForciblyDisabledThroughCss=o}fadeOut(){this._renderer.fadeOutRipple(this)}},nC=ai({passive:!0,capture:!0}),ag=class{_events=new Map;addHandler(n,e,r,o){let i=this._events.get(e);if(i){let s=i.get(r);s?s.add(o):i.set(r,new Set([o]))}else this._events.set(e,new Map([[r,new Set([o])]])),n.runOutsideAngular(()=>{document.addEventListener(e,this._delegateEventHandler,nC)})}removeHandler(n,e,r){let o=this._events.get(n);if(!o)return;let i=o.get(e);i&&(i.delete(r),i.size===0&&o.delete(e),o.size===0&&(this._events.delete(n),document.removeEventListener(n,this._delegateEventHandler,nC)))}_delegateEventHandler=n=>{let e=Je(n);e&&this._events.get(n.type)?.forEach((r,o)=>{(o===e||o.contains(e))&&r.forEach(i=>i.handleEvent(n))})}},na={enterDuration:225,exitDuration:150},uk=800,rC=ai({passive:!0,capture:!0}),oC=["mousedown","touchstart"],iC=["mouseup","mouseleave","touchend","touchcancel"],dk=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275cmp=ke({type:t,selectors:[["ng-component"]],hostAttrs:["mat-ripple-style-loader",""],decls:0,vars:0,template:function(r,o){},styles:[`.mat-ripple { + overflow: hidden; + position: relative; +} +.mat-ripple:not(:empty) { + transform: translateZ(0); +} + +.mat-ripple.mat-ripple-unbounded { + overflow: visible; +} + +.mat-ripple-element { + position: absolute; + border-radius: 50%; + pointer-events: none; + transition: opacity, transform 0ms cubic-bezier(0, 0, 0.2, 1); + transform: scale3d(0, 0, 0); + background-color: var(--mat-ripple-color, color-mix(in srgb, var(--mat-sys-on-surface) 10%, transparent)); +} +@media (forced-colors: active) { + .mat-ripple-element { + display: none; + } +} +.cdk-drag-preview .mat-ripple-element, .cdk-drag-placeholder .mat-ripple-element { + display: none; +} +`],encapsulation:2,changeDetection:0})}return t})(),ra=class t{_target;_ngZone;_platform;_containerElement;_triggerElement=null;_isPointerDown=!1;_activeRipples=new Map;_mostRecentTransientRipple=null;_lastTouchStartEvent;_pointerUpEventsRegistered=!1;_containerRect=null;static _eventManager=new ag;constructor(n,e,r,o,i){this._target=n,this._ngZone=e,this._platform=o,o.isBrowser&&(this._containerElement=Tt(r)),i&&i.get(xt).load(dk)}fadeInRipple(n,e,r={}){let o=this._containerRect=this._containerRect||this._containerElement.getBoundingClientRect(),i=g(g({},na),r.animation);r.centered&&(n=o.left+o.width/2,e=o.top+o.height/2);let s=r.radius||fk(n,e,o),a=n-o.left,c=e-o.top,l=i.enterDuration,u=document.createElement("div");u.classList.add("mat-ripple-element"),u.style.left=`${a-s}px`,u.style.top=`${c-s}px`,u.style.height=`${s*2}px`,u.style.width=`${s*2}px`,r.color!=null&&(u.style.backgroundColor=r.color),u.style.transitionDuration=`${l}ms`,this._containerElement.appendChild(u);let d=window.getComputedStyle(u),h=d.transitionProperty,p=d.transitionDuration,m=h==="none"||p==="0s"||p==="0s, 0s"||o.width===0&&o.height===0,_=new sg(this,u,r,m);u.style.transform="scale3d(1, 1, 1)",_.state=At.FADING_IN,r.persistent||(this._mostRecentTransientRipple=_);let E=null;return!m&&(l||i.exitDuration)&&this._ngZone.runOutsideAngular(()=>{let I=()=>{E&&(E.fallbackTimer=null),clearTimeout(Pe),this._finishRippleTransition(_)},ee=()=>this._destroyRipple(_),Pe=setTimeout(ee,l+100);u.addEventListener("transitionend",I),u.addEventListener("transitioncancel",ee),E={onTransitionEnd:I,onTransitionCancel:ee,fallbackTimer:Pe}}),this._activeRipples.set(_,E),(m||!l)&&this._finishRippleTransition(_),_}fadeOutRipple(n){if(n.state===At.FADING_OUT||n.state===At.HIDDEN)return;let e=n.element,r=g(g({},na),n.config.animation);e.style.transitionDuration=`${r.exitDuration}ms`,e.style.opacity="0",n.state=At.FADING_OUT,(n._animationForciblyDisabledThroughCss||!r.exitDuration)&&this._finishRippleTransition(n)}fadeOutAll(){this._getActiveRipples().forEach(n=>n.fadeOut())}fadeOutAllNonPersistent(){this._getActiveRipples().forEach(n=>{n.config.persistent||n.fadeOut()})}setupTriggerEvents(n){let e=Tt(n);!this._platform.isBrowser||!e||e===this._triggerElement||(this._removeTriggerEvents(),this._triggerElement=e,oC.forEach(r=>{t._eventManager.addHandler(this._ngZone,r,e,this)}))}handleEvent(n){n.type==="mousedown"?this._onMousedown(n):n.type==="touchstart"?this._onTouchStart(n):this._onPointerUp(),this._pointerUpEventsRegistered||(this._ngZone.runOutsideAngular(()=>{iC.forEach(e=>{this._triggerElement.addEventListener(e,this,rC)})}),this._pointerUpEventsRegistered=!0)}_finishRippleTransition(n){n.state===At.FADING_IN?this._startFadeOutTransition(n):n.state===At.FADING_OUT&&this._destroyRipple(n)}_startFadeOutTransition(n){let e=n===this._mostRecentTransientRipple,{persistent:r}=n.config;n.state=At.VISIBLE,!r&&(!e||!this._isPointerDown)&&n.fadeOut()}_destroyRipple(n){let e=this._activeRipples.get(n)??null;this._activeRipples.delete(n),this._activeRipples.size||(this._containerRect=null),n===this._mostRecentTransientRipple&&(this._mostRecentTransientRipple=null),n.state=At.HIDDEN,e!==null&&(n.element.removeEventListener("transitionend",e.onTransitionEnd),n.element.removeEventListener("transitioncancel",e.onTransitionCancel),e.fallbackTimer!==null&&clearTimeout(e.fallbackTimer)),n.element.remove()}_onMousedown(n){let e=Qs(n),r=this._lastTouchStartEvent&&Date.now(){let e=n.state===At.VISIBLE||n.config.terminateOnPointerUp&&n.state===At.FADING_IN;!n.config.persistent&&e&&n.fadeOut()}))}_getActiveRipples(){return Array.from(this._activeRipples.keys())}_removeTriggerEvents(){let n=this._triggerElement;n&&(oC.forEach(e=>t._eventManager.removeHandler(e,n,this)),this._pointerUpEventsRegistered&&(iC.forEach(e=>n.removeEventListener(e,this,rC)),this._pointerUpEventsRegistered=!1))}};function fk(t,n,e){let r=Math.max(Math.abs(t-e.left),Math.abs(t-e.right)),o=Math.max(Math.abs(n-e.top),Math.abs(n-e.bottom));return Math.sqrt(r*r+o*o)}var cg=new y("mat-ripple-global-options"),C7=(()=>{class t{_elementRef=f(z);_animationsDisabled=mr();color;unbounded=!1;centered=!1;radius=0;animation;get disabled(){return this._disabled}set disabled(e){e&&this.fadeOutAllNonPersistent(),this._disabled=e,this._setupTriggerEventsIfEnabled()}_disabled=!1;get trigger(){return this._trigger||this._elementRef.nativeElement}set trigger(e){this._trigger=e,this._setupTriggerEventsIfEnabled()}_trigger;_rippleRenderer;_globalOptions;_isInitialized=!1;constructor(){let e=f(j),r=f(he),o=f(cg,{optional:!0}),i=f($);this._globalOptions=o||{},this._rippleRenderer=new ra(this,e,this._elementRef,r,i)}ngOnInit(){this._isInitialized=!0,this._setupTriggerEventsIfEnabled()}ngOnDestroy(){this._rippleRenderer._removeTriggerEvents()}fadeOutAll(){this._rippleRenderer.fadeOutAll()}fadeOutAllNonPersistent(){this._rippleRenderer.fadeOutAllNonPersistent()}get rippleConfig(){return{centered:this.centered,radius:this.radius,color:this.color,animation:g(g(g({},this._globalOptions.animation),this._animationsDisabled?{enterDuration:0,exitDuration:0}:{}),this.animation),terminateOnPointerUp:this._globalOptions.terminateOnPointerUp}}get rippleDisabled(){return this.disabled||!!this._globalOptions.disabled}_setupTriggerEventsIfEnabled(){!this.disabled&&this._isInitialized&&this._rippleRenderer.setupTriggerEvents(this.trigger)}launch(e,r=0,o){return typeof e=="number"?this._rippleRenderer.fadeInRipple(e,r,g(g({},this.rippleConfig),o)):this._rippleRenderer.fadeInRipple(0,0,g(g({},this.rippleConfig),e))}static \u0275fac=function(r){return new(r||t)};static \u0275dir=M({type:t,selectors:[["","mat-ripple",""],["","matRipple",""]],hostAttrs:[1,"mat-ripple"],hostVars:2,hostBindings:function(r,o){r&2&&Xe("mat-ripple-unbounded",o.unbounded)},inputs:{color:[0,"matRippleColor","color"],unbounded:[0,"matRippleUnbounded","unbounded"],centered:[0,"matRippleCentered","centered"],radius:[0,"matRippleRadius","radius"],animation:[0,"matRippleAnimation","animation"],disabled:[0,"matRippleDisabled","disabled"],trigger:[0,"matRippleTrigger","trigger"]},exportAs:["matRipple"]})}return t})();var hk={capture:!0},pk=["focus","mousedown","mouseenter","touchstart"],lg="mat-ripple-loader-uninitialized",ug="mat-ripple-loader-class-name",sC="mat-ripple-loader-centered",Cu="mat-ripple-loader-disabled",aC=(()=>{class t{_document=f(L);_animationsDisabled=mr();_globalRippleOptions=f(cg,{optional:!0});_platform=f(he);_ngZone=f(j);_injector=f($);_eventCleanups;_hosts=new Map;constructor(){let e=f(je).createRenderer(null,null);this._eventCleanups=this._ngZone.runOutsideAngular(()=>pk.map(r=>e.listen(this._document,r,this._onInteraction,hk)))}ngOnDestroy(){let e=this._hosts.keys();for(let r of e)this.destroyRipple(r);this._eventCleanups.forEach(r=>r())}configureRipple(e,r){e.setAttribute(lg,this._globalRippleOptions?.namespace??""),(r.className||!e.hasAttribute(ug))&&e.setAttribute(ug,r.className||""),r.centered&&e.setAttribute(sC,""),r.disabled&&e.setAttribute(Cu,"")}setDisabled(e,r){let o=this._hosts.get(e);o?(o.target.rippleDisabled=r,!r&&!o.hasSetUpEvents&&(o.hasSetUpEvents=!0,o.renderer.setupTriggerEvents(e))):r?e.setAttribute(Cu,""):e.removeAttribute(Cu)}_onInteraction=e=>{let r=Je(e);if(r instanceof HTMLElement){let o=r.closest(`[${lg}="${this._globalRippleOptions?.namespace??""}"]`);o&&this._createRipple(o)}};_createRipple(e){if(!this._document||this._hosts.has(e))return;e.querySelector(".mat-ripple")?.remove();let r=this._document.createElement("span");r.classList.add("mat-ripple",e.getAttribute(ug)),e.append(r);let o=this._globalRippleOptions,i=this._animationsDisabled?0:o?.animation?.enterDuration??na.enterDuration,s=this._animationsDisabled?0:o?.animation?.exitDuration??na.exitDuration,a={rippleDisabled:this._animationsDisabled||o?.disabled||e.hasAttribute(Cu),rippleConfig:{centered:e.hasAttribute(sC),terminateOnPointerUp:o?.terminateOnPointerUp,animation:{enterDuration:i,exitDuration:s}}},c=new ra(a,this._ngZone,r,this._platform,this._injector),l=!a.rippleDisabled;l&&c.setupTriggerEvents(e),this._hosts.set(e,{target:a,renderer:c,hasSetUpEvents:l}),e.removeAttribute(lg)}destroyRipple(e){let r=this._hosts.get(e);r&&(r.renderer._removeTriggerEvents(),this._hosts.delete(e))}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();var cC=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275cmp=ke({type:t,selectors:[["structural-styles"]],decls:0,vars:0,template:function(r,o){},styles:[`.mat-focus-indicator { + position: relative; +} +.mat-focus-indicator::before { + top: 0; + left: 0; + right: 0; + bottom: 0; + position: absolute; + box-sizing: border-box; + pointer-events: none; + display: var(--mat-focus-indicator-display, none); + border-width: var(--mat-focus-indicator-border-width, 3px); + border-style: var(--mat-focus-indicator-border-style, solid); + border-color: var(--mat-focus-indicator-border-color, transparent); + border-radius: var(--mat-focus-indicator-border-radius, 4px); +} +.mat-focus-indicator:focus-visible::before { + content: ""; +} + +@media (forced-colors: active) { + html { + --mat-focus-indicator-display: block; + } +} +`],encapsulation:2,changeDetection:0})}return t})();var mk=["mat-icon-button",""],gk=["*"],vk=new y("MAT_BUTTON_CONFIG");function lC(t){return t==null?void 0:hm(t)}var dg=(()=>{class t{_elementRef=f(z);_ngZone=f(j);_animationsDisabled=mr();_config=f(vk,{optional:!0});_focusMonitor=f(pu);_cleanupClick;_renderer=f(Oe);_rippleLoader=f(aC);_isAnchor;_isFab=!1;color;get disableRipple(){return this._disableRipple}set disableRipple(e){this._disableRipple=e,this._updateRippleDisabled()}_disableRipple=!1;get disabled(){return this._disabled}set disabled(e){this._disabled=e,this._updateRippleDisabled()}_disabled=!1;ariaDisabled;disabledInteractive;tabIndex;set _tabindex(e){this.tabIndex=e}constructor(){f(xt).load(cC);let e=this._elementRef.nativeElement;this._isAnchor=e.tagName==="A",this.disabledInteractive=this._config?.disabledInteractive??!1,this.color=this._config?.color??null,this._rippleLoader?.configureRipple(e,{className:"mat-mdc-button-ripple"})}ngAfterViewInit(){this._focusMonitor.monitor(this._elementRef,!0),this._isAnchor&&this._setupAsAnchor()}ngOnDestroy(){this._cleanupClick?.(),this._focusMonitor.stopMonitoring(this._elementRef),this._rippleLoader?.destroyRipple(this._elementRef.nativeElement)}focus(e="program",r){e?this._focusMonitor.focusVia(this._elementRef.nativeElement,e,r):this._elementRef.nativeElement.focus(r)}_getAriaDisabled(){return this.ariaDisabled!=null?this.ariaDisabled:this._isAnchor?this.disabled||null:this.disabled&&this.disabledInteractive?!0:null}_getDisabledAttribute(){return this.disabledInteractive||!this.disabled?null:!0}_updateRippleDisabled(){this._rippleLoader?.setDisabled(this._elementRef.nativeElement,this.disableRipple||this.disabled)}_getTabIndex(){return this._isAnchor?this.disabled&&!this.disabledInteractive?-1:this.tabIndex:this.tabIndex}_setupAsAnchor(){this._cleanupClick=this._ngZone.runOutsideAngular(()=>this._renderer.listen(this._elementRef.nativeElement,"click",e=>{this.disabled&&(e.preventDefault(),e.stopImmediatePropagation())}))}static \u0275fac=function(r){return new(r||t)};static \u0275dir=M({type:t,hostAttrs:[1,"mat-mdc-button-base"],hostVars:13,hostBindings:function(r,o){r&2&&(Yt("disabled",o._getDisabledAttribute())("aria-disabled",o._getAriaDisabled())("tabindex",o._getTabIndex()),Yp(o.color?"mat-"+o.color:""),Xe("mat-mdc-button-disabled",o.disabled)("mat-mdc-button-disabled-interactive",o.disabledInteractive)("mat-unthemed",!o.color)("_mat-animation-noopable",o._animationsDisabled))},inputs:{color:"color",disableRipple:[2,"disableRipple","disableRipple",ue],disabled:[2,"disabled","disabled",ue],ariaDisabled:[2,"aria-disabled","ariaDisabled",ue],disabledInteractive:[2,"disabledInteractive","disabledInteractive",ue],tabIndex:[2,"tabIndex","tabIndex",lC],_tabindex:[2,"tabindex","_tabindex",lC]}})}return t})(),yk=(()=>{class t extends dg{constructor(){super(),this._rippleLoader.configureRipple(this._elementRef.nativeElement,{centered:!0})}static \u0275fac=function(r){return new(r||t)};static \u0275cmp=ke({type:t,selectors:[["button","mat-icon-button",""],["a","mat-icon-button",""],["button","matIconButton",""],["a","matIconButton",""]],hostAttrs:[1,"mdc-icon-button","mat-mdc-icon-button"],exportAs:["matButton","matAnchor"],features:[J],attrs:mk,ngContentSelectors:gk,decls:4,vars:0,consts:[[1,"mat-mdc-button-persistent-ripple","mdc-icon-button__ripple"],[1,"mat-focus-indicator"],[1,"mat-mdc-button-touch-target"]],template:function(r,o){r&1&&(ro(),yn(0,"span",0),Vn(1),yn(2,"span",1)(3,"span",2))},styles:[`.mat-mdc-icon-button { + -webkit-user-select: none; + user-select: none; + display: inline-block; + position: relative; + box-sizing: border-box; + border: none; + outline: none; + background-color: transparent; + fill: currentColor; + text-decoration: none; + cursor: pointer; + z-index: 0; + overflow: visible; + border-radius: var(--mat-icon-button-container-shape, var(--mat-sys-corner-full, 50%)); + flex-shrink: 0; + text-align: center; + width: var(--mat-icon-button-state-layer-size, 40px); + height: var(--mat-icon-button-state-layer-size, 40px); + padding: calc(calc(var(--mat-icon-button-state-layer-size, 40px) - var(--mat-icon-button-icon-size, 24px)) / 2); + font-size: var(--mat-icon-button-icon-size, 24px); + color: var(--mat-icon-button-icon-color, var(--mat-sys-on-surface-variant)); + -webkit-tap-highlight-color: transparent; +} +.mat-mdc-icon-button .mat-mdc-button-ripple, +.mat-mdc-icon-button .mat-mdc-button-persistent-ripple, +.mat-mdc-icon-button .mat-mdc-button-persistent-ripple::before { + top: 0; + left: 0; + right: 0; + bottom: 0; + position: absolute; + pointer-events: none; + border-radius: inherit; +} +.mat-mdc-icon-button .mat-mdc-button-ripple { + overflow: hidden; +} +.mat-mdc-icon-button .mat-mdc-button-persistent-ripple::before { + content: ""; + opacity: 0; +} +.mat-mdc-icon-button .mdc-button__label, +.mat-mdc-icon-button .mat-icon { + z-index: 1; + position: relative; +} +.mat-mdc-icon-button .mat-focus-indicator { + top: 0; + left: 0; + right: 0; + bottom: 0; + position: absolute; + border-radius: inherit; +} +.mat-mdc-icon-button:focus-visible > .mat-focus-indicator::before { + content: ""; + border-radius: inherit; +} +.mat-mdc-icon-button .mat-ripple-element { + background-color: var(--mat-icon-button-ripple-color, color-mix(in srgb, var(--mat-sys-on-surface-variant) calc(var(--mat-sys-pressed-state-layer-opacity) * 100%), transparent)); +} +.mat-mdc-icon-button .mat-mdc-button-persistent-ripple::before { + background-color: var(--mat-icon-button-state-layer-color, var(--mat-sys-on-surface-variant)); +} +.mat-mdc-icon-button.mat-mdc-button-disabled .mat-mdc-button-persistent-ripple::before { + background-color: var(--mat-icon-button-disabled-state-layer-color, var(--mat-sys-on-surface-variant)); +} +.mat-mdc-icon-button:hover > .mat-mdc-button-persistent-ripple::before { + opacity: var(--mat-icon-button-hover-state-layer-opacity, var(--mat-sys-hover-state-layer-opacity)); +} +.mat-mdc-icon-button.cdk-program-focused > .mat-mdc-button-persistent-ripple::before, .mat-mdc-icon-button.cdk-keyboard-focused > .mat-mdc-button-persistent-ripple::before, .mat-mdc-icon-button.mat-mdc-button-disabled-interactive:focus > .mat-mdc-button-persistent-ripple::before { + opacity: var(--mat-icon-button-focus-state-layer-opacity, var(--mat-sys-focus-state-layer-opacity)); +} +.mat-mdc-icon-button:active > .mat-mdc-button-persistent-ripple::before { + opacity: var(--mat-icon-button-pressed-state-layer-opacity, var(--mat-sys-pressed-state-layer-opacity)); +} +.mat-mdc-icon-button .mat-mdc-button-touch-target { + position: absolute; + top: 50%; + height: var(--mat-icon-button-touch-target-size, 48px); + display: var(--mat-icon-button-touch-target-display, block); + left: 50%; + width: var(--mat-icon-button-touch-target-size, 48px); + transform: translate(-50%, -50%); +} +.mat-mdc-icon-button._mat-animation-noopable { + transition: none !important; + animation: none !important; +} +.mat-mdc-icon-button[disabled], .mat-mdc-icon-button.mat-mdc-button-disabled { + cursor: default; + pointer-events: none; + color: var(--mat-icon-button-disabled-icon-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); +} +.mat-mdc-icon-button.mat-mdc-button-disabled-interactive { + pointer-events: auto; +} +.mat-mdc-icon-button img, +.mat-mdc-icon-button svg { + width: var(--mat-icon-button-icon-size, 24px); + height: var(--mat-icon-button-icon-size, 24px); + vertical-align: baseline; +} +.mat-mdc-icon-button .mat-mdc-button-persistent-ripple { + border-radius: var(--mat-icon-button-container-shape, var(--mat-sys-corner-full, 50%)); +} +.mat-mdc-icon-button[hidden] { + display: none; +} +.mat-mdc-icon-button.mat-unthemed:not(.mdc-ripple-upgraded):focus::before, .mat-mdc-icon-button.mat-primary:not(.mdc-ripple-upgraded):focus::before, .mat-mdc-icon-button.mat-accent:not(.mdc-ripple-upgraded):focus::before, .mat-mdc-icon-button.mat-warn:not(.mdc-ripple-upgraded):focus::before { + background: transparent; + opacity: 1; +} +`,`@media (forced-colors: active) { + .mat-mdc-button:not(.mdc-button--outlined), + .mat-mdc-unelevated-button:not(.mdc-button--outlined), + .mat-mdc-raised-button:not(.mdc-button--outlined), + .mat-mdc-outlined-button:not(.mdc-button--outlined), + .mat-mdc-button-base.mat-tonal-button, + .mat-mdc-icon-button.mat-mdc-icon-button, + .mat-mdc-outlined-button .mdc-button__ripple { + outline: solid 1px; + } +} +`],encapsulation:2,changeDetection:0})}return t})();var bk=new y("cdk-dir-doc",{providedIn:"root",factory:()=>f(L)}),_k=/^(ar|ckb|dv|he|iw|fa|nqo|ps|sd|ug|ur|yi|.*[-_](Adlm|Arab|Hebr|Nkoo|Rohg|Thaa))(?!.*[-_](Latn|Cyrl)($|-|_))($|-|_)/i;function uC(t){let n=t?.toLowerCase()||"";return n==="auto"&&typeof navigator<"u"&&navigator?.language?_k.test(navigator.language)?"rtl":"ltr":n==="rtl"?"rtl":"ltr"}var fi=(()=>{class t{get value(){return this.valueSignal()}valueSignal=W("ltr");change=new U;constructor(){let e=f(bk,{optional:!0});if(e){let r=e.body?e.body.dir:null,o=e.documentElement?e.documentElement.dir:null;this.valueSignal.set(uC(r||o||"ltr"))}}ngOnDestroy(){this.change.complete()}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();var zn=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275mod=X({type:t});static \u0275inj=Z({})}return t})();var dC=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275mod=X({type:t});static \u0275inj=Z({imports:[zn]})}return t})();var Dk=["matButton",""],Ek=[[["",8,"material-icons",3,"iconPositionEnd",""],["mat-icon",3,"iconPositionEnd",""],["","matButtonIcon","",3,"iconPositionEnd",""]],"*",[["","iconPositionEnd","",8,"material-icons"],["mat-icon","iconPositionEnd",""],["","matButtonIcon","","iconPositionEnd",""]]],wk=[".material-icons:not([iconPositionEnd]), mat-icon:not([iconPositionEnd]), [matButtonIcon]:not([iconPositionEnd])","*",".material-icons[iconPositionEnd], mat-icon[iconPositionEnd], [matButtonIcon][iconPositionEnd]"];var fC=new Map([["text",["mat-mdc-button"]],["filled",["mdc-button--unelevated","mat-mdc-unelevated-button"]],["elevated",["mdc-button--raised","mat-mdc-raised-button"]],["outlined",["mdc-button--outlined","mat-mdc-outlined-button"]],["tonal",["mat-tonal-button"]]]),nK=(()=>{class t extends dg{get appearance(){return this._appearance}set appearance(e){this.setAppearance(e||this._config?.defaultAppearance||"text")}_appearance=null;constructor(){super();let e=Ck(this._elementRef.nativeElement);e&&this.setAppearance(e)}setAppearance(e){if(e===this._appearance)return;let r=this._elementRef.nativeElement.classList,o=this._appearance?fC.get(this._appearance):null,i=fC.get(e);o&&r.remove(...o),r.add(...i),this._appearance=e}static \u0275fac=function(r){return new(r||t)};static \u0275cmp=ke({type:t,selectors:[["button","matButton",""],["a","matButton",""],["button","mat-button",""],["button","mat-raised-button",""],["button","mat-flat-button",""],["button","mat-stroked-button",""],["a","mat-button",""],["a","mat-raised-button",""],["a","mat-flat-button",""],["a","mat-stroked-button",""]],hostAttrs:[1,"mdc-button"],inputs:{appearance:[0,"matButton","appearance"]},exportAs:["matButton","matAnchor"],features:[J],attrs:Dk,ngContentSelectors:wk,decls:7,vars:4,consts:[[1,"mat-mdc-button-persistent-ripple"],[1,"mdc-button__label"],[1,"mat-focus-indicator"],[1,"mat-mdc-button-touch-target"]],template:function(r,o){r&1&&(ro(Ek),yn(0,"span",0),Vn(1),to(2,"span",1),Vn(3,1),no(),Vn(4,2),yn(5,"span",2)(6,"span",3)),r&2&&Xe("mdc-button__ripple",!o._isFab)("mdc-fab__ripple",o._isFab)},styles:[`.mat-mdc-button-base { + text-decoration: none; +} +.mat-mdc-button-base .mat-icon { + min-height: fit-content; + flex-shrink: 0; +} +@media (hover: none) { + .mat-mdc-button-base:hover > span.mat-mdc-button-persistent-ripple::before { + opacity: 0; + } +} + +.mdc-button { + -webkit-user-select: none; + user-select: none; + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + min-width: 64px; + border: none; + outline: none; + line-height: inherit; + -webkit-appearance: none; + overflow: visible; + vertical-align: middle; + background: transparent; + padding: 0 8px; +} +.mdc-button::-moz-focus-inner { + padding: 0; + border: 0; +} +.mdc-button:active { + outline: none; +} +.mdc-button:hover { + cursor: pointer; +} +.mdc-button:disabled { + cursor: default; + pointer-events: none; +} +.mdc-button[hidden] { + display: none; +} +.mdc-button .mdc-button__label { + position: relative; +} + +.mat-mdc-button { + padding: 0 var(--mat-button-text-horizontal-padding, 12px); + height: var(--mat-button-text-container-height, 40px); + font-family: var(--mat-button-text-label-text-font, var(--mat-sys-label-large-font)); + font-size: var(--mat-button-text-label-text-size, var(--mat-sys-label-large-size)); + letter-spacing: var(--mat-button-text-label-text-tracking, var(--mat-sys-label-large-tracking)); + text-transform: var(--mat-button-text-label-text-transform); + font-weight: var(--mat-button-text-label-text-weight, var(--mat-sys-label-large-weight)); +} +.mat-mdc-button, .mat-mdc-button .mdc-button__ripple { + border-radius: var(--mat-button-text-container-shape, var(--mat-sys-corner-full)); +} +.mat-mdc-button:not(:disabled) { + color: var(--mat-button-text-label-text-color, var(--mat-sys-primary)); +} +.mat-mdc-button[disabled], .mat-mdc-button.mat-mdc-button-disabled { + cursor: default; + pointer-events: none; + color: var(--mat-button-text-disabled-label-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); +} +.mat-mdc-button.mat-mdc-button-disabled-interactive { + pointer-events: auto; +} +.mat-mdc-button:has(.material-icons, mat-icon, [matButtonIcon]) { + padding: 0 var(--mat-button-text-with-icon-horizontal-padding, 16px); +} +.mat-mdc-button > .mat-icon { + margin-right: var(--mat-button-text-icon-spacing, 8px); + margin-left: var(--mat-button-text-icon-offset, -4px); +} +[dir=rtl] .mat-mdc-button > .mat-icon { + margin-right: var(--mat-button-text-icon-offset, -4px); + margin-left: var(--mat-button-text-icon-spacing, 8px); +} +.mat-mdc-button .mdc-button__label + .mat-icon { + margin-right: var(--mat-button-text-icon-offset, -4px); + margin-left: var(--mat-button-text-icon-spacing, 8px); +} +[dir=rtl] .mat-mdc-button .mdc-button__label + .mat-icon { + margin-right: var(--mat-button-text-icon-spacing, 8px); + margin-left: var(--mat-button-text-icon-offset, -4px); +} +.mat-mdc-button .mat-ripple-element { + background-color: var(--mat-button-text-ripple-color, color-mix(in srgb, var(--mat-sys-primary) calc(var(--mat-sys-pressed-state-layer-opacity) * 100%), transparent)); +} +.mat-mdc-button .mat-mdc-button-persistent-ripple::before { + background-color: var(--mat-button-text-state-layer-color, var(--mat-sys-primary)); +} +.mat-mdc-button.mat-mdc-button-disabled .mat-mdc-button-persistent-ripple::before { + background-color: var(--mat-button-text-disabled-state-layer-color, var(--mat-sys-on-surface-variant)); +} +.mat-mdc-button:hover > .mat-mdc-button-persistent-ripple::before { + opacity: var(--mat-button-text-hover-state-layer-opacity, var(--mat-sys-hover-state-layer-opacity)); +} +.mat-mdc-button.cdk-program-focused > .mat-mdc-button-persistent-ripple::before, .mat-mdc-button.cdk-keyboard-focused > .mat-mdc-button-persistent-ripple::before, .mat-mdc-button.mat-mdc-button-disabled-interactive:focus > .mat-mdc-button-persistent-ripple::before { + opacity: var(--mat-button-text-focus-state-layer-opacity, var(--mat-sys-focus-state-layer-opacity)); +} +.mat-mdc-button:active > .mat-mdc-button-persistent-ripple::before { + opacity: var(--mat-button-text-pressed-state-layer-opacity, var(--mat-sys-pressed-state-layer-opacity)); +} +.mat-mdc-button .mat-mdc-button-touch-target { + position: absolute; + top: 50%; + height: var(--mat-button-text-touch-target-size, 48px); + display: var(--mat-button-text-touch-target-display, block); + left: 0; + right: 0; + transform: translateY(-50%); +} + +.mat-mdc-unelevated-button { + transition: box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1); + height: var(--mat-button-filled-container-height, 40px); + font-family: var(--mat-button-filled-label-text-font, var(--mat-sys-label-large-font)); + font-size: var(--mat-button-filled-label-text-size, var(--mat-sys-label-large-size)); + letter-spacing: var(--mat-button-filled-label-text-tracking, var(--mat-sys-label-large-tracking)); + text-transform: var(--mat-button-filled-label-text-transform); + font-weight: var(--mat-button-filled-label-text-weight, var(--mat-sys-label-large-weight)); + padding: 0 var(--mat-button-filled-horizontal-padding, 24px); +} +.mat-mdc-unelevated-button > .mat-icon { + margin-right: var(--mat-button-filled-icon-spacing, 8px); + margin-left: var(--mat-button-filled-icon-offset, -8px); +} +[dir=rtl] .mat-mdc-unelevated-button > .mat-icon { + margin-right: var(--mat-button-filled-icon-offset, -8px); + margin-left: var(--mat-button-filled-icon-spacing, 8px); +} +.mat-mdc-unelevated-button .mdc-button__label + .mat-icon { + margin-right: var(--mat-button-filled-icon-offset, -8px); + margin-left: var(--mat-button-filled-icon-spacing, 8px); +} +[dir=rtl] .mat-mdc-unelevated-button .mdc-button__label + .mat-icon { + margin-right: var(--mat-button-filled-icon-spacing, 8px); + margin-left: var(--mat-button-filled-icon-offset, -8px); +} +.mat-mdc-unelevated-button .mat-ripple-element { + background-color: var(--mat-button-filled-ripple-color, color-mix(in srgb, var(--mat-sys-on-primary) calc(var(--mat-sys-pressed-state-layer-opacity) * 100%), transparent)); +} +.mat-mdc-unelevated-button .mat-mdc-button-persistent-ripple::before { + background-color: var(--mat-button-filled-state-layer-color, var(--mat-sys-on-primary)); +} +.mat-mdc-unelevated-button.mat-mdc-button-disabled .mat-mdc-button-persistent-ripple::before { + background-color: var(--mat-button-filled-disabled-state-layer-color, var(--mat-sys-on-surface-variant)); +} +.mat-mdc-unelevated-button:hover > .mat-mdc-button-persistent-ripple::before { + opacity: var(--mat-button-filled-hover-state-layer-opacity, var(--mat-sys-hover-state-layer-opacity)); +} +.mat-mdc-unelevated-button.cdk-program-focused > .mat-mdc-button-persistent-ripple::before, .mat-mdc-unelevated-button.cdk-keyboard-focused > .mat-mdc-button-persistent-ripple::before, .mat-mdc-unelevated-button.mat-mdc-button-disabled-interactive:focus > .mat-mdc-button-persistent-ripple::before { + opacity: var(--mat-button-filled-focus-state-layer-opacity, var(--mat-sys-focus-state-layer-opacity)); +} +.mat-mdc-unelevated-button:active > .mat-mdc-button-persistent-ripple::before { + opacity: var(--mat-button-filled-pressed-state-layer-opacity, var(--mat-sys-pressed-state-layer-opacity)); +} +.mat-mdc-unelevated-button .mat-mdc-button-touch-target { + position: absolute; + top: 50%; + height: var(--mat-button-filled-touch-target-size, 48px); + display: var(--mat-button-filled-touch-target-display, block); + left: 0; + right: 0; + transform: translateY(-50%); +} +.mat-mdc-unelevated-button:not(:disabled) { + color: var(--mat-button-filled-label-text-color, var(--mat-sys-on-primary)); + background-color: var(--mat-button-filled-container-color, var(--mat-sys-primary)); +} +.mat-mdc-unelevated-button, .mat-mdc-unelevated-button .mdc-button__ripple { + border-radius: var(--mat-button-filled-container-shape, var(--mat-sys-corner-full)); +} +.mat-mdc-unelevated-button[disabled], .mat-mdc-unelevated-button.mat-mdc-button-disabled { + cursor: default; + pointer-events: none; + color: var(--mat-button-filled-disabled-label-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); + background-color: var(--mat-button-filled-disabled-container-color, color-mix(in srgb, var(--mat-sys-on-surface) 12%, transparent)); +} +.mat-mdc-unelevated-button.mat-mdc-button-disabled-interactive { + pointer-events: auto; +} + +.mat-mdc-raised-button { + transition: box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: var(--mat-button-protected-container-elevation-shadow, var(--mat-sys-level1)); + height: var(--mat-button-protected-container-height, 40px); + font-family: var(--mat-button-protected-label-text-font, var(--mat-sys-label-large-font)); + font-size: var(--mat-button-protected-label-text-size, var(--mat-sys-label-large-size)); + letter-spacing: var(--mat-button-protected-label-text-tracking, var(--mat-sys-label-large-tracking)); + text-transform: var(--mat-button-protected-label-text-transform); + font-weight: var(--mat-button-protected-label-text-weight, var(--mat-sys-label-large-weight)); + padding: 0 var(--mat-button-protected-horizontal-padding, 24px); +} +.mat-mdc-raised-button > .mat-icon { + margin-right: var(--mat-button-protected-icon-spacing, 8px); + margin-left: var(--mat-button-protected-icon-offset, -8px); +} +[dir=rtl] .mat-mdc-raised-button > .mat-icon { + margin-right: var(--mat-button-protected-icon-offset, -8px); + margin-left: var(--mat-button-protected-icon-spacing, 8px); +} +.mat-mdc-raised-button .mdc-button__label + .mat-icon { + margin-right: var(--mat-button-protected-icon-offset, -8px); + margin-left: var(--mat-button-protected-icon-spacing, 8px); +} +[dir=rtl] .mat-mdc-raised-button .mdc-button__label + .mat-icon { + margin-right: var(--mat-button-protected-icon-spacing, 8px); + margin-left: var(--mat-button-protected-icon-offset, -8px); +} +.mat-mdc-raised-button .mat-ripple-element { + background-color: var(--mat-button-protected-ripple-color, color-mix(in srgb, var(--mat-sys-primary) calc(var(--mat-sys-pressed-state-layer-opacity) * 100%), transparent)); +} +.mat-mdc-raised-button .mat-mdc-button-persistent-ripple::before { + background-color: var(--mat-button-protected-state-layer-color, var(--mat-sys-primary)); +} +.mat-mdc-raised-button.mat-mdc-button-disabled .mat-mdc-button-persistent-ripple::before { + background-color: var(--mat-button-protected-disabled-state-layer-color, var(--mat-sys-on-surface-variant)); +} +.mat-mdc-raised-button:hover > .mat-mdc-button-persistent-ripple::before { + opacity: var(--mat-button-protected-hover-state-layer-opacity, var(--mat-sys-hover-state-layer-opacity)); +} +.mat-mdc-raised-button.cdk-program-focused > .mat-mdc-button-persistent-ripple::before, .mat-mdc-raised-button.cdk-keyboard-focused > .mat-mdc-button-persistent-ripple::before, .mat-mdc-raised-button.mat-mdc-button-disabled-interactive:focus > .mat-mdc-button-persistent-ripple::before { + opacity: var(--mat-button-protected-focus-state-layer-opacity, var(--mat-sys-focus-state-layer-opacity)); +} +.mat-mdc-raised-button:active > .mat-mdc-button-persistent-ripple::before { + opacity: var(--mat-button-protected-pressed-state-layer-opacity, var(--mat-sys-pressed-state-layer-opacity)); +} +.mat-mdc-raised-button .mat-mdc-button-touch-target { + position: absolute; + top: 50%; + height: var(--mat-button-protected-touch-target-size, 48px); + display: var(--mat-button-protected-touch-target-display, block); + left: 0; + right: 0; + transform: translateY(-50%); +} +.mat-mdc-raised-button:not(:disabled) { + color: var(--mat-button-protected-label-text-color, var(--mat-sys-primary)); + background-color: var(--mat-button-protected-container-color, var(--mat-sys-surface)); +} +.mat-mdc-raised-button, .mat-mdc-raised-button .mdc-button__ripple { + border-radius: var(--mat-button-protected-container-shape, var(--mat-sys-corner-full)); +} +@media (hover: hover) { + .mat-mdc-raised-button:hover { + box-shadow: var(--mat-button-protected-hover-container-elevation-shadow, var(--mat-sys-level2)); + } +} +.mat-mdc-raised-button:focus { + box-shadow: var(--mat-button-protected-focus-container-elevation-shadow, var(--mat-sys-level1)); +} +.mat-mdc-raised-button:active, .mat-mdc-raised-button:focus:active { + box-shadow: var(--mat-button-protected-pressed-container-elevation-shadow, var(--mat-sys-level1)); +} +.mat-mdc-raised-button[disabled], .mat-mdc-raised-button.mat-mdc-button-disabled { + cursor: default; + pointer-events: none; + color: var(--mat-button-protected-disabled-label-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); + background-color: var(--mat-button-protected-disabled-container-color, color-mix(in srgb, var(--mat-sys-on-surface) 12%, transparent)); +} +.mat-mdc-raised-button[disabled].mat-mdc-button-disabled, .mat-mdc-raised-button.mat-mdc-button-disabled.mat-mdc-button-disabled { + box-shadow: var(--mat-button-protected-disabled-container-elevation-shadow, var(--mat-sys-level0)); +} +.mat-mdc-raised-button.mat-mdc-button-disabled-interactive { + pointer-events: auto; +} + +.mat-mdc-outlined-button { + border-style: solid; + transition: border 280ms cubic-bezier(0.4, 0, 0.2, 1); + height: var(--mat-button-outlined-container-height, 40px); + font-family: var(--mat-button-outlined-label-text-font, var(--mat-sys-label-large-font)); + font-size: var(--mat-button-outlined-label-text-size, var(--mat-sys-label-large-size)); + letter-spacing: var(--mat-button-outlined-label-text-tracking, var(--mat-sys-label-large-tracking)); + text-transform: var(--mat-button-outlined-label-text-transform); + font-weight: var(--mat-button-outlined-label-text-weight, var(--mat-sys-label-large-weight)); + border-radius: var(--mat-button-outlined-container-shape, var(--mat-sys-corner-full)); + border-width: var(--mat-button-outlined-outline-width, 1px); + padding: 0 var(--mat-button-outlined-horizontal-padding, 24px); +} +.mat-mdc-outlined-button > .mat-icon { + margin-right: var(--mat-button-outlined-icon-spacing, 8px); + margin-left: var(--mat-button-outlined-icon-offset, -8px); +} +[dir=rtl] .mat-mdc-outlined-button > .mat-icon { + margin-right: var(--mat-button-outlined-icon-offset, -8px); + margin-left: var(--mat-button-outlined-icon-spacing, 8px); +} +.mat-mdc-outlined-button .mdc-button__label + .mat-icon { + margin-right: var(--mat-button-outlined-icon-offset, -8px); + margin-left: var(--mat-button-outlined-icon-spacing, 8px); +} +[dir=rtl] .mat-mdc-outlined-button .mdc-button__label + .mat-icon { + margin-right: var(--mat-button-outlined-icon-spacing, 8px); + margin-left: var(--mat-button-outlined-icon-offset, -8px); +} +.mat-mdc-outlined-button .mat-ripple-element { + background-color: var(--mat-button-outlined-ripple-color, color-mix(in srgb, var(--mat-sys-primary) calc(var(--mat-sys-pressed-state-layer-opacity) * 100%), transparent)); +} +.mat-mdc-outlined-button .mat-mdc-button-persistent-ripple::before { + background-color: var(--mat-button-outlined-state-layer-color, var(--mat-sys-primary)); +} +.mat-mdc-outlined-button.mat-mdc-button-disabled .mat-mdc-button-persistent-ripple::before { + background-color: var(--mat-button-outlined-disabled-state-layer-color, var(--mat-sys-on-surface-variant)); +} +.mat-mdc-outlined-button:hover > .mat-mdc-button-persistent-ripple::before { + opacity: var(--mat-button-outlined-hover-state-layer-opacity, var(--mat-sys-hover-state-layer-opacity)); +} +.mat-mdc-outlined-button.cdk-program-focused > .mat-mdc-button-persistent-ripple::before, .mat-mdc-outlined-button.cdk-keyboard-focused > .mat-mdc-button-persistent-ripple::before, .mat-mdc-outlined-button.mat-mdc-button-disabled-interactive:focus > .mat-mdc-button-persistent-ripple::before { + opacity: var(--mat-button-outlined-focus-state-layer-opacity, var(--mat-sys-focus-state-layer-opacity)); +} +.mat-mdc-outlined-button:active > .mat-mdc-button-persistent-ripple::before { + opacity: var(--mat-button-outlined-pressed-state-layer-opacity, var(--mat-sys-pressed-state-layer-opacity)); +} +.mat-mdc-outlined-button .mat-mdc-button-touch-target { + position: absolute; + top: 50%; + height: var(--mat-button-outlined-touch-target-size, 48px); + display: var(--mat-button-outlined-touch-target-display, block); + left: 0; + right: 0; + transform: translateY(-50%); +} +.mat-mdc-outlined-button:not(:disabled) { + color: var(--mat-button-outlined-label-text-color, var(--mat-sys-primary)); + border-color: var(--mat-button-outlined-outline-color, var(--mat-sys-outline)); +} +.mat-mdc-outlined-button[disabled], .mat-mdc-outlined-button.mat-mdc-button-disabled { + cursor: default; + pointer-events: none; + color: var(--mat-button-outlined-disabled-label-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); + border-color: var(--mat-button-outlined-disabled-outline-color, color-mix(in srgb, var(--mat-sys-on-surface) 12%, transparent)); +} +.mat-mdc-outlined-button.mat-mdc-button-disabled-interactive { + pointer-events: auto; +} + +.mat-tonal-button { + transition: box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1); + height: var(--mat-button-tonal-container-height, 40px); + font-family: var(--mat-button-tonal-label-text-font, var(--mat-sys-label-large-font)); + font-size: var(--mat-button-tonal-label-text-size, var(--mat-sys-label-large-size)); + letter-spacing: var(--mat-button-tonal-label-text-tracking, var(--mat-sys-label-large-tracking)); + text-transform: var(--mat-button-tonal-label-text-transform); + font-weight: var(--mat-button-tonal-label-text-weight, var(--mat-sys-label-large-weight)); + padding: 0 var(--mat-button-tonal-horizontal-padding, 24px); +} +.mat-tonal-button:not(:disabled) { + color: var(--mat-button-tonal-label-text-color, var(--mat-sys-on-secondary-container)); + background-color: var(--mat-button-tonal-container-color, var(--mat-sys-secondary-container)); +} +.mat-tonal-button, .mat-tonal-button .mdc-button__ripple { + border-radius: var(--mat-button-tonal-container-shape, var(--mat-sys-corner-full)); +} +.mat-tonal-button[disabled], .mat-tonal-button.mat-mdc-button-disabled { + cursor: default; + pointer-events: none; + color: var(--mat-button-tonal-disabled-label-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); + background-color: var(--mat-button-tonal-disabled-container-color, color-mix(in srgb, var(--mat-sys-on-surface) 12%, transparent)); +} +.mat-tonal-button.mat-mdc-button-disabled-interactive { + pointer-events: auto; +} +.mat-tonal-button > .mat-icon { + margin-right: var(--mat-button-tonal-icon-spacing, 8px); + margin-left: var(--mat-button-tonal-icon-offset, -8px); +} +[dir=rtl] .mat-tonal-button > .mat-icon { + margin-right: var(--mat-button-tonal-icon-offset, -8px); + margin-left: var(--mat-button-tonal-icon-spacing, 8px); +} +.mat-tonal-button .mdc-button__label + .mat-icon { + margin-right: var(--mat-button-tonal-icon-offset, -8px); + margin-left: var(--mat-button-tonal-icon-spacing, 8px); +} +[dir=rtl] .mat-tonal-button .mdc-button__label + .mat-icon { + margin-right: var(--mat-button-tonal-icon-spacing, 8px); + margin-left: var(--mat-button-tonal-icon-offset, -8px); +} +.mat-tonal-button .mat-ripple-element { + background-color: var(--mat-button-tonal-ripple-color, color-mix(in srgb, var(--mat-sys-on-secondary-container) calc(var(--mat-sys-pressed-state-layer-opacity) * 100%), transparent)); +} +.mat-tonal-button .mat-mdc-button-persistent-ripple::before { + background-color: var(--mat-button-tonal-state-layer-color, var(--mat-sys-on-secondary-container)); +} +.mat-tonal-button.mat-mdc-button-disabled .mat-mdc-button-persistent-ripple::before { + background-color: var(--mat-button-tonal-disabled-state-layer-color, var(--mat-sys-on-surface-variant)); +} +.mat-tonal-button:hover > .mat-mdc-button-persistent-ripple::before { + opacity: var(--mat-button-tonal-hover-state-layer-opacity, var(--mat-sys-hover-state-layer-opacity)); +} +.mat-tonal-button.cdk-program-focused > .mat-mdc-button-persistent-ripple::before, .mat-tonal-button.cdk-keyboard-focused > .mat-mdc-button-persistent-ripple::before, .mat-tonal-button.mat-mdc-button-disabled-interactive:focus > .mat-mdc-button-persistent-ripple::before { + opacity: var(--mat-button-tonal-focus-state-layer-opacity, var(--mat-sys-focus-state-layer-opacity)); +} +.mat-tonal-button:active > .mat-mdc-button-persistent-ripple::before { + opacity: var(--mat-button-tonal-pressed-state-layer-opacity, var(--mat-sys-pressed-state-layer-opacity)); +} +.mat-tonal-button .mat-mdc-button-touch-target { + position: absolute; + top: 50%; + height: var(--mat-button-tonal-touch-target-size, 48px); + display: var(--mat-button-tonal-touch-target-display, block); + left: 0; + right: 0; + transform: translateY(-50%); +} + +.mat-mdc-button, +.mat-mdc-unelevated-button, +.mat-mdc-raised-button, +.mat-mdc-outlined-button, +.mat-tonal-button { + -webkit-tap-highlight-color: transparent; +} +.mat-mdc-button .mat-mdc-button-ripple, +.mat-mdc-button .mat-mdc-button-persistent-ripple, +.mat-mdc-button .mat-mdc-button-persistent-ripple::before, +.mat-mdc-unelevated-button .mat-mdc-button-ripple, +.mat-mdc-unelevated-button .mat-mdc-button-persistent-ripple, +.mat-mdc-unelevated-button .mat-mdc-button-persistent-ripple::before, +.mat-mdc-raised-button .mat-mdc-button-ripple, +.mat-mdc-raised-button .mat-mdc-button-persistent-ripple, +.mat-mdc-raised-button .mat-mdc-button-persistent-ripple::before, +.mat-mdc-outlined-button .mat-mdc-button-ripple, +.mat-mdc-outlined-button .mat-mdc-button-persistent-ripple, +.mat-mdc-outlined-button .mat-mdc-button-persistent-ripple::before, +.mat-tonal-button .mat-mdc-button-ripple, +.mat-tonal-button .mat-mdc-button-persistent-ripple, +.mat-tonal-button .mat-mdc-button-persistent-ripple::before { + top: 0; + left: 0; + right: 0; + bottom: 0; + position: absolute; + pointer-events: none; + border-radius: inherit; +} +.mat-mdc-button .mat-mdc-button-ripple, +.mat-mdc-unelevated-button .mat-mdc-button-ripple, +.mat-mdc-raised-button .mat-mdc-button-ripple, +.mat-mdc-outlined-button .mat-mdc-button-ripple, +.mat-tonal-button .mat-mdc-button-ripple { + overflow: hidden; +} +.mat-mdc-button .mat-mdc-button-persistent-ripple::before, +.mat-mdc-unelevated-button .mat-mdc-button-persistent-ripple::before, +.mat-mdc-raised-button .mat-mdc-button-persistent-ripple::before, +.mat-mdc-outlined-button .mat-mdc-button-persistent-ripple::before, +.mat-tonal-button .mat-mdc-button-persistent-ripple::before { + content: ""; + opacity: 0; +} +.mat-mdc-button .mdc-button__label, +.mat-mdc-button .mat-icon, +.mat-mdc-unelevated-button .mdc-button__label, +.mat-mdc-unelevated-button .mat-icon, +.mat-mdc-raised-button .mdc-button__label, +.mat-mdc-raised-button .mat-icon, +.mat-mdc-outlined-button .mdc-button__label, +.mat-mdc-outlined-button .mat-icon, +.mat-tonal-button .mdc-button__label, +.mat-tonal-button .mat-icon { + z-index: 1; + position: relative; +} +.mat-mdc-button .mat-focus-indicator, +.mat-mdc-unelevated-button .mat-focus-indicator, +.mat-mdc-raised-button .mat-focus-indicator, +.mat-mdc-outlined-button .mat-focus-indicator, +.mat-tonal-button .mat-focus-indicator { + top: 0; + left: 0; + right: 0; + bottom: 0; + position: absolute; + border-radius: inherit; +} +.mat-mdc-button:focus-visible > .mat-focus-indicator::before, +.mat-mdc-unelevated-button:focus-visible > .mat-focus-indicator::before, +.mat-mdc-raised-button:focus-visible > .mat-focus-indicator::before, +.mat-mdc-outlined-button:focus-visible > .mat-focus-indicator::before, +.mat-tonal-button:focus-visible > .mat-focus-indicator::before { + content: ""; + border-radius: inherit; +} +.mat-mdc-button._mat-animation-noopable, +.mat-mdc-unelevated-button._mat-animation-noopable, +.mat-mdc-raised-button._mat-animation-noopable, +.mat-mdc-outlined-button._mat-animation-noopable, +.mat-tonal-button._mat-animation-noopable { + transition: none !important; + animation: none !important; +} +.mat-mdc-button > .mat-icon, +.mat-mdc-unelevated-button > .mat-icon, +.mat-mdc-raised-button > .mat-icon, +.mat-mdc-outlined-button > .mat-icon, +.mat-tonal-button > .mat-icon { + display: inline-block; + position: relative; + vertical-align: top; + font-size: 1.125rem; + height: 1.125rem; + width: 1.125rem; +} + +.mat-mdc-outlined-button .mat-mdc-button-ripple, +.mat-mdc-outlined-button .mdc-button__ripple { + top: -1px; + left: -1px; + bottom: -1px; + right: -1px; +} + +.mat-mdc-unelevated-button .mat-focus-indicator::before, +.mat-tonal-button .mat-focus-indicator::before, +.mat-mdc-raised-button .mat-focus-indicator::before { + margin: calc(calc(var(--mat-focus-indicator-border-width, 3px) + 2px) * -1); +} + +.mat-mdc-outlined-button .mat-focus-indicator::before { + margin: calc(calc(var(--mat-focus-indicator-border-width, 3px) + 3px) * -1); +} +`,`@media (forced-colors: active) { + .mat-mdc-button:not(.mdc-button--outlined), + .mat-mdc-unelevated-button:not(.mdc-button--outlined), + .mat-mdc-raised-button:not(.mdc-button--outlined), + .mat-mdc-outlined-button:not(.mdc-button--outlined), + .mat-mdc-button-base.mat-tonal-button, + .mat-mdc-icon-button.mat-mdc-icon-button, + .mat-mdc-outlined-button .mdc-button__ripple { + outline: solid 1px; + } +} +`],encapsulation:2,changeDetection:0})}return t})();function Ck(t){return t.hasAttribute("mat-raised-button")?"elevated":t.hasAttribute("mat-stroked-button")?"outlined":t.hasAttribute("mat-flat-button")?"filled":t.hasAttribute("mat-button")?"text":null}var rK=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275mod=X({type:t});static \u0275inj=Z({imports:[dC,zn]})}return t})();var Iu={production:!0,electron:!1,githubio:!1,solarputty_download_url:"",current_version:"v3",compute_id:"local"};var oa=class{};function Ik(t){return t&&typeof t.connect=="function"&&!(t instanceof ki)}var fg=class extends oa{_data;constructor(n){super(),this._data=n}connect(){return Ft(this._data)?this._data:T(this._data)}disconnect(){}},Dn=(function(t){return t[t.REPLACED=0]="REPLACED",t[t.INSERTED=1]="INSERTED",t[t.MOVED=2]="MOVED",t[t.REMOVED=3]="REMOVED",t})(Dn||{}),hg=class{viewCacheSize=20;_viewCache=[];applyChanges(n,e,r,o,i){n.forEachOperation((s,a,c)=>{let l,u;if(s.previousIndex==null){let d=()=>r(s,a,c);l=this._insertView(d,c,e,o(s)),u=l?Dn.INSERTED:Dn.REPLACED}else c==null?(this._detachAndCacheView(a,e),u=Dn.REMOVED):(l=this._moveView(a,c,e,o(s)),u=Dn.MOVED);i&&i({context:l?.context,operation:u,record:s})})}detach(){for(let n of this._viewCache)n.destroy();this._viewCache=[]}_insertView(n,e,r,o){let i=this._insertViewFromCache(e,r);if(i){i.context.$implicit=o;return}let s=n();return r.createEmbeddedView(s.templateRef,s.context,s.index)}_detachAndCacheView(n,e){let r=e.detach(n);this._maybeCacheView(r,e)}_moveView(n,e,r,o){let i=r.get(n);return r.move(i,e),i.context.$implicit=o,i}_maybeCacheView(n,e){if(this._viewCache.length0?i/this._itemSize:0;if(e.end>o){let c=Math.ceil(r/this._itemSize),l=Math.max(0,Math.min(s,o-c));s!=l&&(s=l,i=l*this._itemSize,e.start=Math.floor(s)),e.end=Math.max(0,Math.min(o,e.start+c))}let a=i-e.start*this._itemSize;if(a0&&(e.end=Math.min(o,e.end+l),e.start=Math.max(0,Math.floor(s-this._minBufferPx/this._itemSize)))}}this._viewport.setRenderedRange(e),this._viewport.setRenderedContentOffset(Math.round(this._itemSize*e.start)),this._scrolledIndexChange.next(Math.floor(s))}};function Tk(t){return t._scrollStrategy}var xk=(()=>{class t{get itemSize(){return this._itemSize}set itemSize(e){this._itemSize=so(e)}_itemSize=20;get minBufferPx(){return this._minBufferPx}set minBufferPx(e){this._minBufferPx=so(e)}_minBufferPx=100;get maxBufferPx(){return this._maxBufferPx}set maxBufferPx(e){this._maxBufferPx=so(e)}_maxBufferPx=200;_scrollStrategy=new pg(this.itemSize,this.minBufferPx,this.maxBufferPx);ngOnChanges(){this._scrollStrategy.updateItemAndBufferSize(this.itemSize,this.minBufferPx,this.maxBufferPx)}static \u0275fac=function(r){return new(r||t)};static \u0275dir=M({type:t,selectors:[["cdk-virtual-scroll-viewport","itemSize",""]],inputs:{itemSize:"itemSize",minBufferPx:"minBufferPx",maxBufferPx:"maxBufferPx"},features:[we([{provide:pC,useFactory:Tk,deps:[be(()=>t)]}]),Re]})}return t})(),Ak=20,ia=(()=>{class t{_ngZone=f(j);_platform=f(he);_renderer=f(je).createRenderer(null,null);_cleanupGlobalListener;constructor(){}_scrolled=new S;_scrolledCount=0;scrollContainers=new Map;register(e){this.scrollContainers.has(e)||this.scrollContainers.set(e,e.elementScrolled().subscribe(()=>this._scrolled.next(e)))}deregister(e){let r=this.scrollContainers.get(e);r&&(r.unsubscribe(),this.scrollContainers.delete(e))}scrolled(e=Ak){return this._platform.isBrowser?new O(r=>{this._cleanupGlobalListener||(this._cleanupGlobalListener=this._ngZone.runOutsideAngular(()=>this._renderer.listen("document","scroll",()=>this._scrolled.next())));let o=e>0?this._scrolled.pipe(Bi(e)).subscribe(r):this._scrolled.subscribe(r);return this._scrolledCount++,()=>{o.unsubscribe(),this._scrolledCount--,this._scrolledCount||(this._cleanupGlobalListener?.(),this._cleanupGlobalListener=void 0)}}):T()}ngOnDestroy(){this._cleanupGlobalListener?.(),this._cleanupGlobalListener=void 0,this.scrollContainers.forEach((e,r)=>this.deregister(r)),this._scrolled.complete()}ancestorScrolled(e,r){let o=this.getAncestorScrollContainers(e);return this.scrolled(r).pipe(fe(i=>!i||o.indexOf(i)>-1))}getAncestorScrollContainers(e){let r=[];return this.scrollContainers.forEach((o,i)=>{this._scrollableContainsElement(i,e)&&r.push(i)}),r}_scrollableContainsElement(e,r){let o=Tt(r),i=e.getElementRef().nativeElement;do if(o==i)return!0;while(o=o.parentElement);return!1}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})(),gg=(()=>{class t{elementRef=f(z);scrollDispatcher=f(ia);ngZone=f(j);dir=f(fi,{optional:!0});_scrollElement=this.elementRef.nativeElement;_destroyed=new S;_renderer=f(Oe);_cleanupScroll;_elementScrolled=new S;constructor(){}ngOnInit(){this._cleanupScroll=this.ngZone.runOutsideAngular(()=>this._renderer.listen(this._scrollElement,"scroll",e=>this._elementScrolled.next(e))),this.scrollDispatcher.register(this)}ngOnDestroy(){this._cleanupScroll?.(),this._elementScrolled.complete(),this.scrollDispatcher.deregister(this),this._destroyed.next(),this._destroyed.complete()}elementScrolled(){return this._elementScrolled}getElementRef(){return this.elementRef}scrollTo(e){let r=this.elementRef.nativeElement,o=this.dir&&this.dir.value=="rtl";e.left==null&&(e.left=o?e.end:e.start),e.right==null&&(e.right=o?e.start:e.end),e.bottom!=null&&(e.top=r.scrollHeight-r.clientHeight-e.bottom),o&&ui()!=en.NORMAL?(e.left!=null&&(e.right=r.scrollWidth-r.clientWidth-e.left),ui()==en.INVERTED?e.left=e.right:ui()==en.NEGATED&&(e.left=e.right?-e.right:e.right)):e.right!=null&&(e.left=r.scrollWidth-r.clientWidth-e.right),this._applyScrollToOptions(e)}_applyScrollToOptions(e){let r=this.elementRef.nativeElement;wu()?r.scrollTo(e):(e.top!=null&&(r.scrollTop=e.top),e.left!=null&&(r.scrollLeft=e.left))}measureScrollOffset(e){let r="left",o="right",i=this.elementRef.nativeElement;if(e=="top")return i.scrollTop;if(e=="bottom")return i.scrollHeight-i.clientHeight-i.scrollTop;let s=this.dir&&this.dir.value=="rtl";return e=="start"?e=s?o:r:e=="end"&&(e=s?r:o),s&&ui()==en.INVERTED?e==r?i.scrollWidth-i.clientWidth-i.scrollLeft:i.scrollLeft:s&&ui()==en.NEGATED?e==r?i.scrollLeft+i.scrollWidth-i.clientWidth:-i.scrollLeft:e==r?i.scrollLeft:i.scrollWidth-i.clientWidth-i.scrollLeft}static \u0275fac=function(r){return new(r||t)};static \u0275dir=M({type:t,selectors:[["","cdk-scrollable",""],["","cdkScrollable",""]]})}return t})(),Rk=20,uo=(()=>{class t{_platform=f(he);_listeners;_viewportSize=null;_change=new S;_document=f(L);constructor(){let e=f(j),r=f(je).createRenderer(null,null);e.runOutsideAngular(()=>{if(this._platform.isBrowser){let o=i=>this._change.next(i);this._listeners=[r.listen("window","resize",o),r.listen("window","orientationchange",o)]}this.change().subscribe(()=>this._viewportSize=null)})}ngOnDestroy(){this._listeners?.forEach(e=>e()),this._change.complete()}getViewportSize(){this._viewportSize||this._updateViewportSize();let e={width:this._viewportSize.width,height:this._viewportSize.height};return this._platform.isBrowser||(this._viewportSize=null),e}getViewportRect(){let e=this.getViewportScrollPosition(),{width:r,height:o}=this.getViewportSize();return{top:e.top,left:e.left,bottom:e.top+o,right:e.left+r,height:o,width:r}}getViewportScrollPosition(){if(!this._platform.isBrowser)return{top:0,left:0};let e=this._document,r=this._getWindow(),o=e.documentElement,i=o.getBoundingClientRect(),s=-i.top||e.body?.scrollTop||r.scrollY||o.scrollTop||0,a=-i.left||e.body?.scrollLeft||r.scrollX||o.scrollLeft||0;return{top:s,left:a}}change(e=Rk){return e>0?this._change.pipe(Bi(e)):this._change}_getWindow(){return this._document.defaultView||window}_updateViewportSize(){let e=this._getWindow();this._viewportSize=this._platform.isBrowser?{width:e.innerWidth,height:e.innerHeight}:{width:0,height:0}}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})(),hC=new y("VIRTUAL_SCROLLABLE"),Nk=(()=>{class t extends gg{constructor(){super()}measureViewportSize(e){let r=this.elementRef.nativeElement;return e==="horizontal"?r.clientWidth:r.clientHeight}static \u0275fac=function(r){return new(r||t)};static \u0275dir=M({type:t,features:[J]})}return t})();function Ok(t,n){return t.start==n.start&&t.end==n.end}var kk=typeof requestAnimationFrame<"u"?Vd:jd,Fk=new y("CDK_VIRTUAL_SCROLL_VIEWPORT"),Pk=(()=>{class t extends Nk{elementRef=f(z);_changeDetectorRef=f(St);_scrollStrategy=f(pC,{optional:!0});scrollable=f(hC,{optional:!0});_platform=f(he);_detachedSubject=new S;_renderedRangeSubject=new S;_renderedContentOffsetSubject=new S;get orientation(){return this._orientation}set orientation(e){this._orientation!==e&&(this._orientation=e,this._calculateSpacerSize())}_orientation="vertical";appendOnly=!1;scrolledIndexChange=new O(e=>this._scrollStrategy.scrolledIndexChange.subscribe(r=>Promise.resolve().then(()=>this.ngZone.run(()=>e.next(r)))));_contentWrapper;renderedRangeStream=this._renderedRangeSubject;renderedContentOffset=this._renderedContentOffsetSubject.pipe(fe(e=>e!==null),xo());_totalContentSize=0;_totalContentWidth=W("");_totalContentHeight=W("");_renderedContentTransform;_renderedRange={start:0,end:0};_dataLength=0;_viewportSize=0;_forOf=null;_renderedContentOffset=0;_renderedContentOffsetNeedsRewrite=!1;_changeDetectionNeeded=W(!1);_runAfterChangeDetection=[];_viewportChanges=G.EMPTY;_injector=f($);_isDestroyed=!1;constructor(){super();let e=f(uo);this._scrollStrategy,this._viewportChanges=e.change().subscribe(()=>{this.checkViewportSize()}),this.scrollable||(this.elementRef.nativeElement.classList.add("cdk-virtual-scrollable"),this.scrollable=this);let r=Go(()=>{this._changeDetectionNeeded()&&this._doChangeDetection()},{injector:f(Be).injector});f(Ae).onDestroy(()=>{r.destroy()})}ngOnInit(){this._platform.isBrowser&&(this.scrollable===this&&super.ngOnInit(),this.ngZone.runOutsideAngular(()=>Promise.resolve().then(()=>{this._measureViewportSize(),this._scrollStrategy.attach(this),this.scrollable.elementScrolled().pipe(Nr(null),Bi(0,kk),at(this._destroyed)).subscribe(()=>this._scrollStrategy.onContentScrolled()),this._markChangeDetectionNeeded()})))}ngOnDestroy(){this.detach(),this._scrollStrategy.detach(),this._renderedRangeSubject.complete(),this._detachedSubject.complete(),this._viewportChanges.unsubscribe(),this._isDestroyed=!0,super.ngOnDestroy()}attach(e){this._forOf,this.ngZone.runOutsideAngular(()=>{this._forOf=e,this._forOf.dataStream.pipe(at(this._detachedSubject)).subscribe(r=>{let o=r.length;o!==this._dataLength&&(this._dataLength=o,this._scrollStrategy.onDataLengthChanged()),this._doChangeDetection()})})}detach(){this._forOf=null,this._detachedSubject.next()}getDataLength(){return this._dataLength}getViewportSize(){return this._viewportSize}getRenderedRange(){return this._renderedRange}measureBoundingClientRectWithScrollOffset(e){return this.getElementRef().nativeElement.getBoundingClientRect()[e]}setTotalContentSize(e){this._totalContentSize!==e&&(this._totalContentSize=e,this._calculateSpacerSize(),this._markChangeDetectionNeeded())}setRenderedRange(e){Ok(this._renderedRange,e)||(this.appendOnly&&(e={start:0,end:Math.max(this._renderedRange.end,e.end)}),this._renderedRangeSubject.next(this._renderedRange=e),this._markChangeDetectionNeeded(()=>this._scrollStrategy.onContentRendered()))}getOffsetToRenderedContentStart(){return this._renderedContentOffsetNeedsRewrite?null:this._renderedContentOffset}setRenderedContentOffset(e,r="to-start"){e=this.appendOnly&&r==="to-start"?0:e;let o=this.dir&&this.dir.value=="rtl",i=this.orientation=="horizontal",s=i?"X":"Y",c=`translate${s}(${Number((i&&o?-1:1)*e)}px)`;this._renderedContentOffset=e,r==="to-end"&&(c+=` translate${s}(-100%)`,this._renderedContentOffsetNeedsRewrite=!0),this._renderedContentTransform!=c&&(this._renderedContentTransform=c,this._markChangeDetectionNeeded(()=>{this._renderedContentOffsetNeedsRewrite?(this._renderedContentOffset-=this.measureRenderedContentSize(),this._renderedContentOffsetNeedsRewrite=!1,this.setRenderedContentOffset(this._renderedContentOffset)):this._scrollStrategy.onRenderedOffsetChanged()}))}scrollToOffset(e,r="auto"){let o={behavior:r};this.orientation==="horizontal"?o.start=e:o.top=e,this.scrollable.scrollTo(o)}scrollToIndex(e,r="auto"){this._scrollStrategy.scrollToIndex(e,r)}measureScrollOffset(e){let r;return this.scrollable==this?r=o=>super.measureScrollOffset(o):r=o=>this.scrollable.measureScrollOffset(o),Math.max(0,r(e??(this.orientation==="horizontal"?"start":"top"))-this.measureViewportOffset())}measureViewportOffset(e){let r,o="left",i="right",s=this.dir?.value=="rtl";e=="start"?r=s?i:o:e=="end"?r=s?o:i:e?r=e:r=this.orientation==="horizontal"?"left":"top";let a=this.scrollable.measureBoundingClientRectWithScrollOffset(r);return this.elementRef.nativeElement.getBoundingClientRect()[r]-a}measureRenderedContentSize(){let e=this._contentWrapper.nativeElement;return this.orientation==="horizontal"?e.offsetWidth:e.offsetHeight}measureRangeSize(e){return this._forOf?this._forOf.measureRangeSize(e,this.orientation):0}checkViewportSize(){this._measureViewportSize(),this._scrollStrategy.onDataLengthChanged()}_measureViewportSize(){this._viewportSize=this.scrollable.measureViewportSize(this.orientation)}_markChangeDetectionNeeded(e){e&&this._runAfterChangeDetection.push(e),!q(this._changeDetectionNeeded)&&this.ngZone.runOutsideAngular(()=>{Promise.resolve().then(()=>{this.ngZone.run(()=>{this._changeDetectionNeeded.set(!0)})})})}_doChangeDetection(){this._isDestroyed||this.ngZone.run(()=>{this._changeDetectorRef.markForCheck(),this._contentWrapper.nativeElement.style.transform=this._renderedContentTransform,this._renderedContentOffsetSubject.next(this.getOffsetToRenderedContentStart()),ht(()=>{this._changeDetectionNeeded.set(!1);let e=this._runAfterChangeDetection;this._runAfterChangeDetection=[];for(let r of e)r()},{injector:this._injector})})}_calculateSpacerSize(){this._totalContentHeight.set(this.orientation==="horizontal"?"":`${this._totalContentSize}px`),this._totalContentWidth.set(this.orientation==="horizontal"?`${this._totalContentSize}px`:"")}static \u0275fac=function(r){return new(r||t)};static \u0275cmp=ke({type:t,selectors:[["cdk-virtual-scroll-viewport"]],viewQuery:function(r,o){if(r&1&&Fl(Sk,7),r&2){let i;Fs(i=Ps())&&(o._contentWrapper=i.first)}},hostAttrs:[1,"cdk-virtual-scroll-viewport"],hostVars:4,hostBindings:function(r,o){r&2&&Xe("cdk-virtual-scroll-orientation-horizontal",o.orientation==="horizontal")("cdk-virtual-scroll-orientation-vertical",o.orientation!=="horizontal")},inputs:{orientation:"orientation",appendOnly:[2,"appendOnly","appendOnly",ue]},outputs:{scrolledIndexChange:"scrolledIndexChange"},features:[we([{provide:gg,useFactory:()=>f(hC,{optional:!0})||f(t)},{provide:Fk,useExisting:t}]),J],ngContentSelectors:Mk,decls:4,vars:4,consts:[["contentWrapper",""],[1,"cdk-virtual-scroll-content-wrapper"],[1,"cdk-virtual-scroll-spacer"]],template:function(r,o){r&1&&(ro(),to(0,"div",1,0),Vn(2),no(),yn(3,"div",2)),r&2&&(bp(3),Pl("width",o._totalContentWidth())("height",o._totalContentHeight()))},styles:[`cdk-virtual-scroll-viewport { + display: block; + position: relative; + transform: translateZ(0); +} + +.cdk-virtual-scrollable { + overflow: auto; + will-change: scroll-position; + contain: strict; +} + +.cdk-virtual-scroll-content-wrapper { + position: absolute; + top: 0; + left: 0; + contain: content; +} +[dir=rtl] .cdk-virtual-scroll-content-wrapper { + right: 0; + left: auto; +} + +.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper { + min-height: 100%; +} +.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper > dl:not([cdkVirtualFor]), .cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper > ol:not([cdkVirtualFor]), .cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper > table:not([cdkVirtualFor]), .cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper > ul:not([cdkVirtualFor]) { + padding-left: 0; + padding-right: 0; + margin-left: 0; + margin-right: 0; + border-left-width: 0; + border-right-width: 0; + outline: none; +} + +.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper { + min-width: 100%; +} +.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper > dl:not([cdkVirtualFor]), .cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper > ol:not([cdkVirtualFor]), .cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper > table:not([cdkVirtualFor]), .cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper > ul:not([cdkVirtualFor]) { + padding-top: 0; + padding-bottom: 0; + margin-top: 0; + margin-bottom: 0; + border-top-width: 0; + border-bottom-width: 0; + outline: none; +} + +.cdk-virtual-scroll-spacer { + height: 1px; + transform-origin: 0 0; + flex: 0 0 auto; +} +[dir=rtl] .cdk-virtual-scroll-spacer { + transform-origin: 100% 0; +} +`],encapsulation:2,changeDetection:0})}return t})();var mg=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275mod=X({type:t});static \u0275inj=Z({})}return t})(),vg=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275mod=X({type:t});static \u0275inj=Z({imports:[zn,mg,zn,mg]})}return t})();var sa=class{_attachedHost=null;attach(n){return this._attachedHost=n,n.attach(this)}detach(){let n=this._attachedHost;n!=null&&(this._attachedHost=null,n.detach())}get isAttached(){return this._attachedHost!=null}setAttachedHost(n){this._attachedHost=n}},yg=class extends sa{component;viewContainerRef;injector;projectableNodes;bindings;constructor(n,e,r,o,i){super(),this.component=n,this.viewContainerRef=e,this.injector=r,this.projectableNodes=o,this.bindings=i||null}},hi=class extends sa{templateRef;viewContainerRef;context;injector;constructor(n,e,r,o){super(),this.templateRef=n,this.viewContainerRef=e,this.context=r,this.injector=o}get origin(){return this.templateRef.elementRef}attach(n,e=this.context){return this.context=e,super.attach(n)}detach(){return this.context=void 0,super.detach()}},bg=class extends sa{element;constructor(n){super(),this.element=n instanceof z?n.nativeElement:n}},Su=class{_attachedPortal=null;_disposeFn=null;_isDisposed=!1;hasAttached(){return!!this._attachedPortal}attach(n){if(n instanceof yg)return this._attachedPortal=n,this.attachComponentPortal(n);if(n instanceof hi)return this._attachedPortal=n,this.attachTemplatePortal(n);if(this.attachDomPortal&&n instanceof bg)return this._attachedPortal=n,this.attachDomPortal(n)}attachDomPortal=null;detach(){this._attachedPortal&&(this._attachedPortal.setAttachedHost(null),this._attachedPortal=null),this._invokeDisposeFn()}dispose(){this.hasAttached()&&this.detach(),this._invokeDisposeFn(),this._isDisposed=!0}setDisposeFn(n){this._disposeFn=n}_invokeDisposeFn(){this._disposeFn&&(this._disposeFn(),this._disposeFn=null)}},Mu=class extends Su{outletElement;_appRef;_defaultInjector;constructor(n,e,r){super(),this.outletElement=n,this._appRef=e,this._defaultInjector=r}attachComponentPortal(n){let e;if(n.viewContainerRef){let r=n.injector||n.viewContainerRef.injector,o=r.get(gn,null,{optional:!0})||void 0;e=n.viewContainerRef.createComponent(n.component,{index:n.viewContainerRef.length,injector:r,ngModuleRef:o,projectableNodes:n.projectableNodes||void 0,bindings:n.bindings||void 0}),this.setDisposeFn(()=>e.destroy())}else{let r=this._appRef,o=n.injector||this._defaultInjector||$.NULL,i=o.get(re,r.injector);e=$l(n.component,{elementInjector:o,environmentInjector:i,projectableNodes:n.projectableNodes||void 0,bindings:n.bindings||void 0}),r.attachView(e.hostView),this.setDisposeFn(()=>{r.viewCount>0&&r.detachView(e.hostView),e.destroy()})}return this.outletElement.appendChild(this._getComponentRootNode(e)),this._attachedPortal=n,e}attachTemplatePortal(n){let e=n.viewContainerRef,r=e.createEmbeddedView(n.templateRef,n.context,{injector:n.injector});return r.rootNodes.forEach(o=>this.outletElement.appendChild(o)),r.detectChanges(),this.setDisposeFn(()=>{let o=e.indexOf(r);o!==-1&&e.remove(o)}),this._attachedPortal=n,r}attachDomPortal=n=>{let e=n.element;e.parentNode;let r=this.outletElement.ownerDocument.createComment("dom-portal");e.parentNode.insertBefore(r,e),this.outletElement.appendChild(e),this._attachedPortal=n,super.setDisposeFn(()=>{r.parentNode&&r.parentNode.replaceChild(e,r)})};dispose(){super.dispose(),this.outletElement.remove()}_getComponentRootNode(n){return n.hostView.rootNodes[0]}},kK=(()=>{class t extends hi{constructor(){let e=f(dt),r=f(qe);super(e,r)}static \u0275fac=function(r){return new(r||t)};static \u0275dir=M({type:t,selectors:[["","cdkPortal",""]],exportAs:["cdkPortal"],features:[J]})}return t})(),FK=(()=>{class t extends Su{_moduleRef=f(gn,{optional:!0});_document=f(L);_viewContainerRef=f(qe);_isInitialized=!1;_attachedRef=null;constructor(){super()}get portal(){return this._attachedPortal}set portal(e){this.hasAttached()&&!e&&!this._isInitialized||(this.hasAttached()&&super.detach(),e&&super.attach(e),this._attachedPortal=e||null)}attached=new U;get attachedRef(){return this._attachedRef}ngOnInit(){this._isInitialized=!0}ngOnDestroy(){super.dispose(),this._attachedRef=this._attachedPortal=null}attachComponentPortal(e){e.setAttachedHost(this);let r=e.viewContainerRef!=null?e.viewContainerRef:this._viewContainerRef,o=r.createComponent(e.component,{index:r.length,injector:e.injector||r.injector,projectableNodes:e.projectableNodes||void 0,ngModuleRef:this._moduleRef||void 0,bindings:e.bindings||void 0});return r!==this._viewContainerRef&&this._getRootNode().appendChild(o.hostView.rootNodes[0]),super.setDisposeFn(()=>o.destroy()),this._attachedPortal=e,this._attachedRef=o,this.attached.emit(o),o}attachTemplatePortal(e){e.setAttachedHost(this);let r=this._viewContainerRef.createEmbeddedView(e.templateRef,e.context,{injector:e.injector});return super.setDisposeFn(()=>this._viewContainerRef.clear()),this._attachedPortal=e,this._attachedRef=r,this.attached.emit(r),r}attachDomPortal=e=>{let r=e.element;r.parentNode;let o=this._document.createComment("dom-portal");e.setAttachedHost(this),r.parentNode.insertBefore(o,r),this._getRootNode().appendChild(r),this._attachedPortal=e,super.setDisposeFn(()=>{o.parentNode&&o.parentNode.replaceChild(r,o)})};_getRootNode(){let e=this._viewContainerRef.element.nativeElement;return e.nodeType===e.ELEMENT_NODE?e:e.parentNode}static \u0275fac=function(r){return new(r||t)};static \u0275dir=M({type:t,selectors:[["","cdkPortalOutlet",""]],inputs:{portal:[0,"cdkPortalOutlet","portal"]},outputs:{attached:"attached"},exportAs:["cdkPortalOutlet"],features:[J]})}return t})(),mC=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275mod=X({type:t});static \u0275inj=Z({})}return t})();var gC=wu();function wC(t){return new Tu(t.get(uo),t.get(L))}var Tu=class{_viewportRuler;_previousHTMLStyles={top:"",left:""};_previousScrollPosition;_isEnabled=!1;_document;constructor(n,e){this._viewportRuler=n,this._document=e}attach(){}enable(){if(this._canBeEnabled()){let n=this._document.documentElement;this._previousScrollPosition=this._viewportRuler.getViewportScrollPosition(),this._previousHTMLStyles.left=n.style.left||"",this._previousHTMLStyles.top=n.style.top||"",n.style.left=De(-this._previousScrollPosition.left),n.style.top=De(-this._previousScrollPosition.top),n.classList.add("cdk-global-scrollblock"),this._isEnabled=!0}}disable(){if(this._isEnabled){let n=this._document.documentElement,e=this._document.body,r=n.style,o=e.style,i=r.scrollBehavior||"",s=o.scrollBehavior||"";this._isEnabled=!1,r.left=this._previousHTMLStyles.left,r.top=this._previousHTMLStyles.top,n.classList.remove("cdk-global-scrollblock"),gC&&(r.scrollBehavior=o.scrollBehavior="auto"),window.scroll(this._previousScrollPosition.left,this._previousScrollPosition.top),gC&&(r.scrollBehavior=i,o.scrollBehavior=s)}}_canBeEnabled(){if(this._document.documentElement.classList.contains("cdk-global-scrollblock")||this._isEnabled)return!1;let e=this._document.documentElement,r=this._viewportRuler.getViewportSize();return e.scrollHeight>r.height||e.scrollWidth>r.width}};function CC(t,n){return new xu(t.get(ia),t.get(j),t.get(uo),n)}var xu=class{_scrollDispatcher;_ngZone;_viewportRuler;_config;_scrollSubscription=null;_overlayRef;_initialScrollPosition;constructor(n,e,r,o){this._scrollDispatcher=n,this._ngZone=e,this._viewportRuler=r,this._config=o}attach(n){this._overlayRef,this._overlayRef=n}enable(){if(this._scrollSubscription)return;let n=this._scrollDispatcher.scrolled(0).pipe(fe(e=>!e||!this._overlayRef.overlayElement.contains(e.getElementRef().nativeElement)));this._config&&this._config.threshold&&this._config.threshold>1?(this._initialScrollPosition=this._viewportRuler.getViewportScrollPosition().top,this._scrollSubscription=n.subscribe(()=>{let e=this._viewportRuler.getViewportScrollPosition().top;Math.abs(e-this._initialScrollPosition)>this._config.threshold?this._detach():this._overlayRef.updatePosition()})):this._scrollSubscription=n.subscribe(this._detach)}disable(){this._scrollSubscription&&(this._scrollSubscription.unsubscribe(),this._scrollSubscription=null)}detach(){this.disable(),this._overlayRef=null}_detach=()=>{this.disable(),this._overlayRef.hasAttached()&&this._ngZone.run(()=>this._overlayRef.detach())}};var aa=class{enable(){}disable(){}attach(){}};function _g(t,n){return n.some(e=>{let r=t.bottome.bottom,i=t.righte.right;return r||o||i||s})}function vC(t,n){return n.some(e=>{let r=t.tope.bottom,i=t.lefte.right;return r||o||i||s})}function wg(t,n){return new Au(t.get(ia),t.get(uo),t.get(j),n)}var Au=class{_scrollDispatcher;_viewportRuler;_ngZone;_config;_scrollSubscription=null;_overlayRef;constructor(n,e,r,o){this._scrollDispatcher=n,this._viewportRuler=e,this._ngZone=r,this._config=o}attach(n){this._overlayRef,this._overlayRef=n}enable(){if(!this._scrollSubscription){let n=this._config?this._config.scrollThrottle:0;this._scrollSubscription=this._scrollDispatcher.scrolled(n).subscribe(()=>{if(this._overlayRef.updatePosition(),this._config&&this._config.autoClose){let e=this._overlayRef.overlayElement.getBoundingClientRect(),{width:r,height:o}=this._viewportRuler.getViewportSize();_g(e,[{width:r,height:o,bottom:o,right:r,top:0,left:0}])&&(this.disable(),this._ngZone.run(()=>this._overlayRef.detach()))}})}}disable(){this._scrollSubscription&&(this._scrollSubscription.unsubscribe(),this._scrollSubscription=null)}detach(){this.disable(),this._overlayRef=null}},IC=(()=>{class t{_injector=f($);constructor(){}noop=()=>new aa;close=e=>CC(this._injector,e);block=()=>wC(this._injector);reposition=e=>wg(this._injector,e);static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})(),ca=class{positionStrategy;scrollStrategy=new aa;panelClass="";hasBackdrop=!1;backdropClass="cdk-overlay-dark-backdrop";disableAnimations;width;height;minWidth;minHeight;maxWidth;maxHeight;direction;disposeOnNavigation=!1;usePopover;eventPredicate;constructor(n){if(n){let e=Object.keys(n);for(let r of e)n[r]!==void 0&&(this[r]=n[r])}}};var Ru=class{connectionPair;scrollableViewProperties;constructor(n,e){this.connectionPair=n,this.scrollableViewProperties=e}};var SC=(()=>{class t{_attachedOverlays=[];_document=f(L);_isAttached=!1;constructor(){}ngOnDestroy(){this.detach()}add(e){this.remove(e),this._attachedOverlays.push(e)}remove(e){let r=this._attachedOverlays.indexOf(e);r>-1&&this._attachedOverlays.splice(r,1),this._attachedOverlays.length===0&&this.detach()}canReceiveEvent(e,r,o){return o.observers.length<1?!1:e.eventPredicate?e.eventPredicate(r):!0}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})(),MC=(()=>{class t extends SC{_ngZone=f(j);_renderer=f(je).createRenderer(null,null);_cleanupKeydown;add(e){super.add(e),this._isAttached||(this._ngZone.runOutsideAngular(()=>{this._cleanupKeydown=this._renderer.listen("body","keydown",this._keydownListener)}),this._isAttached=!0)}detach(){this._isAttached&&(this._cleanupKeydown?.(),this._isAttached=!1)}_keydownListener=e=>{let r=this._attachedOverlays;for(let o=r.length-1;o>-1;o--){let i=r[o];if(this.canReceiveEvent(i,e,i._keydownEvents)){this._ngZone.run(()=>i._keydownEvents.next(e));break}}};static \u0275fac=(()=>{let e;return function(o){return(e||(e=Ne(t)))(o||t)}})();static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})(),TC=(()=>{class t extends SC{_platform=f(he);_ngZone=f(j);_renderer=f(je).createRenderer(null,null);_cursorOriginalValue;_cursorStyleIsSet=!1;_pointerDownEventTarget=null;_cleanups;add(e){if(super.add(e),!this._isAttached){let r=this._document.body,o={capture:!0},i=this._renderer;this._cleanups=this._ngZone.runOutsideAngular(()=>[i.listen(r,"pointerdown",this._pointerDownListener,o),i.listen(r,"click",this._clickListener,o),i.listen(r,"auxclick",this._clickListener,o),i.listen(r,"contextmenu",this._clickListener,o)]),this._platform.IOS&&!this._cursorStyleIsSet&&(this._cursorOriginalValue=r.style.cursor,r.style.cursor="pointer",this._cursorStyleIsSet=!0),this._isAttached=!0}}detach(){this._isAttached&&(this._cleanups?.forEach(e=>e()),this._cleanups=void 0,this._platform.IOS&&this._cursorStyleIsSet&&(this._document.body.style.cursor=this._cursorOriginalValue,this._cursorStyleIsSet=!1),this._isAttached=!1)}_pointerDownListener=e=>{this._pointerDownEventTarget=Je(e)};_clickListener=e=>{let r=Je(e),o=e.type==="click"&&this._pointerDownEventTarget?this._pointerDownEventTarget:r;this._pointerDownEventTarget=null;let i=this._attachedOverlays.slice();for(let s=i.length-1;s>-1;s--){let a=i[s],c=a._outsidePointerEvents;if(!(!a.hasAttached()||!this.canReceiveEvent(a,e,c))){if(yC(a.overlayElement,r)||yC(a.overlayElement,o))break;this._ngZone?this._ngZone.run(()=>c.next(e)):c.next(e)}}};static \u0275fac=(()=>{let e;return function(o){return(e||(e=Ne(t)))(o||t)}})();static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();function yC(t,n){let e=typeof ShadowRoot<"u"&&ShadowRoot,r=n;for(;r;){if(r===t)return!0;r=e&&r instanceof ShadowRoot?r.host:r.parentNode}return!1}var xC=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275cmp=ke({type:t,selectors:[["ng-component"]],hostAttrs:["cdk-overlay-style-loader",""],decls:0,vars:0,template:function(r,o){},styles:[`.cdk-overlay-container, .cdk-global-overlay-wrapper { + pointer-events: none; + top: 0; + left: 0; + height: 100%; + width: 100%; +} + +.cdk-overlay-container { + position: fixed; +} +@layer cdk-overlay { + .cdk-overlay-container { + z-index: 1000; + } +} +.cdk-overlay-container:empty { + display: none; +} + +.cdk-global-overlay-wrapper { + display: flex; + position: absolute; +} +@layer cdk-overlay { + .cdk-global-overlay-wrapper { + z-index: 1000; + } +} + +.cdk-overlay-pane { + position: absolute; + pointer-events: auto; + box-sizing: border-box; + display: flex; + max-width: 100%; + max-height: 100%; +} +@layer cdk-overlay { + .cdk-overlay-pane { + z-index: 1000; + } +} + +.cdk-overlay-backdrop { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + pointer-events: auto; + -webkit-tap-highlight-color: transparent; + opacity: 0; + touch-action: manipulation; +} +@layer cdk-overlay { + .cdk-overlay-backdrop { + z-index: 1000; + transition: opacity 400ms cubic-bezier(0.25, 0.8, 0.25, 1); + } +} +@media (prefers-reduced-motion) { + .cdk-overlay-backdrop { + transition-duration: 1ms; + } +} + +.cdk-overlay-backdrop-showing { + opacity: 1; +} +@media (forced-colors: active) { + .cdk-overlay-backdrop-showing { + opacity: 0.6; + } +} + +@layer cdk-overlay { + .cdk-overlay-dark-backdrop { + background: rgba(0, 0, 0, 0.32); + } +} + +.cdk-overlay-transparent-backdrop { + transition: visibility 1ms linear, opacity 1ms linear; + visibility: hidden; + opacity: 1; +} +.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing, .cdk-high-contrast-active .cdk-overlay-transparent-backdrop { + opacity: 0; + visibility: visible; +} + +.cdk-overlay-backdrop-noop-animation { + transition: none; +} + +.cdk-overlay-connected-position-bounding-box { + position: absolute; + display: flex; + flex-direction: column; + min-width: 1px; + min-height: 1px; +} +@layer cdk-overlay { + .cdk-overlay-connected-position-bounding-box { + z-index: 1000; + } +} + +.cdk-global-scrollblock { + position: fixed; + width: 100%; + overflow-y: scroll; +} + +.cdk-overlay-popover { + background: none; + border: none; + padding: 0; + outline: 0; + overflow: visible; + position: fixed; + pointer-events: none; + white-space: normal; + color: inherit; + text-decoration: none; + width: 100%; + height: 100%; + inset: auto; + top: 0; + left: 0; +} +.cdk-overlay-popover::backdrop { + display: none; +} +.cdk-overlay-popover .cdk-overlay-backdrop { + position: fixed; + z-index: auto; +} +`],encapsulation:2,changeDetection:0})}return t})(),Cg=(()=>{class t{_platform=f(he);_containerElement;_document=f(L);_styleLoader=f(xt);constructor(){}ngOnDestroy(){this._containerElement?.remove()}getContainerElement(){return this._loadStyles(),this._containerElement||this._createContainer(),this._containerElement}_createContainer(){let e="cdk-overlay-container";if(this._platform.isBrowser||ig()){let o=this._document.querySelectorAll(`.${e}[platform="server"], .${e}[platform="test"]`);for(let i=0;i{let n=this.element;clearTimeout(this._fallbackTimeout),this._cleanupTransitionEnd?.(),this._cleanupTransitionEnd=this._renderer.listen(n,"transitionend",this.dispose),this._fallbackTimeout=setTimeout(this.dispose,500),n.style.pointerEvents="none",n.classList.remove("cdk-overlay-backdrop-showing")})}dispose=()=>{clearTimeout(this._fallbackTimeout),this._cleanupClick?.(),this._cleanupTransitionEnd?.(),this._cleanupClick=this._cleanupTransitionEnd=this._fallbackTimeout=void 0,this.element.remove()}};function Ig(t){return t&&t.nodeType===1}var Nu=class{_portalOutlet;_host;_pane;_config;_ngZone;_keyboardDispatcher;_document;_location;_outsideClickDispatcher;_animationsDisabled;_injector;_renderer;_backdropClick=new S;_attachments=new S;_detachments=new S;_positionStrategy;_scrollStrategy;_locationChanges=G.EMPTY;_backdropRef=null;_detachContentMutationObserver;_detachContentAfterRenderRef;_disposed=!1;_previousHostParent;_keydownEvents=new S;_outsidePointerEvents=new S;_afterNextRenderRef;constructor(n,e,r,o,i,s,a,c,l,u=!1,d,h){this._portalOutlet=n,this._host=e,this._pane=r,this._config=o,this._ngZone=i,this._keyboardDispatcher=s,this._document=a,this._location=c,this._outsideClickDispatcher=l,this._animationsDisabled=u,this._injector=d,this._renderer=h,o.scrollStrategy&&(this._scrollStrategy=o.scrollStrategy,this._scrollStrategy.attach(this)),this._positionStrategy=o.positionStrategy}get overlayElement(){return this._pane}get backdropElement(){return this._backdropRef?.element||null}get hostElement(){return this._host}get eventPredicate(){return this._config?.eventPredicate||null}attach(n){if(this._disposed)return null;this._attachHost();let e=this._portalOutlet.attach(n);return this._positionStrategy?.attach(this),this._updateStackingOrder(),this._updateElementSize(),this._updateElementDirection(),this._scrollStrategy&&this._scrollStrategy.enable(),this._afterNextRenderRef?.destroy(),this._afterNextRenderRef=ht(()=>{this.hasAttached()&&this.updatePosition()},{injector:this._injector}),this._togglePointerEvents(!0),this._config.hasBackdrop&&this._attachBackdrop(),this._config.panelClass&&this._toggleClasses(this._pane,this._config.panelClass,!0),this._attachments.next(),this._completeDetachContent(),this._keyboardDispatcher.add(this),this._config.disposeOnNavigation&&(this._locationChanges=this._location.subscribe(()=>this.dispose())),this._outsideClickDispatcher.add(this),typeof e?.onDestroy=="function"&&e.onDestroy(()=>{this.hasAttached()&&this._ngZone.runOutsideAngular(()=>Promise.resolve().then(()=>this.detach()))}),e}detach(){if(!this.hasAttached())return;this.detachBackdrop(),this._togglePointerEvents(!1),this._positionStrategy&&this._positionStrategy.detach&&this._positionStrategy.detach(),this._scrollStrategy&&this._scrollStrategy.disable();let n=this._portalOutlet.detach();return this._detachments.next(),this._completeDetachContent(),this._keyboardDispatcher.remove(this),this._detachContentWhenEmpty(),this._locationChanges.unsubscribe(),this._outsideClickDispatcher.remove(this),n}dispose(){if(this._disposed)return;let n=this.hasAttached();this._positionStrategy&&this._positionStrategy.dispose(),this._disposeScrollStrategy(),this._backdropRef?.dispose(),this._locationChanges.unsubscribe(),this._keyboardDispatcher.remove(this),this._portalOutlet.dispose(),this._attachments.complete(),this._backdropClick.complete(),this._keydownEvents.complete(),this._outsidePointerEvents.complete(),this._outsideClickDispatcher.remove(this),this._host?.remove(),this._afterNextRenderRef?.destroy(),this._previousHostParent=this._pane=this._host=this._backdropRef=null,n&&this._detachments.next(),this._detachments.complete(),this._completeDetachContent(),this._disposed=!0}hasAttached(){return this._portalOutlet.hasAttached()}backdropClick(){return this._backdropClick}attachments(){return this._attachments}detachments(){return this._detachments}keydownEvents(){return this._keydownEvents}outsidePointerEvents(){return this._outsidePointerEvents}getConfig(){return this._config}updatePosition(){this._positionStrategy&&this._positionStrategy.apply()}updatePositionStrategy(n){n!==this._positionStrategy&&(this._positionStrategy&&this._positionStrategy.dispose(),this._positionStrategy=n,this.hasAttached()&&(n.attach(this),this.updatePosition()))}updateSize(n){this._config=g(g({},this._config),n),this._updateElementSize()}setDirection(n){this._config=F(g({},this._config),{direction:n}),this._updateElementDirection()}addPanelClass(n){this._pane&&this._toggleClasses(this._pane,n,!0)}removePanelClass(n){this._pane&&this._toggleClasses(this._pane,n,!1)}getDirection(){let n=this._config.direction;return n?typeof n=="string"?n:n.value:"ltr"}updateScrollStrategy(n){n!==this._scrollStrategy&&(this._disposeScrollStrategy(),this._scrollStrategy=n,this.hasAttached()&&(n.attach(this),n.enable()))}_updateElementDirection(){this._host.setAttribute("dir",this.getDirection())}_updateElementSize(){if(!this._pane)return;let n=this._pane.style;n.width=De(this._config.width),n.height=De(this._config.height),n.minWidth=De(this._config.minWidth),n.minHeight=De(this._config.minHeight),n.maxWidth=De(this._config.maxWidth),n.maxHeight=De(this._config.maxHeight)}_togglePointerEvents(n){this._pane.style.pointerEvents=n?"":"none"}_attachHost(){if(!this._host.parentElement){let n=this._config.usePopover?this._positionStrategy?.getPopoverInsertionPoint?.():null;Ig(n)?n.after(this._host):n?.type==="parent"?n.element.appendChild(this._host):this._previousHostParent?.appendChild(this._host)}if(this._config.usePopover)try{this._host.showPopover()}catch{}}_attachBackdrop(){let n="cdk-overlay-backdrop-showing";this._backdropRef?.dispose(),this._backdropRef=new Dg(this._document,this._renderer,this._ngZone,e=>{this._backdropClick.next(e)}),this._animationsDisabled&&this._backdropRef.element.classList.add("cdk-overlay-backdrop-noop-animation"),this._config.backdropClass&&this._toggleClasses(this._backdropRef.element,this._config.backdropClass,!0),this._config.usePopover?this._host.prepend(this._backdropRef.element):this._host.parentElement.insertBefore(this._backdropRef.element,this._host),!this._animationsDisabled&&typeof requestAnimationFrame<"u"?this._ngZone.runOutsideAngular(()=>{requestAnimationFrame(()=>this._backdropRef?.element.classList.add(n))}):this._backdropRef.element.classList.add(n)}_updateStackingOrder(){!this._config.usePopover&&this._host.nextSibling&&this._host.parentNode.appendChild(this._host)}detachBackdrop(){this._animationsDisabled?(this._backdropRef?.dispose(),this._backdropRef=null):this._backdropRef?.detach()}_toggleClasses(n,e,r){let o=ao(e||[]).filter(i=>!!i);o.length&&(r?n.classList.add(...o):n.classList.remove(...o))}_detachContentWhenEmpty(){let n=!1;try{this._detachContentAfterRenderRef=ht(()=>{n=!0,this._detachContent()},{injector:this._injector})}catch(e){if(n)throw e;this._detachContent()}globalThis.MutationObserver&&this._pane&&(this._detachContentMutationObserver||=new globalThis.MutationObserver(()=>{this._detachContent()}),this._detachContentMutationObserver.observe(this._pane,{childList:!0}))}_detachContent(){(!this._pane||!this._host||this._pane.children.length===0)&&(this._pane&&this._config.panelClass&&this._toggleClasses(this._pane,this._config.panelClass,!1),this._host&&this._host.parentElement&&(this._previousHostParent=this._host.parentElement,this._host.remove()),this._completeDetachContent())}_completeDetachContent(){this._detachContentAfterRenderRef?.destroy(),this._detachContentAfterRenderRef=void 0,this._detachContentMutationObserver?.disconnect()}_disposeScrollStrategy(){let n=this._scrollStrategy;n?.disable(),n?.detach?.()}},bC="cdk-overlay-connected-position-bounding-box",Lk=/([A-Za-z%]+)$/;function Sg(t,n){return new Ou(n,t.get(uo),t.get(L),t.get(he),t.get(Cg))}var Ou=class{_viewportRuler;_document;_platform;_overlayContainer;_overlayRef;_isInitialRender=!1;_lastBoundingBoxSize={width:0,height:0};_isPushed=!1;_canPush=!0;_growAfterOpen=!1;_hasFlexibleDimensions=!0;_positionLocked=!1;_originRect;_overlayRect;_viewportRect;_containerRect;_viewportMargin=0;_scrollables=[];_preferredPositions=[];_origin;_pane;_isDisposed=!1;_boundingBox=null;_lastPosition=null;_lastScrollVisibility=null;_positionChanges=new S;_resizeSubscription=G.EMPTY;_offsetX=0;_offsetY=0;_transformOriginSelector;_appliedPanelClasses=[];_previousPushAmount=null;_popoverLocation="global";positionChanges=this._positionChanges;get positions(){return this._preferredPositions}constructor(n,e,r,o,i){this._viewportRuler=e,this._document=r,this._platform=o,this._overlayContainer=i,this.setOrigin(n)}attach(n){this._overlayRef&&this._overlayRef,this._validatePositions(),n.hostElement.classList.add(bC),this._overlayRef=n,this._boundingBox=n.hostElement,this._pane=n.overlayElement,this._isDisposed=!1,this._isInitialRender=!0,this._lastPosition=null,this._resizeSubscription.unsubscribe(),this._resizeSubscription=this._viewportRuler.change().subscribe(()=>{this._isInitialRender=!0,this.apply()})}apply(){if(this._isDisposed||!this._platform.isBrowser)return;if(!this._isInitialRender&&this._positionLocked&&this._lastPosition){this.reapplyLastPosition();return}this._clearPanelClasses(),this._resetOverlayElementStyles(),this._resetBoundingBoxStyles(),this._viewportRect=this._getNarrowedViewportRect(),this._originRect=this._getOriginRect(),this._overlayRect=this._pane.getBoundingClientRect(),this._containerRect=this._getContainerRect();let n=this._originRect,e=this._overlayRect,r=this._viewportRect,o=this._containerRect,i=[],s;for(let a of this._preferredPositions){let c=this._getOriginPoint(n,o,a),l=this._getOverlayPoint(c,e,a),u=this._getOverlayFit(l,e,r,a);if(u.isCompletelyWithinViewport){this._isPushed=!1,this._applyPosition(a,c);return}if(this._canFitWithFlexibleDimensions(u,l,r)){i.push({position:a,origin:c,overlayRect:e,boundingBoxRect:this._calculateBoundingBoxRect(c,a)});continue}(!s||s.overlayFit.visibleAreac&&(c=u,a=l)}this._isPushed=!1,this._applyPosition(a.position,a.origin);return}if(this._canPush){this._isPushed=!0,this._applyPosition(s.position,s.originPoint);return}this._applyPosition(s.position,s.originPoint)}detach(){this._clearPanelClasses(),this._lastPosition=null,this._previousPushAmount=null,this._resizeSubscription.unsubscribe()}dispose(){this._isDisposed||(this._boundingBox&&fo(this._boundingBox.style,{top:"",left:"",right:"",bottom:"",height:"",width:"",alignItems:"",justifyContent:""}),this._pane&&this._resetOverlayElementStyles(),this._overlayRef&&this._overlayRef.hostElement.classList.remove(bC),this.detach(),this._positionChanges.complete(),this._overlayRef=this._boundingBox=null,this._isDisposed=!0)}reapplyLastPosition(){if(this._isDisposed||!this._platform.isBrowser)return;let n=this._lastPosition;n?(this._originRect=this._getOriginRect(),this._overlayRect=this._pane.getBoundingClientRect(),this._viewportRect=this._getNarrowedViewportRect(),this._containerRect=this._getContainerRect(),this._applyPosition(n,this._getOriginPoint(this._originRect,this._containerRect,n))):this.apply()}withScrollableContainers(n){return this._scrollables=n,this}withPositions(n){return this._preferredPositions=n,n.indexOf(this._lastPosition)===-1&&(this._lastPosition=null),this._validatePositions(),this}withViewportMargin(n){return this._viewportMargin=n,this}withFlexibleDimensions(n=!0){return this._hasFlexibleDimensions=n,this}withGrowAfterOpen(n=!0){return this._growAfterOpen=n,this}withPush(n=!0){return this._canPush=n,this}withLockedPosition(n=!0){return this._positionLocked=n,this}setOrigin(n){return this._origin=n,this}withDefaultOffsetX(n){return this._offsetX=n,this}withDefaultOffsetY(n){return this._offsetY=n,this}withTransformOriginOn(n){return this._transformOriginSelector=n,this}withPopoverLocation(n){return this._popoverLocation=n,this}getPopoverInsertionPoint(){return this._popoverLocation==="global"?null:this._popoverLocation!=="inline"?this._popoverLocation:this._origin instanceof z?this._origin.nativeElement:Ig(this._origin)?this._origin:null}_getOriginPoint(n,e,r){let o;if(r.originX=="center")o=n.left+n.width/2;else{let s=this._isRtl()?n.right:n.left,a=this._isRtl()?n.left:n.right;o=r.originX=="start"?s:a}e.left<0&&(o-=e.left);let i;return r.originY=="center"?i=n.top+n.height/2:i=r.originY=="top"?n.top:n.bottom,e.top<0&&(i-=e.top),{x:o,y:i}}_getOverlayPoint(n,e,r){let o;r.overlayX=="center"?o=-e.width/2:r.overlayX==="start"?o=this._isRtl()?-e.width:0:o=this._isRtl()?0:-e.width;let i;return r.overlayY=="center"?i=-e.height/2:i=r.overlayY=="top"?0:-e.height,{x:n.x+o,y:n.y+i}}_getOverlayFit(n,e,r,o){let i=DC(e),{x:s,y:a}=n,c=this._getOffset(o,"x"),l=this._getOffset(o,"y");c&&(s+=c),l&&(a+=l);let u=0-s,d=s+i.width-r.width,h=0-a,p=a+i.height-r.height,m=this._subtractOverflows(i.width,u,d),_=this._subtractOverflows(i.height,h,p),E=m*_;return{visibleArea:E,isCompletelyWithinViewport:i.width*i.height===E,fitsInViewportVertically:_===i.height,fitsInViewportHorizontally:m==i.width}}_canFitWithFlexibleDimensions(n,e,r){if(this._hasFlexibleDimensions){let o=r.bottom-e.y,i=r.right-e.x,s=_C(this._overlayRef.getConfig().minHeight),a=_C(this._overlayRef.getConfig().minWidth),c=n.fitsInViewportVertically||s!=null&&s<=o,l=n.fitsInViewportHorizontally||a!=null&&a<=i;return c&&l}return!1}_pushOverlayOnScreen(n,e,r){if(this._previousPushAmount&&this._positionLocked)return{x:n.x+this._previousPushAmount.x,y:n.y+this._previousPushAmount.y};let o=DC(e),i=this._viewportRect,s=Math.max(n.x+o.width-i.width,0),a=Math.max(n.y+o.height-i.height,0),c=Math.max(i.top-r.top-n.y,0),l=Math.max(i.left-r.left-n.x,0),u=0,d=0;return o.width<=i.width?u=l||-s:u=n.xm&&!this._isInitialRender&&!this._growAfterOpen&&(s=n.y-m/2)}let c=e.overlayX==="start"&&!o||e.overlayX==="end"&&o,l=e.overlayX==="end"&&!o||e.overlayX==="start"&&o,u,d,h;if(l)h=r.width-n.x+this._getViewportMarginStart()+this._getViewportMarginEnd(),u=n.x-this._getViewportMarginStart();else if(c)d=n.x,u=r.right-n.x-this._getViewportMarginEnd();else{let p=Math.min(r.right-n.x+r.left,n.x),m=this._lastBoundingBoxSize.width;u=p*2,d=n.x-p,u>m&&!this._isInitialRender&&!this._growAfterOpen&&(d=n.x-m/2)}return{top:s,left:d,bottom:a,right:h,width:u,height:i}}_setBoundingBoxStyles(n,e){let r=this._calculateBoundingBoxRect(n,e);!this._isInitialRender&&!this._growAfterOpen&&(r.height=Math.min(r.height,this._lastBoundingBoxSize.height),r.width=Math.min(r.width,this._lastBoundingBoxSize.width));let o={};if(this._hasExactPosition())o.top=o.left="0",o.bottom=o.right="auto",o.maxHeight=o.maxWidth="",o.width=o.height="100%";else{let i=this._overlayRef.getConfig().maxHeight,s=this._overlayRef.getConfig().maxWidth;o.width=De(r.width),o.height=De(r.height),o.top=De(r.top)||"auto",o.bottom=De(r.bottom)||"auto",o.left=De(r.left)||"auto",o.right=De(r.right)||"auto",e.overlayX==="center"?o.alignItems="center":o.alignItems=e.overlayX==="end"?"flex-end":"flex-start",e.overlayY==="center"?o.justifyContent="center":o.justifyContent=e.overlayY==="bottom"?"flex-end":"flex-start",i&&(o.maxHeight=De(i)),s&&(o.maxWidth=De(s))}this._lastBoundingBoxSize=r,fo(this._boundingBox.style,o)}_resetBoundingBoxStyles(){fo(this._boundingBox.style,{top:"0",left:"0",right:"0",bottom:"0",height:"",width:"",alignItems:"",justifyContent:""})}_resetOverlayElementStyles(){fo(this._pane.style,{top:"",left:"",bottom:"",right:"",position:"",transform:""})}_setOverlayElementStyles(n,e){let r={},o=this._hasExactPosition(),i=this._hasFlexibleDimensions,s=this._overlayRef.getConfig();if(o){let u=this._viewportRuler.getViewportScrollPosition();fo(r,this._getExactOverlayY(e,n,u)),fo(r,this._getExactOverlayX(e,n,u))}else r.position="static";let a="",c=this._getOffset(e,"x"),l=this._getOffset(e,"y");c&&(a+=`translateX(${c}px) `),l&&(a+=`translateY(${l}px)`),r.transform=a.trim(),s.maxHeight&&(o?r.maxHeight=De(s.maxHeight):i&&(r.maxHeight="")),s.maxWidth&&(o?r.maxWidth=De(s.maxWidth):i&&(r.maxWidth="")),fo(this._pane.style,r)}_getExactOverlayY(n,e,r){let o={top:"",bottom:""},i=this._getOverlayPoint(e,this._overlayRect,n);if(this._isPushed&&(i=this._pushOverlayOnScreen(i,this._overlayRect,r)),n.overlayY==="bottom"){let s=this._document.documentElement.clientHeight;o.bottom=`${s-(i.y+this._overlayRect.height)}px`}else o.top=De(i.y);return o}_getExactOverlayX(n,e,r){let o={left:"",right:""},i=this._getOverlayPoint(e,this._overlayRect,n);this._isPushed&&(i=this._pushOverlayOnScreen(i,this._overlayRect,r));let s;if(this._isRtl()?s=n.overlayX==="end"?"left":"right":s=n.overlayX==="end"?"right":"left",s==="right"){let a=this._document.documentElement.clientWidth;o.right=`${a-(i.x+this._overlayRect.width)}px`}else o.left=De(i.x);return o}_getScrollVisibility(){let n=this._getOriginRect(),e=this._pane.getBoundingClientRect(),r=this._scrollables.map(o=>o.getElementRef().nativeElement.getBoundingClientRect());return{isOriginClipped:vC(n,r),isOriginOutsideView:_g(n,r),isOverlayClipped:vC(e,r),isOverlayOutsideView:_g(e,r)}}_subtractOverflows(n,...e){return e.reduce((r,o)=>r-Math.max(o,0),n)}_getNarrowedViewportRect(){let n=this._document.documentElement.clientWidth,e=this._document.documentElement.clientHeight,r=this._viewportRuler.getViewportScrollPosition();return{top:r.top+this._getViewportMarginTop(),left:r.left+this._getViewportMarginStart(),right:r.left+n-this._getViewportMarginEnd(),bottom:r.top+e-this._getViewportMarginBottom(),width:n-this._getViewportMarginStart()-this._getViewportMarginEnd(),height:e-this._getViewportMarginTop()-this._getViewportMarginBottom()}}_isRtl(){return this._overlayRef.getDirection()==="rtl"}_hasExactPosition(){return!this._hasFlexibleDimensions||this._isPushed}_getOffset(n,e){return e==="x"?n.offsetX==null?this._offsetX:n.offsetX:n.offsetY==null?this._offsetY:n.offsetY}_validatePositions(){}_addPanelClasses(n){this._pane&&ao(n).forEach(e=>{e!==""&&this._appliedPanelClasses.indexOf(e)===-1&&(this._appliedPanelClasses.push(e),this._pane.classList.add(e))})}_clearPanelClasses(){this._pane&&(this._appliedPanelClasses.forEach(n=>{this._pane.classList.remove(n)}),this._appliedPanelClasses=[])}_getViewportMarginStart(){return typeof this._viewportMargin=="number"?this._viewportMargin:this._viewportMargin?.start??0}_getViewportMarginEnd(){return typeof this._viewportMargin=="number"?this._viewportMargin:this._viewportMargin?.end??0}_getViewportMarginTop(){return typeof this._viewportMargin=="number"?this._viewportMargin:this._viewportMargin?.top??0}_getViewportMarginBottom(){return typeof this._viewportMargin=="number"?this._viewportMargin:this._viewportMargin?.bottom??0}_getOriginRect(){let n=this._origin;if(n instanceof z)return n.nativeElement.getBoundingClientRect();if(n instanceof Element)return n.getBoundingClientRect();let e=n.width||0,r=n.height||0;return{top:n.y,bottom:n.y+r,left:n.x,right:n.x+e,height:r,width:e}}_getContainerRect(){let n=this._overlayRef.getConfig().usePopover&&this._popoverLocation!=="global",e=this._overlayContainer.getContainerElement();n&&(e.style.display="block");let r=e.getBoundingClientRect();return n&&(e.style.display=""),r}};function fo(t,n){for(let e in n)n.hasOwnProperty(e)&&(t[e]=n[e]);return t}function _C(t){if(typeof t!="number"&&t!=null){let[n,e]=t.split(Lk);return!e||e==="px"?parseFloat(n):null}return t||null}function DC(t){return{top:Math.floor(t.top),right:Math.floor(t.right),bottom:Math.floor(t.bottom),left:Math.floor(t.left),width:Math.floor(t.width),height:Math.floor(t.height)}}function jk(t,n){return t===n?!0:t.isOriginClipped===n.isOriginClipped&&t.isOriginOutsideView===n.isOriginOutsideView&&t.isOverlayClipped===n.isOverlayClipped&&t.isOverlayOutsideView===n.isOverlayOutsideView}var EC="cdk-global-overlay-wrapper";function AC(t){return new ku}var ku=class{_overlayRef;_cssPosition="static";_topOffset="";_bottomOffset="";_alignItems="";_xPosition="";_xOffset="";_width="";_height="";_isDisposed=!1;attach(n){let e=n.getConfig();this._overlayRef=n,this._width&&!e.width&&n.updateSize({width:this._width}),this._height&&!e.height&&n.updateSize({height:this._height}),n.hostElement.classList.add(EC),this._isDisposed=!1}top(n=""){return this._bottomOffset="",this._topOffset=n,this._alignItems="flex-start",this}left(n=""){return this._xOffset=n,this._xPosition="left",this}bottom(n=""){return this._topOffset="",this._bottomOffset=n,this._alignItems="flex-end",this}right(n=""){return this._xOffset=n,this._xPosition="right",this}start(n=""){return this._xOffset=n,this._xPosition="start",this}end(n=""){return this._xOffset=n,this._xPosition="end",this}width(n=""){return this._overlayRef?this._overlayRef.updateSize({width:n}):this._width=n,this}height(n=""){return this._overlayRef?this._overlayRef.updateSize({height:n}):this._height=n,this}centerHorizontally(n=""){return this.left(n),this._xPosition="center",this}centerVertically(n=""){return this.top(n),this._alignItems="center",this}apply(){if(!this._overlayRef||!this._overlayRef.hasAttached())return;let n=this._overlayRef.overlayElement.style,e=this._overlayRef.hostElement.style,r=this._overlayRef.getConfig(),{width:o,height:i,maxWidth:s,maxHeight:a}=r,c=(o==="100%"||o==="100vw")&&(!s||s==="100%"||s==="100vw"),l=(i==="100%"||i==="100vh")&&(!a||a==="100%"||a==="100vh"),u=this._xPosition,d=this._xOffset,h=this._overlayRef.getConfig().direction==="rtl",p="",m="",_="";c?_="flex-start":u==="center"?(_="center",h?m=d:p=d):h?u==="left"||u==="end"?(_="flex-end",p=d):(u==="right"||u==="start")&&(_="flex-start",m=d):u==="left"||u==="start"?(_="flex-start",p=d):(u==="right"||u==="end")&&(_="flex-end",m=d),n.position=this._cssPosition,n.marginLeft=c?"0":p,n.marginTop=l?"0":this._topOffset,n.marginBottom=this._bottomOffset,n.marginRight=c?"0":m,e.justifyContent=_,e.alignItems=l?"flex-start":this._alignItems}dispose(){if(this._isDisposed||!this._overlayRef)return;let n=this._overlayRef.overlayElement.style,e=this._overlayRef.hostElement,r=e.style;e.classList.remove(EC),r.justifyContent=r.alignItems=n.marginTop=n.marginBottom=n.marginLeft=n.marginRight=n.position="",this._overlayRef=null,this._isDisposed=!0}},RC=(()=>{class t{_injector=f($);constructor(){}global(){return AC()}flexibleConnectedTo(e){return Sg(this._injector,e)}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})(),Mg=new y("OVERLAY_DEFAULT_CONFIG");function Tg(t,n){t.get(xt).load(xC);let e=t.get(Cg),r=t.get(L),o=t.get(ta),i=t.get(Be),s=t.get(fi),a=t.get(Oe,null,{optional:!0})||t.get(je).createRenderer(null,null),c=new ca(n),l=t.get(Mg,null,{optional:!0})?.usePopover??!0;c.direction=c.direction||s.value,"showPopover"in r.body?c.usePopover=n?.usePopover??l:c.usePopover=!1;let u=r.createElement("div"),d=r.createElement("div");u.id=o.getId("cdk-overlay-"),u.classList.add("cdk-overlay-pane"),d.appendChild(u),c.usePopover&&(d.setAttribute("popover","manual"),d.classList.add("cdk-overlay-popover"));let h=c.usePopover?c.positionStrategy?.getPopoverInsertionPoint?.():null;return Ig(h)?h.after(d):h?.type==="parent"?h.element.appendChild(d):e.getContainerElement().appendChild(d),new Nu(new Mu(u,i,t),d,u,c,t.get(j),t.get(MC),r,t.get(bn),t.get(TC),n?.disableAnimations??t.get(Is,null,{optional:!0})==="NoopAnimations",t.get(re),a)}var NC=(()=>{class t{scrollStrategies=f(IC);_positionBuilder=f(RC);_injector=f($);constructor(){}create(e){return Tg(this._injector,e)}position(){return this._positionBuilder}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})(),Vk=[{originX:"start",originY:"bottom",overlayX:"start",overlayY:"top"},{originX:"start",originY:"top",overlayX:"start",overlayY:"bottom"},{originX:"end",originY:"top",overlayX:"end",overlayY:"bottom"},{originX:"end",originY:"bottom",overlayX:"end",overlayY:"top"}],Bk=new y("cdk-connected-overlay-scroll-strategy",{providedIn:"root",factory:()=>{let t=f($);return()=>wg(t)}}),Eg=(()=>{class t{elementRef=f(z);constructor(){}static \u0275fac=function(r){return new(r||t)};static \u0275dir=M({type:t,selectors:[["","cdk-overlay-origin",""],["","overlay-origin",""],["","cdkOverlayOrigin",""]],exportAs:["cdkOverlayOrigin"]})}return t})(),OC=new y("cdk-connected-overlay-default-config"),Uk=(()=>{class t{_dir=f(fi,{optional:!0});_injector=f($);_overlayRef;_templatePortal;_backdropSubscription=G.EMPTY;_attachSubscription=G.EMPTY;_detachSubscription=G.EMPTY;_positionSubscription=G.EMPTY;_offsetX;_offsetY;_position;_scrollStrategyFactory=f(Bk);_ngZone=f(j);origin;positions;positionStrategy;get offsetX(){return this._offsetX}set offsetX(e){this._offsetX=e,this._position&&this._updatePositionStrategy(this._position)}get offsetY(){return this._offsetY}set offsetY(e){this._offsetY=e,this._position&&this._updatePositionStrategy(this._position)}width;height;minWidth;minHeight;backdropClass;panelClass;viewportMargin=0;scrollStrategy;open=!1;disableClose=!1;transformOriginSelector;hasBackdrop=!1;lockPosition=!1;flexibleDimensions=!1;growAfterOpen=!1;push=!1;disposeOnNavigation=!1;usePopover;matchWidth=!1;set _config(e){typeof e!="string"&&this._assignConfig(e)}backdropClick=new U;positionChange=new U;attach=new U;detach=new U;overlayKeydown=new U;overlayOutsideClick=new U;constructor(){let e=f(dt),r=f(qe),o=f(OC,{optional:!0}),i=f(Mg,{optional:!0});this.usePopover=i?.usePopover===!1?null:"global",this._templatePortal=new hi(e,r),this.scrollStrategy=this._scrollStrategyFactory(),o&&this._assignConfig(o)}get overlayRef(){return this._overlayRef}get dir(){return this._dir?this._dir.value:"ltr"}ngOnDestroy(){this._attachSubscription.unsubscribe(),this._detachSubscription.unsubscribe(),this._backdropSubscription.unsubscribe(),this._positionSubscription.unsubscribe(),this._overlayRef?.dispose()}ngOnChanges(e){this._position&&(this._updatePositionStrategy(this._position),this._overlayRef?.updateSize({width:this._getWidth(),minWidth:this.minWidth,height:this.height,minHeight:this.minHeight}),e.origin&&this.open&&this._position.apply()),e.open&&(this.open?this.attachOverlay():this.detachOverlay())}_createOverlay(){(!this.positions||!this.positions.length)&&(this.positions=Vk);let e=this._overlayRef=Tg(this._injector,this._buildConfig());this._attachSubscription=e.attachments().subscribe(()=>this.attach.emit()),this._detachSubscription=e.detachments().subscribe(()=>this.detach.emit()),e.keydownEvents().subscribe(r=>{this.overlayKeydown.next(r),r.keyCode===27&&!this.disableClose&&!bu(r)&&(r.preventDefault(),this.detachOverlay())}),this._overlayRef.outsidePointerEvents().subscribe(r=>{let o=this._getOriginElement(),i=Je(r);(!o||o!==i&&!o.contains(i))&&this.overlayOutsideClick.next(r)})}_buildConfig(){let e=this._position=this.positionStrategy||this._createPositionStrategy(),r=new ca({direction:this._dir||"ltr",positionStrategy:e,scrollStrategy:this.scrollStrategy,hasBackdrop:this.hasBackdrop,disposeOnNavigation:this.disposeOnNavigation,usePopover:!!this.usePopover});return(this.height||this.height===0)&&(r.height=this.height),(this.minWidth||this.minWidth===0)&&(r.minWidth=this.minWidth),(this.minHeight||this.minHeight===0)&&(r.minHeight=this.minHeight),this.backdropClass&&(r.backdropClass=this.backdropClass),this.panelClass&&(r.panelClass=this.panelClass),r}_updatePositionStrategy(e){let r=this.positions.map(o=>({originX:o.originX,originY:o.originY,overlayX:o.overlayX,overlayY:o.overlayY,offsetX:o.offsetX||this.offsetX,offsetY:o.offsetY||this.offsetY,panelClass:o.panelClass||void 0}));return e.setOrigin(this._getOrigin()).withPositions(r).withFlexibleDimensions(this.flexibleDimensions).withPush(this.push).withGrowAfterOpen(this.growAfterOpen).withViewportMargin(this.viewportMargin).withLockedPosition(this.lockPosition).withTransformOriginOn(this.transformOriginSelector).withPopoverLocation(this.usePopover===null?"global":this.usePopover)}_createPositionStrategy(){let e=Sg(this._injector,this._getOrigin());return this._updatePositionStrategy(e),e}_getOrigin(){return this.origin instanceof Eg?this.origin.elementRef:this.origin}_getOriginElement(){return this.origin instanceof Eg?this.origin.elementRef.nativeElement:this.origin instanceof z?this.origin.nativeElement:typeof Element<"u"&&this.origin instanceof Element?this.origin:null}_getWidth(){return this.width?this.width:this.matchWidth?this._getOriginElement()?.getBoundingClientRect?.().width:void 0}attachOverlay(){this._overlayRef||this._createOverlay();let e=this._overlayRef;e.getConfig().hasBackdrop=this.hasBackdrop,e.updateSize({width:this._getWidth()}),e.hasAttached()||e.attach(this._templatePortal),this.hasBackdrop?this._backdropSubscription=e.backdropClick().subscribe(r=>this.backdropClick.emit(r)):this._backdropSubscription.unsubscribe(),this._positionSubscription.unsubscribe(),this.positionChange.observers.length>0&&(this._positionSubscription=this._position.positionChanges.pipe(Yd(()=>this.positionChange.observers.length>0)).subscribe(r=>{this._ngZone.run(()=>this.positionChange.emit(r)),this.positionChange.observers.length===0&&this._positionSubscription.unsubscribe()})),this.open=!0}detachOverlay(){this._overlayRef?.detach(),this._backdropSubscription.unsubscribe(),this._positionSubscription.unsubscribe(),this.open=!1}_assignConfig(e){this.origin=e.origin??this.origin,this.positions=e.positions??this.positions,this.positionStrategy=e.positionStrategy??this.positionStrategy,this.offsetX=e.offsetX??this.offsetX,this.offsetY=e.offsetY??this.offsetY,this.width=e.width??this.width,this.height=e.height??this.height,this.minWidth=e.minWidth??this.minWidth,this.minHeight=e.minHeight??this.minHeight,this.backdropClass=e.backdropClass??this.backdropClass,this.panelClass=e.panelClass??this.panelClass,this.viewportMargin=e.viewportMargin??this.viewportMargin,this.scrollStrategy=e.scrollStrategy??this.scrollStrategy,this.disableClose=e.disableClose??this.disableClose,this.transformOriginSelector=e.transformOriginSelector??this.transformOriginSelector,this.hasBackdrop=e.hasBackdrop??this.hasBackdrop,this.lockPosition=e.lockPosition??this.lockPosition,this.flexibleDimensions=e.flexibleDimensions??this.flexibleDimensions,this.growAfterOpen=e.growAfterOpen??this.growAfterOpen,this.push=e.push??this.push,this.disposeOnNavigation=e.disposeOnNavigation??this.disposeOnNavigation,this.usePopover=e.usePopover??this.usePopover,this.matchWidth=e.matchWidth??this.matchWidth}static \u0275fac=function(r){return new(r||t)};static \u0275dir=M({type:t,selectors:[["","cdk-connected-overlay",""],["","connected-overlay",""],["","cdkConnectedOverlay",""]],inputs:{origin:[0,"cdkConnectedOverlayOrigin","origin"],positions:[0,"cdkConnectedOverlayPositions","positions"],positionStrategy:[0,"cdkConnectedOverlayPositionStrategy","positionStrategy"],offsetX:[0,"cdkConnectedOverlayOffsetX","offsetX"],offsetY:[0,"cdkConnectedOverlayOffsetY","offsetY"],width:[0,"cdkConnectedOverlayWidth","width"],height:[0,"cdkConnectedOverlayHeight","height"],minWidth:[0,"cdkConnectedOverlayMinWidth","minWidth"],minHeight:[0,"cdkConnectedOverlayMinHeight","minHeight"],backdropClass:[0,"cdkConnectedOverlayBackdropClass","backdropClass"],panelClass:[0,"cdkConnectedOverlayPanelClass","panelClass"],viewportMargin:[0,"cdkConnectedOverlayViewportMargin","viewportMargin"],scrollStrategy:[0,"cdkConnectedOverlayScrollStrategy","scrollStrategy"],open:[0,"cdkConnectedOverlayOpen","open"],disableClose:[0,"cdkConnectedOverlayDisableClose","disableClose"],transformOriginSelector:[0,"cdkConnectedOverlayTransformOriginOn","transformOriginSelector"],hasBackdrop:[2,"cdkConnectedOverlayHasBackdrop","hasBackdrop",ue],lockPosition:[2,"cdkConnectedOverlayLockPosition","lockPosition",ue],flexibleDimensions:[2,"cdkConnectedOverlayFlexibleDimensions","flexibleDimensions",ue],growAfterOpen:[2,"cdkConnectedOverlayGrowAfterOpen","growAfterOpen",ue],push:[2,"cdkConnectedOverlayPush","push",ue],disposeOnNavigation:[2,"cdkConnectedOverlayDisposeOnNavigation","disposeOnNavigation",ue],usePopover:[0,"cdkConnectedOverlayUsePopover","usePopover"],matchWidth:[2,"cdkConnectedOverlayMatchWidth","matchWidth",ue],_config:[0,"cdkConnectedOverlay","_config"]},outputs:{backdropClick:"backdropClick",positionChange:"positionChange",attach:"attach",detach:"detach",overlayKeydown:"overlayKeydown",overlayOutsideClick:"overlayOutsideClick"},exportAs:["cdkConnectedOverlay"],features:[Re]})}return t})(),Hk=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275mod=X({type:t});static \u0275inj=Z({providers:[NC],imports:[zn,mC,vg,vg]})}return t})();var $C=(()=>{class t{_renderer;_elementRef;onChange=e=>{};onTouched=()=>{};constructor(e,r){this._renderer=e,this._elementRef=r}setProperty(e,r){this._renderer.setProperty(this._elementRef.nativeElement,e,r)}registerOnTouched(e){this.onTouched=e}registerOnChange(e){this.onChange=e}setDisabledState(e){this.setProperty("disabled",e)}static \u0275fac=function(r){return new(r||t)(D(Oe),D(z))};static \u0275dir=M({type:t})}return t})(),Gu=(()=>{class t extends $C{static \u0275fac=(()=>{let e;return function(o){return(e||(e=Ne(t)))(o||t)}})();static \u0275dir=M({type:t,features:[J]})}return t})(),po=new y("");var $k={provide:po,useExisting:be(()=>zC),multi:!0};function zk(){let t=mt()?mt().getUserAgent():"";return/android (\d+)/.test(t.toLowerCase())}var Gk=new y(""),zC=(()=>{class t extends $C{_compositionMode;_composing=!1;constructor(e,r,o){super(e,r),this._compositionMode=o,this._compositionMode==null&&(this._compositionMode=!zk())}writeValue(e){let r=e??"";this.setProperty("value",r)}_handleInput(e){(!this._compositionMode||this._compositionMode&&!this._composing)&&this.onChange(e)}_compositionStart(){this._composing=!0}_compositionEnd(e){this._composing=!1,this._compositionMode&&this.onChange(e)}static \u0275fac=function(r){return new(r||t)(D(Oe),D(z),D(Gk,8))};static \u0275dir=M({type:t,selectors:[["input","formControlName","",3,"type","checkbox"],["textarea","formControlName",""],["input","formControl","",3,"type","checkbox"],["textarea","formControl",""],["input","ngModel","",3,"type","checkbox"],["textarea","ngModel",""],["","ngDefaultControl",""]],hostBindings:function(r,o){r&1&&Zt("input",function(s){return o._handleInput(s.target.value)})("blur",function(){return o.onTouched()})("compositionstart",function(){return o._compositionStart()})("compositionend",function(s){return o._compositionEnd(s.target.value)})},standalone:!1,features:[we([$k]),J]})}return t})();function Ng(t){return t==null||Og(t)===0}function Og(t){return t==null?null:Array.isArray(t)||typeof t=="string"?t.length:t instanceof Set?t.size:null}var En=new y(""),mo=new y(""),Wk=/^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,kC=class{static min(n){return GC(n)}static max(n){return WC(n)}static required(n){return qC(n)}static requiredTrue(n){return qk(n)}static email(n){return Yk(n)}static minLength(n){return Zk(n)}static maxLength(n){return Kk(n)}static pattern(n){return Qk(n)}static nullValidator(n){return Pu()}static compose(n){return JC(n)}static composeAsync(n){return eI(n)}};function GC(t){return n=>{if(n.value==null||t==null)return null;let e=parseFloat(n.value);return!isNaN(e)&&e{if(n.value==null||t==null)return null;let e=parseFloat(n.value);return!isNaN(e)&&e>t?{max:{max:t,actual:n.value}}:null}}function qC(t){return Ng(t.value)?{required:!0}:null}function qk(t){return t.value===!0?null:{required:!0}}function Yk(t){return Ng(t.value)||Wk.test(t.value)?null:{email:!0}}function Zk(t){return n=>{let e=n.value?.length??Og(n.value);return e===null||e===0?null:e{let e=n.value?.length??Og(n.value);return e!==null&&e>t?{maxlength:{requiredLength:t,actualLength:e}}:null}}function Qk(t){if(!t)return Pu;let n,e;return typeof t=="string"?(e="",t.charAt(0)!=="^"&&(e+="^"),e+=t,t.charAt(t.length-1)!=="$"&&(e+="$"),n=new RegExp(e)):(e=t.toString(),n=t),r=>{if(Ng(r.value))return null;let o=r.value;return n.test(o)?null:{pattern:{requiredPattern:e,actualValue:o}}}}function Pu(t){return null}function YC(t){return t!=null}function ZC(t){return jn(t)?se(t):t}function KC(t){let n={};return t.forEach(e=>{n=e!=null?g(g({},n),e):n}),Object.keys(n).length===0?null:n}function QC(t,n){return n.map(e=>e(t))}function Xk(t){return!t.validate}function XC(t){return t.map(n=>Xk(n)?n:e=>n.validate(e))}function JC(t){if(!t)return null;let n=t.filter(YC);return n.length==0?null:function(e){return KC(QC(e,n))}}function kg(t){return t!=null?JC(XC(t)):null}function eI(t){if(!t)return null;let n=t.filter(YC);return n.length==0?null:function(e){let r=QC(e,n).map(ZC);return Ud(r).pipe(H(KC))}}function Fg(t){return t!=null?eI(XC(t)):null}function FC(t,n){return t===null?[n]:Array.isArray(t)?[...t,n]:[t,n]}function tI(t){return t._rawValidators}function nI(t){return t._rawAsyncValidators}function xg(t){return t?Array.isArray(t)?t:[t]:[]}function Lu(t,n){return Array.isArray(t)?t.includes(n):t===n}function PC(t,n){let e=xg(n);return xg(t).forEach(o=>{Lu(e,o)||e.push(o)}),e}function LC(t,n){return xg(n).filter(e=>!Lu(t,e))}var ju=class{get value(){return this.control?this.control.value:null}get valid(){return this.control?this.control.valid:null}get invalid(){return this.control?this.control.invalid:null}get pending(){return this.control?this.control.pending:null}get disabled(){return this.control?this.control.disabled:null}get enabled(){return this.control?this.control.enabled:null}get errors(){return this.control?this.control.errors:null}get pristine(){return this.control?this.control.pristine:null}get dirty(){return this.control?this.control.dirty:null}get touched(){return this.control?this.control.touched:null}get status(){return this.control?this.control.status:null}get untouched(){return this.control?this.control.untouched:null}get statusChanges(){return this.control?this.control.statusChanges:null}get valueChanges(){return this.control?this.control.valueChanges:null}get path(){return null}_composedValidatorFn;_composedAsyncValidatorFn;_rawValidators=[];_rawAsyncValidators=[];_setValidators(n){this._rawValidators=n||[],this._composedValidatorFn=kg(this._rawValidators)}_setAsyncValidators(n){this._rawAsyncValidators=n||[],this._composedAsyncValidatorFn=Fg(this._rawAsyncValidators)}get validator(){return this._composedValidatorFn||null}get asyncValidator(){return this._composedAsyncValidatorFn||null}_onDestroyCallbacks=[];_registerOnDestroy(n){this._onDestroyCallbacks.push(n)}_invokeOnDestroyCallbacks(){this._onDestroyCallbacks.forEach(n=>n()),this._onDestroyCallbacks=[]}reset(n=void 0){this.control?.reset(n)}hasError(n,e){return this.control?this.control.hasError(n,e):!1}getError(n,e){return this.control?this.control.getError(n,e):null}},et=class extends ju{name;get formDirective(){return null}get path(){return null}},Gn=class extends ju{_parent=null;name=null;valueAccessor=null},Vu=class{_cd;constructor(n){this._cd=n}get isTouched(){return this._cd?.control?._touched?.(),!!this._cd?.control?.touched}get isUntouched(){return!!this._cd?.control?.untouched}get isPristine(){return this._cd?.control?._pristine?.(),!!this._cd?.control?.pristine}get isDirty(){return!!this._cd?.control?.dirty}get isValid(){return this._cd?.control?._status?.(),!!this._cd?.control?.valid}get isInvalid(){return!!this._cd?.control?.invalid}get isPending(){return!!this._cd?.control?.pending}get isSubmitted(){return this._cd?._submitted?.(),!!this._cd?.submitted}};var BQ=(()=>{class t extends Vu{constructor(e){super(e)}static \u0275fac=function(r){return new(r||t)(D(Gn,2))};static \u0275dir=M({type:t,selectors:[["","formControlName",""],["","ngModel",""],["","formControl",""]],hostVars:14,hostBindings:function(r,o){r&2&&Xe("ng-untouched",o.isUntouched)("ng-touched",o.isTouched)("ng-pristine",o.isPristine)("ng-dirty",o.isDirty)("ng-valid",o.isValid)("ng-invalid",o.isInvalid)("ng-pending",o.isPending)},standalone:!1,features:[J]})}return t})(),UQ=(()=>{class t extends Vu{constructor(e){super(e)}static \u0275fac=function(r){return new(r||t)(D(et,10))};static \u0275dir=M({type:t,selectors:[["","formGroupName",""],["","formArrayName",""],["","ngModelGroup",""],["","formGroup",""],["","formArray",""],["form",3,"ngNoForm",""],["","ngForm",""]],hostVars:16,hostBindings:function(r,o){r&2&&Xe("ng-untouched",o.isUntouched)("ng-touched",o.isTouched)("ng-pristine",o.isPristine)("ng-dirty",o.isDirty)("ng-valid",o.isValid)("ng-invalid",o.isInvalid)("ng-pending",o.isPending)("ng-submitted",o.isSubmitted)},standalone:!1,features:[J]})}return t})();var la="VALID",Fu="INVALID",pi="PENDING",ua="DISABLED",gr=class{},Bu=class extends gr{value;source;constructor(n,e){super(),this.value=n,this.source=e}},fa=class extends gr{pristine;source;constructor(n,e){super(),this.pristine=n,this.source=e}},ha=class extends gr{touched;source;constructor(n,e){super(),this.touched=n,this.source=e}},mi=class extends gr{status;source;constructor(n,e){super(),this.status=n,this.source=e}},Uu=class extends gr{source;constructor(n){super(),this.source=n}},pa=class extends gr{source;constructor(n){super(),this.source=n}};function Pg(t){return(Wu(t)?t.validators:t)||null}function Jk(t){return Array.isArray(t)?kg(t):t||null}function Lg(t,n){return(Wu(n)?n.asyncValidators:t)||null}function eF(t){return Array.isArray(t)?Fg(t):t||null}function Wu(t){return t!=null&&!Array.isArray(t)&&typeof t=="object"}function rI(t,n,e){let r=t.controls;if(!(n?Object.keys(r):r).length)throw new b(1e3,"");if(!r[e])throw new b(1001,"")}function oI(t,n,e){t._forEachChild((r,o)=>{if(e[o]===void 0)throw new b(-1002,"")})}var vi=class{_pendingDirty=!1;_hasOwnPendingAsyncValidator=null;_pendingTouched=!1;_onCollectionChange=()=>{};_updateOn;_parent=null;_asyncValidationSubscription;_composedValidatorFn;_composedAsyncValidatorFn;_rawValidators;_rawAsyncValidators;value;constructor(n,e){this._assignValidators(n),this._assignAsyncValidators(e)}get validator(){return this._composedValidatorFn}set validator(n){this._rawValidators=this._composedValidatorFn=n}get asyncValidator(){return this._composedAsyncValidatorFn}set asyncValidator(n){this._rawAsyncValidators=this._composedAsyncValidatorFn=n}get parent(){return this._parent}get status(){return q(this.statusReactive)}set status(n){q(()=>this.statusReactive.set(n))}_status=Kt(()=>this.statusReactive());statusReactive=W(void 0);get valid(){return this.status===la}get invalid(){return this.status===Fu}get pending(){return this.status===pi}get disabled(){return this.status===ua}get enabled(){return this.status!==ua}errors;get pristine(){return q(this.pristineReactive)}set pristine(n){q(()=>this.pristineReactive.set(n))}_pristine=Kt(()=>this.pristineReactive());pristineReactive=W(!0);get dirty(){return!this.pristine}get touched(){return q(this.touchedReactive)}set touched(n){q(()=>this.touchedReactive.set(n))}_touched=Kt(()=>this.touchedReactive());touchedReactive=W(!1);get untouched(){return!this.touched}_events=new S;events=this._events.asObservable();valueChanges;statusChanges;get updateOn(){return this._updateOn?this._updateOn:this.parent?this.parent.updateOn:"change"}setValidators(n){this._assignValidators(n)}setAsyncValidators(n){this._assignAsyncValidators(n)}addValidators(n){this.setValidators(PC(n,this._rawValidators))}addAsyncValidators(n){this.setAsyncValidators(PC(n,this._rawAsyncValidators))}removeValidators(n){this.setValidators(LC(n,this._rawValidators))}removeAsyncValidators(n){this.setAsyncValidators(LC(n,this._rawAsyncValidators))}hasValidator(n){return Lu(this._rawValidators,n)}hasAsyncValidator(n){return Lu(this._rawAsyncValidators,n)}clearValidators(){this.validator=null}clearAsyncValidators(){this.asyncValidator=null}markAsTouched(n={}){let e=this.touched===!1;this.touched=!0;let r=n.sourceControl??this;n.onlySelf||this._parent?.markAsTouched(F(g({},n),{sourceControl:r})),e&&n.emitEvent!==!1&&this._events.next(new ha(!0,r))}markAllAsDirty(n={}){this.markAsDirty({onlySelf:!0,emitEvent:n.emitEvent,sourceControl:this}),this._forEachChild(e=>e.markAllAsDirty(n))}markAllAsTouched(n={}){this.markAsTouched({onlySelf:!0,emitEvent:n.emitEvent,sourceControl:this}),this._forEachChild(e=>e.markAllAsTouched(n))}markAsUntouched(n={}){let e=this.touched===!0;this.touched=!1,this._pendingTouched=!1;let r=n.sourceControl??this;this._forEachChild(o=>{o.markAsUntouched({onlySelf:!0,emitEvent:n.emitEvent,sourceControl:r})}),n.onlySelf||this._parent?._updateTouched(n,r),e&&n.emitEvent!==!1&&this._events.next(new ha(!1,r))}markAsDirty(n={}){let e=this.pristine===!0;this.pristine=!1;let r=n.sourceControl??this;n.onlySelf||this._parent?.markAsDirty(F(g({},n),{sourceControl:r})),e&&n.emitEvent!==!1&&this._events.next(new fa(!1,r))}markAsPristine(n={}){let e=this.pristine===!1;this.pristine=!0,this._pendingDirty=!1;let r=n.sourceControl??this;this._forEachChild(o=>{o.markAsPristine({onlySelf:!0,emitEvent:n.emitEvent})}),n.onlySelf||this._parent?._updatePristine(n,r),e&&n.emitEvent!==!1&&this._events.next(new fa(!0,r))}markAsPending(n={}){this.status=pi;let e=n.sourceControl??this;n.emitEvent!==!1&&(this._events.next(new mi(this.status,e)),this.statusChanges.emit(this.status)),n.onlySelf||this._parent?.markAsPending(F(g({},n),{sourceControl:e}))}disable(n={}){let e=this._parentMarkedDirty(n.onlySelf);this.status=ua,this.errors=null,this._forEachChild(o=>{o.disable(F(g({},n),{onlySelf:!0}))}),this._updateValue();let r=n.sourceControl??this;n.emitEvent!==!1&&(this._events.next(new Bu(this.value,r)),this._events.next(new mi(this.status,r)),this.valueChanges.emit(this.value),this.statusChanges.emit(this.status)),this._updateAncestors(F(g({},n),{skipPristineCheck:e}),this),this._onDisabledChange.forEach(o=>o(!0))}enable(n={}){let e=this._parentMarkedDirty(n.onlySelf);this.status=la,this._forEachChild(r=>{r.enable(F(g({},n),{onlySelf:!0}))}),this.updateValueAndValidity({onlySelf:!0,emitEvent:n.emitEvent}),this._updateAncestors(F(g({},n),{skipPristineCheck:e}),this),this._onDisabledChange.forEach(r=>r(!1))}_updateAncestors(n,e){n.onlySelf||(this._parent?.updateValueAndValidity(n),n.skipPristineCheck||this._parent?._updatePristine({},e),this._parent?._updateTouched({},e))}setParent(n){this._parent=n}getRawValue(){return this.value}updateValueAndValidity(n={}){if(this._setInitialStatus(),this._updateValue(),this.enabled){let r=this._cancelExistingSubscription();this.errors=this._runValidator(),this.status=this._calculateStatus(),(this.status===la||this.status===pi)&&this._runAsyncValidator(r,n.emitEvent)}let e=n.sourceControl??this;n.emitEvent!==!1&&(this._events.next(new Bu(this.value,e)),this._events.next(new mi(this.status,e)),this.valueChanges.emit(this.value),this.statusChanges.emit(this.status)),n.onlySelf||this._parent?.updateValueAndValidity(F(g({},n),{sourceControl:e}))}_updateTreeValidity(n={emitEvent:!0}){this._forEachChild(e=>e._updateTreeValidity(n)),this.updateValueAndValidity({onlySelf:!0,emitEvent:n.emitEvent})}_setInitialStatus(){this.status=this._allControlsDisabled()?ua:la}_runValidator(){return this.validator?this.validator(this):null}_runAsyncValidator(n,e){if(this.asyncValidator){this.status=pi,this._hasOwnPendingAsyncValidator={emitEvent:e!==!1,shouldHaveEmitted:n!==!1};let r=ZC(this.asyncValidator(this));this._asyncValidationSubscription=r.subscribe(o=>{this._hasOwnPendingAsyncValidator=null,this.setErrors(o,{emitEvent:e,shouldHaveEmitted:n})})}}_cancelExistingSubscription(){if(this._asyncValidationSubscription){this._asyncValidationSubscription.unsubscribe();let n=(this._hasOwnPendingAsyncValidator?.emitEvent||this._hasOwnPendingAsyncValidator?.shouldHaveEmitted)??!1;return this._hasOwnPendingAsyncValidator=null,n}return!1}setErrors(n,e={}){this.errors=n,this._updateControlsErrors(e.emitEvent!==!1,this,e.shouldHaveEmitted)}get(n){let e=n;return e==null||(Array.isArray(e)||(e=e.split(".")),e.length===0)?null:e.reduce((r,o)=>r&&r._find(o),this)}getError(n,e){let r=e?this.get(e):this;return r?.errors?r.errors[n]:null}hasError(n,e){return!!this.getError(n,e)}get root(){let n=this;for(;n._parent;)n=n._parent;return n}_updateControlsErrors(n,e,r){this.status=this._calculateStatus(),n&&this.statusChanges.emit(this.status),(n||r)&&this._events.next(new mi(this.status,e)),this._parent&&this._parent._updateControlsErrors(n,e,r)}_initObservables(){this.valueChanges=new U,this.statusChanges=new U}_calculateStatus(){return this._allControlsDisabled()?ua:this.errors?Fu:this._hasOwnPendingAsyncValidator||this._anyControlsHaveStatus(pi)?pi:this._anyControlsHaveStatus(Fu)?Fu:la}_anyControlsHaveStatus(n){return this._anyControls(e=>e.status===n)}_anyControlsDirty(){return this._anyControls(n=>n.dirty)}_anyControlsTouched(){return this._anyControls(n=>n.touched)}_updatePristine(n,e){let r=!this._anyControlsDirty(),o=this.pristine!==r;this.pristine=r,n.onlySelf||this._parent?._updatePristine(n,e),o&&this._events.next(new fa(this.pristine,e))}_updateTouched(n={},e){this.touched=this._anyControlsTouched(),this._events.next(new ha(this.touched,e)),n.onlySelf||this._parent?._updateTouched(n,e)}_onDisabledChange=[];_registerOnCollectionChange(n){this._onCollectionChange=n}_setUpdateStrategy(n){Wu(n)&&n.updateOn!=null&&(this._updateOn=n.updateOn)}_parentMarkedDirty(n){return!n&&!!this._parent?.dirty&&!this._parent._anyControlsDirty()}_find(n){return null}_assignValidators(n){this._rawValidators=Array.isArray(n)?n.slice():n,this._composedValidatorFn=Jk(this._rawValidators)}_assignAsyncValidators(n){this._rawAsyncValidators=Array.isArray(n)?n.slice():n,this._composedAsyncValidatorFn=eF(this._rawAsyncValidators)}},ho=class extends vi{constructor(n,e,r){super(Pg(e),Lg(r,e)),this.controls=n,this._initObservables(),this._setUpdateStrategy(e),this._setUpControls(),this.updateValueAndValidity({onlySelf:!0,emitEvent:!!this.asyncValidator})}controls;registerControl(n,e){return this.controls[n]?this.controls[n]:(this.controls[n]=e,e.setParent(this),e._registerOnCollectionChange(this._onCollectionChange),e)}addControl(n,e,r={}){this.registerControl(n,e),this.updateValueAndValidity({emitEvent:r.emitEvent}),this._onCollectionChange()}removeControl(n,e={}){this.controls[n]&&this.controls[n]._registerOnCollectionChange(()=>{}),delete this.controls[n],this.updateValueAndValidity({emitEvent:e.emitEvent}),this._onCollectionChange()}setControl(n,e,r={}){this.controls[n]&&this.controls[n]._registerOnCollectionChange(()=>{}),delete this.controls[n],e&&this.registerControl(n,e),this.updateValueAndValidity({emitEvent:r.emitEvent}),this._onCollectionChange()}contains(n){return this.controls.hasOwnProperty(n)&&this.controls[n].enabled}setValue(n,e={}){oI(this,!0,n),Object.keys(n).forEach(r=>{rI(this,!0,r),this.controls[r].setValue(n[r],{onlySelf:!0,emitEvent:e.emitEvent})}),this.updateValueAndValidity(e)}patchValue(n,e={}){n!=null&&(Object.keys(n).forEach(r=>{let o=this.controls[r];o&&o.patchValue(n[r],{onlySelf:!0,emitEvent:e.emitEvent})}),this.updateValueAndValidity(e))}reset(n={},e={}){this._forEachChild((r,o)=>{r.reset(n?n[o]:null,F(g({},e),{onlySelf:!0}))}),this._updatePristine(e,this),this._updateTouched(e,this),this.updateValueAndValidity(e),e?.emitEvent!==!1&&this._events.next(new pa(this))}getRawValue(){return this._reduceChildren({},(n,e,r)=>(n[r]=e.getRawValue(),n))}_syncPendingControls(){let n=this._reduceChildren(!1,(e,r)=>r._syncPendingControls()?!0:e);return n&&this.updateValueAndValidity({onlySelf:!0}),n}_forEachChild(n){Object.keys(this.controls).forEach(e=>{let r=this.controls[e];r&&n(r,e)})}_setUpControls(){this._forEachChild(n=>{n.setParent(this),n._registerOnCollectionChange(this._onCollectionChange)})}_updateValue(){this.value=this._reduceValue()}_anyControls(n){for(let[e,r]of Object.entries(this.controls))if(this.contains(e)&&n(r))return!0;return!1}_reduceValue(){let n={};return this._reduceChildren(n,(e,r,o)=>((r.enabled||this.disabled)&&(e[o]=r.value),e))}_reduceChildren(n,e){let r=n;return this._forEachChild((o,i)=>{r=e(r,o,i)}),r}_allControlsDisabled(){for(let n of Object.keys(this.controls))if(this.controls[n].enabled)return!1;return Object.keys(this.controls).length>0||this.disabled}_find(n){return this.controls.hasOwnProperty(n)?this.controls[n]:null}};var HQ=ho;var Ag=class extends ho{};var yi=new y("",{factory:()=>qu}),qu="always";function Yu(t,n){return[...n.path,t]}function ma(t,n,e=qu){jg(t,n),n.valueAccessor.writeValue(t.value),(t.disabled||e==="always")&&n.valueAccessor.setDisabledState?.(t.disabled),nF(t,n),oF(t,n),rF(t,n),tF(t,n)}function Hu(t,n,e=!0){let r=()=>{};n?.valueAccessor?.registerOnChange(r),n?.valueAccessor?.registerOnTouched(r),zu(t,n),t&&(n._invokeOnDestroyCallbacks(),t._registerOnCollectionChange(()=>{}))}function $u(t,n){t.forEach(e=>{e.registerOnValidatorChange&&e.registerOnValidatorChange(n)})}function tF(t,n){if(n.valueAccessor.setDisabledState){let e=r=>{n.valueAccessor.setDisabledState(r)};t.registerOnDisabledChange(e),n._registerOnDestroy(()=>{t._unregisterOnDisabledChange(e)})}}function jg(t,n){let e=tI(t);n.validator!==null?t.setValidators(FC(e,n.validator)):typeof e=="function"&&t.setValidators([e]);let r=nI(t);n.asyncValidator!==null?t.setAsyncValidators(FC(r,n.asyncValidator)):typeof r=="function"&&t.setAsyncValidators([r]);let o=()=>t.updateValueAndValidity();$u(n._rawValidators,o),$u(n._rawAsyncValidators,o)}function zu(t,n){let e=!1;if(t!==null){if(n.validator!==null){let o=tI(t);if(Array.isArray(o)&&o.length>0){let i=o.filter(s=>s!==n.validator);i.length!==o.length&&(e=!0,t.setValidators(i))}}if(n.asyncValidator!==null){let o=nI(t);if(Array.isArray(o)&&o.length>0){let i=o.filter(s=>s!==n.asyncValidator);i.length!==o.length&&(e=!0,t.setAsyncValidators(i))}}}let r=()=>{};return $u(n._rawValidators,r),$u(n._rawAsyncValidators,r),e}function nF(t,n){n.valueAccessor.registerOnChange(e=>{t._pendingValue=e,t._pendingChange=!0,t._pendingDirty=!0,t.updateOn==="change"&&iI(t,n)})}function rF(t,n){n.valueAccessor.registerOnTouched(()=>{t._pendingTouched=!0,t.updateOn==="blur"&&t._pendingChange&&iI(t,n),t.updateOn!=="submit"&&t.markAsTouched()})}function iI(t,n){t._pendingDirty&&t.markAsDirty(),t.setValue(t._pendingValue,{emitModelToViewChange:!1}),n.viewToModelUpdate(t._pendingValue),t._pendingChange=!1}function oF(t,n){let e=(r,o)=>{n.valueAccessor.writeValue(r),o&&n.viewToModelUpdate(r)};t.registerOnChange(e),n._registerOnDestroy(()=>{t._unregisterOnChange(e)})}function sI(t,n){t==null,jg(t,n)}function iF(t,n){return zu(t,n)}function Vg(t,n){if(!t.hasOwnProperty("model"))return!1;let e=t.model;return e.isFirstChange()?!0:!Object.is(n,e.currentValue)}function sF(t){return Object.getPrototypeOf(t.constructor)===Gu}function aI(t,n){t._syncPendingControls(),n.forEach(e=>{let r=e.control;r.updateOn==="submit"&&r._pendingChange&&(e.viewToModelUpdate(r._pendingValue),r._pendingChange=!1)})}function Bg(t,n){if(!n)return null;Array.isArray(n);let e,r,o;return n.forEach(i=>{i.constructor===zC?e=i:sF(i)?r=i:o=i}),o||r||e||null}function aF(t,n){let e=t.indexOf(n);e>-1&&t.splice(e,1)}var cF={provide:et,useExisting:be(()=>lF)},da=Promise.resolve(),lF=(()=>{class t extends et{callSetDisabledState;get submitted(){return q(this.submittedReactive)}_submitted=Kt(()=>this.submittedReactive());submittedReactive=W(!1);_directives=new Set;form;ngSubmit=new U;options;constructor(e,r,o){super(),this.callSetDisabledState=o,this.form=new ho({},kg(e),Fg(r))}ngAfterViewInit(){this._setUpdateStrategy()}get formDirective(){return this}get control(){return this.form}get path(){return[]}get controls(){return this.form.controls}addControl(e){da.then(()=>{let r=this._findContainer(e.path);e.control=r.registerControl(e.name,e.control),ma(e.control,e,this.callSetDisabledState),e.control.updateValueAndValidity({emitEvent:!1}),this._directives.add(e)})}getControl(e){return this.form.get(e.path)}removeControl(e){da.then(()=>{this._findContainer(e.path)?.removeControl(e.name),this._directives.delete(e)})}addFormGroup(e){da.then(()=>{let r=this._findContainer(e.path),o=new ho({});sI(o,e),r.registerControl(e.name,o),o.updateValueAndValidity({emitEvent:!1})})}removeFormGroup(e){da.then(()=>{this._findContainer(e.path)?.removeControl?.(e.name)})}getFormGroup(e){return this.form.get(e.path)}updateModel(e,r){da.then(()=>{this.form.get(e.path).setValue(r)})}setValue(e){this.control.setValue(e)}onSubmit(e){return this.submittedReactive.set(!0),aI(this.form,this._directives),this.ngSubmit.emit(e),this.form._events.next(new Uu(this.control)),e?.target?.method==="dialog"}onReset(){this.resetForm()}resetForm(e=void 0){this.form.reset(e),this.submittedReactive.set(!1)}_setUpdateStrategy(){this.options&&this.options.updateOn!=null&&(this.form._updateOn=this.options.updateOn)}_findContainer(e){return e.pop(),e.length?this.form.get(e):this.form}static \u0275fac=function(r){return new(r||t)(D(En,10),D(mo,10),D(yi,8))};static \u0275dir=M({type:t,selectors:[["form",3,"ngNoForm","",3,"formGroup","",3,"formArray",""],["ng-form"],["","ngForm",""]],hostBindings:function(r,o){r&1&&Zt("submit",function(s){return o.onSubmit(s)})("reset",function(){return o.onReset()})},inputs:{options:[0,"ngFormOptions","options"]},outputs:{ngSubmit:"ngSubmit"},exportAs:["ngForm"],standalone:!1,features:[we([cF]),J]})}return t})();function jC(t,n){let e=t.indexOf(n);e>-1&&t.splice(e,1)}function VC(t){return typeof t=="object"&&t!==null&&Object.keys(t).length===2&&"value"in t&&"disabled"in t}var gi=class extends vi{defaultValue=null;_onChange=[];_pendingValue;_pendingChange=!1;constructor(n=null,e,r){super(Pg(e),Lg(r,e)),this._applyFormState(n),this._setUpdateStrategy(e),this._initObservables(),this.updateValueAndValidity({onlySelf:!0,emitEvent:!!this.asyncValidator}),Wu(e)&&(e.nonNullable||e.initialValueIsDefault)&&(VC(n)?this.defaultValue=n.value:this.defaultValue=n)}setValue(n,e={}){this.value=this._pendingValue=n,this._onChange.length&&e.emitModelToViewChange!==!1&&this._onChange.forEach(r=>r(this.value,e.emitViewToModelChange!==!1)),this.updateValueAndValidity(e)}patchValue(n,e={}){this.setValue(n,e)}reset(n=this.defaultValue,e={}){this._applyFormState(n),this.markAsPristine(e),this.markAsUntouched(e),this.setValue(this.value,e),e.overwriteDefaultValue&&(this.defaultValue=this.value),this._pendingChange=!1,e?.emitEvent!==!1&&this._events.next(new pa(this))}_updateValue(){}_anyControls(n){return!1}_allControlsDisabled(){return this.disabled}registerOnChange(n){this._onChange.push(n)}_unregisterOnChange(n){jC(this._onChange,n)}registerOnDisabledChange(n){this._onDisabledChange.push(n)}_unregisterOnDisabledChange(n){jC(this._onDisabledChange,n)}_forEachChild(n){}_syncPendingControls(){return this.updateOn==="submit"&&(this._pendingDirty&&this.markAsDirty(),this._pendingTouched&&this.markAsTouched(),this._pendingChange)?(this.setValue(this._pendingValue,{onlySelf:!0,emitModelToViewChange:!1}),!0):!1}_applyFormState(n){VC(n)?(this.value=this._pendingValue=n.value,n.disabled?this.disable({onlySelf:!0,emitEvent:!1}):this.enable({onlySelf:!0,emitEvent:!1})):this.value=this._pendingValue=n}},zQ=gi,uF=t=>t instanceof gi,dF=(()=>{class t extends et{_parent;ngOnInit(){this._checkParentType(),this.formDirective.addFormGroup(this)}ngOnDestroy(){this.formDirective?.removeFormGroup(this)}get control(){return this.formDirective.getFormGroup(this)}get path(){return Yu(this.name==null?this.name:this.name.toString(),this._parent)}get formDirective(){return this._parent?this._parent.formDirective:null}_checkParentType(){}static \u0275fac=(()=>{let e;return function(o){return(e||(e=Ne(t)))(o||t)}})();static \u0275dir=M({type:t,standalone:!1,features:[J]})}return t})();var fF={provide:Gn,useExisting:be(()=>hF)},BC=Promise.resolve(),hF=(()=>{class t extends Gn{_changeDetectorRef;callSetDisabledState;control=new gi;static ngAcceptInputType_isDisabled;_registered=!1;viewModel;name="";isDisabled;model;options;update=new U;constructor(e,r,o,i,s,a){super(),this._changeDetectorRef=s,this.callSetDisabledState=a,this._parent=e,this._setValidators(r),this._setAsyncValidators(o),this.valueAccessor=Bg(this,i)}ngOnChanges(e){if(this._checkForErrors(),!this._registered||"name"in e){if(this._registered&&(this._checkName(),this.formDirective)){let r=e.name.previousValue;this.formDirective.removeControl({name:r,path:this._getPath(r)})}this._setUpControl()}"isDisabled"in e&&this._updateDisabled(e),Vg(e,this.viewModel)&&(this._updateValue(this.model),this.viewModel=this.model)}ngOnDestroy(){this.formDirective?.removeControl(this)}get path(){return this._getPath(this.name)}get formDirective(){return this._parent?this._parent.formDirective:null}viewToModelUpdate(e){this.viewModel=e,this.update.emit(e)}_setUpControl(){this._setUpdateStrategy(),this._isStandalone()?this._setUpStandalone():this.formDirective.addControl(this),this._registered=!0}_setUpdateStrategy(){this.options&&this.options.updateOn!=null&&(this.control._updateOn=this.options.updateOn)}_isStandalone(){return!this._parent||!!(this.options&&this.options.standalone)}_setUpStandalone(){ma(this.control,this,this.callSetDisabledState),this.control.updateValueAndValidity({emitEvent:!1})}_checkForErrors(){this._checkName()}_checkName(){this.options&&this.options.name&&(this.name=this.options.name),!this._isStandalone()&&this.name}_updateValue(e){BC.then(()=>{this.control.setValue(e,{emitViewToModelChange:!1}),this._changeDetectorRef?.markForCheck()})}_updateDisabled(e){let r=e.isDisabled.currentValue,o=r!==0&&ue(r);BC.then(()=>{o&&!this.control.disabled?this.control.disable():!o&&this.control.disabled&&this.control.enable(),this._changeDetectorRef?.markForCheck()})}_getPath(e){return this._parent?Yu(e,this._parent):[e]}static \u0275fac=function(r){return new(r||t)(D(et,9),D(En,10),D(mo,10),D(po,10),D(St,8),D(yi,8))};static \u0275dir=M({type:t,selectors:[["","ngModel","",3,"formControlName","",3,"formControl",""]],inputs:{name:"name",isDisabled:[0,"disabled","isDisabled"],model:[0,"ngModel","model"],options:[0,"ngModelOptions","options"]},outputs:{update:"ngModelChange"},exportAs:["ngModel"],standalone:!1,features:[we([fF]),J,Re]})}return t})();var GQ=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275dir=M({type:t,selectors:[["form",3,"ngNoForm","",3,"ngNativeValidate",""]],hostAttrs:["novalidate",""],standalone:!1})}return t})(),pF={provide:po,useExisting:be(()=>mF),multi:!0},mF=(()=>{class t extends Gu{writeValue(e){let r=e??"";this.setProperty("value",r)}registerOnChange(e){this.onChange=r=>{e(r==""?null:parseFloat(r))}}static \u0275fac=(()=>{let e;return function(o){return(e||(e=Ne(t)))(o||t)}})();static \u0275dir=M({type:t,selectors:[["input","type","number","formControlName",""],["input","type","number","formControl",""],["input","type","number","ngModel",""]],hostBindings:function(r,o){r&1&&Zt("input",function(s){return o.onChange(s.target.value)})("blur",function(){return o.onTouched()})},standalone:!1,features:[we([pF]),J]})}return t})();var Rg=class extends vi{constructor(n,e,r){super(Pg(e),Lg(r,e)),this.controls=n,this._initObservables(),this._setUpdateStrategy(e),this._setUpControls(),this.updateValueAndValidity({onlySelf:!0,emitEvent:!!this.asyncValidator})}controls;at(n){return this.controls[this._adjustIndex(n)]}push(n,e={}){Array.isArray(n)?n.forEach(r=>{this.controls.push(r),this._registerControl(r)}):(this.controls.push(n),this._registerControl(n)),this.updateValueAndValidity({emitEvent:e.emitEvent}),this._onCollectionChange()}insert(n,e,r={}){this.controls.splice(n,0,e),this._registerControl(e),this.updateValueAndValidity({emitEvent:r.emitEvent})}removeAt(n,e={}){let r=this._adjustIndex(n);r<0&&(r=0),this.controls[r]&&this.controls[r]._registerOnCollectionChange(()=>{}),this.controls.splice(r,1),this.updateValueAndValidity({emitEvent:e.emitEvent})}setControl(n,e,r={}){let o=this._adjustIndex(n);o<0&&(o=0),this.controls[o]&&this.controls[o]._registerOnCollectionChange(()=>{}),this.controls.splice(o,1),e&&(this.controls.splice(o,0,e),this._registerControl(e)),this.updateValueAndValidity({emitEvent:r.emitEvent}),this._onCollectionChange()}get length(){return this.controls.length}setValue(n,e={}){oI(this,!1,n),n.forEach((r,o)=>{rI(this,!1,o),this.at(o).setValue(r,{onlySelf:!0,emitEvent:e.emitEvent})}),this.updateValueAndValidity(e)}patchValue(n,e={}){n!=null&&(n.forEach((r,o)=>{this.at(o)&&this.at(o).patchValue(r,{onlySelf:!0,emitEvent:e.emitEvent})}),this.updateValueAndValidity(e))}reset(n=[],e={}){this._forEachChild((r,o)=>{r.reset(n[o],F(g({},e),{onlySelf:!0}))}),this._updatePristine(e,this),this._updateTouched(e,this),this.updateValueAndValidity(e),e?.emitEvent!==!1&&this._events.next(new pa(this))}getRawValue(){return this.controls.map(n=>n.getRawValue())}clear(n={}){this.controls.length<1||(this._forEachChild(e=>e._registerOnCollectionChange(()=>{})),this.controls.splice(0),this.updateValueAndValidity({emitEvent:n.emitEvent}))}_adjustIndex(n){return n<0?n+this.length:n}_syncPendingControls(){let n=this.controls.reduce((e,r)=>r._syncPendingControls()?!0:e,!1);return n&&this.updateValueAndValidity({onlySelf:!0}),n}_forEachChild(n){this.controls.forEach((e,r)=>{n(e,r)})}_updateValue(){this.value=this.controls.filter(n=>n.enabled||this.disabled).map(n=>n.value)}_anyControls(n){return this.controls.some(e=>e.enabled&&n(e))}_setUpControls(){this._forEachChild(n=>this._registerControl(n))}_allControlsDisabled(){for(let n of this.controls)if(n.enabled)return!1;return this.controls.length>0||this.disabled}_registerControl(n){n.setParent(this),n._registerOnCollectionChange(this._onCollectionChange)}_find(n){return this.at(n)??null}};var cI=(()=>{class t extends et{callSetDisabledState;get submitted(){return q(this._submittedReactive)}set submitted(e){this._submittedReactive.set(e)}_submitted=Kt(()=>this._submittedReactive());_submittedReactive=W(!1);_oldForm;_onCollectionChange=()=>this._updateDomValue();directives=[];constructor(e,r,o){super(),this.callSetDisabledState=o,this._setValidators(e),this._setAsyncValidators(r)}ngOnChanges(e){this.onChanges(e)}ngOnDestroy(){this.onDestroy()}onChanges(e){this._checkFormPresent(),e.hasOwnProperty("form")&&(this._updateValidators(),this._updateDomValue(),this._updateRegistrations(),this._oldForm=this.form)}onDestroy(){this.form&&(zu(this.form,this),this.form._onCollectionChange===this._onCollectionChange&&this.form._registerOnCollectionChange(()=>{}))}get formDirective(){return this}get path(){return[]}addControl(e){let r=this.form.get(e.path);return ma(r,e,this.callSetDisabledState),r.updateValueAndValidity({emitEvent:!1}),this.directives.push(e),r}getControl(e){return this.form.get(e.path)}removeControl(e){Hu(e.control||null,e,!1),aF(this.directives,e)}addFormGroup(e){this._setUpFormContainer(e)}removeFormGroup(e){this._cleanUpFormContainer(e)}getFormGroup(e){return this.form.get(e.path)}getFormArray(e){return this.form.get(e.path)}addFormArray(e){this._setUpFormContainer(e)}removeFormArray(e){this._cleanUpFormContainer(e)}updateModel(e,r){this.form.get(e.path).setValue(r)}onReset(){this.resetForm()}resetForm(e=void 0,r={}){this.form.reset(e,r),this._submittedReactive.set(!1)}onSubmit(e){return this.submitted=!0,aI(this.form,this.directives),this.ngSubmit.emit(e),this.form._events.next(new Uu(this.control)),e?.target?.method==="dialog"}_updateDomValue(){this.directives.forEach(e=>{let r=e.control,o=this.form.get(e.path);r!==o&&(Hu(r||null,e),uF(o)&&(ma(o,e,this.callSetDisabledState),e.control=o))}),this.form._updateTreeValidity({emitEvent:!1})}_setUpFormContainer(e){let r=this.form.get(e.path);sI(r,e),r.updateValueAndValidity({emitEvent:!1})}_cleanUpFormContainer(e){let r=this.form?.get(e.path);r&&iF(r,e)&&r.updateValueAndValidity({emitEvent:!1})}_updateRegistrations(){this.form._registerOnCollectionChange(this._onCollectionChange),this._oldForm?._registerOnCollectionChange(()=>{})}_updateValidators(){jg(this.form,this),this._oldForm&&zu(this._oldForm,this)}_checkFormPresent(){this.form}static \u0275fac=function(r){return new(r||t)(D(En,10),D(mo,10),D(yi,8))};static \u0275dir=M({type:t,features:[J,Re]})}return t})();var Ug=new y(""),gF={provide:Gn,useExisting:be(()=>vF)},vF=(()=>{class t extends Gn{_ngModelWarningConfig;callSetDisabledState;viewModel;form;set isDisabled(e){}model;update=new U;static _ngModelWarningSentOnce=!1;_ngModelWarningSent=!1;constructor(e,r,o,i,s){super(),this._ngModelWarningConfig=i,this.callSetDisabledState=s,this._setValidators(e),this._setAsyncValidators(r),this.valueAccessor=Bg(this,o)}ngOnChanges(e){if(this._isControlChanged(e)){let r=e.form.previousValue;r&&Hu(r,this,!1),ma(this.form,this,this.callSetDisabledState),this.form.updateValueAndValidity({emitEvent:!1})}Vg(e,this.viewModel)&&(this.form.setValue(this.model),this.viewModel=this.model)}ngOnDestroy(){this.form&&Hu(this.form,this,!1)}get path(){return[]}get control(){return this.form}viewToModelUpdate(e){this.viewModel=e,this.update.emit(e)}_isControlChanged(e){return e.hasOwnProperty("form")}static \u0275fac=function(r){return new(r||t)(D(En,10),D(mo,10),D(po,10),D(Ug,8),D(yi,8))};static \u0275dir=M({type:t,selectors:[["","formControl",""]],inputs:{form:[0,"formControl","form"],isDisabled:[0,"disabled","isDisabled"],model:[0,"ngModel","model"]},outputs:{update:"ngModelChange"},exportAs:["ngForm"],standalone:!1,features:[we([gF]),J,Re]})}return t})(),yF={provide:et,useExisting:be(()=>lI)},lI=(()=>{class t extends dF{name=null;constructor(e,r,o){super(),this._parent=e,this._setValidators(r),this._setAsyncValidators(o)}_checkParentType(){dI(this._parent)}static \u0275fac=function(r){return new(r||t)(D(et,13),D(En,10),D(mo,10))};static \u0275dir=M({type:t,selectors:[["","formGroupName",""]],inputs:{name:[0,"formGroupName","name"]},standalone:!1,features:[we([yF]),J]})}return t})(),bF={provide:et,useExisting:be(()=>uI)},uI=(()=>{class t extends et{_parent;name=null;constructor(e,r,o){super(),this._parent=e,this._setValidators(r),this._setAsyncValidators(o)}ngOnInit(){dI(this._parent),this.formDirective.addFormArray(this)}ngOnDestroy(){this.formDirective?.removeFormArray(this)}get control(){return this.formDirective.getFormArray(this)}get formDirective(){return this._parent?this._parent.formDirective:null}get path(){return Yu(this.name==null?this.name:this.name.toString(),this._parent)}static \u0275fac=function(r){return new(r||t)(D(et,13),D(En,10),D(mo,10))};static \u0275dir=M({type:t,selectors:[["","formArrayName",""]],inputs:{name:[0,"formArrayName","name"]},standalone:!1,features:[we([bF]),J]})}return t})();function dI(t){return!(t instanceof lI)&&!(t instanceof cI)&&!(t instanceof uI)}var _F={provide:Gn,useExisting:be(()=>DF)},DF=(()=>{class t extends Gn{_ngModelWarningConfig;_added=!1;viewModel;control;name=null;set isDisabled(e){}model;update=new U;static _ngModelWarningSentOnce=!1;_ngModelWarningSent=!1;constructor(e,r,o,i,s){super(),this._ngModelWarningConfig=s,this._parent=e,this._setValidators(r),this._setAsyncValidators(o),this.valueAccessor=Bg(this,i)}ngOnChanges(e){this._added||this._setUpControl(),Vg(e,this.viewModel)&&(this.viewModel=this.model,this.formDirective.updateModel(this,this.model))}ngOnDestroy(){this.formDirective?.removeControl(this)}viewToModelUpdate(e){this.viewModel=e,this.update.emit(e)}get path(){return Yu(this.name==null?this.name:this.name.toString(),this._parent)}get formDirective(){return this._parent?this._parent.formDirective:null}_setUpControl(){this.control=this.formDirective.addControl(this),this._added=!0}static \u0275fac=function(r){return new(r||t)(D(et,13),D(En,10),D(mo,10),D(po,10),D(Ug,8))};static \u0275dir=M({type:t,selectors:[["","formControlName",""]],inputs:{name:[0,"formControlName","name"],isDisabled:[0,"disabled","isDisabled"],model:[0,"ngModel","model"]},outputs:{update:"ngModelChange"},standalone:!1,features:[we([_F]),J,Re]})}return t})();var EF={provide:et,useExisting:be(()=>wF)},wF=(()=>{class t extends cI{form=null;ngSubmit=new U;get control(){return this.form}static \u0275fac=(()=>{let e;return function(o){return(e||(e=Ne(t)))(o||t)}})();static \u0275dir=M({type:t,selectors:[["","formGroup",""]],hostBindings:function(r,o){r&1&&Zt("submit",function(s){return o.onSubmit(s)})("reset",function(){return o.onReset()})},inputs:{form:[0,"formGroup","form"]},outputs:{ngSubmit:"ngSubmit"},exportAs:["ngForm"],standalone:!1,features:[we([EF]),J]})}return t})(),CF={provide:po,useExisting:be(()=>hI),multi:!0};function fI(t,n){return t==null?`${n}`:(n&&typeof n=="object"&&(n="Object"),`${t}: ${n}`.slice(0,50))}function IF(t){return t.split(":")[0]}var hI=(()=>{class t extends Gu{value;_optionMap=new Map;_idCounter=0;set compareWith(e){this._compareWith=e}_compareWith=Object.is;appRefInjector=f(Be).injector;destroyRef=f(Ae);cdr=f(St);_queuedWrite=!1;_writeValueAfterRender(){this._queuedWrite||this.appRefInjector.destroyed||(this._queuedWrite=!0,ht({write:()=>{this.destroyRef.destroyed||(this._queuedWrite=!1,this.writeValue(this.value))}},{injector:this.appRefInjector}))}writeValue(e){this.cdr.markForCheck(),this.value=e;let r=this._getOptionId(e),o=fI(r,e);this.setProperty("value",o)}registerOnChange(e){this.onChange=r=>{this.value=this._getOptionValue(r),e(this.value)}}_registerOption(){return(this._idCounter++).toString()}_getOptionId(e){for(let r of this._optionMap.keys())if(this._compareWith(this._optionMap.get(r),e))return r;return null}_getOptionValue(e){let r=IF(e);return this._optionMap.has(r)?this._optionMap.get(r):e}static \u0275fac=(()=>{let e;return function(o){return(e||(e=Ne(t)))(o||t)}})();static \u0275dir=M({type:t,selectors:[["select","formControlName","",3,"multiple",""],["select","formControl","",3,"multiple",""],["select","ngModel","",3,"multiple",""]],hostBindings:function(r,o){r&1&&Zt("change",function(s){return o.onChange(s.target.value)})("blur",function(){return o.onTouched()})},inputs:{compareWith:"compareWith"},standalone:!1,features:[we([CF]),J]})}return t})(),WQ=(()=>{class t{_element;_renderer;_select;id;constructor(e,r,o){this._element=e,this._renderer=r,this._select=o,this._select&&(this.id=this._select._registerOption())}set ngValue(e){this._select!=null&&(this._select._optionMap.set(this.id,e),this._setElementValue(fI(this.id,e)),this._select._writeValueAfterRender())}set value(e){this._setElementValue(e),this._select?._writeValueAfterRender()}_setElementValue(e){this._renderer.setProperty(this._element.nativeElement,"value",e)}ngOnDestroy(){this._select?._optionMap.delete(this.id),this._select?._writeValueAfterRender()}static \u0275fac=function(r){return new(r||t)(D(z),D(Oe),D(hI,9))};static \u0275dir=M({type:t,selectors:[["option"]],inputs:{ngValue:"ngValue",value:"value"},standalone:!1})}return t})(),SF={provide:po,useExisting:be(()=>pI),multi:!0};function UC(t,n){return t==null?`${n}`:(typeof n=="string"&&(n=`'${n}'`),n&&typeof n=="object"&&(n="Object"),`${t}: ${n}`.slice(0,50))}function MF(t){return t.split(":")[0]}var pI=(()=>{class t extends Gu{value;_optionMap=new Map;_idCounter=0;set compareWith(e){this._compareWith=e}_compareWith=Object.is;writeValue(e){this.value=e;let r;if(Array.isArray(e)){let o=e.map(i=>this._getOptionId(i));r=(i,s)=>{i._setSelected(o.indexOf(s)>-1)}}else r=o=>{o._setSelected(!1)};this._optionMap.forEach(r)}registerOnChange(e){this.onChange=r=>{let o=[],i=r.selectedOptions;if(i!==void 0){let s=i;for(let a=0;a{let e;return function(o){return(e||(e=Ne(t)))(o||t)}})();static \u0275dir=M({type:t,selectors:[["select","multiple","","formControlName",""],["select","multiple","","formControl",""],["select","multiple","","ngModel",""]],hostBindings:function(r,o){r&1&&Zt("change",function(s){return o.onChange(s.target)})("blur",function(){return o.onTouched()})},inputs:{compareWith:"compareWith"},standalone:!1,features:[we([SF]),J]})}return t})(),qQ=(()=>{class t{_element;_renderer;_select;id;_value;constructor(e,r,o){this._element=e,this._renderer=r,this._select=o,this._select&&(this.id=this._select._registerOption(this))}set ngValue(e){this._select!=null&&(this._value=e,this._setElementValue(UC(this.id,e)),this._select.writeValue(this._select.value))}set value(e){this._select?(this._value=e,this._setElementValue(UC(this.id,e)),this._select.writeValue(this._select.value)):this._setElementValue(e)}_setElementValue(e){this._renderer.setProperty(this._element.nativeElement,"value",e)}_setSelected(e){this._renderer.setProperty(this._element.nativeElement,"selected",e)}ngOnDestroy(){this._select&&(this._select._optionMap.delete(this.id),this._select.writeValue(this._select.value))}static \u0275fac=function(r){return new(r||t)(D(z),D(Oe),D(pI,9))};static \u0275dir=M({type:t,selectors:[["option"]],inputs:{ngValue:"ngValue",value:"value"},standalone:!1})}return t})();function mI(t){return typeof t=="number"?t:parseFloat(t)}var Hg=(()=>{class t{_validator=Pu;_onChange;_enabled;ngOnChanges(e){if(this.inputName in e){let r=this.normalizeInput(e[this.inputName].currentValue);this._enabled=this.enabled(r),this._validator=this._enabled?this.createValidator(r):Pu,this._onChange?.()}}validate(e){return this._validator(e)}registerOnValidatorChange(e){this._onChange=e}enabled(e){return e!=null}static \u0275fac=function(r){return new(r||t)};static \u0275dir=M({type:t,features:[Re]})}return t})(),TF={provide:En,useExisting:be(()=>xF),multi:!0},xF=(()=>{class t extends Hg{max;inputName="max";normalizeInput=e=>mI(e);createValidator=e=>WC(e);static \u0275fac=(()=>{let e;return function(o){return(e||(e=Ne(t)))(o||t)}})();static \u0275dir=M({type:t,selectors:[["input","type","number","max","","formControlName",""],["input","type","number","max","","formControl",""],["input","type","number","max","","ngModel",""]],hostVars:1,hostBindings:function(r,o){r&2&&Yt("max",o._enabled?o.max:null)},inputs:{max:"max"},standalone:!1,features:[we([TF]),J]})}return t})(),AF={provide:En,useExisting:be(()=>RF),multi:!0},RF=(()=>{class t extends Hg{min;inputName="min";normalizeInput=e=>mI(e);createValidator=e=>GC(e);static \u0275fac=(()=>{let e;return function(o){return(e||(e=Ne(t)))(o||t)}})();static \u0275dir=M({type:t,selectors:[["input","type","number","min","","formControlName",""],["input","type","number","min","","formControl",""],["input","type","number","min","","ngModel",""]],hostVars:1,hostBindings:function(r,o){r&2&&Yt("min",o._enabled?o.min:null)},inputs:{min:"min"},standalone:!1,features:[we([AF]),J]})}return t})(),NF={provide:En,useExisting:be(()=>OF),multi:!0};var OF=(()=>{class t extends Hg{required;inputName="required";normalizeInput=ue;createValidator=e=>qC;enabled(e){return e}static \u0275fac=(()=>{let e;return function(o){return(e||(e=Ne(t)))(o||t)}})();static \u0275dir=M({type:t,selectors:[["","required","","formControlName","",3,"type","checkbox"],["","required","","formControl","",3,"type","checkbox"],["","required","","ngModel","",3,"type","checkbox"]],hostVars:1,hostBindings:function(r,o){r&2&&Yt("required",o._enabled?"":null)},inputs:{required:"required"},standalone:!1,features:[we([NF]),J]})}return t})();var gI=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275mod=X({type:t});static \u0275inj=Z({})}return t})();function HC(t){return!!t&&(t.asyncValidators!==void 0||t.validators!==void 0||t.updateOn!==void 0)}var kF=(()=>{class t{useNonNullable=!1;get nonNullable(){let e=new t;return e.useNonNullable=!0,e}group(e,r=null){let o=this._reduceControls(e),i={};return HC(r)?i=r:r!==null&&(i.validators=r.validator,i.asyncValidators=r.asyncValidator),new ho(o,i)}record(e,r=null){let o=this._reduceControls(e);return new Ag(o,r)}control(e,r,o){let i={};return this.useNonNullable?(HC(r)?i=r:(i.validators=r,i.asyncValidators=o),new gi(e,F(g({},i),{nonNullable:!0}))):new gi(e,r,o)}array(e,r,o){let i=e.map(s=>this._createControl(s));return new Rg(i,r,o)}_reduceControls(e){let r={};return Object.keys(e).forEach(o=>{r[o]=this._createControl(e[o])}),r}_createControl(e){if(e instanceof gi)return e;if(e instanceof vi)return e;if(Array.isArray(e)){let r=e[0],o=e.length>1?e[1]:null,i=e.length>2?e[2]:null;return this.control(r,o,i)}else return this.control(e)}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();var YQ=(()=>{class t extends kF{group(e,r=null){return super.group(e,r)}control(e,r,o){return super.control(e,r,o)}array(e,r,o){return super.array(e,r,o)}static \u0275fac=(()=>{let e;return function(o){return(e||(e=Ne(t)))(o||t)}})();static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})(),ZQ=(()=>{class t{static withConfig(e){return{ngModule:t,providers:[{provide:yi,useValue:e.callSetDisabledState??qu}]}}static \u0275fac=function(r){return new(r||t)};static \u0275mod=X({type:t});static \u0275inj=Z({imports:[gI]})}return t})(),KQ=(()=>{class t{static withConfig(e){return{ngModule:t,providers:[{provide:Ug,useValue:e.warnOnNgModelWithFormControl??"always"},{provide:yi,useValue:e.callSetDisabledState??qu}]}}static \u0275fac=function(r){return new(r||t)};static \u0275mod=X({type:t});static \u0275inj=Z({imports:[gI]})}return t})();var V="primary",Ta=Symbol("RouteTitle"),qg=class{params;constructor(n){this.params=n||{}}has(n){return Object.prototype.hasOwnProperty.call(this.params,n)}get(n){if(this.has(n)){let e=this.params[n];return Array.isArray(e)?e[0]:e}return null}getAll(n){if(this.has(n)){let e=this.params[n];return Array.isArray(e)?e:[e]}return[]}get keys(){return Object.keys(this.params)}};function vo(t){return new qg(t)}function $g(t,n,e){for(let r=0;rt.length||e.pathMatch==="full"&&(n.hasChildren()||r.lengtht.length||e.pathMatch==="full"&&n.hasChildren()&&e.path!=="**")return null;let a={};return!$g(i,t.slice(0,i.length),a)||!$g(s,t.slice(t.length-s.length),a)?null:{consumed:t,posParams:a}}function ed(t){return new Promise((n,e)=>{t.pipe(Sn()).subscribe({next:r=>n(r),error:r=>e(r)})})}function FF(t,n){if(t.length!==n.length)return!1;for(let e=0;er[i]===o)}else return t===n}function PF(t){return t.length>0?t[t.length-1]:null}function bo(t){return Ft(t)?t:jn(t)?se(Promise.resolve(t)):T(t)}function II(t){return Ft(t)?ed(t):Promise.resolve(t)}var LF={exact:MI,subset:TI},SI={exact:jF,subset:VF,ignored:()=>!0},cv={paths:"exact",fragment:"ignored",matrixParams:"ignored",queryParams:"exact"},_a={paths:"subset",fragment:"ignored",matrixParams:"ignored",queryParams:"subset"};function lv(t,n,e){let r=t instanceof tt?t:n.parseUrl(t);return Kt(()=>Zg(n.lastSuccessfulNavigation()?.finalUrl??new tt,r,g(g({},_a),e)))}function Zg(t,n,e){return LF[e.paths](t.root,n.root,e.matrixParams)&&SI[e.queryParams](t.queryParams,n.queryParams)&&!(e.fragment==="exact"&&t.fragment!==n.fragment)}function jF(t,n){return wn(t,n)}function MI(t,n,e){if(!go(t.segments,n.segments)||!Qu(t.segments,n.segments,e)||t.numberOfChildren!==n.numberOfChildren)return!1;for(let r in n.children)if(!t.children[r]||!MI(t.children[r],n.children[r],e))return!1;return!0}function VF(t,n){return Object.keys(n).length<=Object.keys(t).length&&Object.keys(n).every(e=>CI(t[e],n[e]))}function TI(t,n,e){return xI(t,n,n.segments,e)}function xI(t,n,e,r){if(t.segments.length>e.length){let o=t.segments.slice(0,e.length);return!(!go(o,e)||n.hasChildren()||!Qu(o,e,r))}else if(t.segments.length===e.length){if(!go(t.segments,e)||!Qu(t.segments,e,r))return!1;for(let o in n.children)if(!t.children[o]||!TI(t.children[o],n.children[o],r))return!1;return!0}else{let o=e.slice(0,t.segments.length),i=e.slice(t.segments.length);return!go(t.segments,o)||!Qu(t.segments,o,r)||!t.children[V]?!1:xI(t.children[V],n,i,r)}}function Qu(t,n,e){return n.every((r,o)=>SI[e](t[o].parameters,r.parameters))}var tt=class{root;queryParams;fragment;_queryParamMap;constructor(n=new te([],{}),e={},r=null){this.root=n,this.queryParams=e,this.fragment=r}get queryParamMap(){return this._queryParamMap??=vo(this.queryParams),this._queryParamMap}toString(){return HF.serialize(this)}},te=class{segments;children;parent=null;constructor(n,e){this.segments=n,this.children=e,Object.values(e).forEach(r=>r.parent=this)}hasChildren(){return this.numberOfChildren>0}get numberOfChildren(){return Object.keys(this.children).length}toString(){return Xu(this)}},vr=class{path;parameters;_parameterMap;constructor(n,e){this.path=n,this.parameters=e}get parameterMap(){return this._parameterMap??=vo(this.parameters),this._parameterMap}toString(){return RI(this)}};function BF(t,n){return go(t,n)&&t.every((e,r)=>wn(e.parameters,n[r].parameters))}function go(t,n){return t.length!==n.length?!1:t.every((e,r)=>e.path===n[r].path)}function UF(t,n){let e=[];return Object.entries(t.children).forEach(([r,o])=>{r===V&&(e=e.concat(n(o,r)))}),Object.entries(t.children).forEach(([r,o])=>{r!==V&&(e=e.concat(n(o,r)))}),e}var _r=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:()=>new qn,providedIn:"root"})}return t})(),qn=class{parse(n){let e=new Qg(n);return new tt(e.parseRootSegment(),e.parseQueryParams(),e.parseFragment())}serialize(n){let e=`/${ga(n.root,!0)}`,r=GF(n.queryParams),o=typeof n.fragment=="string"?`#${$F(n.fragment)}`:"";return`${e}${r}${o}`}},HF=new qn;function Xu(t){return t.segments.map(n=>RI(n)).join("/")}function ga(t,n){if(!t.hasChildren())return Xu(t);if(n){let e=t.children[V]?ga(t.children[V],!1):"",r=[];return Object.entries(t.children).forEach(([o,i])=>{o!==V&&r.push(`${o}:${ga(i,!1)}`)}),r.length>0?`${e}(${r.join("//")})`:e}else{let e=UF(t,(r,o)=>o===V?[ga(t.children[V],!1)]:[`${o}:${ga(r,!1)}`]);return Object.keys(t.children).length===1&&t.children[V]!=null?`${Xu(t)}/${e[0]}`:`${Xu(t)}/(${e.join("//")})`}}function AI(t){return encodeURIComponent(t).replace(/%40/g,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",")}function Zu(t){return AI(t).replace(/%3B/gi,";")}function $F(t){return encodeURI(t)}function Kg(t){return AI(t).replace(/\(/g,"%28").replace(/\)/g,"%29").replace(/%26/gi,"&")}function Ju(t){return decodeURIComponent(t)}function vI(t){return Ju(t.replace(/\+/g,"%20"))}function RI(t){return`${Kg(t.path)}${zF(t.parameters)}`}function zF(t){return Object.entries(t).map(([n,e])=>`;${Kg(n)}=${Kg(e)}`).join("")}function GF(t){let n=Object.entries(t).map(([e,r])=>Array.isArray(r)?r.map(o=>`${Zu(e)}=${Zu(o)}`).join("&"):`${Zu(e)}=${Zu(r)}`).filter(e=>e);return n.length?`?${n.join("&")}`:""}var WF=/^[^\/()?;#]+/;function zg(t){let n=t.match(WF);return n?n[0]:""}var qF=/^[^\/()?;=#]+/;function YF(t){let n=t.match(qF);return n?n[0]:""}var ZF=/^[^=?&#]+/;function KF(t){let n=t.match(ZF);return n?n[0]:""}var QF=/^[^&#]+/;function XF(t){let n=t.match(QF);return n?n[0]:""}var Qg=class{url;remaining;constructor(n){this.url=n,this.remaining=n}parseRootSegment(){for(;this.consumeOptional("/"););return this.remaining===""||this.peekStartsWith("?")||this.peekStartsWith("#")?new te([],{}):new te([],this.parseChildren())}parseQueryParams(){let n={};if(this.consumeOptional("?"))do this.parseQueryParam(n);while(this.consumeOptional("&"));return n}parseFragment(){return this.consumeOptional("#")?decodeURIComponent(this.remaining):null}parseChildren(n=0){if(n>50)throw new b(4010,!1);if(this.remaining==="")return{};this.consumeOptional("/");let e=[];for(this.peekStartsWith("(")||e.push(this.parseSegment());this.peekStartsWith("/")&&!this.peekStartsWith("//")&&!this.peekStartsWith("/(");)this.capture("/"),e.push(this.parseSegment());let r={};this.peekStartsWith("/(")&&(this.capture("/"),r=this.parseParens(!0,n));let o={};return this.peekStartsWith("(")&&(o=this.parseParens(!1,n)),(e.length>0||Object.keys(r).length>0)&&(o[V]=new te(e,r)),o}parseSegment(){let n=zg(this.remaining);if(n===""&&this.peekStartsWith(";"))throw new b(4009,!1);return this.capture(n),new vr(Ju(n),this.parseMatrixParams())}parseMatrixParams(){let n={};for(;this.consumeOptional(";");)this.parseParam(n);return n}parseParam(n){let e=YF(this.remaining);if(!e)return;this.capture(e);let r="";if(this.consumeOptional("=")){let o=zg(this.remaining);o&&(r=o,this.capture(r))}n[Ju(e)]=Ju(r)}parseQueryParam(n){let e=KF(this.remaining);if(!e)return;this.capture(e);let r="";if(this.consumeOptional("=")){let s=XF(this.remaining);s&&(r=s,this.capture(r))}let o=vI(e),i=vI(r);if(n.hasOwnProperty(o)){let s=n[o];Array.isArray(s)||(s=[s],n[o]=s),s.push(i)}else n[o]=i}parseParens(n,e){let r={};for(this.capture("(");!this.consumeOptional(")")&&this.remaining.length>0;){let o=zg(this.remaining),i=this.remaining[o.length];if(i!=="/"&&i!==")"&&i!==";")throw new b(4010,!1);let s;o.indexOf(":")>-1?(s=o.slice(0,o.indexOf(":")),this.capture(s),this.capture(":")):n&&(s=V);let a=this.parseChildren(e+1);r[s??V]=Object.keys(a).length===1&&a[V]?a[V]:new te([],a),this.consumeOptional("//")}return r}peekStartsWith(n){return this.remaining.startsWith(n)}consumeOptional(n){return this.peekStartsWith(n)?(this.remaining=this.remaining.substring(n.length),!0):!1}capture(n){if(!this.consumeOptional(n))throw new b(4011,!1)}};function NI(t){return t.segments.length>0?new te([],{[V]:t}):t}function OI(t){let n={};for(let[r,o]of Object.entries(t.children)){let i=OI(o);if(r===V&&i.segments.length===0&&i.hasChildren())for(let[s,a]of Object.entries(i.children))n[s]=a;else(i.segments.length>0||i.hasChildren())&&(n[r]=i)}let e=new te(t.segments,n);return JF(e)}function JF(t){if(t.numberOfChildren===1&&t.children[V]){let n=t.children[V];return new te(t.segments.concat(n.segments),n.children)}return t}function yr(t){return t instanceof tt}function kI(t,n,e=null,r=null,o=new qn){let i=FI(t);return PI(i,n,e,r,o)}function FI(t){let n;function e(i){let s={};for(let c of i.children){let l=e(c);s[c.outlet]=l}let a=new te(i.url,s);return i===t&&(n=a),a}let r=e(t.root),o=NI(r);return n??o}function PI(t,n,e,r,o){let i=t;for(;i.parent;)i=i.parent;if(n.length===0)return Gg(i,i,i,e,r,o);let s=eP(n);if(s.toRoot())return Gg(i,i,new te([],{}),e,r,o);let a=tP(s,i,t),c=a.processChildren?ya(a.segmentGroup,a.index,s.commands):jI(a.segmentGroup,a.index,s.commands);return Gg(i,a.segmentGroup,c,e,r,o)}function td(t){return typeof t=="object"&&t!=null&&!t.outlets&&!t.segmentPath}function Da(t){return typeof t=="object"&&t!=null&&t.outlets}function yI(t,n,e){t||="\u0275";let r=new tt;return r.queryParams={[t]:n},e.parse(e.serialize(r)).queryParams[t]}function Gg(t,n,e,r,o,i){let s={};for(let[l,u]of Object.entries(r??{}))s[l]=Array.isArray(u)?u.map(d=>yI(l,d,i)):yI(l,u,i);let a;t===n?a=e:a=LI(t,n,e);let c=NI(OI(a));return new tt(c,s,o)}function LI(t,n,e){let r={};return Object.entries(t.children).forEach(([o,i])=>{i===n?r[o]=e:r[o]=LI(i,n,e)}),new te(t.segments,r)}var nd=class{isAbsolute;numberOfDoubleDots;commands;constructor(n,e,r){if(this.isAbsolute=n,this.numberOfDoubleDots=e,this.commands=r,n&&r.length>0&&td(r[0]))throw new b(4003,!1);let o=r.find(Da);if(o&&o!==PF(r))throw new b(4004,!1)}toRoot(){return this.isAbsolute&&this.commands.length===1&&this.commands[0]=="/"}};function eP(t){if(typeof t[0]=="string"&&t.length===1&&t[0]==="/")return new nd(!0,0,t);let n=0,e=!1,r=t.reduce((o,i,s)=>{if(typeof i=="object"&&i!=null){if(i.outlets){let a={};return Object.entries(i.outlets).forEach(([c,l])=>{a[c]=typeof l=="string"?l.split("/"):l}),[...o,{outlets:a}]}if(i.segmentPath)return[...o,i.segmentPath]}return typeof i!="string"?[...o,i]:s===0?(i.split("/").forEach((a,c)=>{c==0&&a==="."||(c==0&&a===""?e=!0:a===".."?n++:a!=""&&o.push(a))}),o):[...o,i]},[]);return new nd(e,n,r)}var _i=class{segmentGroup;processChildren;index;constructor(n,e,r){this.segmentGroup=n,this.processChildren=e,this.index=r}};function tP(t,n,e){if(t.isAbsolute)return new _i(n,!0,0);if(!e)return new _i(n,!1,NaN);if(e.parent===null)return new _i(e,!0,0);let r=td(t.commands[0])?0:1,o=e.segments.length-1+r;return nP(e,o,t.numberOfDoubleDots)}function nP(t,n,e){let r=t,o=n,i=e;for(;i>o;){if(i-=o,r=r.parent,!r)throw new b(4005,!1);o=r.segments.length}return new _i(r,!1,o-i)}function rP(t){return Da(t[0])?t[0].outlets:{[V]:t}}function jI(t,n,e){if(t??=new te([],{}),t.segments.length===0&&t.hasChildren())return ya(t,n,e);let r=oP(t,n,e),o=e.slice(r.commandIndex);if(r.match&&r.pathIndexi!==V)&&t.children[V]&&t.numberOfChildren===1&&t.children[V].segments.length===0){let i=ya(t.children[V],n,e);return new te(t.segments,i.children)}return Object.entries(r).forEach(([i,s])=>{typeof s=="string"&&(s=[s]),s!==null&&(o[i]=jI(t.children[i],n,s))}),Object.entries(t.children).forEach(([i,s])=>{r[i]===void 0&&(o[i]=s)}),new te(t.segments,o)}}function oP(t,n,e){let r=0,o=n,i={match:!1,pathIndex:0,commandIndex:0};for(;o=e.length)return i;let s=t.segments[o],a=e[r];if(Da(a))break;let c=`${a}`,l=r0&&c===void 0)break;if(c&&l&&typeof l=="object"&&l.outlets===void 0){if(!_I(c,l,s))return i;r+=2}else{if(!_I(c,{},s))return i;r++}o++}return{match:!0,pathIndex:o,commandIndex:r}}function Xg(t,n,e){let r=t.segments.slice(0,n),o=0;for(;o{typeof r=="string"&&(r=[r]),r!==null&&(n[e]=Xg(new te([],{}),0,r))}),n}function bI(t){let n={};return Object.entries(t).forEach(([e,r])=>n[e]=`${r}`),n}function _I(t,n,e){return t==e.path&&wn(n,e.parameters)}var Di="imperative",Fe=(function(t){return t[t.NavigationStart=0]="NavigationStart",t[t.NavigationEnd=1]="NavigationEnd",t[t.NavigationCancel=2]="NavigationCancel",t[t.NavigationError=3]="NavigationError",t[t.RoutesRecognized=4]="RoutesRecognized",t[t.ResolveStart=5]="ResolveStart",t[t.ResolveEnd=6]="ResolveEnd",t[t.GuardsCheckStart=7]="GuardsCheckStart",t[t.GuardsCheckEnd=8]="GuardsCheckEnd",t[t.RouteConfigLoadStart=9]="RouteConfigLoadStart",t[t.RouteConfigLoadEnd=10]="RouteConfigLoadEnd",t[t.ChildActivationStart=11]="ChildActivationStart",t[t.ChildActivationEnd=12]="ChildActivationEnd",t[t.ActivationStart=13]="ActivationStart",t[t.ActivationEnd=14]="ActivationEnd",t[t.Scroll=15]="Scroll",t[t.NavigationSkipped=16]="NavigationSkipped",t})(Fe||{}),vt=class{id;url;constructor(n,e){this.id=n,this.url=e}},br=class extends vt{type=Fe.NavigationStart;navigationTrigger;restoredState;constructor(n,e,r="imperative",o=null){super(n,e),this.navigationTrigger=r,this.restoredState=o}toString(){return`NavigationStart(id: ${this.id}, url: '${this.url}')`}},yt=class extends vt{urlAfterRedirects;type=Fe.NavigationEnd;constructor(n,e,r){super(n,e),this.urlAfterRedirects=r}toString(){return`NavigationEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}')`}},Ze=(function(t){return t[t.Redirect=0]="Redirect",t[t.SupersededByNewNavigation=1]="SupersededByNewNavigation",t[t.NoDataFromResolver=2]="NoDataFromResolver",t[t.GuardRejected=3]="GuardRejected",t[t.Aborted=4]="Aborted",t})(Ze||{}),wi=(function(t){return t[t.IgnoredSameUrlNavigation=0]="IgnoredSameUrlNavigation",t[t.IgnoredByUrlHandlingStrategy=1]="IgnoredByUrlHandlingStrategy",t})(wi||{}),Rt=class extends vt{reason;code;type=Fe.NavigationCancel;constructor(n,e,r,o){super(n,e),this.reason=r,this.code=o}toString(){return`NavigationCancel(id: ${this.id}, url: '${this.url}')`}};function VI(t){return t instanceof Rt&&(t.code===Ze.Redirect||t.code===Ze.SupersededByNewNavigation)}var Cn=class extends vt{reason;code;type=Fe.NavigationSkipped;constructor(n,e,r,o){super(n,e),this.reason=r,this.code=o}},yo=class extends vt{error;target;type=Fe.NavigationError;constructor(n,e,r,o){super(n,e),this.error=r,this.target=o}toString(){return`NavigationError(id: ${this.id}, url: '${this.url}', error: ${this.error})`}},Ea=class extends vt{urlAfterRedirects;state;type=Fe.RoutesRecognized;constructor(n,e,r,o){super(n,e),this.urlAfterRedirects=r,this.state=o}toString(){return`RoutesRecognized(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`}},rd=class extends vt{urlAfterRedirects;state;type=Fe.GuardsCheckStart;constructor(n,e,r,o){super(n,e),this.urlAfterRedirects=r,this.state=o}toString(){return`GuardsCheckStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`}},od=class extends vt{urlAfterRedirects;state;shouldActivate;type=Fe.GuardsCheckEnd;constructor(n,e,r,o,i){super(n,e),this.urlAfterRedirects=r,this.state=o,this.shouldActivate=i}toString(){return`GuardsCheckEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state}, shouldActivate: ${this.shouldActivate})`}},id=class extends vt{urlAfterRedirects;state;type=Fe.ResolveStart;constructor(n,e,r,o){super(n,e),this.urlAfterRedirects=r,this.state=o}toString(){return`ResolveStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`}},sd=class extends vt{urlAfterRedirects;state;type=Fe.ResolveEnd;constructor(n,e,r,o){super(n,e),this.urlAfterRedirects=r,this.state=o}toString(){return`ResolveEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`}},ad=class{route;type=Fe.RouteConfigLoadStart;constructor(n){this.route=n}toString(){return`RouteConfigLoadStart(path: ${this.route.path})`}},cd=class{route;type=Fe.RouteConfigLoadEnd;constructor(n){this.route=n}toString(){return`RouteConfigLoadEnd(path: ${this.route.path})`}},ld=class{snapshot;type=Fe.ChildActivationStart;constructor(n){this.snapshot=n}toString(){return`ChildActivationStart(path: '${this.snapshot.routeConfig&&this.snapshot.routeConfig.path||""}')`}},ud=class{snapshot;type=Fe.ChildActivationEnd;constructor(n){this.snapshot=n}toString(){return`ChildActivationEnd(path: '${this.snapshot.routeConfig&&this.snapshot.routeConfig.path||""}')`}},dd=class{snapshot;type=Fe.ActivationStart;constructor(n){this.snapshot=n}toString(){return`ActivationStart(path: '${this.snapshot.routeConfig&&this.snapshot.routeConfig.path||""}')`}},fd=class{snapshot;type=Fe.ActivationEnd;constructor(n){this.snapshot=n}toString(){return`ActivationEnd(path: '${this.snapshot.routeConfig&&this.snapshot.routeConfig.path||""}')`}},Ci=class{routerEvent;position;anchor;scrollBehavior;type=Fe.Scroll;constructor(n,e,r,o){this.routerEvent=n,this.position=e,this.anchor=r,this.scrollBehavior=o}toString(){let n=this.position?`${this.position[0]}, ${this.position[1]}`:null;return`Scroll(anchor: '${this.anchor}', position: '${n}')`}},Ii=class{},wa=class{},Si=class{url;navigationBehaviorOptions;constructor(n,e){this.url=n,this.navigationBehaviorOptions=e}};function sP(t){return!(t instanceof Ii)&&!(t instanceof Si)&&!(t instanceof wa)}var hd=class{rootInjector;outlet=null;route=null;children;attachRef=null;get injector(){return this.route?.snapshot._environmentInjector??this.rootInjector}constructor(n){this.rootInjector=n,this.children=new _o(this.rootInjector)}},_o=(()=>{class t{rootInjector;contexts=new Map;constructor(e){this.rootInjector=e}onChildOutletCreated(e,r){let o=this.getOrCreateContext(e);o.outlet=r,this.contexts.set(e,o)}onChildOutletDestroyed(e){let r=this.getContext(e);r&&(r.outlet=null,r.attachRef=null)}onOutletDeactivated(){let e=this.contexts;return this.contexts=new Map,e}onOutletReAttached(e){this.contexts=e}getOrCreateContext(e){let r=this.getContext(e);return r||(r=new hd(this.rootInjector),this.contexts.set(e,r)),r}getContext(e){return this.contexts.get(e)||null}static \u0275fac=function(r){return new(r||t)(w(re))};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})(),pd=class{_root;constructor(n){this._root=n}get root(){return this._root.value}parent(n){let e=this.pathFromRoot(n);return e.length>1?e[e.length-2]:null}children(n){let e=Jg(n,this._root);return e?e.children.map(r=>r.value):[]}firstChild(n){let e=Jg(n,this._root);return e&&e.children.length>0?e.children[0].value:null}siblings(n){let e=ev(n,this._root);return e.length<2?[]:e[e.length-2].children.map(o=>o.value).filter(o=>o!==n)}pathFromRoot(n){return ev(n,this._root).map(e=>e.value)}};function Jg(t,n){if(t===n.value)return n;for(let e of n.children){let r=Jg(t,e);if(r)return r}return null}function ev(t,n){if(t===n.value)return[n];for(let e of n.children){let r=ev(t,e);if(r.length)return r.unshift(n),r}return[]}var gt=class{value;children;constructor(n,e){this.value=n,this.children=e}toString(){return`TreeNode(${this.value})`}};function bi(t){let n={};return t&&t.children.forEach(e=>n[e.value.outlet]=e),n}var Ca=class extends pd{snapshot;constructor(n,e){super(n),this.snapshot=e,dv(this,n)}toString(){return this.snapshot.toString()}};function BI(t,n){let e=aP(t,n),r=new Ie([new vr("",{})]),o=new Ie({}),i=new Ie({}),s=new Ie({}),a=new Ie(""),c=new Yn(r,o,s,a,i,V,t,e.root);return c.snapshot=e.root,new Ca(new gt(c,[]),e)}function aP(t,n){let e={},r={},o={},s=new Mi([],e,o,"",r,V,t,null,{},n);return new Ia("",new gt(s,[]))}var Yn=class{urlSubject;paramsSubject;queryParamsSubject;fragmentSubject;dataSubject;outlet;component;snapshot;_futureSnapshot;_routerState;_paramMap;_queryParamMap;title;url;params;queryParams;fragment;data;constructor(n,e,r,o,i,s,a,c){this.urlSubject=n,this.paramsSubject=e,this.queryParamsSubject=r,this.fragmentSubject=o,this.dataSubject=i,this.outlet=s,this.component=a,this._futureSnapshot=c,this.title=this.dataSubject?.pipe(H(l=>l[Ta]))??T(void 0),this.url=n,this.params=e,this.queryParams=r,this.fragment=o,this.data=i}get routeConfig(){return this._futureSnapshot.routeConfig}get root(){return this._routerState.root}get parent(){return this._routerState.parent(this)}get firstChild(){return this._routerState.firstChild(this)}get children(){return this._routerState.children(this)}get pathFromRoot(){return this._routerState.pathFromRoot(this)}get paramMap(){return this._paramMap??=this.params.pipe(H(n=>vo(n))),this._paramMap}get queryParamMap(){return this._queryParamMap??=this.queryParams.pipe(H(n=>vo(n))),this._queryParamMap}toString(){return this.snapshot?this.snapshot.toString():`Future(${this._futureSnapshot})`}};function uv(t,n,e="emptyOnly"){let r,{routeConfig:o}=t;return n!==null&&(e==="always"||o?.path===""||!n.component&&!n.routeConfig?.loadComponent)?r={params:g(g({},n.params),t.params),data:g(g({},n.data),t.data),resolve:g(g(g(g({},t.data),n.data),o?.data),t._resolvedData)}:r={params:g({},t.params),data:g({},t.data),resolve:g(g({},t.data),t._resolvedData??{})},o&&HI(o)&&(r.resolve[Ta]=o.title),r}var Mi=class{url;params;queryParams;fragment;data;outlet;component;routeConfig;_resolve;_resolvedData;_routerState;_paramMap;_queryParamMap;_environmentInjector;get title(){return this.data?.[Ta]}constructor(n,e,r,o,i,s,a,c,l,u){this.url=n,this.params=e,this.queryParams=r,this.fragment=o,this.data=i,this.outlet=s,this.component=a,this.routeConfig=c,this._resolve=l,this._environmentInjector=u}get root(){return this._routerState.root}get parent(){return this._routerState.parent(this)}get firstChild(){return this._routerState.firstChild(this)}get children(){return this._routerState.children(this)}get pathFromRoot(){return this._routerState.pathFromRoot(this)}get paramMap(){return this._paramMap??=vo(this.params),this._paramMap}get queryParamMap(){return this._queryParamMap??=vo(this.queryParams),this._queryParamMap}toString(){let n=this.url.map(r=>r.toString()).join("/"),e=this.routeConfig?this.routeConfig.path:"";return`Route(url:'${n}', path:'${e}')`}},Ia=class extends pd{url;constructor(n,e){super(e),this.url=n,dv(this,e)}toString(){return UI(this._root)}};function dv(t,n){n.value._routerState=t,n.children.forEach(e=>dv(t,e))}function UI(t){let n=t.children.length>0?` { ${t.children.map(UI).join(", ")} } `:"";return`${t.value}${n}`}function Wg(t){if(t.snapshot){let n=t.snapshot,e=t._futureSnapshot;t.snapshot=e,wn(n.queryParams,e.queryParams)||t.queryParamsSubject.next(e.queryParams),n.fragment!==e.fragment&&t.fragmentSubject.next(e.fragment),wn(n.params,e.params)||t.paramsSubject.next(e.params),FF(n.url,e.url)||t.urlSubject.next(e.url),wn(n.data,e.data)||t.dataSubject.next(e.data)}else t.snapshot=t._futureSnapshot,t.dataSubject.next(t._futureSnapshot.data)}function tv(t,n){let e=wn(t.params,n.params)&&BF(t.url,n.url),r=!t.parent!=!n.parent;return e&&!r&&(!t.parent||tv(t.parent,n.parent))}function HI(t){return typeof t.title=="string"||t.title===null}var $I=new y(""),fv=(()=>{class t{activated=null;get activatedComponentRef(){return this.activated}_activatedRoute=null;name=V;activateEvents=new U;deactivateEvents=new U;attachEvents=new U;detachEvents=new U;routerOutletData=RE();parentContexts=f(_o);location=f(qe);changeDetector=f(St);inputBinder=f(xa,{optional:!0});supportsBindingToComponentInputs=!0;ngOnChanges(e){if(e.name){let{firstChange:r,previousValue:o}=e.name;if(r)return;this.isTrackedInParentContexts(o)&&(this.deactivate(),this.parentContexts.onChildOutletDestroyed(o)),this.initializeOutletWithName()}}ngOnDestroy(){this.isTrackedInParentContexts(this.name)&&this.parentContexts.onChildOutletDestroyed(this.name),this.inputBinder?.unsubscribeFromRouteData(this)}isTrackedInParentContexts(e){return this.parentContexts.getContext(e)?.outlet===this}ngOnInit(){this.initializeOutletWithName()}initializeOutletWithName(){if(this.parentContexts.onChildOutletCreated(this.name,this),this.activated)return;let e=this.parentContexts.getContext(this.name);e?.route&&(e.attachRef?this.attach(e.attachRef,e.route):this.activateWith(e.route,e.injector))}get isActivated(){return!!this.activated}get component(){if(!this.activated)throw new b(4012,!1);return this.activated.instance}get activatedRoute(){if(!this.activated)throw new b(4012,!1);return this._activatedRoute}get activatedRouteData(){return this._activatedRoute?this._activatedRoute.snapshot.data:{}}detach(){if(!this.activated)throw new b(4012,!1);this.location.detach();let e=this.activated;return this.activated=null,this._activatedRoute=null,this.detachEvents.emit(e.instance),e}attach(e,r){this.activated=e,this._activatedRoute=r,this.location.insert(e.hostView),this.inputBinder?.bindActivatedRouteToOutletComponent(this),this.attachEvents.emit(e.instance)}deactivate(){if(this.activated){let e=this.component;this.activated.destroy(),this.activated=null,this._activatedRoute=null,this.deactivateEvents.emit(e)}}activateWith(e,r){if(this.isActivated)throw new b(4013,!1);this._activatedRoute=e;let o=this.location,s=e.snapshot.component,a=this.parentContexts.getOrCreateContext(this.name).children,c=new nv(e,a,o.injector,this.routerOutletData);this.activated=o.createComponent(s,{index:o.length,injector:c,environmentInjector:r}),this.changeDetector.markForCheck(),this.inputBinder?.bindActivatedRouteToOutletComponent(this),this.activateEvents.emit(this.activated.instance)}static \u0275fac=function(r){return new(r||t)};static \u0275dir=M({type:t,selectors:[["router-outlet"]],inputs:{name:"name",routerOutletData:[1,"routerOutletData"]},outputs:{activateEvents:"activate",deactivateEvents:"deactivate",attachEvents:"attach",detachEvents:"detach"},exportAs:["outlet"],features:[Re]})}return t})(),nv=class{route;childContexts;parent;outletData;constructor(n,e,r,o){this.route=n,this.childContexts=e,this.parent=r,this.outletData=o}get(n,e){return n===Yn?this.route:n===_o?this.childContexts:n===$I?this.outletData:this.parent.get(n,e)}},xa=new y(""),hv=(()=>{class t{outletDataSubscriptions=new Map;bindActivatedRouteToOutletComponent(e){this.unsubscribeFromRouteData(e),this.subscribeToRouteData(e)}unsubscribeFromRouteData(e){this.outletDataSubscriptions.get(e)?.unsubscribe(),this.outletDataSubscriptions.delete(e)}subscribeToRouteData(e){let{activatedRoute:r}=e,o=To([r.queryParams,r.params,r.data]).pipe(He(([i,s,a],c)=>(a=g(g(g({},i),s),a),c===0?T(a):Promise.resolve(a)))).subscribe(i=>{if(!e.isActivated||!e.activatedComponentRef||e.activatedRoute!==r||r.component===null){this.unsubscribeFromRouteData(e);return}let s=PE(r.component);if(!s){this.unsubscribeFromRouteData(e);return}for(let{templateName:a}of s.inputs)e.activatedComponentRef.setInput(a,i[a])});this.outletDataSubscriptions.set(e,o)}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac})}return t})(),pv=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275cmp=ke({type:t,selectors:[["ng-component"]],exportAs:["emptyRouterOutlet"],decls:1,vars:0,template:function(r,o){r&1&&Ol(0,"router-outlet")},dependencies:[fv],encapsulation:2})}return t})();function mv(t){let n=t.children&&t.children.map(mv),e=n?F(g({},t),{children:n}):g({},t);return!e.component&&!e.loadComponent&&(n||e.loadChildren)&&e.outlet&&e.outlet!==V&&(e.component=pv),e}function cP(t,n,e){let r=Sa(t,n._root,e?e._root:void 0);return new Ca(r,n)}function Sa(t,n,e){if(e&&t.shouldReuseRoute(n.value,e.value.snapshot)){let r=e.value;r._futureSnapshot=n.value;let o=lP(t,n,e);return new gt(r,o)}else{if(t.shouldAttach(n.value)){let i=t.retrieve(n.value);if(i!==null){let s=i.route;return s.value._futureSnapshot=n.value,s.children=n.children.map(a=>Sa(t,a)),s}}let r=uP(n.value),o=n.children.map(i=>Sa(t,i));return new gt(r,o)}}function lP(t,n,e){return n.children.map(r=>{for(let o of e.children)if(t.shouldReuseRoute(r.value,o.value.snapshot))return Sa(t,r,o);return Sa(t,r)})}function uP(t){return new Yn(new Ie(t.url),new Ie(t.params),new Ie(t.queryParams),new Ie(t.fragment),new Ie(t.data),t.outlet,t.component,t)}var Ti=class{redirectTo;navigationBehaviorOptions;constructor(n,e){this.redirectTo=n,this.navigationBehaviorOptions=e}},zI="ngNavigationCancelingError";function md(t,n){let{redirectTo:e,navigationBehaviorOptions:r}=yr(n)?{redirectTo:n,navigationBehaviorOptions:void 0}:n,o=GI(!1,Ze.Redirect);return o.url=e,o.navigationBehaviorOptions=r,o}function GI(t,n){let e=new Error(`NavigationCancelingError: ${t||""}`);return e[zI]=!0,e.cancellationCode=n,e}function dP(t){return WI(t)&&yr(t.url)}function WI(t){return!!t&&t[zI]}var rv=class{routeReuseStrategy;futureState;currState;forwardEvent;inputBindingEnabled;constructor(n,e,r,o,i){this.routeReuseStrategy=n,this.futureState=e,this.currState=r,this.forwardEvent=o,this.inputBindingEnabled=i}activate(n){let e=this.futureState._root,r=this.currState?this.currState._root:null;this.deactivateChildRoutes(e,r,n),Wg(this.futureState.root),this.activateChildRoutes(e,r,n)}deactivateChildRoutes(n,e,r){let o=bi(e);n.children.forEach(i=>{let s=i.value.outlet;this.deactivateRoutes(i,o[s],r),delete o[s]}),Object.values(o).forEach(i=>{this.deactivateRouteAndItsChildren(i,r)})}deactivateRoutes(n,e,r){let o=n.value,i=e?e.value:null;if(o===i)if(o.component){let s=r.getContext(o.outlet);s&&this.deactivateChildRoutes(n,e,s.children)}else this.deactivateChildRoutes(n,e,r);else i&&this.deactivateRouteAndItsChildren(e,r)}deactivateRouteAndItsChildren(n,e){n.value.component&&this.routeReuseStrategy.shouldDetach(n.value.snapshot)?this.detachAndStoreRouteSubtree(n,e):this.deactivateRouteAndOutlet(n,e)}detachAndStoreRouteSubtree(n,e){let r=e.getContext(n.value.outlet),o=r&&n.value.component?r.children:e,i=bi(n);for(let s of Object.values(i))this.deactivateRouteAndItsChildren(s,o);if(r&&r.outlet){let s=r.outlet.detach(),a=r.children.onOutletDeactivated();this.routeReuseStrategy.store(n.value.snapshot,{componentRef:s,route:n,contexts:a})}}deactivateRouteAndOutlet(n,e){let r=e.getContext(n.value.outlet),o=r&&n.value.component?r.children:e,i=bi(n);for(let s of Object.values(i))this.deactivateRouteAndItsChildren(s,o);r&&(r.outlet&&(r.outlet.deactivate(),r.children.onOutletDeactivated()),r.attachRef=null,r.route=null)}activateChildRoutes(n,e,r){let o=bi(e);n.children.forEach(i=>{this.activateRoutes(i,o[i.value.outlet],r),this.forwardEvent(new fd(i.value.snapshot))}),n.children.length&&this.forwardEvent(new ud(n.value.snapshot))}activateRoutes(n,e,r){let o=n.value,i=e?e.value:null;if(Wg(o),o===i)if(o.component){let s=r.getOrCreateContext(o.outlet);this.activateChildRoutes(n,e,s.children)}else this.activateChildRoutes(n,e,r);else if(o.component){let s=r.getOrCreateContext(o.outlet);if(this.routeReuseStrategy.shouldAttach(o.snapshot)){let a=this.routeReuseStrategy.retrieve(o.snapshot);this.routeReuseStrategy.store(o.snapshot,null),s.children.onOutletReAttached(a.contexts),s.attachRef=a.componentRef,s.route=a.route.value,s.outlet&&s.outlet.attach(a.componentRef,a.route.value),Wg(a.route.value),this.activateChildRoutes(n,null,s.children)}else s.attachRef=null,s.route=o,s.outlet&&s.outlet.activateWith(o,s.injector),this.activateChildRoutes(n,null,s.children)}else this.activateChildRoutes(n,null,r)}},gd=class{path;route;constructor(n){this.path=n,this.route=this.path[this.path.length-1]}},Ei=class{component;route;constructor(n,e){this.component=n,this.route=e}};function fP(t,n,e){let r=t._root,o=n?n._root:null;return va(r,o,e,[r.value])}function hP(t){let n=t.routeConfig?t.routeConfig.canActivateChild:null;return!n||n.length===0?null:{node:t,guards:n}}function Ai(t,n){let e=Symbol(),r=n.get(t,e);return r===e?typeof t=="function"&&!bf(t)?t:n.get(t):r}function va(t,n,e,r,o={canDeactivateChecks:[],canActivateChecks:[]}){let i=bi(n);return t.children.forEach(s=>{pP(s,i[s.value.outlet],e,r.concat([s.value]),o),delete i[s.value.outlet]}),Object.entries(i).forEach(([s,a])=>ba(a,e.getContext(s),o)),o}function pP(t,n,e,r,o={canDeactivateChecks:[],canActivateChecks:[]}){let i=t.value,s=n?n.value:null,a=e?e.getContext(t.value.outlet):null;if(s&&i.routeConfig===s.routeConfig){let c=mP(s,i,i.routeConfig.runGuardsAndResolvers);c?o.canActivateChecks.push(new gd(r)):(i.data=s.data,i._resolvedData=s._resolvedData),i.component?va(t,n,a?a.children:null,r,o):va(t,n,e,r,o),c&&a&&a.outlet&&a.outlet.isActivated&&o.canDeactivateChecks.push(new Ei(a.outlet.component,s))}else s&&ba(n,a,o),o.canActivateChecks.push(new gd(r)),i.component?va(t,null,a?a.children:null,r,o):va(t,null,e,r,o);return o}function mP(t,n,e){if(typeof e=="function")return xe(n._environmentInjector,()=>e(t,n));switch(e){case"pathParamsChange":return!go(t.url,n.url);case"pathParamsOrQueryParamsChange":return!go(t.url,n.url)||!wn(t.queryParams,n.queryParams);case"always":return!0;case"paramsOrQueryParamsChange":return!tv(t,n)||!wn(t.queryParams,n.queryParams);default:return!tv(t,n)}}function ba(t,n,e){let r=bi(t),o=t.value;Object.entries(r).forEach(([i,s])=>{o.component?n?ba(s,n.children.getContext(i),e):ba(s,null,e):ba(s,n,e)}),o.component?n&&n.outlet&&n.outlet.isActivated?e.canDeactivateChecks.push(new Ei(n.outlet.component,o)):e.canDeactivateChecks.push(new Ei(null,o)):e.canDeactivateChecks.push(new Ei(null,o))}function Aa(t){return typeof t=="function"}function gP(t){return typeof t=="boolean"}function vP(t){return t&&Aa(t.canLoad)}function yP(t){return t&&Aa(t.canActivate)}function bP(t){return t&&Aa(t.canActivateChild)}function _P(t){return t&&Aa(t.canDeactivate)}function DP(t){return t&&Aa(t.canMatch)}function qI(t){return t instanceof In||t?.name==="EmptyError"}var Ku=Symbol("INITIAL_VALUE");function xi(){return He(t=>To(t.map(n=>n.pipe(Ue(1),Nr(Ku)))).pipe(H(n=>{for(let e of n)if(e!==!0){if(e===Ku)return Ku;if(e===!1||EP(e))return e}return!0}),fe(n=>n!==Ku),Ue(1)))}function EP(t){return yr(t)||t instanceof Ti}function YI(t){return t.aborted?T(void 0).pipe(Ue(1)):new O(n=>{let e=()=>{n.next(),n.complete()};return t.addEventListener("abort",e),()=>t.removeEventListener("abort",e)})}function ZI(t){return at(YI(t))}function wP(t){return ve(n=>{let{targetSnapshot:e,currentSnapshot:r,guards:{canActivateChecks:o,canDeactivateChecks:i}}=n;return i.length===0&&o.length===0?T(F(g({},n),{guardsResult:!0})):CP(i,e,r).pipe(ve(s=>s&&gP(s)?IP(e,o,t):T(s)),H(s=>F(g({},n),{guardsResult:s})))})}function CP(t,n,e){return se(t).pipe(ve(r=>AP(r.component,r.route,e,n)),Sn(r=>r!==!0,!0))}function IP(t,n,e){return se(n).pipe(Xn(r=>on(MP(r.route.parent,e),SP(r.route,e),xP(t,r.path),TP(t,r.route))),Sn(r=>r!==!0,!0))}function SP(t,n){return t!==null&&n&&n(new dd(t)),T(!0)}function MP(t,n){return t!==null&&n&&n(new ld(t)),T(!0)}function TP(t,n){let e=n.routeConfig?n.routeConfig.canActivate:null;if(!e||e.length===0)return T(!0);let r=e.map(o=>Vi(()=>{let i=n._environmentInjector,s=Ai(o,i),a=yP(s)?s.canActivate(n,t):xe(i,()=>s(n,t));return bo(a).pipe(Sn())}));return T(r).pipe(xi())}function xP(t,n){let e=n[n.length-1],o=n.slice(0,n.length-1).reverse().map(i=>hP(i)).filter(i=>i!==null).map(i=>Vi(()=>{let s=i.guards.map(a=>{let c=i.node._environmentInjector,l=Ai(a,c),u=bP(l)?l.canActivateChild(e,t):xe(c,()=>l(e,t));return bo(u).pipe(Sn())});return T(s).pipe(xi())}));return T(o).pipe(xi())}function AP(t,n,e,r){let o=n&&n.routeConfig?n.routeConfig.canDeactivate:null;if(!o||o.length===0)return T(!0);let i=o.map(s=>{let a=n._environmentInjector,c=Ai(s,a),l=_P(c)?c.canDeactivate(t,n,e,r):xe(a,()=>c(t,n,e,r));return bo(l).pipe(Sn())});return T(i).pipe(xi())}function RP(t,n,e,r,o){let i=n.canLoad;if(i===void 0||i.length===0)return T(!0);let s=i.map(a=>{let c=Ai(a,t),l=vP(c)?c.canLoad(n,e):xe(t,()=>c(n,e)),u=bo(l);return o?u.pipe(ZI(o)):u});return T(s).pipe(xi(),KI(r))}function KI(t){return Nd(nt(n=>{if(typeof n!="boolean")throw md(t,n)}),H(n=>n===!0))}function NP(t,n,e,r,o,i){let s=n.canMatch;if(!s||s.length===0)return T(!0);let a=s.map(c=>{let l=Ai(c,t),u=DP(l)?l.canMatch(n,e,o):xe(t,()=>l(n,e,o));return bo(u).pipe(ZI(i))});return T(a).pipe(xi(),KI(r))}var Wn=class t extends Error{segmentGroup;constructor(n){super(),this.segmentGroup=n||null,Object.setPrototypeOf(this,t.prototype)}},Ma=class t extends Error{urlTree;constructor(n){super(),this.urlTree=n,Object.setPrototypeOf(this,t.prototype)}};function OP(t){throw new b(4e3,!1)}function kP(t){throw GI(!1,Ze.GuardRejected)}var ov=class{urlSerializer;urlTree;constructor(n,e){this.urlSerializer=n,this.urlTree=e}async lineralizeSegments(n,e){let r=[],o=e.root;for(;;){if(r=r.concat(o.segments),o.numberOfChildren===0)return r;if(o.numberOfChildren>1||!o.children[V])throw OP(`${n.redirectTo}`);o=o.children[V]}}async applyRedirectCommands(n,e,r,o,i){let s=await FP(e,o,i);if(s instanceof tt)throw new Ma(s);let a=this.applyRedirectCreateUrlTree(s,this.urlSerializer.parse(s),n,r);if(s[0]==="/")throw new Ma(a);return a}applyRedirectCreateUrlTree(n,e,r,o){let i=this.createSegmentGroup(n,e.root,r,o);return new tt(i,this.createQueryParams(e.queryParams,this.urlTree.queryParams),e.fragment)}createQueryParams(n,e){let r={};return Object.entries(n).forEach(([o,i])=>{if(typeof i=="string"&&i[0]===":"){let a=i.substring(1);r[o]=e[a]}else r[o]=i}),r}createSegmentGroup(n,e,r,o){let i=this.createSegments(n,e.segments,r,o),s={};return Object.entries(e.children).forEach(([a,c])=>{s[a]=this.createSegmentGroup(n,c,r,o)}),new te(i,s)}createSegments(n,e,r,o){return e.map(i=>i.path[0]===":"?this.findPosParam(n,i,o):this.findOrReturn(i,r))}findPosParam(n,e,r){let o=r[e.path.substring(1)];if(!o)throw new b(4001,!1);return o}findOrReturn(n,e){let r=0;for(let o of e){if(o.path===n.path)return e.splice(r),o;r++}return n}};function FP(t,n,e){if(typeof t=="string")return Promise.resolve(t);let r=t;return ed(bo(xe(e,()=>r(n))))}function PP(t,n){return t.providers&&!t._injector&&(t._injector=ni(t.providers,n,`Route: ${t.path}`)),t._injector??n}function tn(t){return t.outlet||V}function LP(t,n){let e=t.filter(r=>tn(r)===n);return e.push(...t.filter(r=>tn(r)!==n)),e}var iv={matched:!1,consumedSegments:[],remainingSegments:[],parameters:{},positionalParamSegments:{}};function QI(t){return{routeConfig:t.routeConfig,url:t.url,params:t.params,queryParams:t.queryParams,fragment:t.fragment,data:t.data,outlet:t.outlet,title:t.title,paramMap:t.paramMap,queryParamMap:t.queryParamMap}}function jP(t,n,e,r,o,i,s){let a=XI(t,n,e);if(!a.matched)return T(a);let c=QI(i(a));return r=PP(n,r),NP(r,n,e,o,c,s).pipe(H(l=>l===!0?a:g({},iv)))}function XI(t,n,e){if(n.path==="")return n.pathMatch==="full"&&(t.hasChildren()||e.length>0)?g({},iv):{matched:!0,consumedSegments:[],remainingSegments:e,parameters:{},positionalParamSegments:{}};let o=(n.matcher||wI)(e,t,n);if(!o)return g({},iv);let i={};Object.entries(o.posParams??{}).forEach(([a,c])=>{i[a]=c.path});let s=o.consumed.length>0?g(g({},i),o.consumed[o.consumed.length-1].parameters):i;return{matched:!0,consumedSegments:o.consumed,remainingSegments:e.slice(o.consumed.length),parameters:s,positionalParamSegments:o.posParams??{}}}function DI(t,n,e,r,o){return e.length>0&&UP(t,e,r,o)?{segmentGroup:new te(n,BP(r,new te(e,t.children))),slicedSegments:[]}:e.length===0&&HP(t,e,r)?{segmentGroup:new te(t.segments,VP(t,e,r,t.children)),slicedSegments:e}:{segmentGroup:new te(t.segments,t.children),slicedSegments:e}}function VP(t,n,e,r){let o={};for(let i of e)if(yd(t,n,i)&&!r[tn(i)]){let s=new te([],{});o[tn(i)]=s}return g(g({},r),o)}function BP(t,n){let e={};e[V]=n;for(let r of t)if(r.path===""&&tn(r)!==V){let o=new te([],{});e[tn(r)]=o}return e}function UP(t,n,e,r){return e.some(o=>!yd(t,n,o)||!(tn(o)!==V)?!1:!(r!==void 0&&tn(o)===r))}function HP(t,n,e){return e.some(r=>yd(t,n,r))}function yd(t,n,e){return(t.hasChildren()||n.length>0)&&e.pathMatch==="full"?!1:e.path===""}function $P(t,n,e){return n.length===0&&!t.children[e]}var sv=class{};async function zP(t,n,e,r,o,i,s="emptyOnly",a){return new av(t,n,e,r,o,s,i,a).recognize()}var GP=31,av=class{injector;configLoader;rootComponentType;config;urlTree;paramsInheritanceStrategy;urlSerializer;abortSignal;applyRedirects;absoluteRedirectCount=0;allowRedirects=!0;constructor(n,e,r,o,i,s,a,c){this.injector=n,this.configLoader=e,this.rootComponentType=r,this.config=o,this.urlTree=i,this.paramsInheritanceStrategy=s,this.urlSerializer=a,this.abortSignal=c,this.applyRedirects=new ov(this.urlSerializer,this.urlTree)}noMatchError(n){return new b(4002,`'${n.segmentGroup}'`)}async recognize(){let n=DI(this.urlTree.root,[],[],this.config).segmentGroup,{children:e,rootSnapshot:r}=await this.match(n),o=new gt(r,e),i=new Ia("",o),s=kI(r,[],this.urlTree.queryParams,this.urlTree.fragment);return s.queryParams=this.urlTree.queryParams,i.url=this.urlSerializer.serialize(s),{state:i,tree:s}}async match(n){let e=new Mi([],Object.freeze({}),Object.freeze(g({},this.urlTree.queryParams)),this.urlTree.fragment,Object.freeze({}),V,this.rootComponentType,null,{},this.injector);try{return{children:await this.processSegmentGroup(this.injector,this.config,n,V,e),rootSnapshot:e}}catch(r){if(r instanceof Ma)return this.urlTree=r.urlTree,this.match(r.urlTree.root);throw r instanceof Wn?this.noMatchError(r):r}}async processSegmentGroup(n,e,r,o,i){if(r.segments.length===0&&r.hasChildren())return this.processChildren(n,e,r,i);let s=await this.processSegment(n,e,r,r.segments,o,!0,i);return s instanceof gt?[s]:[]}async processChildren(n,e,r,o){let i=[];for(let c of Object.keys(r.children))c==="primary"?i.unshift(c):i.push(c);let s=[];for(let c of i){let l=r.children[c],u=LP(e,c),d=await this.processSegmentGroup(n,u,l,c,o);s.push(...d)}let a=JI(s);return WP(a),a}async processSegment(n,e,r,o,i,s,a){for(let c of e)try{return await this.processSegmentAgainstRoute(c._injector??n,e,c,r,o,i,s,a)}catch(l){if(l instanceof Wn||qI(l))continue;throw l}if($P(r,o,i))return new sv;throw new Wn(r)}async processSegmentAgainstRoute(n,e,r,o,i,s,a,c){if(tn(r)!==s&&(s===V||!yd(o,i,r)))throw new Wn(o);if(r.redirectTo===void 0)return this.matchSegmentAgainstRoute(n,o,r,i,s,c);if(this.allowRedirects&&a)return this.expandSegmentAgainstRouteUsingRedirect(n,o,e,r,i,s,c);throw new Wn(o)}async expandSegmentAgainstRouteUsingRedirect(n,e,r,o,i,s,a){let{matched:c,parameters:l,consumedSegments:u,positionalParamSegments:d,remainingSegments:h}=XI(e,o,i);if(!c)throw new Wn(e);typeof o.redirectTo=="string"&&o.redirectTo[0]==="/"&&(this.absoluteRedirectCount++,this.absoluteRedirectCount>GP&&(this.allowRedirects=!1));let p=this.createSnapshot(n,o,i,l,a);if(this.abortSignal.aborted)throw new Error(this.abortSignal.reason);let m=await this.applyRedirects.applyRedirectCommands(u,o.redirectTo,d,QI(p),n),_=await this.applyRedirects.lineralizeSegments(o,m);return this.processSegment(n,r,e,_.concat(h),s,!1,a)}createSnapshot(n,e,r,o,i){let s=new Mi(r,o,Object.freeze(g({},this.urlTree.queryParams)),this.urlTree.fragment,YP(e),tn(e),e.component??e._loadedComponent??null,e,ZP(e),n),a=uv(s,i,this.paramsInheritanceStrategy);return s.params=Object.freeze(a.params),s.data=Object.freeze(a.data),s}async matchSegmentAgainstRoute(n,e,r,o,i,s){if(this.abortSignal.aborted)throw new Error(this.abortSignal.reason);let a=Pe=>this.createSnapshot(n,r,Pe.consumedSegments,Pe.parameters,s),c=await ed(jP(e,r,o,n,this.urlSerializer,a,this.abortSignal));if(r.path==="**"&&(e.children={}),!c?.matched)throw new Wn(e);n=r._injector??n;let{routes:l}=await this.getChildConfig(n,r,o),u=r._loadedInjector??n,{parameters:d,consumedSegments:h,remainingSegments:p}=c,m=this.createSnapshot(n,r,h,d,s),{segmentGroup:_,slicedSegments:E}=DI(e,h,p,l,i);if(E.length===0&&_.hasChildren()){let Pe=await this.processChildren(u,l,_,m);return new gt(m,Pe)}if(l.length===0&&E.length===0)return new gt(m,[]);let I=tn(r)===i,ee=await this.processSegment(u,l,_,E,I?V:i,!0,m);return new gt(m,ee instanceof gt?[ee]:[])}async getChildConfig(n,e,r){if(e.children)return{routes:e.children,injector:n};if(e.loadChildren){if(e._loadedRoutes!==void 0){let i=e._loadedNgModuleFactory;return i&&!e._loadedInjector&&(e._loadedInjector=i.create(n).injector),{routes:e._loadedRoutes,injector:e._loadedInjector}}if(this.abortSignal.aborted)throw new Error(this.abortSignal.reason);if(await ed(RP(n,e,r,this.urlSerializer,this.abortSignal))){let i=await this.configLoader.loadChildren(n,e);return e._loadedRoutes=i.routes,e._loadedInjector=i.injector,e._loadedNgModuleFactory=i.factory,i}throw kP(e)}return{routes:[],injector:n}}};function WP(t){t.sort((n,e)=>n.value.outlet===V?-1:e.value.outlet===V?1:n.value.outlet.localeCompare(e.value.outlet))}function qP(t){let n=t.value.routeConfig;return n&&n.path===""}function JI(t){let n=[],e=new Set;for(let r of t){if(!qP(r)){n.push(r);continue}let o=n.find(i=>r.value.routeConfig===i.value.routeConfig);o!==void 0?(o.children.push(...r.children),e.add(o)):n.push(r)}for(let r of e){let o=JI(r.children);n.push(new gt(r.value,o))}return n.filter(r=>!e.has(r))}function YP(t){return t.data||{}}function ZP(t){return t.resolve||{}}function KP(t,n,e,r,o,i,s){return ve(async a=>{let{state:c,tree:l}=await zP(t,n,e,r,a.extractedUrl,o,i,s);return F(g({},a),{targetSnapshot:c,urlAfterRedirects:l})})}function QP(t){return ve(n=>{let{targetSnapshot:e,guards:{canActivateChecks:r}}=n;if(!r.length)return T(n);let o=new Set(r.map(a=>a.route)),i=new Set;for(let a of o)if(!i.has(a))for(let c of eS(a))i.add(c);let s=0;return se(i).pipe(Xn(a=>o.has(a)?XP(a,e,t):(a.data=uv(a,a.parent,t).resolve,T(void 0))),nt(()=>s++),ac(1),ve(a=>s===i.size?T(n):Se))})}function eS(t){let n=t.children.map(e=>eS(e)).flat();return[t,...n]}function XP(t,n,e){let r=t.routeConfig,o=t._resolve;return r?.title!==void 0&&!HI(r)&&(o[Ta]=r.title),Vi(()=>(t.data=uv(t,t.parent,e).resolve,JP(o,t,n).pipe(H(i=>(t._resolvedData=i,t.data=g(g({},t.data),i),null)))))}function JP(t,n,e){let r=Yg(t);if(r.length===0)return T({});let o={};return se(r).pipe(ve(i=>eL(t[i],n,e).pipe(Sn(),nt(s=>{if(s instanceof Ti)throw md(new qn,s);o[i]=s}))),ac(1),H(()=>o),sn(i=>qI(i)?Se:Tr(i)))}function eL(t,n,e){let r=n._environmentInjector,o=Ai(t,r),i=o.resolve?o.resolve(n,e):xe(r,()=>o(n,e));return bo(i)}function EI(t){return He(n=>{let e=t(n);return e?se(e).pipe(H(()=>n)):T(n)})}var gv=(()=>{class t{buildTitle(e){let r,o=e.root;for(;o!==void 0;)r=this.getResolvedTitleForRoute(o)??r,o=o.children.find(i=>i.outlet===V);return r}getResolvedTitleForRoute(e){return e.data[Ta]}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:()=>f(tS),providedIn:"root"})}return t})(),tS=(()=>{class t extends gv{title;constructor(e){super(),this.title=e}updateTitle(e){let r=this.buildTitle(e);r!==void 0&&this.title.setTitle(r)}static \u0275fac=function(r){return new(r||t)(w(xw))};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})(),Dr=new y("",{factory:()=>({})}),Ri=new y(""),bd=(()=>{class t{componentLoaders=new WeakMap;childrenLoaders=new WeakMap;onLoadStartListener;onLoadEndListener;compiler=f(Xp);async loadComponent(e,r){if(this.componentLoaders.get(r))return this.componentLoaders.get(r);if(r._loadedComponent)return Promise.resolve(r._loadedComponent);this.onLoadStartListener&&this.onLoadStartListener(r);let o=(async()=>{try{let i=await II(xe(e,()=>r.loadComponent())),s=await oS(rS(i));return this.onLoadEndListener&&this.onLoadEndListener(r),r._loadedComponent=s,s}finally{this.componentLoaders.delete(r)}})();return this.componentLoaders.set(r,o),o}loadChildren(e,r){if(this.childrenLoaders.get(r))return this.childrenLoaders.get(r);if(r._loadedRoutes)return Promise.resolve({routes:r._loadedRoutes,injector:r._loadedInjector});this.onLoadStartListener&&this.onLoadStartListener(r);let o=(async()=>{try{let i=await nS(r,this.compiler,e,this.onLoadEndListener);return r._loadedRoutes=i.routes,r._loadedInjector=i.injector,r._loadedNgModuleFactory=i.factory,i}finally{this.childrenLoaders.delete(r)}})();return this.childrenLoaders.set(r,o),o}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();async function nS(t,n,e,r){let o=await II(xe(e,()=>t.loadChildren())),i=await oS(rS(o)),s;i instanceof Sl||Array.isArray(i)?s=i:s=await n.compileModuleAsync(i),r&&r(t);let a,c,l=!1,u;return Array.isArray(s)?(c=s,l=!0):(a=s.create(e).injector,u=s,c=a.get(Ri,[],{optional:!0,self:!0}).flat()),{routes:c.map(mv),injector:a,factory:u}}function tL(t){return t&&typeof t=="object"&&"default"in t}function rS(t){return tL(t)?t.default:t}async function oS(t){return t}var _d=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:()=>f(nL),providedIn:"root"})}return t})(),nL=(()=>{class t{shouldProcessUrl(e){return!0}extract(e){return e}merge(e,r){return e}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})(),vv=new y(""),yv=new y("");function iS(t,n,e){let r=t.get(yv),o=t.get(L);if(!o.startViewTransition||r.skipNextTransition)return r.skipNextTransition=!1,new Promise(l=>setTimeout(l));let i,s=new Promise(l=>{i=l}),a=o.startViewTransition(()=>(i(),rL(t)));a.updateCallbackDone.catch(l=>{}),a.ready.catch(l=>{}),a.finished.catch(l=>{});let{onViewTransitionCreated:c}=r;return c&&xe(t,()=>c({transition:a,from:n,to:e})),s}function rL(t){return new Promise(n=>{ht({read:()=>setTimeout(n)},{injector:t})})}var oL=()=>{},bv=new y(""),Dd=(()=>{class t{currentNavigation=W(null,{equal:()=>!1});currentTransition=null;lastSuccessfulNavigation=W(null);events=new S;transitionAbortWithErrorSubject=new S;configLoader=f(bd);environmentInjector=f(re);destroyRef=f(Ae);urlSerializer=f(_r);rootContexts=f(_o);location=f(bn);inputBindingEnabled=f(xa,{optional:!0})!==null;titleStrategy=f(gv);options=f(Dr,{optional:!0})||{};paramsInheritanceStrategy=this.options.paramsInheritanceStrategy||"emptyOnly";urlHandlingStrategy=f(_d);createViewTransition=f(vv,{optional:!0});navigationErrorHandler=f(bv,{optional:!0});navigationId=0;get hasRequestedNavigation(){return this.navigationId!==0}transitions;afterPreactivation=()=>T(void 0);rootComponentType=null;destroyed=!1;constructor(){let e=o=>this.events.next(new ad(o)),r=o=>this.events.next(new cd(o));this.configLoader.onLoadEndListener=r,this.configLoader.onLoadStartListener=e,this.destroyRef.onDestroy(()=>{this.destroyed=!0})}complete(){this.transitions?.complete()}handleNavigationRequest(e){let r=++this.navigationId;q(()=>{this.transitions?.next(F(g({},e),{extractedUrl:this.urlHandlingStrategy.extract(e.rawUrl),targetSnapshot:null,targetRouterState:null,guards:{canActivateChecks:[],canDeactivateChecks:[]},guardsResult:null,id:r,routesRecognizeHandler:{},beforeActivateHandler:{}}))})}setupNavigations(e){return this.transitions=new Ie(null),this.transitions.pipe(fe(r=>r!==null),He(r=>{let o=!1,i=new AbortController,s=()=>!o&&this.currentTransition?.id===r.id;return T(r).pipe(He(a=>{if(this.navigationId>r.id)return this.cancelNavigationTransition(r,"",Ze.SupersededByNewNavigation),Se;this.currentTransition=r;let c=this.lastSuccessfulNavigation();this.currentNavigation.set({id:a.id,initialUrl:a.rawUrl,extractedUrl:a.extractedUrl,targetBrowserUrl:typeof a.extras.browserUrl=="string"?this.urlSerializer.parse(a.extras.browserUrl):a.extras.browserUrl,trigger:a.source,extras:a.extras,previousNavigation:c?F(g({},c),{previousNavigation:null}):null,abort:()=>i.abort(),routesRecognizeHandler:a.routesRecognizeHandler,beforeActivateHandler:a.beforeActivateHandler});let l=!e.navigated||this.isUpdatingInternalState()||this.isUpdatedBrowserUrl(),u=a.extras.onSameUrlNavigation??e.onSameUrlNavigation;if(!l&&u!=="reload")return this.events.next(new Cn(a.id,this.urlSerializer.serialize(a.rawUrl),"",wi.IgnoredSameUrlNavigation)),a.resolve(!1),Se;if(this.urlHandlingStrategy.shouldProcessUrl(a.rawUrl))return T(a).pipe(He(d=>(this.events.next(new br(d.id,this.urlSerializer.serialize(d.extractedUrl),d.source,d.restoredState)),d.id!==this.navigationId?Se:Promise.resolve(d))),KP(this.environmentInjector,this.configLoader,this.rootComponentType,e.config,this.urlSerializer,this.paramsInheritanceStrategy,i.signal),nt(d=>{r.targetSnapshot=d.targetSnapshot,r.urlAfterRedirects=d.urlAfterRedirects,this.currentNavigation.update(h=>(h.finalUrl=d.urlAfterRedirects,h)),this.events.next(new wa)}),He(d=>se(r.routesRecognizeHandler.deferredHandle??T(void 0)).pipe(H(()=>d))),nt(()=>{let d=new Ea(a.id,this.urlSerializer.serialize(a.extractedUrl),this.urlSerializer.serialize(a.urlAfterRedirects),a.targetSnapshot);this.events.next(d)}));if(l&&this.urlHandlingStrategy.shouldProcessUrl(a.currentRawUrl)){let{id:d,extractedUrl:h,source:p,restoredState:m,extras:_}=a,E=new br(d,this.urlSerializer.serialize(h),p,m);this.events.next(E);let I=BI(this.rootComponentType,this.environmentInjector).snapshot;return this.currentTransition=r=F(g({},a),{targetSnapshot:I,urlAfterRedirects:h,extras:F(g({},_),{skipLocationChange:!1,replaceUrl:!1})}),this.currentNavigation.update(ee=>(ee.finalUrl=h,ee)),T(r)}else return this.events.next(new Cn(a.id,this.urlSerializer.serialize(a.extractedUrl),"",wi.IgnoredByUrlHandlingStrategy)),a.resolve(!1),Se}),H(a=>{let c=new rd(a.id,this.urlSerializer.serialize(a.extractedUrl),this.urlSerializer.serialize(a.urlAfterRedirects),a.targetSnapshot);return this.events.next(c),this.currentTransition=r=F(g({},a),{guards:fP(a.targetSnapshot,a.currentSnapshot,this.rootContexts)}),r}),wP(a=>this.events.next(a)),He(a=>{if(r.guardsResult=a.guardsResult,a.guardsResult&&typeof a.guardsResult!="boolean")throw md(this.urlSerializer,a.guardsResult);let c=new od(a.id,this.urlSerializer.serialize(a.extractedUrl),this.urlSerializer.serialize(a.urlAfterRedirects),a.targetSnapshot,!!a.guardsResult);if(this.events.next(c),!s())return Se;if(!a.guardsResult)return this.cancelNavigationTransition(a,"",Ze.GuardRejected),Se;if(a.guards.canActivateChecks.length===0)return T(a);let l=new id(a.id,this.urlSerializer.serialize(a.extractedUrl),this.urlSerializer.serialize(a.urlAfterRedirects),a.targetSnapshot);if(this.events.next(l),!s())return Se;let u=!1;return T(a).pipe(QP(this.paramsInheritanceStrategy),nt({next:()=>{u=!0;let d=new sd(a.id,this.urlSerializer.serialize(a.extractedUrl),this.urlSerializer.serialize(a.urlAfterRedirects),a.targetSnapshot);this.events.next(d)},complete:()=>{u||this.cancelNavigationTransition(a,"",Ze.NoDataFromResolver)}}))}),EI(a=>{let c=u=>{let d=[];if(u.routeConfig?._loadedComponent)u.component=u.routeConfig?._loadedComponent;else if(u.routeConfig?.loadComponent){let h=u._environmentInjector;d.push(this.configLoader.loadComponent(h,u.routeConfig).then(p=>{u.component=p}))}for(let h of u.children)d.push(...c(h));return d},l=c(a.targetSnapshot.root);return l.length===0?T(a):se(Promise.all(l).then(()=>a))}),EI(()=>this.afterPreactivation()),He(()=>{let{currentSnapshot:a,targetSnapshot:c}=r,l=this.createViewTransition?.(this.environmentInjector,a.root,c.root);return l?se(l).pipe(H(()=>r)):T(r)}),Ue(1),He(a=>{let c=cP(e.routeReuseStrategy,a.targetSnapshot,a.currentRouterState);this.currentTransition=r=a=F(g({},a),{targetRouterState:c}),this.currentNavigation.update(u=>(u.targetRouterState=c,u)),this.events.next(new Ii);let l=r.beforeActivateHandler.deferredHandle;return l?se(l.then(()=>a)):T(a)}),nt(a=>{new rv(e.routeReuseStrategy,r.targetRouterState,r.currentRouterState,c=>this.events.next(c),this.inputBindingEnabled).activate(this.rootContexts),s()&&(o=!0,this.currentNavigation.update(c=>(c.abort=oL,c)),this.lastSuccessfulNavigation.set(q(this.currentNavigation)),this.events.next(new yt(a.id,this.urlSerializer.serialize(a.extractedUrl),this.urlSerializer.serialize(a.urlAfterRedirects))),this.titleStrategy?.updateTitle(a.targetRouterState.snapshot),a.resolve(!0))}),at(YI(i.signal).pipe(fe(()=>!o&&!r.targetRouterState),nt(()=>{this.cancelNavigationTransition(r,i.signal.reason+"",Ze.Aborted)}))),nt({complete:()=>{o=!0}}),at(this.transitionAbortWithErrorSubject.pipe(nt(a=>{throw a}))),Rr(()=>{i.abort(),o||this.cancelNavigationTransition(r,"",Ze.SupersededByNewNavigation),this.currentTransition?.id===r.id&&(this.currentNavigation.set(null),this.currentTransition=null)}),sn(a=>{if(o=!0,this.destroyed)return r.resolve(!1),Se;if(WI(a))this.events.next(new Rt(r.id,this.urlSerializer.serialize(r.extractedUrl),a.message,a.cancellationCode)),dP(a)?this.events.next(new Si(a.url,a.navigationBehaviorOptions)):r.resolve(!1);else{let c=new yo(r.id,this.urlSerializer.serialize(r.extractedUrl),a,r.targetSnapshot??void 0);try{let l=xe(this.environmentInjector,()=>this.navigationErrorHandler?.(c));if(l instanceof Ti){let{message:u,cancellationCode:d}=md(this.urlSerializer,l);this.events.next(new Rt(r.id,this.urlSerializer.serialize(r.extractedUrl),u,d)),this.events.next(new Si(l.redirectTo,l.navigationBehaviorOptions))}else throw this.events.next(c),a}catch(l){this.options.resolveNavigationPromiseOnError?r.resolve(!1):r.reject(l)}}return Se}))}))}cancelNavigationTransition(e,r,o){let i=new Rt(e.id,this.urlSerializer.serialize(e.extractedUrl),r,o);this.events.next(i),e.resolve(!1)}isUpdatingInternalState(){return this.currentTransition?.extractedUrl.toString()!==this.currentTransition?.currentUrlTree.toString()}isUpdatedBrowserUrl(){let e=this.urlHandlingStrategy.extract(this.urlSerializer.parse(this.location.path(!0))),r=q(this.currentNavigation),o=r?.targetBrowserUrl??r?.extractedUrl;return e.toString()!==o?.toString()&&!r?.extras.skipLocationChange}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();function iL(t){return t!==Di}var sS=new y("");var aS=(()=>{class t{static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:()=>f(sL),providedIn:"root"})}return t})(),vd=class{shouldDetach(n){return!1}store(n,e){}shouldAttach(n){return!1}retrieve(n){return null}shouldReuseRoute(n,e){return n.routeConfig===e.routeConfig}shouldDestroyInjector(n){return!0}},sL=(()=>{class t extends vd{static \u0275fac=(()=>{let e;return function(o){return(e||(e=Ne(t)))(o||t)}})();static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})(),Ed=(()=>{class t{urlSerializer=f(_r);options=f(Dr,{optional:!0})||{};canceledNavigationResolution=this.options.canceledNavigationResolution||"replace";location=f(bn);urlHandlingStrategy=f(_d);urlUpdateStrategy=this.options.urlUpdateStrategy||"deferred";currentUrlTree=new tt;getCurrentUrlTree(){return this.currentUrlTree}rawUrlTree=this.currentUrlTree;getRawUrlTree(){return this.rawUrlTree}createBrowserPath({finalUrl:e,initialUrl:r,targetBrowserUrl:o}){let i=e!==void 0?this.urlHandlingStrategy.merge(e,r):r,s=o??i;return s instanceof tt?this.urlSerializer.serialize(s):s}routerUrlState(e){return e?.targetBrowserUrl===void 0||e?.finalUrl===void 0?{}:{\u0275routerUrl:this.urlSerializer.serialize(e.finalUrl)}}commitTransition({targetRouterState:e,finalUrl:r,initialUrl:o}){r&&e?(this.currentUrlTree=r,this.rawUrlTree=this.urlHandlingStrategy.merge(r,o),this.routerState=e):this.rawUrlTree=o}routerState=BI(null,f(re));getRouterState(){return this.routerState}_stateMemento=this.createStateMemento();get stateMemento(){return this._stateMemento}updateStateMemento(){this._stateMemento=this.createStateMemento()}createStateMemento(){return{rawUrlTree:this.rawUrlTree,currentUrlTree:this.currentUrlTree,routerState:this.routerState}}restoredState(){return this.location.getState()}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:()=>f(aL),providedIn:"root"})}return t})(),aL=(()=>{class t extends Ed{currentPageId=0;lastSuccessfulId=-1;get browserPageId(){return this.canceledNavigationResolution!=="computed"?this.currentPageId:this.restoredState()?.\u0275routerPageId??this.currentPageId}registerNonRouterCurrentEntryChangeListener(e){return this.location.subscribe(r=>{r.type==="popstate"&&setTimeout(()=>{e(r.url,r.state,"popstate",{replaceUrl:!0})})})}handleRouterEvent(e,r){e instanceof br?this.updateStateMemento():e instanceof Cn?this.commitTransition(r):e instanceof Ea?this.urlUpdateStrategy==="eager"&&(r.extras.skipLocationChange||this.setBrowserUrl(this.createBrowserPath(r),r)):e instanceof Ii?(this.commitTransition(r),this.urlUpdateStrategy==="deferred"&&!r.extras.skipLocationChange&&this.setBrowserUrl(this.createBrowserPath(r),r)):e instanceof Rt&&!VI(e)?this.restoreHistory(r):e instanceof yo?this.restoreHistory(r,!0):e instanceof yt&&(this.lastSuccessfulId=e.id,this.currentPageId=this.browserPageId)}setBrowserUrl(e,r){let{extras:o,id:i}=r,{replaceUrl:s,state:a}=o;if(this.location.isCurrentPathEqualTo(e)||s){let c=this.browserPageId,l=g(g({},a),this.generateNgRouterState(i,c,r));this.location.replaceState(e,"",l)}else{let c=g(g({},a),this.generateNgRouterState(i,this.browserPageId+1,r));this.location.go(e,"",c)}}restoreHistory(e,r=!1){if(this.canceledNavigationResolution==="computed"){let o=this.browserPageId,i=this.currentPageId-o;i!==0?this.location.historyGo(i):this.getCurrentUrlTree()===e.finalUrl&&i===0&&(this.resetInternalState(e),this.resetUrlToCurrentUrlTree())}else this.canceledNavigationResolution==="replace"&&(r&&this.resetInternalState(e),this.resetUrlToCurrentUrlTree())}resetInternalState({finalUrl:e}){this.routerState=this.stateMemento.routerState,this.currentUrlTree=this.stateMemento.currentUrlTree,this.rawUrlTree=this.urlHandlingStrategy.merge(this.currentUrlTree,e??this.rawUrlTree)}resetUrlToCurrentUrlTree(){this.location.replaceState(this.urlSerializer.serialize(this.getRawUrlTree()),"",this.generateNgRouterState(this.lastSuccessfulId,this.currentPageId))}generateNgRouterState(e,r,o){return this.canceledNavigationResolution==="computed"?g({navigationId:e,\u0275routerPageId:r},this.routerUrlState(o)):g({navigationId:e},this.routerUrlState(o))}static \u0275fac=(()=>{let e;return function(o){return(e||(e=Ne(t)))(o||t)}})();static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();function wd(t,n){t.events.pipe(fe(e=>e instanceof yt||e instanceof Rt||e instanceof yo||e instanceof Cn),H(e=>e instanceof yt||e instanceof Cn?0:(e instanceof Rt?e.code===Ze.Redirect||e.code===Ze.SupersededByNewNavigation:!1)?2:1),fe(e=>e!==2),Ue(1)).subscribe(()=>{n()})}var bt=(()=>{class t{get currentUrlTree(){return this.stateManager.getCurrentUrlTree()}get rawUrlTree(){return this.stateManager.getRawUrlTree()}disposed=!1;nonRouterCurrentEntryChangeSubscription;console=f(Ml);stateManager=f(Ed);options=f(Dr,{optional:!0})||{};pendingTasks=f(kn);urlUpdateStrategy=this.options.urlUpdateStrategy||"deferred";navigationTransitions=f(Dd);urlSerializer=f(_r);location=f(bn);urlHandlingStrategy=f(_d);injector=f(re);_events=new S;get events(){return this._events}get routerState(){return this.stateManager.getRouterState()}navigated=!1;routeReuseStrategy=f(aS);injectorCleanup=f(sS,{optional:!0});onSameUrlNavigation=this.options.onSameUrlNavigation||"ignore";config=f(Ri,{optional:!0})?.flat()??[];componentInputBindingEnabled=!!f(xa,{optional:!0});currentNavigation=this.navigationTransitions.currentNavigation.asReadonly();constructor(){this.resetConfig(this.config),this.navigationTransitions.setupNavigations(this).subscribe({error:e=>{}}),this.subscribeToNavigationEvents()}eventsSubscription=new G;subscribeToNavigationEvents(){let e=this.navigationTransitions.events.subscribe(r=>{try{let o=this.navigationTransitions.currentTransition,i=q(this.navigationTransitions.currentNavigation);if(o!==null&&i!==null){if(this.stateManager.handleRouterEvent(r,i),r instanceof Rt&&r.code!==Ze.Redirect&&r.code!==Ze.SupersededByNewNavigation)this.navigated=!0;else if(r instanceof yt)this.navigated=!0,this.injectorCleanup?.(this.routeReuseStrategy,this.routerState,this.config);else if(r instanceof Si){let s=r.navigationBehaviorOptions,a=this.urlHandlingStrategy.merge(r.url,o.currentRawUrl),c=g({scroll:o.extras.scroll,browserUrl:o.extras.browserUrl,info:o.extras.info,skipLocationChange:o.extras.skipLocationChange,replaceUrl:o.extras.replaceUrl||this.urlUpdateStrategy==="eager"||iL(o.source)},s);this.scheduleNavigation(a,Di,null,c,{resolve:o.resolve,reject:o.reject,promise:o.promise})}}sP(r)&&this._events.next(r)}catch(o){this.navigationTransitions.transitionAbortWithErrorSubject.next(o)}});this.eventsSubscription.add(e)}resetRootComponentType(e){this.routerState.root.component=e,this.navigationTransitions.rootComponentType=e}initialNavigation(){this.setUpLocationChangeListener(),this.navigationTransitions.hasRequestedNavigation||this.navigateToSyncWithBrowser(this.location.path(!0),Di,this.stateManager.restoredState(),{replaceUrl:!0})}setUpLocationChangeListener(){this.nonRouterCurrentEntryChangeSubscription??=this.stateManager.registerNonRouterCurrentEntryChangeListener((e,r,o,i)=>{this.navigateToSyncWithBrowser(e,o,r,i)})}navigateToSyncWithBrowser(e,r,o,i){let s=o?.navigationId?o:null,a=o?.\u0275routerUrl??e;if(o?.\u0275routerUrl&&(i=F(g({},i),{browserUrl:e})),o){let l=g({},o);delete l.navigationId,delete l.\u0275routerPageId,delete l.\u0275routerUrl,Object.keys(l).length!==0&&(i.state=l)}let c=this.parseUrl(a);this.scheduleNavigation(c,r,s,i).catch(l=>{this.disposed||this.injector.get(ut)(l)})}get url(){return this.serializeUrl(this.currentUrlTree)}getCurrentNavigation(){return q(this.navigationTransitions.currentNavigation)}get lastSuccessfulNavigation(){return this.navigationTransitions.lastSuccessfulNavigation}resetConfig(e){this.config=e.map(mv),this.navigated=!1}ngOnDestroy(){this.dispose()}dispose(){this._events.unsubscribe(),this.navigationTransitions.complete(),this.nonRouterCurrentEntryChangeSubscription?.unsubscribe(),this.nonRouterCurrentEntryChangeSubscription=void 0,this.disposed=!0,this.eventsSubscription.unsubscribe()}createUrlTree(e,r={}){let{relativeTo:o,queryParams:i,fragment:s,queryParamsHandling:a,preserveFragment:c}=r,l=c?this.currentUrlTree.fragment:s,u=null;switch(a??this.options.defaultQueryParamsHandling){case"merge":u=g(g({},this.currentUrlTree.queryParams),i);break;case"preserve":u=this.currentUrlTree.queryParams;break;default:u=i||null}u!==null&&(u=this.removeEmptyProps(u));let d;try{let h=o?o.snapshot:this.routerState.snapshot.root;d=FI(h)}catch{(typeof e[0]!="string"||e[0][0]!=="/")&&(e=[]),d=this.currentUrlTree.root}return PI(d,e,u,l??null,this.urlSerializer)}navigateByUrl(e,r={skipLocationChange:!1}){let o=yr(e)?e:this.parseUrl(e),i=this.urlHandlingStrategy.merge(o,this.rawUrlTree);return this.scheduleNavigation(i,Di,null,r)}navigate(e,r={skipLocationChange:!1}){return cL(e),this.navigateByUrl(this.createUrlTree(e,r),r)}serializeUrl(e){return this.urlSerializer.serialize(e)}parseUrl(e){try{return this.urlSerializer.parse(e)}catch{return this.console.warn(Dt(4018,!1)),this.urlSerializer.parse("/")}}isActive(e,r){let o;if(r===!0?o=g({},cv):r===!1?o=g({},_a):o=g(g({},_a),r),yr(e))return Zg(this.currentUrlTree,e,o);let i=this.parseUrl(e);return Zg(this.currentUrlTree,i,o)}removeEmptyProps(e){return Object.entries(e).reduce((r,[o,i])=>(i!=null&&(r[o]=i),r),{})}scheduleNavigation(e,r,o,i,s){if(this.disposed)return Promise.resolve(!1);let a,c,l;s?(a=s.resolve,c=s.reject,l=s.promise):l=new Promise((d,h)=>{a=d,c=h});let u=this.pendingTasks.add();return wd(this,()=>{queueMicrotask(()=>this.pendingTasks.remove(u))}),this.navigationTransitions.handleNavigationRequest({source:r,restoredState:o,currentUrlTree:this.currentUrlTree,currentRawUrl:this.currentUrlTree,rawUrl:e,extras:i,resolve:a,reject:c,promise:l,currentSnapshot:this.routerState.snapshot,currentRouterState:this.routerState}),l.catch(Promise.reject.bind(Promise))}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();function cL(t){for(let n=0;n{class t{router=f(bt);stateManager=f(Ed);fragment=W("");queryParams=W({});path=W("");serializer=f(_r);constructor(){this.updateState(),this.router.events?.subscribe(e=>{e instanceof yt&&this.updateState()})}updateState(){let{fragment:e,root:r,queryParams:o}=this.stateManager.getCurrentUrlTree();this.fragment.set(e),this.queryParams.set(o),this.path.set(this.serializer.serialize(new tt(r)))}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})(),Cd=(()=>{class t{router;route;tabIndexAttribute;renderer;el;locationStrategy;hrefAttributeValue=f(new Vl("href"),{optional:!0});reactiveHref=em(()=>this.isAnchorElement?this.computeHref(this._urlTree()):this.hrefAttributeValue);get href(){return q(this.reactiveHref)}set href(e){this.reactiveHref.set(e)}set target(e){this._target.set(e)}get target(){return q(this._target)}_target=W(void 0);set queryParams(e){this._queryParams.set(e)}get queryParams(){return q(this._queryParams)}_queryParams=W(void 0,{equal:()=>!1});set fragment(e){this._fragment.set(e)}get fragment(){return q(this._fragment)}_fragment=W(void 0);set queryParamsHandling(e){this._queryParamsHandling.set(e)}get queryParamsHandling(){return q(this._queryParamsHandling)}_queryParamsHandling=W(void 0);set state(e){this._state.set(e)}get state(){return q(this._state)}_state=W(void 0,{equal:()=>!1});set info(e){this._info.set(e)}get info(){return q(this._info)}_info=W(void 0,{equal:()=>!1});set relativeTo(e){this._relativeTo.set(e)}get relativeTo(){return q(this._relativeTo)}_relativeTo=W(void 0);set preserveFragment(e){this._preserveFragment.set(e)}get preserveFragment(){return q(this._preserveFragment)}_preserveFragment=W(!1);set skipLocationChange(e){this._skipLocationChange.set(e)}get skipLocationChange(){return q(this._skipLocationChange)}_skipLocationChange=W(!1);set replaceUrl(e){this._replaceUrl.set(e)}get replaceUrl(){return q(this._replaceUrl)}_replaceUrl=W(!1);isAnchorElement;onChanges=new S;applicationErrorHandler=f(ut);options=f(Dr,{optional:!0});reactiveRouterState=f(lL);constructor(e,r,o,i,s,a){this.router=e,this.route=r,this.tabIndexAttribute=o,this.renderer=i,this.el=s,this.locationStrategy=a;let c=s.nativeElement.tagName?.toLowerCase();this.isAnchorElement=c==="a"||c==="area"||!!(typeof customElements=="object"&&customElements.get(c)?.observedAttributes?.includes?.("href"))}setTabIndexIfNotOnNativeEl(e){this.tabIndexAttribute!=null||this.isAnchorElement||this.applyAttributeValue("tabindex",e)}ngOnChanges(e){this.onChanges.next(this)}routerLinkInput=W(null);set routerLink(e){e==null?(this.routerLinkInput.set(null),this.setTabIndexIfNotOnNativeEl(null)):(yr(e)?this.routerLinkInput.set(e):this.routerLinkInput.set(Array.isArray(e)?e:[e]),this.setTabIndexIfNotOnNativeEl("0"))}onClick(e,r,o,i,s){let a=this._urlTree();if(a===null||this.isAnchorElement&&(e!==0||r||o||i||s||typeof this.target=="string"&&this.target!="_self"))return!0;let c={skipLocationChange:this.skipLocationChange,replaceUrl:this.replaceUrl,state:this.state,info:this.info};return this.router.navigateByUrl(a,c)?.catch(l=>{this.applicationErrorHandler(l)}),!this.isAnchorElement}ngOnDestroy(){}applyAttributeValue(e,r){let o=this.renderer,i=this.el.nativeElement;r!==null?o.setAttribute(i,e,r):o.removeAttribute(i,e)}_urlTree=Kt(()=>{this.reactiveRouterState.path(),this._preserveFragment()&&this.reactiveRouterState.fragment();let e=o=>o==="preserve"||o==="merge";(e(this._queryParamsHandling())||e(this.options?.defaultQueryParamsHandling))&&this.reactiveRouterState.queryParams();let r=this.routerLinkInput();return r===null||!this.router.createUrlTree?null:yr(r)?r:this.router.createUrlTree(r,{relativeTo:this._relativeTo()!==void 0?this._relativeTo():this.route,queryParams:this._queryParams(),fragment:this._fragment(),queryParamsHandling:this._queryParamsHandling(),preserveFragment:this._preserveFragment()})},{equal:(e,r)=>this.computeHref(e)===this.computeHref(r)});get urlTree(){return q(this._urlTree)}computeHref(e){return e!==null&&this.locationStrategy?this.locationStrategy?.prepareExternalUrl(this.router.serializeUrl(e))??"":null}static \u0275fac=function(r){return new(r||t)(D(bt),D(Yn),Cs("tabindex"),D(Oe),D(z),D(Xt))};static \u0275dir=M({type:t,selectors:[["","routerLink",""]],hostVars:2,hostBindings:function(r,o){r&1&&Zt("click",function(s){return o.onClick(s.button,s.ctrlKey,s.shiftKey,s.altKey,s.metaKey)}),r&2&&Yt("href",o.reactiveHref(),hp)("target",o._target())},inputs:{target:"target",queryParams:"queryParams",fragment:"fragment",queryParamsHandling:"queryParamsHandling",state:"state",info:"info",relativeTo:"relativeTo",preserveFragment:[2,"preserveFragment","preserveFragment",ue],skipLocationChange:[2,"skipLocationChange","skipLocationChange",ue],replaceUrl:[2,"replaceUrl","replaceUrl",ue],routerLink:"routerLink"},features:[Re]})}return t})(),uL=(()=>{class t{router;element;renderer;cdr;links;classes=[];routerEventsSubscription;linkInputChangesSubscription;_isActive=!1;get isActive(){return this._isActive}routerLinkActiveOptions={exact:!1};ariaCurrentWhenActive;isActiveChange=new U;link=f(Cd,{optional:!0});constructor(e,r,o,i){this.router=e,this.element=r,this.renderer=o,this.cdr=i,this.routerEventsSubscription=e.events.subscribe(s=>{s instanceof yt&&this.update()})}ngAfterContentInit(){T(this.links.changes,T(null)).pipe(rn()).subscribe(e=>{this.update(),this.subscribeToEachLinkOnChanges()})}subscribeToEachLinkOnChanges(){this.linkInputChangesSubscription?.unsubscribe();let e=[...this.links.toArray(),this.link].filter(r=>!!r).map(r=>r.onChanges);this.linkInputChangesSubscription=se(e).pipe(rn()).subscribe(r=>{this._isActive!==this.isLinkActive(this.router)(r)&&this.update()})}set routerLinkActive(e){let r=Array.isArray(e)?e:e.split(" ");this.classes=r.filter(o=>!!o)}ngOnChanges(e){this.update()}ngOnDestroy(){this.routerEventsSubscription.unsubscribe(),this.linkInputChangesSubscription?.unsubscribe()}update(){!this.links||!this.router.navigated||queueMicrotask(()=>{let e=this.hasActiveLinks();this.classes.forEach(r=>{e?this.renderer.addClass(this.element.nativeElement,r):this.renderer.removeClass(this.element.nativeElement,r)}),e&&this.ariaCurrentWhenActive!==void 0?this.renderer.setAttribute(this.element.nativeElement,"aria-current",this.ariaCurrentWhenActive.toString()):this.renderer.removeAttribute(this.element.nativeElement,"aria-current"),this._isActive!==e&&(this._isActive=e,this.cdr.markForCheck(),this.isActiveChange.emit(e))})}isLinkActive(e){let r=dL(this.routerLinkActiveOptions)?this.routerLinkActiveOptions:this.routerLinkActiveOptions.exact??!1?g({},cv):g({},_a);return o=>{let i=o.urlTree;return i?q(lv(i,e,r)):!1}}hasActiveLinks(){let e=this.isLinkActive(this.router);return this.link&&e(this.link)||this.links.some(e)}static \u0275fac=function(r){return new(r||t)(D(bt),D(z),D(Oe),D(St))};static \u0275dir=M({type:t,selectors:[["","routerLinkActive",""]],contentQueries:function(r,o,i){if(r&1&&kl(i,Cd,5),r&2){let s;Fs(s=Ps())&&(o.links=s)}},inputs:{routerLinkActiveOptions:"routerLinkActiveOptions",ariaCurrentWhenActive:"ariaCurrentWhenActive",routerLinkActive:"routerLinkActive"},outputs:{isActiveChange:"isActiveChange"},exportAs:["routerLinkActive"],features:[Re]})}return t})();function dL(t){let n=t;return!!(n.paths||n.matrixParams||n.queryParams||n.fragment)}var Ra=class{};var cS=(()=>{class t{router;injector;preloadingStrategy;loader;subscription;constructor(e,r,o,i){this.router=e,this.injector=r,this.preloadingStrategy=o,this.loader=i}setUpPreloading(){this.subscription=this.router.events.pipe(fe(e=>e instanceof yt),Xn(()=>this.preload())).subscribe(()=>{})}preload(){return this.processRoutes(this.injector,this.router.config)}ngOnDestroy(){this.subscription?.unsubscribe()}processRoutes(e,r){let o=[];for(let i of r){i.providers&&!i._injector&&(i._injector=ni(i.providers,e,""));let s=i._injector??e;i._loadedNgModuleFactory&&!i._loadedInjector&&(i._loadedInjector=i._loadedNgModuleFactory.create(s).injector);let a=i._loadedInjector??s;(i.loadChildren&&!i._loadedRoutes&&i.canLoad===void 0||i.loadComponent&&!i._loadedComponent)&&o.push(this.preloadConfig(s,i)),(i.children||i._loadedRoutes)&&o.push(this.processRoutes(a,i.children??i._loadedRoutes))}return se(o).pipe(rn())}preloadConfig(e,r){return this.preloadingStrategy.preload(r,()=>{if(e.destroyed)return T(null);let o;r.loadChildren&&r.canLoad===void 0?o=se(this.loader.loadChildren(e,r)):o=T(null);let i=o.pipe(ve(s=>s===null?T(void 0):(r._loadedRoutes=s.routes,r._loadedInjector=s.injector,r._loadedNgModuleFactory=s.factory,this.processRoutes(s.injector??e,s.routes))));if(r.loadComponent&&!r._loadedComponent){let s=this.loader.loadComponent(e,r);return se([i,s]).pipe(rn())}else return i})}static \u0275fac=function(r){return new(r||t)(w(bt),w(re),w(Ra),w(bd))};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})(),lS=new y(""),fL=(()=>{class t{options;routerEventsSubscription;scrollEventsSubscription;lastId=0;lastSource=Di;restoredId=0;store={};isHydrating=f(sp,{optional:!0})??!1;urlSerializer=f(_r);zone=f(j);viewportScroller=f(Mm);transitions=f(Dd);constructor(e){this.options=e,this.options.scrollPositionRestoration||="disabled",this.options.anchorScrolling||="disabled",this.isHydrating&&f(Be).whenStable().then(()=>{this.isHydrating=!1})}init(){this.options.scrollPositionRestoration!=="disabled"&&this.viewportScroller.setHistoryScrollRestoration("manual"),this.routerEventsSubscription=this.createScrollEvents(),this.scrollEventsSubscription=this.consumeScrollEvents()}createScrollEvents(){return this.transitions.events.subscribe(e=>{e instanceof br?(this.store[this.lastId]=this.viewportScroller.getScrollPosition(),this.lastSource=e.navigationTrigger,this.restoredId=e.restoredState?e.restoredState.navigationId:0):e instanceof yt?(this.lastId=e.id,this.scheduleScrollEvent(e,this.urlSerializer.parse(e.urlAfterRedirects).fragment)):e instanceof Cn&&e.code===wi.IgnoredSameUrlNavigation&&(this.lastSource=void 0,this.restoredId=0,this.scheduleScrollEvent(e,this.urlSerializer.parse(e.url).fragment))})}consumeScrollEvents(){return this.transitions.events.subscribe(e=>{if(!(e instanceof Ci)||e.scrollBehavior==="manual")return;let r={behavior:"instant"};e.position?this.options.scrollPositionRestoration==="top"?this.viewportScroller.scrollToPosition([0,0],r):this.options.scrollPositionRestoration==="enabled"&&this.viewportScroller.scrollToPosition(e.position,r):e.anchor&&this.options.anchorScrolling==="enabled"?this.viewportScroller.scrollToAnchor(e.anchor):this.options.scrollPositionRestoration!=="disabled"&&this.viewportScroller.scrollToPosition([0,0])})}scheduleScrollEvent(e,r){if(this.isHydrating)return;let o=q(this.transitions.currentNavigation)?.extras.scroll;this.zone.runOutsideAngular(async()=>{await new Promise(i=>{setTimeout(i),typeof requestAnimationFrame<"u"&&requestAnimationFrame(i)}),this.zone.run(()=>{this.transitions.events.next(new Ci(e,this.lastSource==="popstate"?this.store[this.restoredId]:null,r,o))})})}ngOnDestroy(){this.routerEventsSubscription?.unsubscribe(),this.scrollEventsSubscription?.unsubscribe()}static \u0275fac=function(r){Rp()};static \u0275prov=v({token:t,factory:t.\u0275fac})}return t})();function hL(){return f(bt).routerState.root}function Na(t,n){return{\u0275kind:t,\u0275providers:n}}function pL(){let t=f($);return n=>{let e=t.get(Be);if(n!==e.components[0])return;let r=t.get(bt),o=t.get(uS);t.get(Dv)===1&&r.initialNavigation(),t.get(hS,null,{optional:!0})?.setUpPreloading(),t.get(lS,null,{optional:!0})?.init(),r.resetRootComponentType(e.componentTypes[0]),o.closed||(o.next(),o.complete(),o.unsubscribe())}}var uS=new y("",{factory:()=>new S}),Dv=new y("",{factory:()=>1});function dS(){let t=[{provide:fl,useValue:!0},{provide:Dv,useValue:0},Rl(()=>{let n=f($);return n.get(mm,Promise.resolve()).then(()=>new Promise(r=>{let o=n.get(bt),i=n.get(uS);wd(o,()=>{r(!0)}),n.get(Dd).afterPreactivation=()=>(r(!0),i.closed?T(void 0):i),o.initialNavigation()}))})];return Na(2,t)}function fS(){let t=[Rl(()=>{f(bt).setUpLocationChangeListener()}),{provide:Dv,useValue:2}];return Na(3,t)}var hS=new y("");function pS(t){return Na(0,[{provide:hS,useExisting:cS},{provide:Ra,useExisting:t}])}function mS(){return Na(8,[hv,{provide:xa,useExisting:hv}])}function gS(t){qt("NgRouterViewTransitions");let n=[{provide:vv,useValue:iS},{provide:yv,useValue:g({skipNextTransition:!!t?.skipInitialTransition},t)}];return Na(9,n)}var vS=[bn,{provide:_r,useClass:qn},bt,_o,{provide:Yn,useFactory:hL},bd,[]],mL=(()=>{class t{constructor(){}static forRoot(e,r){return{ngModule:t,providers:[vS,[],{provide:Ri,multi:!0,useValue:e},[],r?.errorHandler?{provide:bv,useValue:r.errorHandler}:[],{provide:Dr,useValue:r||{}},r?.useHash?vL():yL(),gL(),r?.preloadingStrategy?pS(r.preloadingStrategy).\u0275providers:[],r?.initialNavigation?bL(r):[],r?.bindToComponentInputs?mS().\u0275providers:[],r?.enableViewTransitions?gS().\u0275providers:[],_L()]}}static forChild(e){return{ngModule:t,providers:[{provide:Ri,multi:!0,useValue:e}]}}static \u0275fac=function(r){return new(r||t)};static \u0275mod=X({type:t});static \u0275inj=Z({})}return t})();function gL(){return{provide:lS,useFactory:()=>{let t=f(Mm),n=f(Dr);return n.scrollOffset&&t.setOffset(n.scrollOffset),new fL(n)}}}function vL(){return{provide:Xt,useClass:Em}}function yL(){return{provide:Xt,useClass:Wl}}function bL(t){return[t.initialNavigation==="disabled"?fS().\u0275providers:[],t.initialNavigation==="enabledBlocking"?dS().\u0275providers:[]]}var _v=new y("");function _L(){return[{provide:_v,useFactory:pL},{provide:Nl,multi:!0,useExisting:_v}]}var Id=class t extends Error{originalError;constructor(n){super(n)}static fromError(n,e){let r=new t(n);return r.originalError=e,r}},EL=(()=>{class t{handleError(e){let r=e;return e.name==="HttpErrorResponse"&&e.status===0?r=Id.fromError("Controller is unreachable",e):e.error?.message&&(r=Id.fromError(e.error.message,e)),Tr(()=>r)}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac})}return t})(),tJ=(()=>{class t{http;errorHandler;router;requestsNotificationEmitter=new U;isRefreshing=!1;failedQueue=[];constructor(e,r,o){this.http=e,this.errorHandler=r,this.router=o}get(e,r,o){o=this.getJsonOptions(o);let i=this.getOptionsForController(e,r,o);return this.requestsNotificationEmitter.emit(`GET ${i.url}`),this.handleResponse(e,this.http.get(i.url,i.options),"GET",r,null,o)}getText(e,r,o){o=this.getTextOptions(o);let i=this.getOptionsForController(e,r,o);return this.requestsNotificationEmitter.emit(`GET ${i.url}`),this.handleResponse(e,this.http.get(i.url,i.options),"GET",r,null,o)}getBlob(e,r,o){o=this.getBlobOptions(o);let i=this.getOptionsForController(e,r,o);return this.requestsNotificationEmitter.emit(`GET ${i.url}`),this.handleResponse(e,this.http.get(i.url,i.options),"GET",r,null,o)}post(e,r,o,i){i=this.getJsonOptions(i);let s=this.getOptionsForController(e,r,i);return this.requestsNotificationEmitter.emit(`POST ${s.url}`),this.handleResponse(e,this.http.post(s.url,o,s.options),"POST",r,o,i)}postBlob(e,r,o){let i={responseType:"blob",headers:{}},s=this.getOptionsForController(e,r,i);return this.requestsNotificationEmitter.emit(`POST ${s.url}`),this.handleResponse(e,this.http.post(s.url,o,s.options),"POST",r,o,i)}put(e,r,o,i){i=this.getJsonOptions(i);let s=this.getOptionsForController(e,r,i);return this.requestsNotificationEmitter.emit(`PUT ${s.url}`),this.handleResponse(e,this.http.put(s.url,o,s.options),"PUT",r,o,i)}delete(e,r,o){o=this.getJsonOptions(o);let i=this.getOptionsForController(e,r,o);return this.requestsNotificationEmitter.emit(`DELETE ${i.url}`),this.handleResponse(e,this.http.delete(i.url,i.options),"DELETE",r,null,o)}patch(e,r,o,i){i=this.getJsonOptions(i);let s=this.getOptionsForController(e,r,i);return this.handleResponse(e,this.http.patch(s.url,o,s.options),"PATCH",r,o,i)}head(e,r,o){o=this.getJsonOptions(o);let i=this.getOptionsForController(e,r,o);return this.handleResponse(e,this.http.head(i.url,i.options),"HEAD",r,null,o)}options(e,r,o){o=this.getJsonOptions(o);let i=this.getOptionsForController(e,r,o);return this.handleResponse(e,this.http.options(i.url,i.options),"OPTIONS",r,null,o)}getJsonOptions(e){return e||{responseType:"json"}}getTextOptions(e){return e||{responseType:"text"}}getBlobOptions(e){return e||{responseType:"blob"}}getOptionsForController(e,r,o){return e&&e.host&&e.port?(e.protocol||(e.protocol=location.protocol),r=`${e.protocol}//${e.host}:${e.port}/${Iu.current_version}${r}`):r=`/${Iu.current_version}${r}`,o.headers||(o.headers={}),e&&e.authToken&&!e.tokenExpired&&(o.headers.Authorization=`Bearer ${e.authToken}`),{url:r,options:o}}handleResponse(e,r,o,i,s,a){return r.pipe(sn(c=>{if(c.status!==401)return this.errorHandler.handleError(c);if(i.endsWith("/access/users/login")||i.endsWith("/access/users/authenticate")||i.endsWith("/access/users/refresh"))return this.errorHandler.handleError(c);let l=localStorage.getItem(`refresh_token_${e.id}`);return l?this.retryAfterRefresh(e,o,i,s,a,l):(this.redirectToLogin(e),Tr(()=>c))}))}retryAfterRefresh(e,r,o,i,s,a){return this.isRefreshing?new O(c=>{this.failedQueue.push({resolve:()=>{this.executeRequest(e,r,o,i,s).subscribe({next:l=>{c.next(l),c.complete()},error:l=>c.error(l)})},reject:l=>c.error(l)})}):(this.isRefreshing=!0,this.doRefreshToken(e,a).pipe(He(c=>(e.authToken=c.access_token,localStorage.setItem(`controller-${e.id}`,JSON.stringify(e)),c.refresh_token&&localStorage.setItem(`refresh_token_${e.id}`,c.refresh_token),this.isRefreshing=!1,this.processQueue(null),this.executeRequest(e,r,o,i,s))),sn(c=>(this.isRefreshing=!1,this.processQueue(c),c.status===401&&this.redirectToLogin(e),Tr(()=>c)))))}doRefreshToken(e,r){let o=`${e.protocol}//${e.host}:${e.port}/${Iu.current_version}/access/users/refresh`;return this.http.post(o,{refresh_token:r},{headers:new _n({"Content-Type":"application/json"})})}executeRequest(e,r,o,i,s){let a=this.getOptionsForController(e,o,s);return this.requestsNotificationEmitter.emit(`${r} ${a.url}`),this.http.request(r,a.url,{body:i,headers:a.options.headers,params:a.options.params,responseType:a.options.responseType||"json"})}processQueue(e){e?this.failedQueue.forEach(r=>r.reject(e)):this.failedQueue.forEach(r=>r.resolve()),this.failedQueue=[]}clearTokens(e){localStorage.removeItem(`refresh_token_${e.id}`),e.authToken=null,localStorage.setItem(`controller-${e.id}`,JSON.stringify(e))}redirectToLogin(e){this.clearTokens(e),e.tokenExpired=!0,localStorage.setItem(`controller-${e.id}`,JSON.stringify(e)),this.isRefreshing=!1,this.processQueue(new Error("Session expired, redirecting to login")),this.router.navigate(["/controller",e.id,"login"])}static \u0275fac=function(r){return new(r||t)(w(fu),w(EL),w(bt))};static \u0275prov=v({token:t,factory:t.\u0275fac})}return t})();var Ev=class{_multiple;_emitChanges;compareWith;_selection=new Set;_deselectedToEmit=[];_selectedToEmit=[];_selected=null;get selected(){return this._selected||(this._selected=Array.from(this._selection.values())),this._selected}changed=new S;constructor(n=!1,e,r=!0,o){this._multiple=n,this._emitChanges=r,this.compareWith=o,e&&e.length&&(n?e.forEach(i=>this._markSelected(i)):this._markSelected(e[0]),this._selectedToEmit.length=0)}select(...n){this._verifyValueAssignment(n),n.forEach(r=>this._markSelected(r));let e=this._hasQueuedChanges();return this._emitChangeEvent(),e}deselect(...n){this._verifyValueAssignment(n),n.forEach(r=>this._unmarkSelected(r));let e=this._hasQueuedChanges();return this._emitChangeEvent(),e}setSelection(...n){this._verifyValueAssignment(n);let e=this.selected,r=new Set(n.map(i=>this._getConcreteValue(i)));n.forEach(i=>this._markSelected(i)),e.filter(i=>!r.has(this._getConcreteValue(i,r))).forEach(i=>this._unmarkSelected(i));let o=this._hasQueuedChanges();return this._emitChangeEvent(),o}toggle(n){return this.isSelected(n)?this.deselect(n):this.select(n)}clear(n=!0){this._unmarkAll();let e=this._hasQueuedChanges();return n&&this._emitChangeEvent(),e}isSelected(n){return this._selection.has(this._getConcreteValue(n))}isEmpty(){return this._selection.size===0}hasValue(){return!this.isEmpty()}sort(n){this._multiple&&this.selected&&this._selected.sort(n)}isMultipleSelection(){return this._multiple}_emitChangeEvent(){this._selected=null,(this._selectedToEmit.length||this._deselectedToEmit.length)&&(this.changed.next({source:this,added:this._selectedToEmit,removed:this._deselectedToEmit}),this._deselectedToEmit=[],this._selectedToEmit=[])}_markSelected(n){n=this._getConcreteValue(n),this.isSelected(n)||(this._multiple||this._unmarkAll(),this.isSelected(n)||this._selection.add(n),this._emitChanges&&this._selectedToEmit.push(n))}_unmarkSelected(n){n=this._getConcreteValue(n),this.isSelected(n)&&(this._selection.delete(n),this._emitChanges&&this._deselectedToEmit.push(n))}_unmarkAll(){this.isEmpty()||this._selection.forEach(n=>this._unmarkSelected(n))}_verifyValueAssignment(n){n.length>1&&this._multiple}_hasQueuedChanges(){return!!(this._deselectedToEmit.length||this._selectedToEmit.length)}_getConcreteValue(n,e){if(this.compareWith){e=e??this._selection;for(let r of e)if(this.compareWith(n,r))return r;return n}else return n}};var wL=(()=>{class t{_listeners=[];notify(e,r){for(let o of this._listeners)o(e,r)}listen(e){return this._listeners.push(e),()=>{this._listeners=this._listeners.filter(r=>e!==r)}}ngOnDestroy(){this._listeners=[]}static \u0275fac=function(r){return new(r||t)};static \u0275prov=v({token:t,factory:t.\u0275fac,providedIn:"root"})}return t})();var yS=class{applyChanges(n,e,r,o,i){n.forEachOperation((s,a,c)=>{let l,u;if(s.previousIndex==null){let d=r(s,a,c);l=e.createEmbeddedView(d.templateRef,d.context,d.index),u=Dn.INSERTED}else c==null?(e.remove(a),u=Dn.REMOVED):(l=e.get(a),e.move(l,c),u=Dn.MOVED);i&&i({context:l?.context,operation:u,record:s})})}detach(){}};var mJ=(()=>{class t{_animationsDisabled=mr();state="unchecked";disabled=!1;appearance="full";constructor(){}static \u0275fac=function(r){return new(r||t)};static \u0275cmp=ke({type:t,selectors:[["mat-pseudo-checkbox"]],hostAttrs:[1,"mat-pseudo-checkbox"],hostVars:12,hostBindings:function(r,o){r&2&&Xe("mat-pseudo-checkbox-indeterminate",o.state==="indeterminate")("mat-pseudo-checkbox-checked",o.state==="checked")("mat-pseudo-checkbox-disabled",o.disabled)("mat-pseudo-checkbox-minimal",o.appearance==="minimal")("mat-pseudo-checkbox-full",o.appearance==="full")("_mat-animation-noopable",o._animationsDisabled)},inputs:{state:"state",disabled:"disabled",appearance:"appearance"},decls:0,vars:0,template:function(r,o){},styles:[`.mat-pseudo-checkbox { + border-radius: 2px; + cursor: pointer; + display: inline-block; + vertical-align: middle; + box-sizing: border-box; + position: relative; + flex-shrink: 0; + transition: border-color 90ms cubic-bezier(0, 0, 0.2, 0.1), background-color 90ms cubic-bezier(0, 0, 0.2, 0.1); +} +.mat-pseudo-checkbox::after { + position: absolute; + opacity: 0; + content: ""; + border-bottom: 2px solid currentColor; + transition: opacity 90ms cubic-bezier(0, 0, 0.2, 0.1); +} +.mat-pseudo-checkbox._mat-animation-noopable { + transition: none !important; + animation: none !important; +} +.mat-pseudo-checkbox._mat-animation-noopable::after { + transition: none; +} + +.mat-pseudo-checkbox-disabled { + cursor: default; +} + +.mat-pseudo-checkbox-indeterminate::after { + left: 1px; + opacity: 1; + border-radius: 2px; +} + +.mat-pseudo-checkbox-checked::after { + left: 1px; + border-left: 2px solid currentColor; + transform: rotate(-45deg); + opacity: 1; + box-sizing: content-box; +} + +.mat-pseudo-checkbox-minimal.mat-pseudo-checkbox-checked::after, .mat-pseudo-checkbox-minimal.mat-pseudo-checkbox-indeterminate::after { + color: var(--mat-pseudo-checkbox-minimal-selected-checkmark-color, var(--mat-sys-primary)); +} +.mat-pseudo-checkbox-minimal.mat-pseudo-checkbox-checked.mat-pseudo-checkbox-disabled::after, .mat-pseudo-checkbox-minimal.mat-pseudo-checkbox-indeterminate.mat-pseudo-checkbox-disabled::after { + color: var(--mat-pseudo-checkbox-minimal-disabled-selected-checkmark-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); +} + +.mat-pseudo-checkbox-full { + border-color: var(--mat-pseudo-checkbox-full-unselected-icon-color, var(--mat-sys-on-surface-variant)); + border-width: 2px; + border-style: solid; +} +.mat-pseudo-checkbox-full.mat-pseudo-checkbox-disabled { + border-color: var(--mat-pseudo-checkbox-full-disabled-unselected-icon-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); +} +.mat-pseudo-checkbox-full.mat-pseudo-checkbox-checked, .mat-pseudo-checkbox-full.mat-pseudo-checkbox-indeterminate { + background-color: var(--mat-pseudo-checkbox-full-selected-icon-color, var(--mat-sys-primary)); + border-color: transparent; +} +.mat-pseudo-checkbox-full.mat-pseudo-checkbox-checked::after, .mat-pseudo-checkbox-full.mat-pseudo-checkbox-indeterminate::after { + color: var(--mat-pseudo-checkbox-full-selected-checkmark-color, var(--mat-sys-on-primary)); +} +.mat-pseudo-checkbox-full.mat-pseudo-checkbox-checked.mat-pseudo-checkbox-disabled, .mat-pseudo-checkbox-full.mat-pseudo-checkbox-indeterminate.mat-pseudo-checkbox-disabled { + background-color: var(--mat-pseudo-checkbox-full-disabled-selected-icon-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent)); +} +.mat-pseudo-checkbox-full.mat-pseudo-checkbox-checked.mat-pseudo-checkbox-disabled::after, .mat-pseudo-checkbox-full.mat-pseudo-checkbox-indeterminate.mat-pseudo-checkbox-disabled::after { + color: var(--mat-pseudo-checkbox-full-disabled-selected-checkmark-color, var(--mat-sys-surface)); +} + +.mat-pseudo-checkbox { + width: 18px; + height: 18px; +} + +.mat-pseudo-checkbox-minimal.mat-pseudo-checkbox-checked::after { + width: 14px; + height: 6px; + transform-origin: center; + top: -4.2426406871px; + left: 0; + bottom: 0; + right: 0; + margin: auto; +} +.mat-pseudo-checkbox-minimal.mat-pseudo-checkbox-indeterminate::after { + top: 8px; + width: 16px; +} + +.mat-pseudo-checkbox-full.mat-pseudo-checkbox-checked::after { + width: 10px; + height: 4px; + transform-origin: center; + top: -2.8284271247px; + left: 0; + bottom: 0; + right: 0; + margin: auto; +} +.mat-pseudo-checkbox-full.mat-pseudo-checkbox-indeterminate::after { + top: 6px; + width: 12px; +} +`],encapsulation:2,changeDetection:0})}return t})();export{g as a,F as b,IS as c,CL as d,IL as e,SL as f,G as g,O as h,S as i,Ie as j,Pi as k,jd as l,Vd as m,Se as n,se as o,T as p,Tr as q,Ft as r,HS as s,zS as t,H as u,To as v,ve as w,on as x,Vi as y,Ud as z,Hd as A,xr as B,sM as C,aM as D,fe as E,Bi as F,sn as G,$d as H,Xn as I,cM as J,Ar as K,Ue as L,zd as M,lM as N,xo as O,Rr as P,ac as Q,ry as R,qd as S,oy as T,Ui as U,Nr as V,He as W,at as X,nt as Y,b as Z,be as _,v as $,Z as aa,y as ba,w as ca,f as da,xy as ea,re as fa,Hy as ga,$y as ha,tb as ia,nb as ja,$ as ka,L as la,Ae as ma,U as na,j as oa,_t as pa,W as qa,Go as ra,Re as sa,Ne as ta,z as ua,Fn as va,Jr as wa,Is as xa,ot as ya,c0 as za,k_ as Aa,F_ as Ba,d0 as Ca,f0 as Da,bp as Ea,ht as Fa,dt as Ga,je as Ha,Oe as Ia,D as Ja,Rp as Ka,qe as La,ke as Ma,X as Na,M as Oa,Ns as Pa,pA as Qa,J as Ra,jD as Sa,VD as Ta,ri as Ua,Be as Va,zD as Wa,Yt as Xa,MA as Ya,xA as Za,zp as _a,AA as $a,RA as ab,NA as bb,OA as cb,kA as db,GD as eb,cl as fb,Gp as gb,Ol as hb,to as ib,no as jb,yn as kb,Wp as lb,qp as mb,qD as nb,BA as ob,YD as pb,Zt as qb,KD as rb,GA as sb,ro as tb,Vn as ub,kl as vb,Fl as wb,Fs as xb,Ps as yb,XD as zb,JD as Ab,YA as Bb,ZA as Cb,Pl as Db,Xe as Eb,Yp as Fb,gR as Gb,cE as Hb,Zp as Ib,lE as Jb,uE as Kb,dE as Lb,_R as Mb,fE as Nb,DR as Ob,ER as Pb,we as Qb,SR as Rb,MR as Sb,TR as Tb,xR as Ub,AR as Vb,NR as Wb,kR as Xb,FR as Yb,PR as Zb,LR as _b,jR as $b,q as ac,Kt as bc,Vl as cc,Zq as dc,Kq as ec,RE as fc,Qq as gc,Xq as hc,Jq as ic,e5 as jc,St as kc,Hl as lc,ue as mc,hm as nc,n5 as oc,bn as pc,AN as qc,nw as rc,RN as sc,NN as tc,ON as uc,PN as vc,jN as wc,UN as xc,Cm as yc,iw as zc,km as Ac,nO as Bc,lO as Cc,_n as Dc,$n as Ec,oi as Fc,io as Gc,ii as Hc,Cw as Ic,fu as Jc,RO as Kc,xw as Lc,k9 as Mc,zm as Nc,xt as Oc,Wm as Pc,LO as Qc,Je as Rc,Qs as Sc,Xs as Tc,so as Uc,Nw as Vc,Tt as Wc,he as Xc,fi as Yc,oa as Zc,Ik as _c,fg as $c,Dn as ad,hg as bd,zn as cd,xk as dd,ia as ed,gg as fd,uo as gd,Fk as hd,Pk as id,mg as jd,vg as kd,ta as ld,ao as md,yg as nd,hi as od,Su as pd,Mu as qd,kK as rd,FK as sd,mC as td,bu as ud,wC as vd,wg as wd,ca as xd,Cg as yd,Nu as zd,Sg as Ad,AC as Bd,Mg as Cd,Tg as Dd,Eg as Ed,Uk as Fd,Hk as Gd,yS as Hd,po as Id,zC as Jd,En as Kd,kC as Ld,et as Md,Gn as Nd,BQ as Od,UQ as Pd,ho as Qd,HQ as Rd,lF as Sd,gi as Td,zQ as Ud,hF as Vd,GQ as Wd,mF as Xd,vF as Yd,lI as Zd,uI as _d,DF as $d,wF as ae,WQ as be,qQ as ce,xF as de,RF as ee,OF as fe,kF as ge,YQ as he,ZQ as ie,KQ as je,pu as ke,jO as le,vu as me,BO as ne,yu as oe,Zm as pe,FY as qe,$w as re,zO as se,tk as te,rk as ue,ok as ve,Xm as we,Jm as xe,eg as ye,TZ as ze,sk as Ae,ak as Be,UZ as Ce,l7 as De,QZ as Ee,n7 as Fe,lk as Ge,mr as He,br as Ie,yt as Je,Rt as Ke,yo as Le,Yn as Me,fv as Ne,bt as Oe,Cd as Pe,uL as Qe,mL as Re,ra as Se,cg as Te,C7 as Ue,aC as Ve,cC as We,yk as Xe,dC as Ye,nK as Ze,rK as _e,Iu as $e,EL as af,tJ as bf,Ev as cf,wL as df,mJ as ef}; diff --git a/gns3server/static/web-ui/index.html b/gns3server/static/web-ui/index.html index bd7e3c0b2..ad6e9c4ad 100644 --- a/gns3server/static/web-ui/index.html +++ b/gns3server/static/web-ui/index.html @@ -33,7 +33,7 @@ } })(); - + @@ -115,5 +115,5 @@ } })(); - + diff --git a/gns3server/static/web-ui/main-PLOAN52W.js b/gns3server/static/web-ui/main-PLOAN52W.js deleted file mode 100644 index 89e7bc6be..000000000 --- a/gns3server/static/web-ui/main-PLOAN52W.js +++ /dev/null @@ -1,406 +0,0 @@ -import{a as zi,b as Mn,c as O5,d as ce,e as re,f as F5,g as Je,h as B5,i as V5,j as z5,k as j5,l as Ie,m as Mt,n as nt,o as hc,p as Fe,q as Rt,r as Re,s as ve,t as nR,u as iR,v as R1,w as oR,x as wh}from"./chunk-KS2DZGNZ.js";import{$ as rR,A as Q5,B as Hr,C as tl,D as ee,E as Sh,F as QT,G as ns,H as ba,I as X5,J as am,K as Y5,L as no,M as di,N as K5,O as Z5,P as Io,Q as Ao,R as J5,S as eR,T as tR,U as io,V as Ur,W as sm,X as wi,Y as lm,Z as ar,_ as ei,a as Vt,b as Et,c as ot,d as hi,e as I1,f as el,g as to,h as A1,i as yh,j as ke,k as we,l as vd,m as O1,n as $5,o as H5,p as N1,q as U5,r as vt,s as G5,t as W5,u as Xo,v as Dt,w as bt,x as Po,y as fc,z as q5}from"./chunk-6EPHFCHO.js";import{$ as yi,$a as Gt,$b as Si,$c as p1,$e as Qo,A as sh,Aa as dn,Ab as yo,Ac as ZN,Ad as g5,Ae as M1,B as Ki,Ba as bi,Bb as Eo,Bd as _5,Be as Pn,C as ra,Ca as LN,Cb as Ts,Cc as H,Ce as Ze,D as es,Da as Qt,Db as Be,Dc as X,De as At,E as e1,Ea as jl,Eb as Ve,Ec as ud,Ed as hd,Ee as Fa,F as En,Fa as lh,Fb as mo,Fc as gt,Fd as v5,G as Yn,Ga as BT,Gb as z,Gc as Lo,Gd as fd,Ge as k1,H as t1,Ha as BN,Hb as qo,Hd as C5,I as Zi,Ia as VT,Ib as _,Ic as JN,Id as Do,J as LT,Ja as Lp,Jb as s1,Jc as e5,Jd as b5,Je as T1,K as Jd,Ka as Ra,Kb as C,Kc as w_,L as EN,La as o1,Lb as ii,Lc as t5,Ld as im,Ma as VN,Mb as nn,Mc as dc,N as Gi,Na as dr,Nb as Vi,Nc as n5,O as DN,Oa as ch,Ob as Dn,Oc as eo,Od as fh,P as PN,Pa as u,Pb as pt,Pc as i5,Pd as gh,Q as b_,Qa as zT,Qb as ut,Qc as o5,Qd as Ca,Qe as D5,R as IN,Ra as aa,Rc as tm,Rd as x5,Re as rm,S as dd,Sa as jo,Sb as en,Sc as HT,Sd as v1,Se as E1,T as x_,Ta as pd,Tb as tn,Tc as va,Td as C1,Te as P5,U as n1,Ua as pi,Ub as Pe,Uc as r5,Ud as _h,Ue as WT,V as Vl,Va as rt,Vb as wn,Vc as m1,Ve as D1,W as Fp,Wa as r1,Wb as Ue,Wc as ne,Wd as b1,We as I5,Xa as Ji,Xb as or,Xc as Es,Xd as y5,Xe as P1,Y as xi,Ya as zN,Yb as d,Yc as UT,Ye as qT,Z as hn,Za as a1,Zb as $,Zc as a5,Zd as x1,Ze as _d,_ as tt,_a as F,_b as te,_c as s5,_e as A5,a as W,aa as fn,ab as ft,ac as l1,ad as u1,ae as vh,b as Qe,ba as AN,bb as mr,bc as mh,bd as l5,be as S5,bf as N5,c as wN,ca as md,cc as ph,cd as c5,ce as gd,cf as Js,d as ws,da as K,db as ci,dc as uh,dd as GT,de as Lt,df as R5,e as Tn,ea as Ut,eb as Se,ec as qi,ed as d5,ee as y1,ef as sa,f as Xs,fa as ON,fb as dh,fc as y_,fd as m5,fe as $e,ff as ze,g as go,ga as $t,gb as jN,gc as Cn,gd as mc,ge as w5,gf as uc,h as MN,ha as ge,hb as $N,hc as Kt,hd as p5,he as S1,hf as pe,i as Pr,ia as f,ib as HN,ic as ln,id as nm,ie as Ot,if as U,j as je,ja as NN,jb as jT,jc as Ys,jd as u5,je as at,jf as Fi,k as zt,ka as zl,kb as UN,kc as c1,kd as Cr,ke as w1,kf as L5,l as rh,la as Ms,lb as $T,lc as d1,ld as h1,le as br,lf as gn,m as kN,ma as T,mb as GN,mc as on,md as f1,me as Gn,mf as xh,n as ah,na as E,nb as Xt,nc as oi,nd as Bp,ne as Ch,nf as La,o as $r,oa as Jn,oc as Ar,od as pr,oe as Ke,p as nr,pa as Ir,pb as A,pc as qN,pd as g1,pe as M5,q as _t,qa as Wo,qb as WN,qc as S_,qd as _1,qe as st,r as zo,ra as co,rb as O,rc as QN,re as xr,s as Zd,sa as em,sb as Wi,sc as XN,sd as h5,se as om,t as TN,ta as RN,tb as Ae,tc as rr,td as $l,te as k5,ua as _e,ub as Z,uc as mn,ud as Zs,ue as T5,v as xt,va as Pi,vb as J,vc as YN,vd as ts,ve as Bt,w as ir,wa as FN,wb as b,wc as Ks,wd as ui,we as Nt,x as vr,xa as i1,xb as s,xc as KN,xd as Hl,xe as bh,y as v_,ya as se,yb as l,yc as ae,yd as hh,ye as pc,z as C_,za as ks,zb as B,zc as jt,zd as f5,ze as E5}from"./chunk-LG2N72QL.js";var KB=ws((mit,gS)=>{(function(n,i,e){if(!n)return;for(var t={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",18:"alt",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"ins",46:"del",91:"meta",93:"meta",224:"meta"},o={106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},r={"~":"`","!":"1","@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\"},a={option:"alt",command:"meta",return:"enter",escape:"esc",plus:"+",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},c,m=1;m<20;++m)t[111+m]="f"+m;for(m=0;m<=9;++m)t[m+96]=m.toString();function p(D,N,q){if(D.addEventListener){D.addEventListener(N,q,!1);return}D.attachEvent("on"+N,q)}function h(D){if(D.type=="keypress"){var N=String.fromCharCode(D.which);return D.shiftKey||(N=N.toLowerCase()),N}return t[D.which]?t[D.which]:o[D.which]?o[D.which]:String.fromCharCode(D.which).toLowerCase()}function g(D,N){return D.sort().join(",")===N.sort().join(",")}function S(D){var N=[];return D.shiftKey&&N.push("shift"),D.altKey&&N.push("alt"),D.ctrlKey&&N.push("ctrl"),D.metaKey&&N.push("meta"),N}function x(D){if(D.preventDefault){D.preventDefault();return}D.returnValue=!1}function v(D){if(D.stopPropagation){D.stopPropagation();return}D.cancelBubble=!0}function M(D){return D=="shift"||D=="ctrl"||D=="alt"||D=="meta"}function w(){if(!c){c={};for(var D in t)D>95&&D<112||t.hasOwnProperty(D)&&(c[t[D]]=D)}return c}function y(D,N,q){return q||(q=w()[D]?"keydown":"keypress"),q=="keypress"&&N.length&&(q="keydown"),q}function k(D){return D==="+"?["+"]:(D=D.replace(/\+{2}/g,"+plus"),D.split("+"))}function I(D,N){var q,de,fe,G=[];for(q=k(D),fe=0;fe1){Y(oe,xe,Te,Le);return}Q=I(oe,Le),N._callbacks[Q.key]=N._callbacks[Q.key]||[],le(Q.key,Q.modifiers,{type:Q.action},Ye,oe,Xe),N._callbacks[Q.key][Ye?"unshift":"push"]({callback:Te,modifiers:Q.modifiers,action:Q.action,seq:Ye,level:Xe,combo:oe})}N._bindMultiple=function(oe,Te,Le){for(var Ye=0;Ye-1||P(N,q.target))return!1;if("composedPath"in D&&typeof D.composedPath=="function"){var de=D.composedPath()[0];de!==D.target&&(N=de)}return N.tagName=="INPUT"||N.tagName=="SELECT"||N.tagName=="TEXTAREA"||N.isContentEditable},R.prototype.handleKey=function(){var D=this;return D._handleKey.apply(D,arguments)},R.addKeycodes=function(D){for(var N in D)D.hasOwnProperty(N)&&(t[N]=D[N]);c=null},R.init=function(){var D=R(i);for(var N in D)N.charAt(0)!=="_"&&(R[N]=(function(q){return function(){return D[q].apply(D,arguments)}})(N))},R.init(),n.Mousetrap=R,typeof gS<"u"&&gS.exports&&(gS.exports=R),typeof define=="function"&&define.amd&&define(function(){return R})})(typeof window<"u"?window:null,typeof window<"u"?document:null)});var JV=ws(B3=>{var ZV="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split("");B3.encode=function(n){if(0<=n&&n{var ez=JV(),V3=5,tz=1<>1;return i?-e:e}z3.encode=function(i){var e="",t,o=uhe(i);do t=o&nz,o>>>=V3,o>0&&(t|=iz),e+=ez.encode(t);while(o>0);return e};z3.decode=function(i,e,t){var o=i.length,r=0,a=0,c,m;do{if(e>=o)throw new Error("Expected more digits in base 64 VLQ value.");if(m=ez.decode(i.charCodeAt(e++)),m===-1)throw new Error("Invalid base64 digit: "+i.charAt(e-1));c=!!(m&iz),m&=nz,r=r+(m<{function fhe(n,i,e){if(i in n)return n[i];if(arguments.length===3)return e;throw new Error('"'+i+'" is a required argument.')}ta.getArg=fhe;var rz=/^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/,ghe=/^data:.+\,.+$/;function Jv(n){var i=n.match(rz);return i?{scheme:i[1],auth:i[2],host:i[3],port:i[4],path:i[5]}:null}ta.urlParse=Jv;function Zf(n){var i="";return n.scheme&&(i+=n.scheme+":"),i+="//",n.auth&&(i+=n.auth+"@"),n.host&&(i+=n.host),n.port&&(i+=":"+n.port),n.path&&(i+=n.path),i}ta.urlGenerate=Zf;var _he=32;function vhe(n){var i=[];return function(e){for(var t=0;t_he&&i.pop(),r}}var j3=vhe(function(i){var e=i,t=Jv(i);if(t){if(!t.path)return i;e=t.path}for(var o=ta.isAbsolute(e),r=[],a=0,c=0;;)if(a=c,c=e.indexOf("/",a),c===-1){r.push(e.slice(a));break}else for(r.push(e.slice(a,c));c=0;c--)m=r[c],m==="."?r.splice(c,1):m===".."?p++:p>0&&(m===""?(r.splice(c+1,p),p=0):(r.splice(c,2),p--));return e=r.join("/"),e===""&&(e=o?"/":"."),t?(t.path=e,Zf(t)):e});ta.normalize=j3;function az(n,i){n===""&&(n="."),i===""&&(i=".");var e=Jv(i),t=Jv(n);if(t&&(n=t.path||"/"),e&&!e.scheme)return t&&(e.scheme=t.scheme),Zf(e);if(e||i.match(ghe))return i;if(t&&!t.host&&!t.path)return t.host=i,Zf(t);var o=i.charAt(0)==="/"?i:j3(n.replace(/\/+$/,"")+"/"+i);return t?(t.path=o,Zf(t)):o}ta.join=az;ta.isAbsolute=function(n){return n.charAt(0)==="/"||rz.test(n)};function Che(n,i){n===""&&(n="."),n=n.replace(/\/$/,"");for(var e=0;i.indexOf(n+"/")!==0;){var t=n.lastIndexOf("/");if(t<0||(n=n.slice(0,t),n.match(/^([^\/]+:\/)?\/*$/)))return i;++e}return Array(e+1).join("../")+i.substr(n.length+1)}ta.relative=Che;var sz=(function(){var n=Object.create(null);return!("__proto__"in n)})();function lz(n){return n}function bhe(n){return cz(n)?"$"+n:n}ta.toSetString=sz?lz:bhe;function xhe(n){return cz(n)?n.slice(1):n}ta.fromSetString=sz?lz:xhe;function cz(n){if(!n)return!1;var i=n.length;if(i<9||n.charCodeAt(i-1)!==95||n.charCodeAt(i-2)!==95||n.charCodeAt(i-3)!==111||n.charCodeAt(i-4)!==116||n.charCodeAt(i-5)!==111||n.charCodeAt(i-6)!==114||n.charCodeAt(i-7)!==112||n.charCodeAt(i-8)!==95||n.charCodeAt(i-9)!==95)return!1;for(var e=i-10;e>=0;e--)if(n.charCodeAt(e)!==36)return!1;return!0}function yhe(n,i,e){var t=zd(n.source,i.source);return t!==0||(t=n.originalLine-i.originalLine,t!==0)||(t=n.originalColumn-i.originalColumn,t!==0||e)||(t=n.generatedColumn-i.generatedColumn,t!==0)||(t=n.generatedLine-i.generatedLine,t!==0)?t:zd(n.name,i.name)}ta.compareByOriginalPositions=yhe;function She(n,i,e){var t;return t=n.originalLine-i.originalLine,t!==0||(t=n.originalColumn-i.originalColumn,t!==0||e)||(t=n.generatedColumn-i.generatedColumn,t!==0)||(t=n.generatedLine-i.generatedLine,t!==0)?t:zd(n.name,i.name)}ta.compareByOriginalPositionsNoSource=She;function whe(n,i,e){var t=n.generatedLine-i.generatedLine;return t!==0||(t=n.generatedColumn-i.generatedColumn,t!==0||e)||(t=zd(n.source,i.source),t!==0)||(t=n.originalLine-i.originalLine,t!==0)||(t=n.originalColumn-i.originalColumn,t!==0)?t:zd(n.name,i.name)}ta.compareByGeneratedPositionsDeflated=whe;function Mhe(n,i,e){var t=n.generatedColumn-i.generatedColumn;return t!==0||e||(t=zd(n.source,i.source),t!==0)||(t=n.originalLine-i.originalLine,t!==0)||(t=n.originalColumn-i.originalColumn,t!==0)?t:zd(n.name,i.name)}ta.compareByGeneratedPositionsDeflatedNoLine=Mhe;function zd(n,i){return n===i?0:n===null?1:i===null?-1:n>i?1:-1}function khe(n,i){var e=n.generatedLine-i.generatedLine;return e!==0||(e=n.generatedColumn-i.generatedColumn,e!==0)||(e=zd(n.source,i.source),e!==0)||(e=n.originalLine-i.originalLine,e!==0)||(e=n.originalColumn-i.originalColumn,e!==0)?e:zd(n.name,i.name)}ta.compareByGeneratedPositionsInflated=khe;function The(n){return JSON.parse(n.replace(/^\)]}'[^\n]*\n/,""))}ta.parseSourceMapInput=The;function Ehe(n,i,e){if(i=i||"",n&&(n[n.length-1]!=="/"&&i[0]!=="/"&&(n+="/"),i=n+i),e){var t=Jv(e);if(!t)throw new Error("sourceMapURL could not be parsed");if(t.path){var o=t.path.lastIndexOf("/");o>=0&&(t.path=t.path.substring(0,o+1))}i=az(Zf(t),i)}return j3(i)}ta.computeSourceURL=Ehe});var mz=ws(dz=>{var $3=RS(),H3=Object.prototype.hasOwnProperty,Bu=typeof Map<"u";function jd(){this._array=[],this._set=Bu?new Map:Object.create(null)}jd.fromArray=function(i,e){for(var t=new jd,o=0,r=i.length;o=0)return e}else{var t=$3.toSetString(i);if(H3.call(this._set,t))return this._set[t]}throw new Error('"'+i+'" is not in the set.')};jd.prototype.at=function(i){if(i>=0&&i{var pz=RS();function Dhe(n,i){var e=n.generatedLine,t=i.generatedLine,o=n.generatedColumn,r=i.generatedColumn;return t>e||t==e&&r>=o||pz.compareByGeneratedPositionsInflated(n,i)<=0}function FS(){this._array=[],this._sorted=!0,this._last={generatedLine:-1,generatedColumn:0}}FS.prototype.unsortedForEach=function(i,e){this._array.forEach(i,e)};FS.prototype.add=function(i){Dhe(this._last,i)?(this._last=i,this._array.push(i)):(this._sorted=!1,this._array.push(i))};FS.prototype.toArray=function(){return this._sorted||(this._array.sort(pz.compareByGeneratedPositionsInflated),this._sorted=!0),this._array};uz.MappingList=FS});var gz=ws(fz=>{var eC=oz(),gr=RS(),LS=mz().ArraySet,Phe=hz().MappingList;function Pl(n){n||(n={}),this._file=gr.getArg(n,"file",null),this._sourceRoot=gr.getArg(n,"sourceRoot",null),this._skipValidation=gr.getArg(n,"skipValidation",!1),this._ignoreInvalidMapping=gr.getArg(n,"ignoreInvalidMapping",!1),this._sources=new LS,this._names=new LS,this._mappings=new Phe,this._sourcesContents=null}Pl.prototype._version=3;Pl.fromSourceMap=function(i,e){var t=i.sourceRoot,o=new Pl(Object.assign(e||{},{file:i.file,sourceRoot:t}));return i.eachMapping(function(r){var a={generated:{line:r.generatedLine,column:r.generatedColumn}};r.source!=null&&(a.source=r.source,t!=null&&(a.source=gr.relative(t,a.source)),a.original={line:r.originalLine,column:r.originalColumn},r.name!=null&&(a.name=r.name)),o.addMapping(a)}),i.sources.forEach(function(r){var a=r;t!==null&&(a=gr.relative(t,r)),o._sources.has(a)||o._sources.add(a);var c=i.sourceContentFor(r);c!=null&&o.setSourceContent(r,c)}),o};Pl.prototype.addMapping=function(i){var e=gr.getArg(i,"generated"),t=gr.getArg(i,"original",null),o=gr.getArg(i,"source",null),r=gr.getArg(i,"name",null);!this._skipValidation&&this._validateMapping(e,t,o,r)===!1||(o!=null&&(o=String(o),this._sources.has(o)||this._sources.add(o)),r!=null&&(r=String(r),this._names.has(r)||this._names.add(r)),this._mappings.add({generatedLine:e.line,generatedColumn:e.column,originalLine:t!=null&&t.line,originalColumn:t!=null&&t.column,source:o,name:r}))};Pl.prototype.setSourceContent=function(i,e){var t=i;this._sourceRoot!=null&&(t=gr.relative(this._sourceRoot,t)),e!=null?(this._sourcesContents||(this._sourcesContents=Object.create(null)),this._sourcesContents[gr.toSetString(t)]=e):this._sourcesContents&&(delete this._sourcesContents[gr.toSetString(t)],Object.keys(this._sourcesContents).length===0&&(this._sourcesContents=null))};Pl.prototype.applySourceMap=function(i,e,t){var o=e;if(e==null){if(i.file==null)throw new Error(`SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, or the source map's "file" property. Both were omitted.`);o=i.file}var r=this._sourceRoot;r!=null&&(o=gr.relative(r,o));var a=new LS,c=new LS;this._mappings.unsortedForEach(function(m){if(m.source===o&&m.originalLine!=null){var p=i.originalPositionFor({line:m.originalLine,column:m.originalColumn});p.source!=null&&(m.source=p.source,t!=null&&(m.source=gr.join(t,m.source)),r!=null&&(m.source=gr.relative(r,m.source)),m.originalLine=p.line,m.originalColumn=p.column,p.name!=null&&(m.name=p.name))}var h=m.source;h!=null&&!a.has(h)&&a.add(h);var g=m.name;g!=null&&!c.has(g)&&c.add(g)},this),this._sources=a,this._names=c,i.sources.forEach(function(m){var p=i.sourceContentFor(m);p!=null&&(t!=null&&(m=gr.join(t,m)),r!=null&&(m=gr.relative(r,m)),this.setSourceContent(m,p))},this)};Pl.prototype._validateMapping=function(i,e,t,o){if(e&&typeof e.line!="number"&&typeof e.column!="number"){var r="original.line and original.column are not numbers -- you probably meant to omit the original mapping entirely and only map the generated position. If so, pass null for the original mapping instead of an object with empty or null values.";if(this._ignoreInvalidMapping)return typeof console<"u"&&console.warn&&console.warn(r),!1;throw new Error(r)}if(!(i&&"line"in i&&"column"in i&&i.line>0&&i.column>=0&&!e&&!t&&!o)){if(i&&"line"in i&&"column"in i&&e&&"line"in e&&"column"in e&&i.line>0&&i.column>=0&&e.line>0&&e.column>=0&&t)return;var r="Invalid mapping: "+JSON.stringify({generated:i,source:t,original:e,name:o});if(this._ignoreInvalidMapping)return typeof console<"u"&&console.warn&&console.warn(r),!1;throw new Error(r)}};Pl.prototype._serializeMappings=function(){for(var i=0,e=1,t=0,o=0,r=0,a=0,c="",m,p,h,g,S=this._mappings.toArray(),x=0,v=S.length;x0){if(!gr.compareByGeneratedPositionsInflated(p,S[x-1]))continue;m+=","}m+=eC.encode(p.generatedColumn-i),i=p.generatedColumn,p.source!=null&&(g=this._sources.indexOf(p.source),m+=eC.encode(g-a),a=g,m+=eC.encode(p.originalLine-1-o),o=p.originalLine-1,m+=eC.encode(p.originalColumn-t),t=p.originalColumn,p.name!=null&&(h=this._names.indexOf(p.name),m+=eC.encode(h-r),r=h)),c+=m}return c};Pl.prototype._generateSourcesContent=function(i,e){return i.map(function(t){if(!this._sourcesContents)return null;e!=null&&(t=gr.relative(e,t));var o=gr.toSetString(t);return Object.prototype.hasOwnProperty.call(this._sourcesContents,o)?this._sourcesContents[o]:null},this)};Pl.prototype.toJSON=function(){var i={version:this._version,sources:this._sources.toArray(),names:this._names.toArray(),mappings:this._serializeMappings()};return this._file!=null&&(i.file=this._file),this._sourceRoot!=null&&(i.sourceRoot=this._sourceRoot),this._sourcesContents&&(i.sourcesContent=this._generateSourcesContent(i.sources,i.sourceRoot)),i};Pl.prototype.toString=function(){return JSON.stringify(this.toJSON())};fz.SourceMapGenerator=Pl});var oN=ws((mH,rM)=>{(function(n){"use strict";let i="(0?\\d+|0x[a-f0-9]+)",e={fourOctet:new RegExp(`^${i}\\.${i}\\.${i}\\.${i}$`,"i"),threeOctet:new RegExp(`^${i}\\.${i}\\.${i}$`,"i"),twoOctet:new RegExp(`^${i}\\.${i}$`,"i"),longValue:new RegExp(`^${i}$`,"i")},t=new RegExp("^0[0-7]+$","i"),o=new RegExp("^0x[a-f0-9]+$","i"),r="%[0-9a-z]{1,}",a="(?:[0-9a-f]+::?)+",c={zoneIndex:new RegExp(r,"i"),native:new RegExp(`^(::)?(${a})?([0-9a-f]+)?(::)?(${r})?$`,"i"),deprecatedTransitional:new RegExp(`^(?:::)(${i}\\.${i}\\.${i}\\.${i}(${r})?)$`,"i"),transitional:new RegExp(`^((?:${a})|(?:::)(?:${a})?)${i}\\.${i}\\.${i}\\.${i}(${r})?$`,"i")};function m(x,v){if(x.indexOf("::")!==x.lastIndexOf("::"))return null;let M=0,w=-1,y=(x.match(c.zoneIndex)||[])[0],k,I;for(y&&(y=y.substring(1),x=x.replace(/%.+$/,""));(w=x.indexOf(":",w+1))>=0;)M++;if(x.substr(0,2)==="::"&&M--,x.substr(-2,2)==="::"&&M--,M>v)return null;for(I=v-M,k=":";I--;)k+="0:";return x=x.replace("::",k),x[0]===":"&&(x=x.slice(1)),x[x.length-1]===":"&&(x=x.slice(0,-1)),v=(function(){let P=x.split(":"),R=[];for(let D=0;D0;){if(k=M-w,k<0&&(k=0),x[y]>>k!==v[y]>>k)return!1;w-=M,y+=1}return!0}function h(x){if(o.test(x))return parseInt(x,16);if(x[0]==="0"&&!isNaN(parseInt(x[1],10))){if(t.test(x))return parseInt(x,8);throw new Error(`ipaddr: cannot parse ${x} as octal`)}return parseInt(x,10)}function g(x,v){for(;x.length=0;y-=1)if(k=this.octets[y],k in w){if(I=w[k],M&&I!==0)return null;I!==8&&(M=!0),v+=I}else return null;return 32-v},x.prototype.range=function(){return S.subnetMatch(this,this.SpecialRanges)},x.prototype.toByteArray=function(){return this.octets.slice(0)},x.prototype.toIPv4MappedAddress=function(){return S.IPv6.parse(`::ffff:${this.toString()}`)},x.prototype.toNormalizedString=function(){return this.toString()},x.prototype.toString=function(){return this.octets.join(".")},x})(),S.IPv4.broadcastAddressFromCIDR=function(x){try{let v=this.parseCIDR(x),M=v[0].toByteArray(),w=this.subnetMaskFromPrefixLength(v[1]).toByteArray(),y=[],k=0;for(;k<4;)y.push(parseInt(M[k],10)|parseInt(w[k],10)^255),k++;return new this(y)}catch{throw new Error("ipaddr: the address does not have IPv4 CIDR format")}},S.IPv4.isIPv4=function(x){return this.parser(x)!==null},S.IPv4.isValid=function(x){try{return new this(this.parser(x)),!0}catch{return!1}},S.IPv4.isValidCIDR=function(x){try{return this.parseCIDR(x),!0}catch{return!1}},S.IPv4.isValidFourPartDecimal=function(x){return!!(S.IPv4.isValid(x)&&x.match(/^(0|[1-9]\d*)(\.(0|[1-9]\d*)){3}$/))},S.IPv4.isValidCIDRFourPartDecimal=function(x){let v=x.match(/^(.+)\/(\d+)$/);return!S.IPv4.isValidCIDR(x)||!v?!1:S.IPv4.isValidFourPartDecimal(v[1])},S.IPv4.networkAddressFromCIDR=function(x){let v,M,w,y,k;try{for(v=this.parseCIDR(x),w=v[0].toByteArray(),k=this.subnetMaskFromPrefixLength(v[1]).toByteArray(),y=[],M=0;M<4;)y.push(parseInt(w[M],10)&parseInt(k[M],10)),M++;return new this(y)}catch{throw new Error("ipaddr: the address does not have IPv4 CIDR format")}},S.IPv4.parse=function(x){let v=this.parser(x);if(v===null)throw new Error("ipaddr: string is not formatted like an IPv4 Address");return new this(v)},S.IPv4.parseCIDR=function(x){let v;if(v=x.match(/^(.+)\/(\d+)$/)){let M=parseInt(v[2]);if(M>=0&&M<=32){let w=[this.parse(v[1]),M];return Object.defineProperty(w,"toString",{value:function(){return this.join("/")}}),w}}throw new Error("ipaddr: string is not formatted like an IPv4 CIDR range")},S.IPv4.parser=function(x){let v,M,w;if(v=x.match(e.fourOctet))return(function(){let y=v.slice(1,6),k=[];for(let I=0;I4294967295||w<0)throw new Error("ipaddr: address outside defined range");return(function(){let y=[],k;for(k=0;k<=24;k+=8)y.push(w>>k&255);return y})().reverse()}else return(v=x.match(e.twoOctet))?(function(){let y=v.slice(1,4),k=[];if(w=h(y[1]),w>16777215||w<0)throw new Error("ipaddr: address outside defined range");return k.push(h(y[0])),k.push(w>>16&255),k.push(w>>8&255),k.push(w&255),k})():(v=x.match(e.threeOctet))?(function(){let y=v.slice(1,5),k=[];if(w=h(y[2]),w>65535||w<0)throw new Error("ipaddr: address outside defined range");return k.push(h(y[0])),k.push(h(y[1])),k.push(w>>8&255),k.push(w&255),k})():null},S.IPv4.subnetMaskFromPrefixLength=function(x){if(x=parseInt(x),x<0||x>32)throw new Error("ipaddr: invalid IPv4 prefix length");let v=[0,0,0,0],M=0,w=Math.floor(x/8);for(;M=0;I-=1)if(y=this.parts[I],y in w){if(k=w[y],M&&k!==0)return null;k!==16&&(M=!0),v+=k}else return null;return 128-v},x.prototype.range=function(){return S.subnetMatch(this,this.SpecialRanges)},x.prototype.toByteArray=function(){let v,M=[],w=this.parts;for(let y=0;y>8),M.push(v&255);return M},x.prototype.toFixedLengthString=function(){let v=function(){let w=[];for(let y=0;y>8,M&255,w>>8,w&255])},x.prototype.toNormalizedString=function(){let v=function(){let w=[];for(let y=0;yy&&(w=k.index,y=k[0].length);return y<0?M:`${M.substring(0,w)}::${M.substring(w+y)}`},x.prototype.toString=function(){return this.toRFC5952String()},x})(),S.IPv6.broadcastAddressFromCIDR=function(x){try{let v=this.parseCIDR(x),M=v[0].toByteArray(),w=this.subnetMaskFromPrefixLength(v[1]).toByteArray(),y=[],k=0;for(;k<16;)y.push(parseInt(M[k],10)|parseInt(w[k],10)^255),k++;return new this(y)}catch(v){throw new Error(`ipaddr: the address does not have IPv6 CIDR format (${v})`)}},S.IPv6.isIPv6=function(x){return this.parser(x)!==null},S.IPv6.isValid=function(x){if(typeof x=="string"&&x.indexOf(":")===-1)return!1;try{let v=this.parser(x);return new this(v.parts,v.zoneId),!0}catch{return!1}},S.IPv6.isValidCIDR=function(x){if(typeof x=="string"&&x.indexOf(":")===-1)return!1;try{return this.parseCIDR(x),!0}catch{return!1}},S.IPv6.networkAddressFromCIDR=function(x){let v,M,w,y,k;try{for(v=this.parseCIDR(x),w=v[0].toByteArray(),k=this.subnetMaskFromPrefixLength(v[1]).toByteArray(),y=[],M=0;M<16;)y.push(parseInt(w[M],10)&parseInt(k[M],10)),M++;return new this(y)}catch(I){throw new Error(`ipaddr: the address does not have IPv6 CIDR format (${I})`)}},S.IPv6.parse=function(x){let v=this.parser(x);if(v.parts===null)throw new Error("ipaddr: string is not formatted like an IPv6 Address");return new this(v.parts,v.zoneId)},S.IPv6.parseCIDR=function(x){let v,M,w;if((M=x.match(/^(.+)\/(\d+)$/))&&(v=parseInt(M[2]),v>=0&&v<=128))return w=[this.parse(M[1]),v],Object.defineProperty(w,"toString",{value:function(){return this.join("/")}}),w;throw new Error("ipaddr: string is not formatted like an IPv6 CIDR range")},S.IPv6.parser=function(x){let v,M,w,y,k,I;if(w=x.match(c.deprecatedTransitional))return this.parser(`::ffff:${w[1]}`);if(c.native.test(x))return m(x,8);if((w=x.match(c.transitional))&&(I=w[6]||"",v=w[1],w[1].endsWith("::")||(v=v.slice(0,-1)),v=m(v+I,6),v.parts)){for(k=[parseInt(w[2]),parseInt(w[3]),parseInt(w[4]),parseInt(w[5])],M=0;M128)throw new Error("ipaddr: invalid IPv6 prefix length");let v=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],M=0,w=Math.floor(x/8);for(;M{(function(n){if(typeof _H=="object")vH.exports=n();else if(typeof define=="function"&&define.amd)define(n);else{var i;try{i=window}catch{i=self}i.SparkMD5=n()}})(function(n){"use strict";var i=function(y,k){return y+k&4294967295},e=["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"];function t(y,k,I,P,R,D){return k=i(i(k,y),i(P,D)),i(k<>>32-R,I)}function o(y,k){var I=y[0],P=y[1],R=y[2],D=y[3];I+=(P&R|~P&D)+k[0]-680876936|0,I=(I<<7|I>>>25)+P|0,D+=(I&P|~I&R)+k[1]-389564586|0,D=(D<<12|D>>>20)+I|0,R+=(D&I|~D&P)+k[2]+606105819|0,R=(R<<17|R>>>15)+D|0,P+=(R&D|~R&I)+k[3]-1044525330|0,P=(P<<22|P>>>10)+R|0,I+=(P&R|~P&D)+k[4]-176418897|0,I=(I<<7|I>>>25)+P|0,D+=(I&P|~I&R)+k[5]+1200080426|0,D=(D<<12|D>>>20)+I|0,R+=(D&I|~D&P)+k[6]-1473231341|0,R=(R<<17|R>>>15)+D|0,P+=(R&D|~R&I)+k[7]-45705983|0,P=(P<<22|P>>>10)+R|0,I+=(P&R|~P&D)+k[8]+1770035416|0,I=(I<<7|I>>>25)+P|0,D+=(I&P|~I&R)+k[9]-1958414417|0,D=(D<<12|D>>>20)+I|0,R+=(D&I|~D&P)+k[10]-42063|0,R=(R<<17|R>>>15)+D|0,P+=(R&D|~R&I)+k[11]-1990404162|0,P=(P<<22|P>>>10)+R|0,I+=(P&R|~P&D)+k[12]+1804603682|0,I=(I<<7|I>>>25)+P|0,D+=(I&P|~I&R)+k[13]-40341101|0,D=(D<<12|D>>>20)+I|0,R+=(D&I|~D&P)+k[14]-1502002290|0,R=(R<<17|R>>>15)+D|0,P+=(R&D|~R&I)+k[15]+1236535329|0,P=(P<<22|P>>>10)+R|0,I+=(P&D|R&~D)+k[1]-165796510|0,I=(I<<5|I>>>27)+P|0,D+=(I&R|P&~R)+k[6]-1069501632|0,D=(D<<9|D>>>23)+I|0,R+=(D&P|I&~P)+k[11]+643717713|0,R=(R<<14|R>>>18)+D|0,P+=(R&I|D&~I)+k[0]-373897302|0,P=(P<<20|P>>>12)+R|0,I+=(P&D|R&~D)+k[5]-701558691|0,I=(I<<5|I>>>27)+P|0,D+=(I&R|P&~R)+k[10]+38016083|0,D=(D<<9|D>>>23)+I|0,R+=(D&P|I&~P)+k[15]-660478335|0,R=(R<<14|R>>>18)+D|0,P+=(R&I|D&~I)+k[4]-405537848|0,P=(P<<20|P>>>12)+R|0,I+=(P&D|R&~D)+k[9]+568446438|0,I=(I<<5|I>>>27)+P|0,D+=(I&R|P&~R)+k[14]-1019803690|0,D=(D<<9|D>>>23)+I|0,R+=(D&P|I&~P)+k[3]-187363961|0,R=(R<<14|R>>>18)+D|0,P+=(R&I|D&~I)+k[8]+1163531501|0,P=(P<<20|P>>>12)+R|0,I+=(P&D|R&~D)+k[13]-1444681467|0,I=(I<<5|I>>>27)+P|0,D+=(I&R|P&~R)+k[2]-51403784|0,D=(D<<9|D>>>23)+I|0,R+=(D&P|I&~P)+k[7]+1735328473|0,R=(R<<14|R>>>18)+D|0,P+=(R&I|D&~I)+k[12]-1926607734|0,P=(P<<20|P>>>12)+R|0,I+=(P^R^D)+k[5]-378558|0,I=(I<<4|I>>>28)+P|0,D+=(I^P^R)+k[8]-2022574463|0,D=(D<<11|D>>>21)+I|0,R+=(D^I^P)+k[11]+1839030562|0,R=(R<<16|R>>>16)+D|0,P+=(R^D^I)+k[14]-35309556|0,P=(P<<23|P>>>9)+R|0,I+=(P^R^D)+k[1]-1530992060|0,I=(I<<4|I>>>28)+P|0,D+=(I^P^R)+k[4]+1272893353|0,D=(D<<11|D>>>21)+I|0,R+=(D^I^P)+k[7]-155497632|0,R=(R<<16|R>>>16)+D|0,P+=(R^D^I)+k[10]-1094730640|0,P=(P<<23|P>>>9)+R|0,I+=(P^R^D)+k[13]+681279174|0,I=(I<<4|I>>>28)+P|0,D+=(I^P^R)+k[0]-358537222|0,D=(D<<11|D>>>21)+I|0,R+=(D^I^P)+k[3]-722521979|0,R=(R<<16|R>>>16)+D|0,P+=(R^D^I)+k[6]+76029189|0,P=(P<<23|P>>>9)+R|0,I+=(P^R^D)+k[9]-640364487|0,I=(I<<4|I>>>28)+P|0,D+=(I^P^R)+k[12]-421815835|0,D=(D<<11|D>>>21)+I|0,R+=(D^I^P)+k[15]+530742520|0,R=(R<<16|R>>>16)+D|0,P+=(R^D^I)+k[2]-995338651|0,P=(P<<23|P>>>9)+R|0,I+=(R^(P|~D))+k[0]-198630844|0,I=(I<<6|I>>>26)+P|0,D+=(P^(I|~R))+k[7]+1126891415|0,D=(D<<10|D>>>22)+I|0,R+=(I^(D|~P))+k[14]-1416354905|0,R=(R<<15|R>>>17)+D|0,P+=(D^(R|~I))+k[5]-57434055|0,P=(P<<21|P>>>11)+R|0,I+=(R^(P|~D))+k[12]+1700485571|0,I=(I<<6|I>>>26)+P|0,D+=(P^(I|~R))+k[3]-1894986606|0,D=(D<<10|D>>>22)+I|0,R+=(I^(D|~P))+k[10]-1051523|0,R=(R<<15|R>>>17)+D|0,P+=(D^(R|~I))+k[1]-2054922799|0,P=(P<<21|P>>>11)+R|0,I+=(R^(P|~D))+k[8]+1873313359|0,I=(I<<6|I>>>26)+P|0,D+=(P^(I|~R))+k[15]-30611744|0,D=(D<<10|D>>>22)+I|0,R+=(I^(D|~P))+k[6]-1560198380|0,R=(R<<15|R>>>17)+D|0,P+=(D^(R|~I))+k[13]+1309151649|0,P=(P<<21|P>>>11)+R|0,I+=(R^(P|~D))+k[4]-145523070|0,I=(I<<6|I>>>26)+P|0,D+=(P^(I|~R))+k[11]-1120210379|0,D=(D<<10|D>>>22)+I|0,R+=(I^(D|~P))+k[2]+718787259|0,R=(R<<15|R>>>17)+D|0,P+=(D^(R|~I))+k[9]-343485551|0,P=(P<<21|P>>>11)+R|0,y[0]=I+y[0]|0,y[1]=P+y[1]|0,y[2]=R+y[2]|0,y[3]=D+y[3]|0}function r(y){var k=[],I;for(I=0;I<64;I+=4)k[I>>2]=y.charCodeAt(I)+(y.charCodeAt(I+1)<<8)+(y.charCodeAt(I+2)<<16)+(y.charCodeAt(I+3)<<24);return k}function a(y){var k=[],I;for(I=0;I<64;I+=4)k[I>>2]=y[I]+(y[I+1]<<8)+(y[I+2]<<16)+(y[I+3]<<24);return k}function c(y){var k=y.length,I=[1732584193,-271733879,-1732584194,271733878],P,R,D,N,q,de;for(P=64;P<=k;P+=64)o(I,r(y.substring(P-64,P)));for(y=y.substring(P-64),R=y.length,D=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],P=0;P>2]|=y.charCodeAt(P)<<(P%4<<3);if(D[P>>2]|=128<<(P%4<<3),P>55)for(o(I,D),P=0;P<16;P+=1)D[P]=0;return N=k*8,N=N.toString(16).match(/(.*?)(.{0,8})$/),q=parseInt(N[2],16),de=parseInt(N[1],16)||0,D[14]=q,D[15]=de,o(I,D),I}function m(y){var k=y.length,I=[1732584193,-271733879,-1732584194,271733878],P,R,D,N,q,de;for(P=64;P<=k;P+=64)o(I,a(y.subarray(P-64,P)));for(y=P-64>2]|=y[P]<<(P%4<<3);if(D[P>>2]|=128<<(P%4<<3),P>55)for(o(I,D),P=0;P<16;P+=1)D[P]=0;return N=k*8,N=N.toString(16).match(/(.*?)(.{0,8})$/),q=parseInt(N[2],16),de=parseInt(N[1],16)||0,D[14]=q,D[15]=de,o(I,D),I}function p(y){var k="",I;for(I=0;I<4;I+=1)k+=e[y>>I*8+4&15]+e[y>>I*8&15];return k}function h(y){var k;for(k=0;k>16)+(k>>16)+(I>>16);return P<<16|I&65535}),typeof ArrayBuffer<"u"&&!ArrayBuffer.prototype.slice&&(function(){function y(k,I){return k=k|0||0,k<0?Math.max(k+I,0):Math.min(k,I)}ArrayBuffer.prototype.slice=function(k,I){var P=this.byteLength,R=y(k,P),D=P,N,q,de,fe;return I!==n&&(D=y(I,P)),R>D?new ArrayBuffer(0):(N=D-R,q=new ArrayBuffer(N),de=new Uint8Array(q),fe=new Uint8Array(this,R,N),de.set(fe),q)}})();function g(y){return/[\u0080-\uFFFF]/.test(y)&&(y=unescape(encodeURIComponent(y))),y}function S(y,k){var I=y.length,P=new ArrayBuffer(I),R=new Uint8Array(P),D;for(D=0;D>2]|=k.charCodeAt(P)<<(P%4<<3);return this._finish(R,I),D=h(this._hash),y&&(D=M(D)),this.reset(),D},w.prototype.reset=function(){return this._buff="",this._length=0,this._hash=[1732584193,-271733879,-1732584194,271733878],this},w.prototype.getState=function(){return{buff:this._buff,length:this._length,hash:this._hash.slice()}},w.prototype.setState=function(y){return this._buff=y.buff,this._length=y.length,this._hash=y.hash,this},w.prototype.destroy=function(){delete this._hash,delete this._buff,delete this._length},w.prototype._finish=function(y,k){var I=k,P,R,D;if(y[I>>2]|=128<<(I%4<<3),I>55)for(o(this._hash,y),I=0;I<16;I+=1)y[I]=0;P=this._length*8,P=P.toString(16).match(/(.*?)(.{0,8})$/),R=parseInt(P[2],16),D=parseInt(P[1],16)||0,y[14]=R,y[15]=D,o(this._hash,y)},w.hash=function(y,k){return w.hashBinary(g(y),k)},w.hashBinary=function(y,k){var I=c(y),P=h(I);return k?M(P):P},w.ArrayBuffer=function(){this.reset()},w.ArrayBuffer.prototype.append=function(y){var k=v(this._buff.buffer,y,!0),I=k.length,P;for(this._length+=y.byteLength,P=64;P<=I;P+=64)o(this._hash,a(k.subarray(P-64,P)));return this._buff=P-64>2]|=k[R]<<(R%4<<3);return this._finish(P,I),D=h(this._hash),y&&(D=M(D)),this.reset(),D},w.ArrayBuffer.prototype.reset=function(){return this._buff=new Uint8Array(0),this._length=0,this._hash=[1732584193,-271733879,-1732584194,271733878],this},w.ArrayBuffer.prototype.getState=function(){var y=w.prototype.getState.call(this);return y.buff=x(y.buff),y},w.ArrayBuffer.prototype.setState=function(y){return y.buff=S(y.buff,!0),w.prototype.setState.call(this,y)},w.ArrayBuffer.prototype.destroy=w.prototype.destroy,w.ArrayBuffer.prototype._finish=w.prototype._finish,w.ArrayBuffer.hash=function(y,k){var I=m(new Uint8Array(y)),P=h(I);return k?M(P):P},w})});var wH=ws(sN=>{"use strict";(function(){var n=typeof sN<"u"&&sN||typeof define<"u"&&{}||this||window;typeof define<"u"&&define("save-svg-as-png",[],function(){return n}),n.default=n;var i="http://www.w3.org/2000/xmlns/",e="http://www.w3.org/1999/xhtml",t="http://www.w3.org/2000/svg",o=']>',r=/url\(["']?(.+?)["']?\)/,a={woff2:"font/woff2",woff:"font/woff",otf:"application/x-font-opentype",ttf:"application/x-font-ttf",eot:"application/vnd.ms-fontobject",sfnt:"application/font-sfnt",svg:"image/svg+xml"},c=function(G){return G instanceof HTMLElement||G instanceof SVGElement},m=function(G){if(!c(G))throw new Error("an HTMLElement or SVGElement is required; got "+G)},p=function(G){return new Promise(function(ue,be){c(G)?ue(G):be(new Error("an HTMLElement or SVGElement is required; got "+G))})},h=function(G){return G&&G.lastIndexOf("http",0)===0&&G.lastIndexOf(window.location.host)===-1},g=function(G){var ue=Object.keys(a).filter(function(be){return G.indexOf("."+be)>0}).map(function(be){return a[be]});return ue?ue[0]:(console.error("Unknown font format for "+G+". Fonts may not be working correctly."),"application/octet-stream")},S=function(G){for(var ue="",be=new Uint8Array(G),le=0;le"u"||le===null||isNaN(parseFloat(le))?0:le},v=function(G,ue,be,le){if(G.tagName==="svg")return{width:be||x(G,ue,"width"),height:le||x(G,ue,"height")};if(G.getBBox){var De=G.getBBox(),me=De.x,V=De.y,Y=De.width,ie=De.height;return{width:me+Y,height:V+ie}}},M=function(G){return decodeURIComponent(encodeURIComponent(G).replace(/%([0-9A-F]{2})/g,function(ue,be){var le=String.fromCharCode("0x"+be);return le==="%"?"%25":le}))},w=function(G){for(var ue=window.atob(G.split(",")[1]),be=G.split(",")[0].split(":")[1].split(";")[0],le=new ArrayBuffer(ue.length),De=new Uint8Array(le),me=0;me"u",Le=V||[];return N().forEach(function(Ye){var Xe=Ye.rules,xe=Ye.href;Xe&&Array.from(Xe).forEach(function(Q){if(typeof Q.style<"u")if(y(G,Q.selectorText))oe.push(ie(Q.selectorText,Q.style.cssText));else if(Te&&Q.cssText.match(/^@font-face/)){var Oe=k(Q,xe);Oe&&Le.push(Oe)}else Y||oe.push(Q.cssText)})}),R(Le).then(function(Ye){return oe.join(` -`)+Ye})},de=function(){if(!navigator.msSaveOrOpenBlob&&!("download"in document.createElement("a")))return{popup:window.open()}};n.prepareSvg=function(fe,G,ue){m(fe);var be=G||{},le=be.left,De=le===void 0?0:le,me=be.top,V=me===void 0?0:me,Y=be.width,ie=be.height,oe=be.scale,Te=oe===void 0?1:oe,Le=be.responsive,Ye=Le===void 0?!1:Le,Xe=be.excludeCss,xe=Xe===void 0?!1:Xe;return I(fe).then(function(){var Q=fe.cloneNode(!0);Q.style.backgroundColor=(G||{}).backgroundColor||fe.style.backgroundColor;var Oe=v(fe,Q,Y,ie),Ge=Oe.width,ct=Oe.height;if(fe.tagName!=="svg")if(fe.getBBox){Q.getAttribute("transform")!=null&&Q.setAttribute("transform",Q.getAttribute("transform").replace(/translate\(.*?\)/,""));var kt=document.createElementNS("http://www.w3.org/2000/svg","svg");kt.appendChild(Q),Q=kt}else{console.error("Attempted to render non-SVG element",fe);return}if(Q.setAttribute("version","1.1"),Q.setAttribute("viewBox",[De,V,Ge,ct].join(" ")),Q.getAttribute("xmlns")||Q.setAttributeNS(i,"xmlns",t),Q.getAttribute("xmlns:xlink")||Q.setAttributeNS(i,"xmlns:xlink","http://www.w3.org/1999/xlink"),Ye?(Q.removeAttribute("width"),Q.removeAttribute("height"),Q.setAttribute("preserveAspectRatio","xMinYMin meet")):(Q.setAttribute("width",Ge*Te),Q.setAttribute("height",ct*Te)),Array.from(Q.querySelectorAll("foreignObject > *")).forEach(function(xo){xo.setAttributeNS(i,"xmlns",xo.tagName==="svg"?t:e)}),xe){var Xn=document.createElement("div");Xn.appendChild(Q);var Fo=Xn.innerHTML;if(typeof ue=="function")ue(Fo,Ge,ct);else return{src:Fo,width:Ge,height:ct}}else return q(fe,G).then(function(xo){var jr=document.createElement("style");jr.setAttribute("type","text/css"),jr.innerHTML=``;var kn=document.createElement("defs");kn.appendChild(jr),Q.insertBefore(kn,Q.firstChild);var Xd=document.createElement("div");Xd.appendChild(Q);var oh=Xd.innerHTML.replace(/NS\d+:href/gi,'xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href');if(typeof ue=="function")ue(oh,Ge,ct);else return{src:oh,width:Ge,height:ct}})})},n.svgAsDataUri=function(fe,G,ue){return m(fe),n.prepareSvg(fe,G).then(function(be){var le=be.src,De=be.width,me=be.height,V="data:image/svg+xml;base64,"+window.btoa(M(o+le));return typeof ue=="function"&&ue(V,De,me),V})},n.svgAsPngUri=function(fe,G,ue){m(fe);var be=G||{},le=be.encoderType,De=le===void 0?"image/png":le,me=be.encoderOptions,V=me===void 0?.8:me,Y=be.canvg,ie=function(Te){var Le=Te.src,Ye=Te.width,Xe=Te.height,xe=document.createElement("canvas"),Q=xe.getContext("2d"),Oe=window.devicePixelRatio||1;xe.width=Ye*Oe,xe.height=Xe*Oe,xe.style.width=xe.width+"px",xe.style.height=xe.height+"px",Q.setTransform(Oe,0,0,Oe,0,0),Y?Y(xe,Le):Q.drawImage(Le,0,0);var Ge=void 0;try{Ge=xe.toDataURL(De,V)}catch(ct){if(typeof SecurityError<"u"&&ct instanceof SecurityError||ct.name==="SecurityError"){console.error("Rendered SVG images cannot be downloaded in this browser.");return}else throw ct}return typeof ue=="function"&&ue(Ge,xe.width,xe.height),Promise.resolve(Ge)};return Y?n.prepareSvg(fe,G).then(ie):n.svgAsDataUri(fe,G).then(function(oe){return new Promise(function(Te,Le){var Ye=new Image;Ye.onload=function(){return Te(ie({src:Ye,width:Ye.width,height:Ye.height}))},Ye.onerror=function(){Le(`There was an error loading the data URI as an image on the following SVG -`+window.atob(oe.slice(26))+`Open the following link to see browser's diagnosis -`+oe)},Ye.src=oe})})},n.download=function(fe,G,ue){if(navigator.msSaveOrOpenBlob)navigator.msSaveOrOpenBlob(w(G),fe);else{var be=document.createElement("a");if("download"in be){be.download=fe,be.style.display="none",document.body.appendChild(be);try{var le=w(G),De=URL.createObjectURL(le);be.href=De,be.onclick=function(){return requestAnimationFrame(function(){return URL.revokeObjectURL(De)})}}catch(me){console.error(me),console.warn("Error while getting object URL. Falling back to string URL."),be.href=G}be.click(),document.body.removeChild(be)}else ue&&ue.popup&&(ue.popup.document.title=fe,ue.popup.location.replace(G))}},n.saveSvg=function(fe,G,ue){var be=de();return p(fe).then(function(le){return n.svgAsDataUri(le,ue||{})}).then(function(le){return n.download(G,le,be)})},n.saveSvgAsPng=function(fe,G,ue){var be=de();return p(fe).then(function(le){return n.svgAsPngUri(le,ue||{})}).then(function(le){return n.download(G,le,be)})}})()});var mN=ws((sk,dN)=>{(function(n,i){if(typeof sk=="object"&&typeof dN=="object")dN.exports=i();else if(typeof define=="function"&&define.amd)define([],i);else{var e=i();for(var t in e)(typeof sk=="object"?sk:n)[t]=e[t]}})(globalThis,()=>(()=>{"use strict";var n={4567:function(o,r,a){var c=this&&this.__decorate||function(w,y,k,I){var P,R=arguments.length,D=R<3?y:I===null?I=Object.getOwnPropertyDescriptor(y,k):I;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")D=Reflect.decorate(w,y,k,I);else for(var N=w.length-1;N>=0;N--)(P=w[N])&&(D=(R<3?P(D):R>3?P(y,k,D):P(y,k))||D);return R>3&&D&&Object.defineProperty(y,k,D),D},m=this&&this.__param||function(w,y){return function(k,I){y(k,I,w)}};Object.defineProperty(r,"__esModule",{value:!0}),r.AccessibilityManager=void 0;let p=a(9042),h=a(9924),g=a(844),S=a(4725),x=a(2585),v=a(3656),M=r.AccessibilityManager=class extends g.Disposable{constructor(w,y,k,I){super(),this._terminal=w,this._coreBrowserService=k,this._renderService=I,this._rowColumns=new WeakMap,this._liveRegionLineCount=0,this._charsToConsume=[],this._charsToAnnounce="",this._accessibilityContainer=this._coreBrowserService.mainDocument.createElement("div"),this._accessibilityContainer.classList.add("xterm-accessibility"),this._rowContainer=this._coreBrowserService.mainDocument.createElement("div"),this._rowContainer.setAttribute("role","list"),this._rowContainer.classList.add("xterm-accessibility-tree"),this._rowElements=[];for(let P=0;Pthis._handleBoundaryFocus(P,0),this._bottomBoundaryFocusListener=P=>this._handleBoundaryFocus(P,1),this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions(),this._accessibilityContainer.appendChild(this._rowContainer),this._liveRegion=this._coreBrowserService.mainDocument.createElement("div"),this._liveRegion.classList.add("live-region"),this._liveRegion.setAttribute("aria-live","assertive"),this._accessibilityContainer.appendChild(this._liveRegion),this._liveRegionDebouncer=this.register(new h.TimeBasedDebouncer(this._renderRows.bind(this))),!this._terminal.element)throw new Error("Cannot enable accessibility before Terminal.open");this._terminal.element.insertAdjacentElement("afterbegin",this._accessibilityContainer),this.register(this._terminal.onResize(P=>this._handleResize(P.rows))),this.register(this._terminal.onRender(P=>this._refreshRows(P.start,P.end))),this.register(this._terminal.onScroll(()=>this._refreshRows())),this.register(this._terminal.onA11yChar(P=>this._handleChar(P))),this.register(this._terminal.onLineFeed(()=>this._handleChar(` -`))),this.register(this._terminal.onA11yTab(P=>this._handleTab(P))),this.register(this._terminal.onKey(P=>this._handleKey(P.key))),this.register(this._terminal.onBlur(()=>this._clearLiveRegion())),this.register(this._renderService.onDimensionsChange(()=>this._refreshRowsDimensions())),this.register((0,v.addDisposableDomListener)(document,"selectionchange",()=>this._handleSelectionChange())),this.register(this._coreBrowserService.onDprChange(()=>this._refreshRowsDimensions())),this._refreshRows(),this.register((0,g.toDisposable)(()=>{this._accessibilityContainer.remove(),this._rowElements.length=0}))}_handleTab(w){for(let y=0;y0?this._charsToConsume.shift()!==w&&(this._charsToAnnounce+=w):this._charsToAnnounce+=w,w===` -`&&(this._liveRegionLineCount++,this._liveRegionLineCount===21&&(this._liveRegion.textContent+=p.tooMuchOutput)))}_clearLiveRegion(){this._liveRegion.textContent="",this._liveRegionLineCount=0}_handleKey(w){this._clearLiveRegion(),new RegExp("\\p{Control}","u").test(w)||this._charsToConsume.push(w)}_refreshRows(w,y){this._liveRegionDebouncer.refresh(w,y,this._terminal.rows)}_renderRows(w,y){let k=this._terminal.buffer,I=k.lines.length.toString();for(let P=w;P<=y;P++){let R=k.lines.get(k.ydisp+P),D=[],N=R?.translateToString(!0,void 0,void 0,D)||"",q=(k.ydisp+P+1).toString(),de=this._rowElements[P];de&&(N.length===0?(de.innerText="\xA0",this._rowColumns.set(de,[0,1])):(de.textContent=N,this._rowColumns.set(de,D)),de.setAttribute("aria-posinset",q),de.setAttribute("aria-setsize",I))}this._announceCharacters()}_announceCharacters(){this._charsToAnnounce.length!==0&&(this._liveRegion.textContent+=this._charsToAnnounce,this._charsToAnnounce="")}_handleBoundaryFocus(w,y){let k=w.target,I=this._rowElements[y===0?1:this._rowElements.length-2];if(k.getAttribute("aria-posinset")===(y===0?"1":`${this._terminal.buffer.lines.length}`)||w.relatedTarget!==I)return;let P,R;if(y===0?(P=k,R=this._rowElements.pop(),this._rowContainer.removeChild(R)):(P=this._rowElements.shift(),R=k,this._rowContainer.removeChild(P)),P.removeEventListener("focus",this._topBoundaryFocusListener),R.removeEventListener("focus",this._bottomBoundaryFocusListener),y===0){let D=this._createAccessibilityTreeNode();this._rowElements.unshift(D),this._rowContainer.insertAdjacentElement("afterbegin",D)}else{let D=this._createAccessibilityTreeNode();this._rowElements.push(D),this._rowContainer.appendChild(D)}this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._terminal.scrollLines(y===0?-1:1),this._rowElements[y===0?1:this._rowElements.length-2].focus(),w.preventDefault(),w.stopImmediatePropagation()}_handleSelectionChange(){if(this._rowElements.length===0)return;let w=document.getSelection();if(!w)return;if(w.isCollapsed)return void(this._rowContainer.contains(w.anchorNode)&&this._terminal.clearSelection());if(!w.anchorNode||!w.focusNode)return void console.error("anchorNode and/or focusNode are null");let y={node:w.anchorNode,offset:w.anchorOffset},k={node:w.focusNode,offset:w.focusOffset};if((y.node.compareDocumentPosition(k.node)&Node.DOCUMENT_POSITION_PRECEDING||y.node===k.node&&y.offset>k.offset)&&([y,k]=[k,y]),y.node.compareDocumentPosition(this._rowElements[0])&(Node.DOCUMENT_POSITION_CONTAINED_BY|Node.DOCUMENT_POSITION_FOLLOWING)&&(y={node:this._rowElements[0].childNodes[0],offset:0}),!this._rowContainer.contains(y.node))return;let I=this._rowElements.slice(-1)[0];if(k.node.compareDocumentPosition(I)&(Node.DOCUMENT_POSITION_CONTAINED_BY|Node.DOCUMENT_POSITION_PRECEDING)&&(k={node:I,offset:I.textContent?.length??0}),!this._rowContainer.contains(k.node))return;let P=({node:N,offset:q})=>{let de=N instanceof Text?N.parentNode:N,fe=parseInt(de?.getAttribute("aria-posinset"),10)-1;if(isNaN(fe))return console.warn("row is invalid. Race condition?"),null;let G=this._rowColumns.get(de);if(!G)return console.warn("columns is null. Race condition?"),null;let ue=q=this._terminal.cols&&(++fe,ue=0),{row:fe,column:ue}},R=P(y),D=P(k);if(R&&D){if(R.row>D.row||R.row===D.row&&R.column>=D.column)throw new Error("invalid range");this._terminal.select(R.column,R.row,(D.row-R.row)*this._terminal.cols-R.column+D.column)}}_handleResize(w){this._rowElements[this._rowElements.length-1].removeEventListener("focus",this._bottomBoundaryFocusListener);for(let y=this._rowContainer.children.length;yw;)this._rowContainer.removeChild(this._rowElements.pop());this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions()}_createAccessibilityTreeNode(){let w=this._coreBrowserService.mainDocument.createElement("div");return w.setAttribute("role","listitem"),w.tabIndex=-1,this._refreshRowDimensions(w),w}_refreshRowsDimensions(){if(this._renderService.dimensions.css.cell.height){this._accessibilityContainer.style.width=`${this._renderService.dimensions.css.canvas.width}px`,this._rowElements.length!==this._terminal.rows&&this._handleResize(this._terminal.rows);for(let w=0;w{function a(h){return h.replace(/\r?\n/g,"\r")}function c(h,g){return g?"\x1B[200~"+h+"\x1B[201~":h}function m(h,g,S,x){h=c(h=a(h),S.decPrivateModes.bracketedPasteMode&&x.rawOptions.ignoreBracketedPasteMode!==!0),S.triggerDataEvent(h,!0),g.value=""}function p(h,g,S){let x=S.getBoundingClientRect(),v=h.clientX-x.left-10,M=h.clientY-x.top-10;g.style.width="20px",g.style.height="20px",g.style.left=`${v}px`,g.style.top=`${M}px`,g.style.zIndex="1000",g.focus()}Object.defineProperty(r,"__esModule",{value:!0}),r.rightClickHandler=r.moveTextAreaUnderMouseCursor=r.paste=r.handlePasteEvent=r.copyHandler=r.bracketTextForPaste=r.prepareTextForTerminal=void 0,r.prepareTextForTerminal=a,r.bracketTextForPaste=c,r.copyHandler=function(h,g){h.clipboardData&&h.clipboardData.setData("text/plain",g.selectionText),h.preventDefault()},r.handlePasteEvent=function(h,g,S,x){h.stopPropagation(),h.clipboardData&&m(h.clipboardData.getData("text/plain"),g,S,x)},r.paste=m,r.moveTextAreaUnderMouseCursor=p,r.rightClickHandler=function(h,g,S,x,v){p(h,g,S),v&&x.rightClickSelect(h),g.value=x.selectionText,g.select()}},7239:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.ColorContrastCache=void 0;let c=a(1505);r.ColorContrastCache=class{constructor(){this._color=new c.TwoKeyMap,this._css=new c.TwoKeyMap}setCss(m,p,h){this._css.set(m,p,h)}getCss(m,p){return this._css.get(m,p)}setColor(m,p,h){this._color.set(m,p,h)}getColor(m,p){return this._color.get(m,p)}clear(){this._color.clear(),this._css.clear()}}},3656:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.addDisposableDomListener=void 0,r.addDisposableDomListener=function(a,c,m,p){a.addEventListener(c,m,p);let h=!1;return{dispose:()=>{h||(h=!0,a.removeEventListener(c,m,p))}}}},3551:function(o,r,a){var c=this&&this.__decorate||function(M,w,y,k){var I,P=arguments.length,R=P<3?w:k===null?k=Object.getOwnPropertyDescriptor(w,y):k;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")R=Reflect.decorate(M,w,y,k);else for(var D=M.length-1;D>=0;D--)(I=M[D])&&(R=(P<3?I(R):P>3?I(w,y,R):I(w,y))||R);return P>3&&R&&Object.defineProperty(w,y,R),R},m=this&&this.__param||function(M,w){return function(y,k){w(y,k,M)}};Object.defineProperty(r,"__esModule",{value:!0}),r.Linkifier=void 0;let p=a(3656),h=a(8460),g=a(844),S=a(2585),x=a(4725),v=r.Linkifier=class extends g.Disposable{get currentLink(){return this._currentLink}constructor(M,w,y,k,I){super(),this._element=M,this._mouseService=w,this._renderService=y,this._bufferService=k,this._linkProviderService=I,this._linkCacheDisposables=[],this._isMouseOut=!0,this._wasResized=!1,this._activeLine=-1,this._onShowLinkUnderline=this.register(new h.EventEmitter),this.onShowLinkUnderline=this._onShowLinkUnderline.event,this._onHideLinkUnderline=this.register(new h.EventEmitter),this.onHideLinkUnderline=this._onHideLinkUnderline.event,this.register((0,g.getDisposeArrayDisposable)(this._linkCacheDisposables)),this.register((0,g.toDisposable)(()=>{this._lastMouseEvent=void 0,this._activeProviderReplies?.clear()})),this.register(this._bufferService.onResize(()=>{this._clearCurrentLink(),this._wasResized=!0})),this.register((0,p.addDisposableDomListener)(this._element,"mouseleave",()=>{this._isMouseOut=!0,this._clearCurrentLink()})),this.register((0,p.addDisposableDomListener)(this._element,"mousemove",this._handleMouseMove.bind(this))),this.register((0,p.addDisposableDomListener)(this._element,"mousedown",this._handleMouseDown.bind(this))),this.register((0,p.addDisposableDomListener)(this._element,"mouseup",this._handleMouseUp.bind(this)))}_handleMouseMove(M){this._lastMouseEvent=M;let w=this._positionFromMouseEvent(M,this._element,this._mouseService);if(!w)return;this._isMouseOut=!1;let y=M.composedPath();for(let k=0;k{k?.forEach(I=>{I.link.dispose&&I.link.dispose()})}),this._activeProviderReplies=new Map,this._activeLine=M.y);let y=!1;for(let[k,I]of this._linkProviderService.linkProviders.entries())w?this._activeProviderReplies?.get(k)&&(y=this._checkLinkProviderResult(k,M,y)):I.provideLinks(M.y,P=>{if(this._isMouseOut)return;let R=P?.map(D=>({link:D}));this._activeProviderReplies?.set(k,R),y=this._checkLinkProviderResult(k,M,y),this._activeProviderReplies?.size===this._linkProviderService.linkProviders.length&&this._removeIntersectingLinks(M.y,this._activeProviderReplies)})}_removeIntersectingLinks(M,w){let y=new Set;for(let k=0;kM?this._bufferService.cols:R.link.range.end.x;for(let q=D;q<=N;q++){if(y.has(q)){I.splice(P--,1);break}y.add(q)}}}}_checkLinkProviderResult(M,w,y){if(!this._activeProviderReplies)return y;let k=this._activeProviderReplies.get(M),I=!1;for(let P=0;Pthis._linkAtPosition(R.link,w));P&&(y=!0,this._handleNewLink(P))}if(this._activeProviderReplies.size===this._linkProviderService.linkProviders.length&&!y)for(let P=0;Pthis._linkAtPosition(D.link,w));if(R){y=!0,this._handleNewLink(R);break}}return y}_handleMouseDown(){this._mouseDownLink=this._currentLink}_handleMouseUp(M){if(!this._currentLink)return;let w=this._positionFromMouseEvent(M,this._element,this._mouseService);w&&this._mouseDownLink===this._currentLink&&this._linkAtPosition(this._currentLink.link,w)&&this._currentLink.link.activate(M,this._currentLink.link.text)}_clearCurrentLink(M,w){this._currentLink&&this._lastMouseEvent&&(!M||!w||this._currentLink.link.range.start.y>=M&&this._currentLink.link.range.end.y<=w)&&(this._linkLeave(this._element,this._currentLink.link,this._lastMouseEvent),this._currentLink=void 0,(0,g.disposeArray)(this._linkCacheDisposables))}_handleNewLink(M){if(!this._lastMouseEvent)return;let w=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);w&&this._linkAtPosition(M.link,w)&&(this._currentLink=M,this._currentLink.state={decorations:{underline:M.link.decorations===void 0||M.link.decorations.underline,pointerCursor:M.link.decorations===void 0||M.link.decorations.pointerCursor},isHovered:!0},this._linkHover(this._element,M.link,this._lastMouseEvent),M.link.decorations={},Object.defineProperties(M.link.decorations,{pointerCursor:{get:()=>this._currentLink?.state?.decorations.pointerCursor,set:y=>{this._currentLink?.state&&this._currentLink.state.decorations.pointerCursor!==y&&(this._currentLink.state.decorations.pointerCursor=y,this._currentLink.state.isHovered&&this._element.classList.toggle("xterm-cursor-pointer",y))}},underline:{get:()=>this._currentLink?.state?.decorations.underline,set:y=>{this._currentLink?.state&&this._currentLink?.state?.decorations.underline!==y&&(this._currentLink.state.decorations.underline=y,this._currentLink.state.isHovered&&this._fireUnderlineEvent(M.link,y))}}}),this._linkCacheDisposables.push(this._renderService.onRenderedViewportChange(y=>{if(!this._currentLink)return;let k=y.start===0?0:y.start+1+this._bufferService.buffer.ydisp,I=this._bufferService.buffer.ydisp+1+y.end;if(this._currentLink.link.range.start.y>=k&&this._currentLink.link.range.end.y<=I&&(this._clearCurrentLink(k,I),this._lastMouseEvent)){let P=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);P&&this._askForLink(P,!1)}})))}_linkHover(M,w,y){this._currentLink?.state&&(this._currentLink.state.isHovered=!0,this._currentLink.state.decorations.underline&&this._fireUnderlineEvent(w,!0),this._currentLink.state.decorations.pointerCursor&&M.classList.add("xterm-cursor-pointer")),w.hover&&w.hover(y,w.text)}_fireUnderlineEvent(M,w){let y=M.range,k=this._bufferService.buffer.ydisp,I=this._createLinkUnderlineEvent(y.start.x-1,y.start.y-k-1,y.end.x,y.end.y-k-1,void 0);(w?this._onShowLinkUnderline:this._onHideLinkUnderline).fire(I)}_linkLeave(M,w,y){this._currentLink?.state&&(this._currentLink.state.isHovered=!1,this._currentLink.state.decorations.underline&&this._fireUnderlineEvent(w,!1),this._currentLink.state.decorations.pointerCursor&&M.classList.remove("xterm-cursor-pointer")),w.leave&&w.leave(y,w.text)}_linkAtPosition(M,w){let y=M.range.start.y*this._bufferService.cols+M.range.start.x,k=M.range.end.y*this._bufferService.cols+M.range.end.x,I=w.y*this._bufferService.cols+w.x;return y<=I&&I<=k}_positionFromMouseEvent(M,w,y){let k=y.getCoords(M,w,this._bufferService.cols,this._bufferService.rows);if(k)return{x:k[0],y:k[1]+this._bufferService.buffer.ydisp}}_createLinkUnderlineEvent(M,w,y,k,I){return{x1:M,y1:w,x2:y,y2:k,cols:this._bufferService.cols,fg:I}}};r.Linkifier=v=c([m(1,x.IMouseService),m(2,x.IRenderService),m(3,S.IBufferService),m(4,x.ILinkProviderService)],v)},9042:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.tooMuchOutput=r.promptLabel=void 0,r.promptLabel="Terminal input",r.tooMuchOutput="Too much output to announce, navigate to rows manually to read"},3730:function(o,r,a){var c=this&&this.__decorate||function(x,v,M,w){var y,k=arguments.length,I=k<3?v:w===null?w=Object.getOwnPropertyDescriptor(v,M):w;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")I=Reflect.decorate(x,v,M,w);else for(var P=x.length-1;P>=0;P--)(y=x[P])&&(I=(k<3?y(I):k>3?y(v,M,I):y(v,M))||I);return k>3&&I&&Object.defineProperty(v,M,I),I},m=this&&this.__param||function(x,v){return function(M,w){v(M,w,x)}};Object.defineProperty(r,"__esModule",{value:!0}),r.OscLinkProvider=void 0;let p=a(511),h=a(2585),g=r.OscLinkProvider=class{constructor(x,v,M){this._bufferService=x,this._optionsService=v,this._oscLinkService=M}provideLinks(x,v){let M=this._bufferService.buffer.lines.get(x-1);if(!M)return void v(void 0);let w=[],y=this._optionsService.rawOptions.linkHandler,k=new p.CellData,I=M.getTrimmedLength(),P=-1,R=-1,D=!1;for(let N=0;Ny?y.activate(G,ue,de):S(0,ue),hover:(G,ue)=>y?.hover?.(G,ue,de),leave:(G,ue)=>y?.leave?.(G,ue,de)})}D=!1,k.hasExtendedAttrs()&&k.extended.urlId?(R=N,P=k.extended.urlId):(R=-1,P=-1)}}v(w)}};function S(x,v){if(confirm(`Do you want to navigate to ${v}? - -WARNING: This link could potentially be dangerous`)){let M=window.open();if(M){try{M.opener=null}catch{}M.location.href=v}else console.warn("Opening link blocked as opener could not be cleared")}}r.OscLinkProvider=g=c([m(0,h.IBufferService),m(1,h.IOptionsService),m(2,h.IOscLinkService)],g)},6193:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.RenderDebouncer=void 0,r.RenderDebouncer=class{constructor(a,c){this._renderCallback=a,this._coreBrowserService=c,this._refreshCallbacks=[]}dispose(){this._animationFrame&&(this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame),this._animationFrame=void 0)}addRefreshCallback(a){return this._refreshCallbacks.push(a),this._animationFrame||(this._animationFrame=this._coreBrowserService.window.requestAnimationFrame(()=>this._innerRefresh())),this._animationFrame}refresh(a,c,m){this._rowCount=m,a=a!==void 0?a:0,c=c!==void 0?c:this._rowCount-1,this._rowStart=this._rowStart!==void 0?Math.min(this._rowStart,a):a,this._rowEnd=this._rowEnd!==void 0?Math.max(this._rowEnd,c):c,this._animationFrame||(this._animationFrame=this._coreBrowserService.window.requestAnimationFrame(()=>this._innerRefresh()))}_innerRefresh(){if(this._animationFrame=void 0,this._rowStart===void 0||this._rowEnd===void 0||this._rowCount===void 0)return void this._runRefreshCallbacks();let a=Math.max(this._rowStart,0),c=Math.min(this._rowEnd,this._rowCount-1);this._rowStart=void 0,this._rowEnd=void 0,this._renderCallback(a,c),this._runRefreshCallbacks()}_runRefreshCallbacks(){for(let a of this._refreshCallbacks)a(0);this._refreshCallbacks=[]}}},3236:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.Terminal=void 0;let c=a(3614),m=a(3656),p=a(3551),h=a(9042),g=a(3730),S=a(1680),x=a(3107),v=a(5744),M=a(2950),w=a(1296),y=a(428),k=a(4269),I=a(5114),P=a(8934),R=a(3230),D=a(9312),N=a(4725),q=a(6731),de=a(8055),fe=a(8969),G=a(8460),ue=a(844),be=a(6114),le=a(8437),De=a(2584),me=a(7399),V=a(5941),Y=a(9074),ie=a(2585),oe=a(5435),Te=a(4567),Le=a(779);class Ye extends fe.CoreTerminal{get onFocus(){return this._onFocus.event}get onBlur(){return this._onBlur.event}get onA11yChar(){return this._onA11yCharEmitter.event}get onA11yTab(){return this._onA11yTabEmitter.event}get onWillOpen(){return this._onWillOpen.event}constructor(xe={}){super(xe),this.browser=be,this._keyDownHandled=!1,this._keyDownSeen=!1,this._keyPressHandled=!1,this._unprocessedDeadKey=!1,this._accessibilityManager=this.register(new ue.MutableDisposable),this._onCursorMove=this.register(new G.EventEmitter),this.onCursorMove=this._onCursorMove.event,this._onKey=this.register(new G.EventEmitter),this.onKey=this._onKey.event,this._onRender=this.register(new G.EventEmitter),this.onRender=this._onRender.event,this._onSelectionChange=this.register(new G.EventEmitter),this.onSelectionChange=this._onSelectionChange.event,this._onTitleChange=this.register(new G.EventEmitter),this.onTitleChange=this._onTitleChange.event,this._onBell=this.register(new G.EventEmitter),this.onBell=this._onBell.event,this._onFocus=this.register(new G.EventEmitter),this._onBlur=this.register(new G.EventEmitter),this._onA11yCharEmitter=this.register(new G.EventEmitter),this._onA11yTabEmitter=this.register(new G.EventEmitter),this._onWillOpen=this.register(new G.EventEmitter),this._setup(),this._decorationService=this._instantiationService.createInstance(Y.DecorationService),this._instantiationService.setService(ie.IDecorationService,this._decorationService),this._linkProviderService=this._instantiationService.createInstance(Le.LinkProviderService),this._instantiationService.setService(N.ILinkProviderService,this._linkProviderService),this._linkProviderService.registerLinkProvider(this._instantiationService.createInstance(g.OscLinkProvider)),this.register(this._inputHandler.onRequestBell(()=>this._onBell.fire())),this.register(this._inputHandler.onRequestRefreshRows((Q,Oe)=>this.refresh(Q,Oe))),this.register(this._inputHandler.onRequestSendFocus(()=>this._reportFocus())),this.register(this._inputHandler.onRequestReset(()=>this.reset())),this.register(this._inputHandler.onRequestWindowsOptionsReport(Q=>this._reportWindowsOptions(Q))),this.register(this._inputHandler.onColor(Q=>this._handleColorEvent(Q))),this.register((0,G.forwardEvent)(this._inputHandler.onCursorMove,this._onCursorMove)),this.register((0,G.forwardEvent)(this._inputHandler.onTitleChange,this._onTitleChange)),this.register((0,G.forwardEvent)(this._inputHandler.onA11yChar,this._onA11yCharEmitter)),this.register((0,G.forwardEvent)(this._inputHandler.onA11yTab,this._onA11yTabEmitter)),this.register(this._bufferService.onResize(Q=>this._afterResize(Q.cols,Q.rows))),this.register((0,ue.toDisposable)(()=>{this._customKeyEventHandler=void 0,this.element?.parentNode?.removeChild(this.element)}))}_handleColorEvent(xe){if(this._themeService)for(let Q of xe){let Oe,Ge="";switch(Q.index){case 256:Oe="foreground",Ge="10";break;case 257:Oe="background",Ge="11";break;case 258:Oe="cursor",Ge="12";break;default:Oe="ansi",Ge="4;"+Q.index}switch(Q.type){case 0:let ct=de.color.toColorRGB(Oe==="ansi"?this._themeService.colors.ansi[Q.index]:this._themeService.colors[Oe]);this.coreService.triggerDataEvent(`${De.C0.ESC}]${Ge};${(0,V.toRgbString)(ct)}${De.C1_ESCAPED.ST}`);break;case 1:if(Oe==="ansi")this._themeService.modifyColors(kt=>kt.ansi[Q.index]=de.channels.toColor(...Q.color));else{let kt=Oe;this._themeService.modifyColors(Xn=>Xn[kt]=de.channels.toColor(...Q.color))}break;case 2:this._themeService.restoreColor(Q.index)}}}_setup(){super._setup(),this._customKeyEventHandler=void 0}get buffer(){return this.buffers.active}focus(){this.textarea&&this.textarea.focus({preventScroll:!0})}_handleScreenReaderModeOptionChange(xe){xe?!this._accessibilityManager.value&&this._renderService&&(this._accessibilityManager.value=this._instantiationService.createInstance(Te.AccessibilityManager,this)):this._accessibilityManager.clear()}_handleTextAreaFocus(xe){this.coreService.decPrivateModes.sendFocus&&this.coreService.triggerDataEvent(De.C0.ESC+"[I"),this.element.classList.add("focus"),this._showCursor(),this._onFocus.fire()}blur(){return this.textarea?.blur()}_handleTextAreaBlur(){this.textarea.value="",this.refresh(this.buffer.y,this.buffer.y),this.coreService.decPrivateModes.sendFocus&&this.coreService.triggerDataEvent(De.C0.ESC+"[O"),this.element.classList.remove("focus"),this._onBlur.fire()}_syncTextArea(){if(!this.textarea||!this.buffer.isCursorInViewport||this._compositionHelper.isComposing||!this._renderService)return;let xe=this.buffer.ybase+this.buffer.y,Q=this.buffer.lines.get(xe);if(!Q)return;let Oe=Math.min(this.buffer.x,this.cols-1),Ge=this._renderService.dimensions.css.cell.height,ct=Q.getWidth(Oe),kt=this._renderService.dimensions.css.cell.width*ct,Xn=this.buffer.y*this._renderService.dimensions.css.cell.height,Fo=Oe*this._renderService.dimensions.css.cell.width;this.textarea.style.left=Fo+"px",this.textarea.style.top=Xn+"px",this.textarea.style.width=kt+"px",this.textarea.style.height=Ge+"px",this.textarea.style.lineHeight=Ge+"px",this.textarea.style.zIndex="-5"}_initGlobal(){this._bindKeys(),this.register((0,m.addDisposableDomListener)(this.element,"copy",Q=>{this.hasSelection()&&(0,c.copyHandler)(Q,this._selectionService)}));let xe=Q=>(0,c.handlePasteEvent)(Q,this.textarea,this.coreService,this.optionsService);this.register((0,m.addDisposableDomListener)(this.textarea,"paste",xe)),this.register((0,m.addDisposableDomListener)(this.element,"paste",xe)),be.isFirefox?this.register((0,m.addDisposableDomListener)(this.element,"mousedown",Q=>{Q.button===2&&(0,c.rightClickHandler)(Q,this.textarea,this.screenElement,this._selectionService,this.options.rightClickSelectsWord)})):this.register((0,m.addDisposableDomListener)(this.element,"contextmenu",Q=>{(0,c.rightClickHandler)(Q,this.textarea,this.screenElement,this._selectionService,this.options.rightClickSelectsWord)})),be.isLinux&&this.register((0,m.addDisposableDomListener)(this.element,"auxclick",Q=>{Q.button===1&&(0,c.moveTextAreaUnderMouseCursor)(Q,this.textarea,this.screenElement)}))}_bindKeys(){this.register((0,m.addDisposableDomListener)(this.textarea,"keyup",xe=>this._keyUp(xe),!0)),this.register((0,m.addDisposableDomListener)(this.textarea,"keydown",xe=>this._keyDown(xe),!0)),this.register((0,m.addDisposableDomListener)(this.textarea,"keypress",xe=>this._keyPress(xe),!0)),this.register((0,m.addDisposableDomListener)(this.textarea,"compositionstart",()=>this._compositionHelper.compositionstart())),this.register((0,m.addDisposableDomListener)(this.textarea,"compositionupdate",xe=>this._compositionHelper.compositionupdate(xe))),this.register((0,m.addDisposableDomListener)(this.textarea,"compositionend",()=>this._compositionHelper.compositionend())),this.register((0,m.addDisposableDomListener)(this.textarea,"input",xe=>this._inputEvent(xe),!0)),this.register(this.onRender(()=>this._compositionHelper.updateCompositionElements()))}open(xe){if(!xe)throw new Error("Terminal requires a parent element.");if(xe.isConnected||this._logService.debug("Terminal.open was called on an element that was not attached to the DOM"),this.element?.ownerDocument.defaultView&&this._coreBrowserService)return void(this.element.ownerDocument.defaultView!==this._coreBrowserService.window&&(this._coreBrowserService.window=this.element.ownerDocument.defaultView));this._document=xe.ownerDocument,this.options.documentOverride&&this.options.documentOverride instanceof Document&&(this._document=this.optionsService.rawOptions.documentOverride),this.element=this._document.createElement("div"),this.element.dir="ltr",this.element.classList.add("terminal"),this.element.classList.add("xterm"),xe.appendChild(this.element);let Q=this._document.createDocumentFragment();this._viewportElement=this._document.createElement("div"),this._viewportElement.classList.add("xterm-viewport"),Q.appendChild(this._viewportElement),this._viewportScrollArea=this._document.createElement("div"),this._viewportScrollArea.classList.add("xterm-scroll-area"),this._viewportElement.appendChild(this._viewportScrollArea),this.screenElement=this._document.createElement("div"),this.screenElement.classList.add("xterm-screen"),this.register((0,m.addDisposableDomListener)(this.screenElement,"mousemove",Oe=>this.updateCursorStyle(Oe))),this._helperContainer=this._document.createElement("div"),this._helperContainer.classList.add("xterm-helpers"),this.screenElement.appendChild(this._helperContainer),Q.appendChild(this.screenElement),this.textarea=this._document.createElement("textarea"),this.textarea.classList.add("xterm-helper-textarea"),this.textarea.setAttribute("aria-label",h.promptLabel),be.isChromeOS||this.textarea.setAttribute("aria-multiline","false"),this.textarea.setAttribute("autocorrect","off"),this.textarea.setAttribute("autocapitalize","off"),this.textarea.setAttribute("spellcheck","false"),this.textarea.tabIndex=0,this._coreBrowserService=this.register(this._instantiationService.createInstance(I.CoreBrowserService,this.textarea,xe.ownerDocument.defaultView??window,this._document??typeof window<"u"?window.document:null)),this._instantiationService.setService(N.ICoreBrowserService,this._coreBrowserService),this.register((0,m.addDisposableDomListener)(this.textarea,"focus",Oe=>this._handleTextAreaFocus(Oe))),this.register((0,m.addDisposableDomListener)(this.textarea,"blur",()=>this._handleTextAreaBlur())),this._helperContainer.appendChild(this.textarea),this._charSizeService=this._instantiationService.createInstance(y.CharSizeService,this._document,this._helperContainer),this._instantiationService.setService(N.ICharSizeService,this._charSizeService),this._themeService=this._instantiationService.createInstance(q.ThemeService),this._instantiationService.setService(N.IThemeService,this._themeService),this._characterJoinerService=this._instantiationService.createInstance(k.CharacterJoinerService),this._instantiationService.setService(N.ICharacterJoinerService,this._characterJoinerService),this._renderService=this.register(this._instantiationService.createInstance(R.RenderService,this.rows,this.screenElement)),this._instantiationService.setService(N.IRenderService,this._renderService),this.register(this._renderService.onRenderedViewportChange(Oe=>this._onRender.fire(Oe))),this.onResize(Oe=>this._renderService.resize(Oe.cols,Oe.rows)),this._compositionView=this._document.createElement("div"),this._compositionView.classList.add("composition-view"),this._compositionHelper=this._instantiationService.createInstance(M.CompositionHelper,this.textarea,this._compositionView),this._helperContainer.appendChild(this._compositionView),this._mouseService=this._instantiationService.createInstance(P.MouseService),this._instantiationService.setService(N.IMouseService,this._mouseService),this.linkifier=this.register(this._instantiationService.createInstance(p.Linkifier,this.screenElement)),this.element.appendChild(Q);try{this._onWillOpen.fire(this.element)}catch{}this._renderService.hasRenderer()||this._renderService.setRenderer(this._createRenderer()),this.viewport=this._instantiationService.createInstance(S.Viewport,this._viewportElement,this._viewportScrollArea),this.viewport.onRequestScrollLines(Oe=>this.scrollLines(Oe.amount,Oe.suppressScrollEvent,1)),this.register(this._inputHandler.onRequestSyncScrollBar(()=>this.viewport.syncScrollArea())),this.register(this.viewport),this.register(this.onCursorMove(()=>{this._renderService.handleCursorMove(),this._syncTextArea()})),this.register(this.onResize(()=>this._renderService.handleResize(this.cols,this.rows))),this.register(this.onBlur(()=>this._renderService.handleBlur())),this.register(this.onFocus(()=>this._renderService.handleFocus())),this.register(this._renderService.onDimensionsChange(()=>this.viewport.syncScrollArea())),this._selectionService=this.register(this._instantiationService.createInstance(D.SelectionService,this.element,this.screenElement,this.linkifier)),this._instantiationService.setService(N.ISelectionService,this._selectionService),this.register(this._selectionService.onRequestScrollLines(Oe=>this.scrollLines(Oe.amount,Oe.suppressScrollEvent))),this.register(this._selectionService.onSelectionChange(()=>this._onSelectionChange.fire())),this.register(this._selectionService.onRequestRedraw(Oe=>this._renderService.handleSelectionChanged(Oe.start,Oe.end,Oe.columnSelectMode))),this.register(this._selectionService.onLinuxMouseSelection(Oe=>{this.textarea.value=Oe,this.textarea.focus(),this.textarea.select()})),this.register(this._onScroll.event(Oe=>{this.viewport.syncScrollArea(),this._selectionService.refresh()})),this.register((0,m.addDisposableDomListener)(this._viewportElement,"scroll",()=>this._selectionService.refresh())),this.register(this._instantiationService.createInstance(x.BufferDecorationRenderer,this.screenElement)),this.register((0,m.addDisposableDomListener)(this.element,"mousedown",Oe=>this._selectionService.handleMouseDown(Oe))),this.coreMouseService.areMouseEventsActive?(this._selectionService.disable(),this.element.classList.add("enable-mouse-events")):this._selectionService.enable(),this.options.screenReaderMode&&(this._accessibilityManager.value=this._instantiationService.createInstance(Te.AccessibilityManager,this)),this.register(this.optionsService.onSpecificOptionChange("screenReaderMode",Oe=>this._handleScreenReaderModeOptionChange(Oe))),this.options.overviewRulerWidth&&(this._overviewRulerRenderer=this.register(this._instantiationService.createInstance(v.OverviewRulerRenderer,this._viewportElement,this.screenElement))),this.optionsService.onSpecificOptionChange("overviewRulerWidth",Oe=>{!this._overviewRulerRenderer&&Oe&&this._viewportElement&&this.screenElement&&(this._overviewRulerRenderer=this.register(this._instantiationService.createInstance(v.OverviewRulerRenderer,this._viewportElement,this.screenElement)))}),this._charSizeService.measure(),this.refresh(0,this.rows-1),this._initGlobal(),this.bindMouse()}_createRenderer(){return this._instantiationService.createInstance(w.DomRenderer,this,this._document,this.element,this.screenElement,this._viewportElement,this._helperContainer,this.linkifier)}bindMouse(){let xe=this,Q=this.element;function Oe(kt){let Xn=xe._mouseService.getMouseReportCoords(kt,xe.screenElement);if(!Xn)return!1;let Fo,xo;switch(kt.overrideType||kt.type){case"mousemove":xo=32,kt.buttons===void 0?(Fo=3,kt.button!==void 0&&(Fo=kt.button<3?kt.button:3)):Fo=1&kt.buttons?0:4&kt.buttons?1:2&kt.buttons?2:3;break;case"mouseup":xo=0,Fo=kt.button<3?kt.button:3;break;case"mousedown":xo=1,Fo=kt.button<3?kt.button:3;break;case"wheel":if(xe._customWheelEventHandler&&xe._customWheelEventHandler(kt)===!1||xe.viewport.getLinesScrolled(kt)===0)return!1;xo=kt.deltaY<0?0:1,Fo=4;break;default:return!1}return!(xo===void 0||Fo===void 0||Fo>4)&&xe.coreMouseService.triggerMouseEvent({col:Xn.col,row:Xn.row,x:Xn.x,y:Xn.y,button:Fo,action:xo,ctrl:kt.ctrlKey,alt:kt.altKey,shift:kt.shiftKey})}let Ge={mouseup:null,wheel:null,mousedrag:null,mousemove:null},ct={mouseup:kt=>(Oe(kt),kt.buttons||(this._document.removeEventListener("mouseup",Ge.mouseup),Ge.mousedrag&&this._document.removeEventListener("mousemove",Ge.mousedrag)),this.cancel(kt)),wheel:kt=>(Oe(kt),this.cancel(kt,!0)),mousedrag:kt=>{kt.buttons&&Oe(kt)},mousemove:kt=>{kt.buttons||Oe(kt)}};this.register(this.coreMouseService.onProtocolChange(kt=>{kt?(this.optionsService.rawOptions.logLevel==="debug"&&this._logService.debug("Binding to mouse events:",this.coreMouseService.explainEvents(kt)),this.element.classList.add("enable-mouse-events"),this._selectionService.disable()):(this._logService.debug("Unbinding from mouse events."),this.element.classList.remove("enable-mouse-events"),this._selectionService.enable()),8&kt?Ge.mousemove||(Q.addEventListener("mousemove",ct.mousemove),Ge.mousemove=ct.mousemove):(Q.removeEventListener("mousemove",Ge.mousemove),Ge.mousemove=null),16&kt?Ge.wheel||(Q.addEventListener("wheel",ct.wheel,{passive:!1}),Ge.wheel=ct.wheel):(Q.removeEventListener("wheel",Ge.wheel),Ge.wheel=null),2&kt?Ge.mouseup||(Ge.mouseup=ct.mouseup):(this._document.removeEventListener("mouseup",Ge.mouseup),Ge.mouseup=null),4&kt?Ge.mousedrag||(Ge.mousedrag=ct.mousedrag):(this._document.removeEventListener("mousemove",Ge.mousedrag),Ge.mousedrag=null)})),this.coreMouseService.activeProtocol=this.coreMouseService.activeProtocol,this.register((0,m.addDisposableDomListener)(Q,"mousedown",kt=>{if(kt.preventDefault(),this.focus(),this.coreMouseService.areMouseEventsActive&&!this._selectionService.shouldForceSelection(kt))return Oe(kt),Ge.mouseup&&this._document.addEventListener("mouseup",Ge.mouseup),Ge.mousedrag&&this._document.addEventListener("mousemove",Ge.mousedrag),this.cancel(kt)})),this.register((0,m.addDisposableDomListener)(Q,"wheel",kt=>{if(!Ge.wheel){if(this._customWheelEventHandler&&this._customWheelEventHandler(kt)===!1)return!1;if(!this.buffer.hasScrollback){let Xn=this.viewport.getLinesScrolled(kt);if(Xn===0)return;let Fo=De.C0.ESC+(this.coreService.decPrivateModes.applicationCursorKeys?"O":"[")+(kt.deltaY<0?"A":"B"),xo="";for(let jr=0;jr{if(!this.coreMouseService.areMouseEventsActive)return this.viewport.handleTouchStart(kt),this.cancel(kt)},{passive:!0})),this.register((0,m.addDisposableDomListener)(Q,"touchmove",kt=>{if(!this.coreMouseService.areMouseEventsActive)return this.viewport.handleTouchMove(kt)?void 0:this.cancel(kt)},{passive:!1}))}refresh(xe,Q){this._renderService?.refreshRows(xe,Q)}updateCursorStyle(xe){this._selectionService?.shouldColumnSelect(xe)?this.element.classList.add("column-select"):this.element.classList.remove("column-select")}_showCursor(){this.coreService.isCursorInitialized||(this.coreService.isCursorInitialized=!0,this.refresh(this.buffer.y,this.buffer.y))}scrollLines(xe,Q,Oe=0){Oe===1?(super.scrollLines(xe,Q,Oe),this.refresh(0,this.rows-1)):this.viewport?.scrollLines(xe)}paste(xe){(0,c.paste)(xe,this.textarea,this.coreService,this.optionsService)}attachCustomKeyEventHandler(xe){this._customKeyEventHandler=xe}attachCustomWheelEventHandler(xe){this._customWheelEventHandler=xe}registerLinkProvider(xe){return this._linkProviderService.registerLinkProvider(xe)}registerCharacterJoiner(xe){if(!this._characterJoinerService)throw new Error("Terminal must be opened first");let Q=this._characterJoinerService.register(xe);return this.refresh(0,this.rows-1),Q}deregisterCharacterJoiner(xe){if(!this._characterJoinerService)throw new Error("Terminal must be opened first");this._characterJoinerService.deregister(xe)&&this.refresh(0,this.rows-1)}get markers(){return this.buffer.markers}registerMarker(xe){return this.buffer.addMarker(this.buffer.ybase+this.buffer.y+xe)}registerDecoration(xe){return this._decorationService.registerDecoration(xe)}hasSelection(){return!!this._selectionService&&this._selectionService.hasSelection}select(xe,Q,Oe){this._selectionService.setSelection(xe,Q,Oe)}getSelection(){return this._selectionService?this._selectionService.selectionText:""}getSelectionPosition(){if(this._selectionService&&this._selectionService.hasSelection)return{start:{x:this._selectionService.selectionStart[0],y:this._selectionService.selectionStart[1]},end:{x:this._selectionService.selectionEnd[0],y:this._selectionService.selectionEnd[1]}}}clearSelection(){this._selectionService?.clearSelection()}selectAll(){this._selectionService?.selectAll()}selectLines(xe,Q){this._selectionService?.selectLines(xe,Q)}_keyDown(xe){if(this._keyDownHandled=!1,this._keyDownSeen=!0,this._customKeyEventHandler&&this._customKeyEventHandler(xe)===!1)return!1;let Q=this.browser.isMac&&this.options.macOptionIsMeta&&xe.altKey;if(!Q&&!this._compositionHelper.keydown(xe))return this.options.scrollOnUserInput&&this.buffer.ybase!==this.buffer.ydisp&&this.scrollToBottom(),!1;Q||xe.key!=="Dead"&&xe.key!=="AltGraph"||(this._unprocessedDeadKey=!0);let Oe=(0,me.evaluateKeyboardEvent)(xe,this.coreService.decPrivateModes.applicationCursorKeys,this.browser.isMac,this.options.macOptionIsMeta);if(this.updateCursorStyle(xe),Oe.type===3||Oe.type===2){let Ge=this.rows-1;return this.scrollLines(Oe.type===2?-Ge:Ge),this.cancel(xe,!0)}return Oe.type===1&&this.selectAll(),!!this._isThirdLevelShift(this.browser,xe)||(Oe.cancel&&this.cancel(xe,!0),!Oe.key||!!(xe.key&&!xe.ctrlKey&&!xe.altKey&&!xe.metaKey&&xe.key.length===1&&xe.key.charCodeAt(0)>=65&&xe.key.charCodeAt(0)<=90)||(this._unprocessedDeadKey?(this._unprocessedDeadKey=!1,!0):(Oe.key!==De.C0.ETX&&Oe.key!==De.C0.CR||(this.textarea.value=""),this._onKey.fire({key:Oe.key,domEvent:xe}),this._showCursor(),this.coreService.triggerDataEvent(Oe.key,!0),!this.optionsService.rawOptions.screenReaderMode||xe.altKey||xe.ctrlKey?this.cancel(xe,!0):void(this._keyDownHandled=!0))))}_isThirdLevelShift(xe,Q){let Oe=xe.isMac&&!this.options.macOptionIsMeta&&Q.altKey&&!Q.ctrlKey&&!Q.metaKey||xe.isWindows&&Q.altKey&&Q.ctrlKey&&!Q.metaKey||xe.isWindows&&Q.getModifierState("AltGraph");return Q.type==="keypress"?Oe:Oe&&(!Q.keyCode||Q.keyCode>47)}_keyUp(xe){this._keyDownSeen=!1,this._customKeyEventHandler&&this._customKeyEventHandler(xe)===!1||((function(Q){return Q.keyCode===16||Q.keyCode===17||Q.keyCode===18})(xe)||this.focus(),this.updateCursorStyle(xe),this._keyPressHandled=!1)}_keyPress(xe){let Q;if(this._keyPressHandled=!1,this._keyDownHandled||this._customKeyEventHandler&&this._customKeyEventHandler(xe)===!1)return!1;if(this.cancel(xe),xe.charCode)Q=xe.charCode;else if(xe.which===null||xe.which===void 0)Q=xe.keyCode;else{if(xe.which===0||xe.charCode===0)return!1;Q=xe.which}return!(!Q||(xe.altKey||xe.ctrlKey||xe.metaKey)&&!this._isThirdLevelShift(this.browser,xe)||(Q=String.fromCharCode(Q),this._onKey.fire({key:Q,domEvent:xe}),this._showCursor(),this.coreService.triggerDataEvent(Q,!0),this._keyPressHandled=!0,this._unprocessedDeadKey=!1,0))}_inputEvent(xe){if(xe.data&&xe.inputType==="insertText"&&(!xe.composed||!this._keyDownSeen)&&!this.optionsService.rawOptions.screenReaderMode){if(this._keyPressHandled)return!1;this._unprocessedDeadKey=!1;let Q=xe.data;return this.coreService.triggerDataEvent(Q,!0),this.cancel(xe),!0}return!1}resize(xe,Q){xe!==this.cols||Q!==this.rows?super.resize(xe,Q):this._charSizeService&&!this._charSizeService.hasValidSize&&this._charSizeService.measure()}_afterResize(xe,Q){this._charSizeService?.measure(),this.viewport?.syncScrollArea(!0)}clear(){if(this.buffer.ybase!==0||this.buffer.y!==0){this.buffer.clearAllMarkers(),this.buffer.lines.set(0,this.buffer.lines.get(this.buffer.ybase+this.buffer.y)),this.buffer.lines.length=1,this.buffer.ydisp=0,this.buffer.ybase=0,this.buffer.y=0;for(let xe=1;xe{Object.defineProperty(r,"__esModule",{value:!0}),r.TimeBasedDebouncer=void 0,r.TimeBasedDebouncer=class{constructor(a,c=1e3){this._renderCallback=a,this._debounceThresholdMS=c,this._lastRefreshMs=0,this._additionalRefreshRequested=!1}dispose(){this._refreshTimeoutID&&clearTimeout(this._refreshTimeoutID)}refresh(a,c,m){this._rowCount=m,a=a!==void 0?a:0,c=c!==void 0?c:this._rowCount-1,this._rowStart=this._rowStart!==void 0?Math.min(this._rowStart,a):a,this._rowEnd=this._rowEnd!==void 0?Math.max(this._rowEnd,c):c;let p=Date.now();if(p-this._lastRefreshMs>=this._debounceThresholdMS)this._lastRefreshMs=p,this._innerRefresh();else if(!this._additionalRefreshRequested){let h=p-this._lastRefreshMs,g=this._debounceThresholdMS-h;this._additionalRefreshRequested=!0,this._refreshTimeoutID=window.setTimeout(()=>{this._lastRefreshMs=Date.now(),this._innerRefresh(),this._additionalRefreshRequested=!1,this._refreshTimeoutID=void 0},g)}}_innerRefresh(){if(this._rowStart===void 0||this._rowEnd===void 0||this._rowCount===void 0)return;let a=Math.max(this._rowStart,0),c=Math.min(this._rowEnd,this._rowCount-1);this._rowStart=void 0,this._rowEnd=void 0,this._renderCallback(a,c)}}},1680:function(o,r,a){var c=this&&this.__decorate||function(M,w,y,k){var I,P=arguments.length,R=P<3?w:k===null?k=Object.getOwnPropertyDescriptor(w,y):k;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")R=Reflect.decorate(M,w,y,k);else for(var D=M.length-1;D>=0;D--)(I=M[D])&&(R=(P<3?I(R):P>3?I(w,y,R):I(w,y))||R);return P>3&&R&&Object.defineProperty(w,y,R),R},m=this&&this.__param||function(M,w){return function(y,k){w(y,k,M)}};Object.defineProperty(r,"__esModule",{value:!0}),r.Viewport=void 0;let p=a(3656),h=a(4725),g=a(8460),S=a(844),x=a(2585),v=r.Viewport=class extends S.Disposable{constructor(M,w,y,k,I,P,R,D){super(),this._viewportElement=M,this._scrollArea=w,this._bufferService=y,this._optionsService=k,this._charSizeService=I,this._renderService=P,this._coreBrowserService=R,this.scrollBarWidth=0,this._currentRowHeight=0,this._currentDeviceCellHeight=0,this._lastRecordedBufferLength=0,this._lastRecordedViewportHeight=0,this._lastRecordedBufferHeight=0,this._lastTouchY=0,this._lastScrollTop=0,this._wheelPartialScroll=0,this._refreshAnimationFrame=null,this._ignoreNextScrollEvent=!1,this._smoothScrollState={startTime:0,origin:-1,target:-1},this._onRequestScrollLines=this.register(new g.EventEmitter),this.onRequestScrollLines=this._onRequestScrollLines.event,this.scrollBarWidth=this._viewportElement.offsetWidth-this._scrollArea.offsetWidth||15,this.register((0,p.addDisposableDomListener)(this._viewportElement,"scroll",this._handleScroll.bind(this))),this._activeBuffer=this._bufferService.buffer,this.register(this._bufferService.buffers.onBufferActivate(N=>this._activeBuffer=N.activeBuffer)),this._renderDimensions=this._renderService.dimensions,this.register(this._renderService.onDimensionsChange(N=>this._renderDimensions=N)),this._handleThemeChange(D.colors),this.register(D.onChangeColors(N=>this._handleThemeChange(N))),this.register(this._optionsService.onSpecificOptionChange("scrollback",()=>this.syncScrollArea())),setTimeout(()=>this.syncScrollArea())}_handleThemeChange(M){this._viewportElement.style.backgroundColor=M.background.css}reset(){this._currentRowHeight=0,this._currentDeviceCellHeight=0,this._lastRecordedBufferLength=0,this._lastRecordedViewportHeight=0,this._lastRecordedBufferHeight=0,this._lastTouchY=0,this._lastScrollTop=0,this._coreBrowserService.window.requestAnimationFrame(()=>this.syncScrollArea())}_refresh(M){if(M)return this._innerRefresh(),void(this._refreshAnimationFrame!==null&&this._coreBrowserService.window.cancelAnimationFrame(this._refreshAnimationFrame));this._refreshAnimationFrame===null&&(this._refreshAnimationFrame=this._coreBrowserService.window.requestAnimationFrame(()=>this._innerRefresh()))}_innerRefresh(){if(this._charSizeService.height>0){this._currentRowHeight=this._renderDimensions.device.cell.height/this._coreBrowserService.dpr,this._currentDeviceCellHeight=this._renderDimensions.device.cell.height,this._lastRecordedViewportHeight=this._viewportElement.offsetHeight;let w=Math.round(this._currentRowHeight*this._lastRecordedBufferLength)+(this._lastRecordedViewportHeight-this._renderDimensions.css.canvas.height);this._lastRecordedBufferHeight!==w&&(this._lastRecordedBufferHeight=w,this._scrollArea.style.height=this._lastRecordedBufferHeight+"px")}let M=this._bufferService.buffer.ydisp*this._currentRowHeight;this._viewportElement.scrollTop!==M&&(this._ignoreNextScrollEvent=!0,this._viewportElement.scrollTop=M),this._refreshAnimationFrame=null}syncScrollArea(M=!1){if(this._lastRecordedBufferLength!==this._bufferService.buffer.lines.length)return this._lastRecordedBufferLength=this._bufferService.buffer.lines.length,void this._refresh(M);this._lastRecordedViewportHeight===this._renderService.dimensions.css.canvas.height&&this._lastScrollTop===this._activeBuffer.ydisp*this._currentRowHeight&&this._renderDimensions.device.cell.height===this._currentDeviceCellHeight||this._refresh(M)}_handleScroll(M){if(this._lastScrollTop=this._viewportElement.scrollTop,!this._viewportElement.offsetParent)return;if(this._ignoreNextScrollEvent)return this._ignoreNextScrollEvent=!1,void this._onRequestScrollLines.fire({amount:0,suppressScrollEvent:!0});let w=Math.round(this._lastScrollTop/this._currentRowHeight)-this._bufferService.buffer.ydisp;this._onRequestScrollLines.fire({amount:w,suppressScrollEvent:!0})}_smoothScroll(){if(this._isDisposed||this._smoothScrollState.origin===-1||this._smoothScrollState.target===-1)return;let M=this._smoothScrollPercent();this._viewportElement.scrollTop=this._smoothScrollState.origin+Math.round(M*(this._smoothScrollState.target-this._smoothScrollState.origin)),M<1?this._coreBrowserService.window.requestAnimationFrame(()=>this._smoothScroll()):this._clearSmoothScrollState()}_smoothScrollPercent(){return this._optionsService.rawOptions.smoothScrollDuration&&this._smoothScrollState.startTime?Math.max(Math.min((Date.now()-this._smoothScrollState.startTime)/this._optionsService.rawOptions.smoothScrollDuration,1),0):1}_clearSmoothScrollState(){this._smoothScrollState.startTime=0,this._smoothScrollState.origin=-1,this._smoothScrollState.target=-1}_bubbleScroll(M,w){let y=this._viewportElement.scrollTop+this._lastRecordedViewportHeight;return!(w<0&&this._viewportElement.scrollTop!==0||w>0&&y0&&(y=de),k=""}}return{bufferElements:I,cursorElement:y}}getLinesScrolled(M){if(M.deltaY===0||M.shiftKey)return 0;let w=this._applyScrollModifier(M.deltaY,M);return M.deltaMode===WheelEvent.DOM_DELTA_PIXEL?(w/=this._currentRowHeight+0,this._wheelPartialScroll+=w,w=Math.floor(Math.abs(this._wheelPartialScroll))*(this._wheelPartialScroll>0?1:-1),this._wheelPartialScroll%=1):M.deltaMode===WheelEvent.DOM_DELTA_PAGE&&(w*=this._bufferService.rows),w}_applyScrollModifier(M,w){let y=this._optionsService.rawOptions.fastScrollModifier;return y==="alt"&&w.altKey||y==="ctrl"&&w.ctrlKey||y==="shift"&&w.shiftKey?M*this._optionsService.rawOptions.fastScrollSensitivity*this._optionsService.rawOptions.scrollSensitivity:M*this._optionsService.rawOptions.scrollSensitivity}handleTouchStart(M){this._lastTouchY=M.touches[0].pageY}handleTouchMove(M){let w=this._lastTouchY-M.touches[0].pageY;return this._lastTouchY=M.touches[0].pageY,w!==0&&(this._viewportElement.scrollTop+=w,this._bubbleScroll(M,w))}};r.Viewport=v=c([m(2,x.IBufferService),m(3,x.IOptionsService),m(4,h.ICharSizeService),m(5,h.IRenderService),m(6,h.ICoreBrowserService),m(7,h.IThemeService)],v)},3107:function(o,r,a){var c=this&&this.__decorate||function(x,v,M,w){var y,k=arguments.length,I=k<3?v:w===null?w=Object.getOwnPropertyDescriptor(v,M):w;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")I=Reflect.decorate(x,v,M,w);else for(var P=x.length-1;P>=0;P--)(y=x[P])&&(I=(k<3?y(I):k>3?y(v,M,I):y(v,M))||I);return k>3&&I&&Object.defineProperty(v,M,I),I},m=this&&this.__param||function(x,v){return function(M,w){v(M,w,x)}};Object.defineProperty(r,"__esModule",{value:!0}),r.BufferDecorationRenderer=void 0;let p=a(4725),h=a(844),g=a(2585),S=r.BufferDecorationRenderer=class extends h.Disposable{constructor(x,v,M,w,y){super(),this._screenElement=x,this._bufferService=v,this._coreBrowserService=M,this._decorationService=w,this._renderService=y,this._decorationElements=new Map,this._altBufferIsActive=!1,this._dimensionsChanged=!1,this._container=document.createElement("div"),this._container.classList.add("xterm-decoration-container"),this._screenElement.appendChild(this._container),this.register(this._renderService.onRenderedViewportChange(()=>this._doRefreshDecorations())),this.register(this._renderService.onDimensionsChange(()=>{this._dimensionsChanged=!0,this._queueRefresh()})),this.register(this._coreBrowserService.onDprChange(()=>this._queueRefresh())),this.register(this._bufferService.buffers.onBufferActivate(()=>{this._altBufferIsActive=this._bufferService.buffer===this._bufferService.buffers.alt})),this.register(this._decorationService.onDecorationRegistered(()=>this._queueRefresh())),this.register(this._decorationService.onDecorationRemoved(k=>this._removeDecoration(k))),this.register((0,h.toDisposable)(()=>{this._container.remove(),this._decorationElements.clear()}))}_queueRefresh(){this._animationFrame===void 0&&(this._animationFrame=this._renderService.addRefreshCallback(()=>{this._doRefreshDecorations(),this._animationFrame=void 0}))}_doRefreshDecorations(){for(let x of this._decorationService.decorations)this._renderDecoration(x);this._dimensionsChanged=!1}_renderDecoration(x){this._refreshStyle(x),this._dimensionsChanged&&this._refreshXPosition(x)}_createElement(x){let v=this._coreBrowserService.mainDocument.createElement("div");v.classList.add("xterm-decoration"),v.classList.toggle("xterm-decoration-top-layer",x?.options?.layer==="top"),v.style.width=`${Math.round((x.options.width||1)*this._renderService.dimensions.css.cell.width)}px`,v.style.height=(x.options.height||1)*this._renderService.dimensions.css.cell.height+"px",v.style.top=(x.marker.line-this._bufferService.buffers.active.ydisp)*this._renderService.dimensions.css.cell.height+"px",v.style.lineHeight=`${this._renderService.dimensions.css.cell.height}px`;let M=x.options.x??0;return M&&M>this._bufferService.cols&&(v.style.display="none"),this._refreshXPosition(x,v),v}_refreshStyle(x){let v=x.marker.line-this._bufferService.buffers.active.ydisp;if(v<0||v>=this._bufferService.rows)x.element&&(x.element.style.display="none",x.onRenderEmitter.fire(x.element));else{let M=this._decorationElements.get(x);M||(M=this._createElement(x),x.element=M,this._decorationElements.set(x,M),this._container.appendChild(M),x.onDispose(()=>{this._decorationElements.delete(x),M.remove()})),M.style.top=v*this._renderService.dimensions.css.cell.height+"px",M.style.display=this._altBufferIsActive?"none":"block",x.onRenderEmitter.fire(M)}}_refreshXPosition(x,v=x.element){if(!v)return;let M=x.options.x??0;(x.options.anchor||"left")==="right"?v.style.right=M?M*this._renderService.dimensions.css.cell.width+"px":"":v.style.left=M?M*this._renderService.dimensions.css.cell.width+"px":""}_removeDecoration(x){this._decorationElements.get(x)?.remove(),this._decorationElements.delete(x),x.dispose()}};r.BufferDecorationRenderer=S=c([m(1,g.IBufferService),m(2,p.ICoreBrowserService),m(3,g.IDecorationService),m(4,p.IRenderService)],S)},5871:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.ColorZoneStore=void 0,r.ColorZoneStore=class{constructor(){this._zones=[],this._zonePool=[],this._zonePoolIndex=0,this._linePadding={full:0,left:0,center:0,right:0}}get zones(){return this._zonePool.length=Math.min(this._zonePool.length,this._zones.length),this._zones}clear(){this._zones.length=0,this._zonePoolIndex=0}addDecoration(a){if(a.options.overviewRulerOptions){for(let c of this._zones)if(c.color===a.options.overviewRulerOptions.color&&c.position===a.options.overviewRulerOptions.position){if(this._lineIntersectsZone(c,a.marker.line))return;if(this._lineAdjacentToZone(c,a.marker.line,a.options.overviewRulerOptions.position))return void this._addLineToZone(c,a.marker.line)}if(this._zonePoolIndex=a.startBufferLine&&c<=a.endBufferLine}_lineAdjacentToZone(a,c,m){return c>=a.startBufferLine-this._linePadding[m||"full"]&&c<=a.endBufferLine+this._linePadding[m||"full"]}_addLineToZone(a,c){a.startBufferLine=Math.min(a.startBufferLine,c),a.endBufferLine=Math.max(a.endBufferLine,c)}}},5744:function(o,r,a){var c=this&&this.__decorate||function(y,k,I,P){var R,D=arguments.length,N=D<3?k:P===null?P=Object.getOwnPropertyDescriptor(k,I):P;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")N=Reflect.decorate(y,k,I,P);else for(var q=y.length-1;q>=0;q--)(R=y[q])&&(N=(D<3?R(N):D>3?R(k,I,N):R(k,I))||N);return D>3&&N&&Object.defineProperty(k,I,N),N},m=this&&this.__param||function(y,k){return function(I,P){k(I,P,y)}};Object.defineProperty(r,"__esModule",{value:!0}),r.OverviewRulerRenderer=void 0;let p=a(5871),h=a(4725),g=a(844),S=a(2585),x={full:0,left:0,center:0,right:0},v={full:0,left:0,center:0,right:0},M={full:0,left:0,center:0,right:0},w=r.OverviewRulerRenderer=class extends g.Disposable{get _width(){return this._optionsService.options.overviewRulerWidth||0}constructor(y,k,I,P,R,D,N){super(),this._viewportElement=y,this._screenElement=k,this._bufferService=I,this._decorationService=P,this._renderService=R,this._optionsService=D,this._coreBrowserService=N,this._colorZoneStore=new p.ColorZoneStore,this._shouldUpdateDimensions=!0,this._shouldUpdateAnchor=!0,this._lastKnownBufferLength=0,this._canvas=this._coreBrowserService.mainDocument.createElement("canvas"),this._canvas.classList.add("xterm-decoration-overview-ruler"),this._refreshCanvasDimensions(),this._viewportElement.parentElement?.insertBefore(this._canvas,this._viewportElement);let q=this._canvas.getContext("2d");if(!q)throw new Error("Ctx cannot be null");this._ctx=q,this._registerDecorationListeners(),this._registerBufferChangeListeners(),this._registerDimensionChangeListeners(),this.register((0,g.toDisposable)(()=>{this._canvas?.remove()}))}_registerDecorationListeners(){this.register(this._decorationService.onDecorationRegistered(()=>this._queueRefresh(void 0,!0))),this.register(this._decorationService.onDecorationRemoved(()=>this._queueRefresh(void 0,!0)))}_registerBufferChangeListeners(){this.register(this._renderService.onRenderedViewportChange(()=>this._queueRefresh())),this.register(this._bufferService.buffers.onBufferActivate(()=>{this._canvas.style.display=this._bufferService.buffer===this._bufferService.buffers.alt?"none":"block"})),this.register(this._bufferService.onScroll(()=>{this._lastKnownBufferLength!==this._bufferService.buffers.normal.lines.length&&(this._refreshDrawHeightConstants(),this._refreshColorZonePadding())}))}_registerDimensionChangeListeners(){this.register(this._renderService.onRender(()=>{this._containerHeight&&this._containerHeight===this._screenElement.clientHeight||(this._queueRefresh(!0),this._containerHeight=this._screenElement.clientHeight)})),this.register(this._optionsService.onSpecificOptionChange("overviewRulerWidth",()=>this._queueRefresh(!0))),this.register(this._coreBrowserService.onDprChange(()=>this._queueRefresh(!0))),this._queueRefresh(!0)}_refreshDrawConstants(){let y=Math.floor(this._canvas.width/3),k=Math.ceil(this._canvas.width/3);v.full=this._canvas.width,v.left=y,v.center=k,v.right=y,this._refreshDrawHeightConstants(),M.full=0,M.left=0,M.center=v.left,M.right=v.left+v.center}_refreshDrawHeightConstants(){x.full=Math.round(2*this._coreBrowserService.dpr);let y=this._canvas.height/this._bufferService.buffer.lines.length,k=Math.round(Math.max(Math.min(y,12),6)*this._coreBrowserService.dpr);x.left=k,x.center=k,x.right=k}_refreshColorZonePadding(){this._colorZoneStore.setPadding({full:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*x.full),left:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*x.left),center:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*x.center),right:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*x.right)}),this._lastKnownBufferLength=this._bufferService.buffers.normal.lines.length}_refreshCanvasDimensions(){this._canvas.style.width=`${this._width}px`,this._canvas.width=Math.round(this._width*this._coreBrowserService.dpr),this._canvas.style.height=`${this._screenElement.clientHeight}px`,this._canvas.height=Math.round(this._screenElement.clientHeight*this._coreBrowserService.dpr),this._refreshDrawConstants(),this._refreshColorZonePadding()}_refreshDecorations(){this._shouldUpdateDimensions&&this._refreshCanvasDimensions(),this._ctx.clearRect(0,0,this._canvas.width,this._canvas.height),this._colorZoneStore.clear();for(let k of this._decorationService.decorations)this._colorZoneStore.addDecoration(k);this._ctx.lineWidth=1;let y=this._colorZoneStore.zones;for(let k of y)k.position!=="full"&&this._renderColorZone(k);for(let k of y)k.position==="full"&&this._renderColorZone(k);this._shouldUpdateDimensions=!1,this._shouldUpdateAnchor=!1}_renderColorZone(y){this._ctx.fillStyle=y.color,this._ctx.fillRect(M[y.position||"full"],Math.round((this._canvas.height-1)*(y.startBufferLine/this._bufferService.buffers.active.lines.length)-x[y.position||"full"]/2),v[y.position||"full"],Math.round((this._canvas.height-1)*((y.endBufferLine-y.startBufferLine)/this._bufferService.buffers.active.lines.length)+x[y.position||"full"]))}_queueRefresh(y,k){this._shouldUpdateDimensions=y||this._shouldUpdateDimensions,this._shouldUpdateAnchor=k||this._shouldUpdateAnchor,this._animationFrame===void 0&&(this._animationFrame=this._coreBrowserService.window.requestAnimationFrame(()=>{this._refreshDecorations(),this._animationFrame=void 0}))}};r.OverviewRulerRenderer=w=c([m(2,S.IBufferService),m(3,S.IDecorationService),m(4,h.IRenderService),m(5,S.IOptionsService),m(6,h.ICoreBrowserService)],w)},2950:function(o,r,a){var c=this&&this.__decorate||function(x,v,M,w){var y,k=arguments.length,I=k<3?v:w===null?w=Object.getOwnPropertyDescriptor(v,M):w;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")I=Reflect.decorate(x,v,M,w);else for(var P=x.length-1;P>=0;P--)(y=x[P])&&(I=(k<3?y(I):k>3?y(v,M,I):y(v,M))||I);return k>3&&I&&Object.defineProperty(v,M,I),I},m=this&&this.__param||function(x,v){return function(M,w){v(M,w,x)}};Object.defineProperty(r,"__esModule",{value:!0}),r.CompositionHelper=void 0;let p=a(4725),h=a(2585),g=a(2584),S=r.CompositionHelper=class{get isComposing(){return this._isComposing}constructor(x,v,M,w,y,k){this._textarea=x,this._compositionView=v,this._bufferService=M,this._optionsService=w,this._coreService=y,this._renderService=k,this._isComposing=!1,this._isSendingComposition=!1,this._compositionPosition={start:0,end:0},this._dataAlreadySent=""}compositionstart(){this._isComposing=!0,this._compositionPosition.start=this._textarea.value.length,this._compositionView.textContent="",this._dataAlreadySent="",this._compositionView.classList.add("active")}compositionupdate(x){this._compositionView.textContent=x.data,this.updateCompositionElements(),setTimeout(()=>{this._compositionPosition.end=this._textarea.value.length},0)}compositionend(){this._finalizeComposition(!0)}keydown(x){if(this._isComposing||this._isSendingComposition){if(x.keyCode===229||x.keyCode===16||x.keyCode===17||x.keyCode===18)return!1;this._finalizeComposition(!1)}return x.keyCode!==229||(this._handleAnyTextareaChanges(),!1)}_finalizeComposition(x){if(this._compositionView.classList.remove("active"),this._isComposing=!1,x){let v={start:this._compositionPosition.start,end:this._compositionPosition.end};this._isSendingComposition=!0,setTimeout(()=>{if(this._isSendingComposition){let M;this._isSendingComposition=!1,v.start+=this._dataAlreadySent.length,M=this._isComposing?this._textarea.value.substring(v.start,v.end):this._textarea.value.substring(v.start),M.length>0&&this._coreService.triggerDataEvent(M,!0)}},0)}else{this._isSendingComposition=!1;let v=this._textarea.value.substring(this._compositionPosition.start,this._compositionPosition.end);this._coreService.triggerDataEvent(v,!0)}}_handleAnyTextareaChanges(){let x=this._textarea.value;setTimeout(()=>{if(!this._isComposing){let v=this._textarea.value,M=v.replace(x,"");this._dataAlreadySent=M,v.length>x.length?this._coreService.triggerDataEvent(M,!0):v.lengththis.updateCompositionElements(!0),0)}}};r.CompositionHelper=S=c([m(2,h.IBufferService),m(3,h.IOptionsService),m(4,h.ICoreService),m(5,p.IRenderService)],S)},9806:(o,r)=>{function a(c,m,p){let h=p.getBoundingClientRect(),g=c.getComputedStyle(p),S=parseInt(g.getPropertyValue("padding-left")),x=parseInt(g.getPropertyValue("padding-top"));return[m.clientX-h.left-S,m.clientY-h.top-x]}Object.defineProperty(r,"__esModule",{value:!0}),r.getCoords=r.getCoordsRelativeToElement=void 0,r.getCoordsRelativeToElement=a,r.getCoords=function(c,m,p,h,g,S,x,v,M){if(!S)return;let w=a(c,m,p);return w?(w[0]=Math.ceil((w[0]+(M?x/2:0))/x),w[1]=Math.ceil(w[1]/v),w[0]=Math.min(Math.max(w[0],1),h+(M?1:0)),w[1]=Math.min(Math.max(w[1],1),g),w):void 0}},9504:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.moveToCellSequence=void 0;let c=a(2584);function m(v,M,w,y){let k=v-p(v,w),I=M-p(M,w),P=Math.abs(k-I)-(function(R,D,N){let q=0,de=R-p(R,N),fe=D-p(D,N);for(let G=0;G=0&&vM?"A":"B"}function g(v,M,w,y,k,I){let P=v,R=M,D="";for(;P!==w||R!==y;)P+=k?1:-1,k&&P>I.cols-1?(D+=I.buffer.translateBufferLineToString(R,!1,v,P),P=0,v=0,R++):!k&&P<0&&(D+=I.buffer.translateBufferLineToString(R,!1,0,v+1),P=I.cols-1,v=P,R--);return D+I.buffer.translateBufferLineToString(R,!1,v,P)}function S(v,M){let w=M?"O":"[";return c.C0.ESC+w+v}function x(v,M){v=Math.floor(v);let w="";for(let y=0;y0?de-p(de,fe):N;let be=de,le=(function(De,me,V,Y,ie,oe){let Te;return Te=m(V,Y,ie,oe).length>0?Y-p(Y,ie):me,De=V&&Tev?"D":"C",x(Math.abs(k-v),S(P,y));P=I>M?"D":"C";let R=Math.abs(I-M);return x((function(D,N){return N.cols-D})(I>M?v:k,w)+(R-1)*w.cols+1+((I>M?k:v)-1),S(P,y))}},1296:function(o,r,a){var c=this&&this.__decorate||function(G,ue,be,le){var De,me=arguments.length,V=me<3?ue:le===null?le=Object.getOwnPropertyDescriptor(ue,be):le;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")V=Reflect.decorate(G,ue,be,le);else for(var Y=G.length-1;Y>=0;Y--)(De=G[Y])&&(V=(me<3?De(V):me>3?De(ue,be,V):De(ue,be))||V);return me>3&&V&&Object.defineProperty(ue,be,V),V},m=this&&this.__param||function(G,ue){return function(be,le){ue(be,le,G)}};Object.defineProperty(r,"__esModule",{value:!0}),r.DomRenderer=void 0;let p=a(3787),h=a(2550),g=a(2223),S=a(6171),x=a(6052),v=a(4725),M=a(8055),w=a(8460),y=a(844),k=a(2585),I="xterm-dom-renderer-owner-",P="xterm-rows",R="xterm-fg-",D="xterm-bg-",N="xterm-focus",q="xterm-selection",de=1,fe=r.DomRenderer=class extends y.Disposable{constructor(G,ue,be,le,De,me,V,Y,ie,oe,Te,Le,Ye){super(),this._terminal=G,this._document=ue,this._element=be,this._screenElement=le,this._viewportElement=De,this._helperContainer=me,this._linkifier2=V,this._charSizeService=ie,this._optionsService=oe,this._bufferService=Te,this._coreBrowserService=Le,this._themeService=Ye,this._terminalClass=de++,this._rowElements=[],this._selectionRenderModel=(0,x.createSelectionRenderModel)(),this.onRequestRedraw=this.register(new w.EventEmitter).event,this._rowContainer=this._document.createElement("div"),this._rowContainer.classList.add(P),this._rowContainer.style.lineHeight="normal",this._rowContainer.setAttribute("aria-hidden","true"),this._refreshRowElements(this._bufferService.cols,this._bufferService.rows),this._selectionContainer=this._document.createElement("div"),this._selectionContainer.classList.add(q),this._selectionContainer.setAttribute("aria-hidden","true"),this.dimensions=(0,S.createRenderDimensions)(),this._updateDimensions(),this.register(this._optionsService.onOptionChange(()=>this._handleOptionsChanged())),this.register(this._themeService.onChangeColors(Xe=>this._injectCss(Xe))),this._injectCss(this._themeService.colors),this._rowFactory=Y.createInstance(p.DomRendererRowFactory,document),this._element.classList.add(I+this._terminalClass),this._screenElement.appendChild(this._rowContainer),this._screenElement.appendChild(this._selectionContainer),this.register(this._linkifier2.onShowLinkUnderline(Xe=>this._handleLinkHover(Xe))),this.register(this._linkifier2.onHideLinkUnderline(Xe=>this._handleLinkLeave(Xe))),this.register((0,y.toDisposable)(()=>{this._element.classList.remove(I+this._terminalClass),this._rowContainer.remove(),this._selectionContainer.remove(),this._widthCache.dispose(),this._themeStyleElement.remove(),this._dimensionsStyleElement.remove()})),this._widthCache=new h.WidthCache(this._document,this._helperContainer),this._widthCache.setFont(this._optionsService.rawOptions.fontFamily,this._optionsService.rawOptions.fontSize,this._optionsService.rawOptions.fontWeight,this._optionsService.rawOptions.fontWeightBold),this._setDefaultSpacing()}_updateDimensions(){let G=this._coreBrowserService.dpr;this.dimensions.device.char.width=this._charSizeService.width*G,this.dimensions.device.char.height=Math.ceil(this._charSizeService.height*G),this.dimensions.device.cell.width=this.dimensions.device.char.width+Math.round(this._optionsService.rawOptions.letterSpacing),this.dimensions.device.cell.height=Math.floor(this.dimensions.device.char.height*this._optionsService.rawOptions.lineHeight),this.dimensions.device.char.left=0,this.dimensions.device.char.top=0,this.dimensions.device.canvas.width=this.dimensions.device.cell.width*this._bufferService.cols,this.dimensions.device.canvas.height=this.dimensions.device.cell.height*this._bufferService.rows,this.dimensions.css.canvas.width=Math.round(this.dimensions.device.canvas.width/G),this.dimensions.css.canvas.height=Math.round(this.dimensions.device.canvas.height/G),this.dimensions.css.cell.width=this.dimensions.css.canvas.width/this._bufferService.cols,this.dimensions.css.cell.height=this.dimensions.css.canvas.height/this._bufferService.rows;for(let be of this._rowElements)be.style.width=`${this.dimensions.css.canvas.width}px`,be.style.height=`${this.dimensions.css.cell.height}px`,be.style.lineHeight=`${this.dimensions.css.cell.height}px`,be.style.overflow="hidden";this._dimensionsStyleElement||(this._dimensionsStyleElement=this._document.createElement("style"),this._screenElement.appendChild(this._dimensionsStyleElement));let ue=`${this._terminalSelector} .${P} span { display: inline-block; height: 100%; vertical-align: top;}`;this._dimensionsStyleElement.textContent=ue,this._selectionContainer.style.height=this._viewportElement.style.height,this._screenElement.style.width=`${this.dimensions.css.canvas.width}px`,this._screenElement.style.height=`${this.dimensions.css.canvas.height}px`}_injectCss(G){this._themeStyleElement||(this._themeStyleElement=this._document.createElement("style"),this._screenElement.appendChild(this._themeStyleElement));let ue=`${this._terminalSelector} .${P} { color: ${G.foreground.css}; font-family: ${this._optionsService.rawOptions.fontFamily}; font-size: ${this._optionsService.rawOptions.fontSize}px; font-kerning: none; white-space: pre}`;ue+=`${this._terminalSelector} .${P} .xterm-dim { color: ${M.color.multiplyOpacity(G.foreground,.5).css};}`,ue+=`${this._terminalSelector} span:not(.xterm-bold) { font-weight: ${this._optionsService.rawOptions.fontWeight};}${this._terminalSelector} span.xterm-bold { font-weight: ${this._optionsService.rawOptions.fontWeightBold};}${this._terminalSelector} span.xterm-italic { font-style: italic;}`;let be=`blink_underline_${this._terminalClass}`,le=`blink_bar_${this._terminalClass}`,De=`blink_block_${this._terminalClass}`;ue+=`@keyframes ${be} { 50% { border-bottom-style: hidden; }}`,ue+=`@keyframes ${le} { 50% { box-shadow: none; }}`,ue+=`@keyframes ${De} { 0% { background-color: ${G.cursor.css}; color: ${G.cursorAccent.css}; } 50% { background-color: inherit; color: ${G.cursor.css}; }}`,ue+=`${this._terminalSelector} .${P}.${N} .xterm-cursor.xterm-cursor-blink.xterm-cursor-underline { animation: ${be} 1s step-end infinite;}${this._terminalSelector} .${P}.${N} .xterm-cursor.xterm-cursor-blink.xterm-cursor-bar { animation: ${le} 1s step-end infinite;}${this._terminalSelector} .${P}.${N} .xterm-cursor.xterm-cursor-blink.xterm-cursor-block { animation: ${De} 1s step-end infinite;}${this._terminalSelector} .${P} .xterm-cursor.xterm-cursor-block { background-color: ${G.cursor.css}; color: ${G.cursorAccent.css};}${this._terminalSelector} .${P} .xterm-cursor.xterm-cursor-block:not(.xterm-cursor-blink) { background-color: ${G.cursor.css} !important; color: ${G.cursorAccent.css} !important;}${this._terminalSelector} .${P} .xterm-cursor.xterm-cursor-outline { outline: 1px solid ${G.cursor.css}; outline-offset: -1px;}${this._terminalSelector} .${P} .xterm-cursor.xterm-cursor-bar { box-shadow: ${this._optionsService.rawOptions.cursorWidth}px 0 0 ${G.cursor.css} inset;}${this._terminalSelector} .${P} .xterm-cursor.xterm-cursor-underline { border-bottom: 1px ${G.cursor.css}; border-bottom-style: solid; height: calc(100% - 1px);}`,ue+=`${this._terminalSelector} .${q} { position: absolute; top: 0; left: 0; z-index: 1; pointer-events: none;}${this._terminalSelector}.focus .${q} div { position: absolute; background-color: ${G.selectionBackgroundOpaque.css};}${this._terminalSelector} .${q} div { position: absolute; background-color: ${G.selectionInactiveBackgroundOpaque.css};}`;for(let[me,V]of G.ansi.entries())ue+=`${this._terminalSelector} .${R}${me} { color: ${V.css}; }${this._terminalSelector} .${R}${me}.xterm-dim { color: ${M.color.multiplyOpacity(V,.5).css}; }${this._terminalSelector} .${D}${me} { background-color: ${V.css}; }`;ue+=`${this._terminalSelector} .${R}${g.INVERTED_DEFAULT_COLOR} { color: ${M.color.opaque(G.background).css}; }${this._terminalSelector} .${R}${g.INVERTED_DEFAULT_COLOR}.xterm-dim { color: ${M.color.multiplyOpacity(M.color.opaque(G.background),.5).css}; }${this._terminalSelector} .${D}${g.INVERTED_DEFAULT_COLOR} { background-color: ${G.foreground.css}; }`,this._themeStyleElement.textContent=ue}_setDefaultSpacing(){let G=this.dimensions.css.cell.width-this._widthCache.get("W",!1,!1);this._rowContainer.style.letterSpacing=`${G}px`,this._rowFactory.defaultSpacing=G}handleDevicePixelRatioChange(){this._updateDimensions(),this._widthCache.clear(),this._setDefaultSpacing()}_refreshRowElements(G,ue){for(let be=this._rowElements.length;be<=ue;be++){let le=this._document.createElement("div");this._rowContainer.appendChild(le),this._rowElements.push(le)}for(;this._rowElements.length>ue;)this._rowContainer.removeChild(this._rowElements.pop())}handleResize(G,ue){this._refreshRowElements(G,ue),this._updateDimensions(),this.handleSelectionChanged(this._selectionRenderModel.selectionStart,this._selectionRenderModel.selectionEnd,this._selectionRenderModel.columnSelectMode)}handleCharSizeChanged(){this._updateDimensions(),this._widthCache.clear(),this._setDefaultSpacing()}handleBlur(){this._rowContainer.classList.remove(N),this.renderRows(0,this._bufferService.rows-1)}handleFocus(){this._rowContainer.classList.add(N),this.renderRows(this._bufferService.buffer.y,this._bufferService.buffer.y)}handleSelectionChanged(G,ue,be){if(this._selectionContainer.replaceChildren(),this._rowFactory.handleSelectionChanged(G,ue,be),this.renderRows(0,this._bufferService.rows-1),!G||!ue)return;this._selectionRenderModel.update(this._terminal,G,ue,be);let le=this._selectionRenderModel.viewportStartRow,De=this._selectionRenderModel.viewportEndRow,me=this._selectionRenderModel.viewportCappedStartRow,V=this._selectionRenderModel.viewportCappedEndRow;if(me>=this._bufferService.rows||V<0)return;let Y=this._document.createDocumentFragment();if(be){let ie=G[0]>ue[0];Y.appendChild(this._createSelectionElement(me,ie?ue[0]:G[0],ie?G[0]:ue[0],V-me+1))}else{let ie=le===me?G[0]:0,oe=me===De?ue[0]:this._bufferService.cols;Y.appendChild(this._createSelectionElement(me,ie,oe));let Te=V-me-1;if(Y.appendChild(this._createSelectionElement(me+1,0,this._bufferService.cols,Te)),me!==V){let Le=De===V?ue[0]:this._bufferService.cols;Y.appendChild(this._createSelectionElement(V,0,Le))}}this._selectionContainer.appendChild(Y)}_createSelectionElement(G,ue,be,le=1){let De=this._document.createElement("div"),me=ue*this.dimensions.css.cell.width,V=this.dimensions.css.cell.width*(be-ue);return me+V>this.dimensions.css.canvas.width&&(V=this.dimensions.css.canvas.width-me),De.style.height=le*this.dimensions.css.cell.height+"px",De.style.top=G*this.dimensions.css.cell.height+"px",De.style.left=`${me}px`,De.style.width=`${V}px`,De}handleCursorMove(){}_handleOptionsChanged(){this._updateDimensions(),this._injectCss(this._themeService.colors),this._widthCache.setFont(this._optionsService.rawOptions.fontFamily,this._optionsService.rawOptions.fontSize,this._optionsService.rawOptions.fontWeight,this._optionsService.rawOptions.fontWeightBold),this._setDefaultSpacing()}clear(){for(let G of this._rowElements)G.replaceChildren()}renderRows(G,ue){let be=this._bufferService.buffer,le=be.ybase+be.y,De=Math.min(be.x,this._bufferService.cols-1),me=this._optionsService.rawOptions.cursorBlink,V=this._optionsService.rawOptions.cursorStyle,Y=this._optionsService.rawOptions.cursorInactiveStyle;for(let ie=G;ie<=ue;ie++){let oe=ie+be.ydisp,Te=this._rowElements[ie],Le=be.lines.get(oe);if(!Te||!Le)break;Te.replaceChildren(...this._rowFactory.createRow(Le,oe,oe===le,V,Y,De,me,this.dimensions.css.cell.width,this._widthCache,-1,-1))}}get _terminalSelector(){return`.${I}${this._terminalClass}`}_handleLinkHover(G){this._setCellUnderline(G.x1,G.x2,G.y1,G.y2,G.cols,!0)}_handleLinkLeave(G){this._setCellUnderline(G.x1,G.x2,G.y1,G.y2,G.cols,!1)}_setCellUnderline(G,ue,be,le,De,me){be<0&&(G=0),le<0&&(ue=0);let V=this._bufferService.rows-1;be=Math.max(Math.min(be,V),0),le=Math.max(Math.min(le,V),0),De=Math.min(De,this._bufferService.cols);let Y=this._bufferService.buffer,ie=Y.ybase+Y.y,oe=Math.min(Y.x,De-1),Te=this._optionsService.rawOptions.cursorBlink,Le=this._optionsService.rawOptions.cursorStyle,Ye=this._optionsService.rawOptions.cursorInactiveStyle;for(let Xe=be;Xe<=le;++Xe){let xe=Xe+Y.ydisp,Q=this._rowElements[Xe],Oe=Y.lines.get(xe);if(!Q||!Oe)break;Q.replaceChildren(...this._rowFactory.createRow(Oe,xe,xe===ie,Le,Ye,oe,Te,this.dimensions.css.cell.width,this._widthCache,me?Xe===be?G:0:-1,me?(Xe===le?ue:De)-1:-1))}}};r.DomRenderer=fe=c([m(7,k.IInstantiationService),m(8,v.ICharSizeService),m(9,k.IOptionsService),m(10,k.IBufferService),m(11,v.ICoreBrowserService),m(12,v.IThemeService)],fe)},3787:function(o,r,a){var c=this&&this.__decorate||function(P,R,D,N){var q,de=arguments.length,fe=de<3?R:N===null?N=Object.getOwnPropertyDescriptor(R,D):N;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")fe=Reflect.decorate(P,R,D,N);else for(var G=P.length-1;G>=0;G--)(q=P[G])&&(fe=(de<3?q(fe):de>3?q(R,D,fe):q(R,D))||fe);return de>3&&fe&&Object.defineProperty(R,D,fe),fe},m=this&&this.__param||function(P,R){return function(D,N){R(D,N,P)}};Object.defineProperty(r,"__esModule",{value:!0}),r.DomRendererRowFactory=void 0;let p=a(2223),h=a(643),g=a(511),S=a(2585),x=a(8055),v=a(4725),M=a(4269),w=a(6171),y=a(3734),k=r.DomRendererRowFactory=class{constructor(P,R,D,N,q,de,fe){this._document=P,this._characterJoinerService=R,this._optionsService=D,this._coreBrowserService=N,this._coreService=q,this._decorationService=de,this._themeService=fe,this._workCell=new g.CellData,this._columnSelectMode=!1,this.defaultSpacing=0}handleSelectionChanged(P,R,D){this._selectionStart=P,this._selectionEnd=R,this._columnSelectMode=D}createRow(P,R,D,N,q,de,fe,G,ue,be,le){let De=[],me=this._characterJoinerService.getJoinedCharacters(R),V=this._themeService.colors,Y,ie=P.getNoBgTrimmedLength();D&&ie0&&Xn===me[0][0]){xo=!0;let Go=me.shift();kn=new M.JoinedCellData(this._workCell,P.translateToString(!0,Go[0],Go[1]),Go[1]-Go[0]),jr=Go[1]-1,Fo=kn.getWidth()}let Xd=this._isCellInSelection(Xn,R),oh=D&&Xn===de,NT=kt&&Xn>=be&&Xn<=le,RT=!1;this._decorationService.forEachDecorationAtCell(Xn,R,void 0,Go=>{RT=!0});let ZC=kn.getChars()||h.WHITESPACE_CELL_CHAR;if(ZC===" "&&(kn.isUnderline()||kn.isOverline())&&(ZC="\xA0"),Ge=Fo*G-ue.get(ZC,kn.isBold(),kn.isItalic()),Y){if(oe&&(Xd&&Oe||!Xd&&!Oe&&kn.bg===Le)&&(Xd&&Oe&&V.selectionForeground||kn.fg===Ye)&&kn.extended.ext===Xe&&NT===xe&&Ge===Q&&!oh&&!xo&&!RT){kn.isInvisible()?Te+=h.WHITESPACE_CELL_CHAR:Te+=ZC,oe++;continue}oe&&(Y.textContent=Te),Y=this._document.createElement("span"),oe=0,Te=""}else Y=this._document.createElement("span");if(Le=kn.bg,Ye=kn.fg,Xe=kn.extended.ext,xe=NT,Q=Ge,Oe=Xd,xo&&de>=Xn&&de<=jr&&(de=Xn),!this._coreService.isCursorHidden&&oh&&this._coreService.isCursorInitialized){if(ct.push("xterm-cursor"),this._coreBrowserService.isFocused)fe&&ct.push("xterm-cursor-blink"),ct.push(N==="bar"?"xterm-cursor-bar":N==="underline"?"xterm-cursor-underline":"xterm-cursor-block");else if(q)switch(q){case"outline":ct.push("xterm-cursor-outline");break;case"block":ct.push("xterm-cursor-block");break;case"bar":ct.push("xterm-cursor-bar");break;case"underline":ct.push("xterm-cursor-underline")}}if(kn.isBold()&&ct.push("xterm-bold"),kn.isItalic()&&ct.push("xterm-italic"),kn.isDim()&&ct.push("xterm-dim"),Te=kn.isInvisible()?h.WHITESPACE_CELL_CHAR:kn.getChars()||h.WHITESPACE_CELL_CHAR,kn.isUnderline()&&(ct.push(`xterm-underline-${kn.extended.underlineStyle}`),Te===" "&&(Te="\xA0"),!kn.isUnderlineColorDefault()))if(kn.isUnderlineColorRGB())Y.style.textDecorationColor=`rgb(${y.AttributeData.toColorRGB(kn.getUnderlineColor()).join(",")})`;else{let Go=kn.getUnderlineColor();this._optionsService.rawOptions.drawBoldTextInBrightColors&&kn.isBold()&&Go<8&&(Go+=8),Y.style.textDecorationColor=V.ansi[Go].css}kn.isOverline()&&(ct.push("xterm-overline"),Te===" "&&(Te="\xA0")),kn.isStrikethrough()&&ct.push("xterm-strikethrough"),NT&&(Y.style.textDecoration="underline");let Qs=kn.getFgColor(),f_=kn.getFgColorMode(),cc=kn.getBgColor(),g_=kn.getBgColorMode(),FT=!!kn.isInverse();if(FT){let Go=Qs;Qs=cc,cc=Go;let tG=f_;f_=g_,g_=tG}let Yd,JC,Kd,__=!1;switch(this._decorationService.forEachDecorationAtCell(Xn,R,void 0,Go=>{Go.options.layer!=="top"&&__||(Go.backgroundColorRGB&&(g_=50331648,cc=Go.backgroundColorRGB.rgba>>8&16777215,Yd=Go.backgroundColorRGB),Go.foregroundColorRGB&&(f_=50331648,Qs=Go.foregroundColorRGB.rgba>>8&16777215,JC=Go.foregroundColorRGB),__=Go.options.layer==="top")}),!__&&Xd&&(Yd=this._coreBrowserService.isFocused?V.selectionBackgroundOpaque:V.selectionInactiveBackgroundOpaque,cc=Yd.rgba>>8&16777215,g_=50331648,__=!0,V.selectionForeground&&(f_=50331648,Qs=V.selectionForeground.rgba>>8&16777215,JC=V.selectionForeground)),__&&ct.push("xterm-decoration-top"),g_){case 16777216:case 33554432:Kd=V.ansi[cc],ct.push(`xterm-bg-${cc}`);break;case 50331648:Kd=x.channels.toColor(cc>>16,cc>>8&255,255&cc),this._addStyle(Y,`background-color:#${I((cc>>>0).toString(16),"0",6)}`);break;default:FT?(Kd=V.foreground,ct.push(`xterm-bg-${p.INVERTED_DEFAULT_COLOR}`)):Kd=V.background}switch(Yd||kn.isDim()&&(Yd=x.color.multiplyOpacity(Kd,.5)),f_){case 16777216:case 33554432:kn.isBold()&&Qs<8&&this._optionsService.rawOptions.drawBoldTextInBrightColors&&(Qs+=8),this._applyMinimumContrast(Y,Kd,V.ansi[Qs],kn,Yd,void 0)||ct.push(`xterm-fg-${Qs}`);break;case 50331648:let Go=x.channels.toColor(Qs>>16&255,Qs>>8&255,255&Qs);this._applyMinimumContrast(Y,Kd,Go,kn,Yd,JC)||this._addStyle(Y,`color:#${I(Qs.toString(16),"0",6)}`);break;default:this._applyMinimumContrast(Y,Kd,V.foreground,kn,Yd,JC)||FT&&ct.push(`xterm-fg-${p.INVERTED_DEFAULT_COLOR}`)}ct.length&&(Y.className=ct.join(" "),ct.length=0),oh||xo||RT?Y.textContent=Te:oe++,Ge!==this.defaultSpacing&&(Y.style.letterSpacing=`${Ge}px`),De.push(Y),Xn=jr}return Y&&oe&&(Y.textContent=Te),De}_applyMinimumContrast(P,R,D,N,q,de){if(this._optionsService.rawOptions.minimumContrastRatio===1||(0,w.treatGlyphAsBackgroundColor)(N.getCode()))return!1;let fe=this._getContrastCache(N),G;if(q||de||(G=fe.getColor(R.rgba,D.rgba)),G===void 0){let ue=this._optionsService.rawOptions.minimumContrastRatio/(N.isDim()?2:1);G=x.color.ensureContrastRatio(q||R,de||D,ue),fe.setColor((q||R).rgba,(de||D).rgba,G??null)}return!!G&&(this._addStyle(P,`color:${G.css}`),!0)}_getContrastCache(P){return P.isDim()?this._themeService.colors.halfContrastCache:this._themeService.colors.contrastCache}_addStyle(P,R){P.setAttribute("style",`${P.getAttribute("style")||""}${R};`)}_isCellInSelection(P,R){let D=this._selectionStart,N=this._selectionEnd;return!(!D||!N)&&(this._columnSelectMode?D[0]<=N[0]?P>=D[0]&&R>=D[1]&&P=D[1]&&P>=N[0]&&R<=N[1]:R>D[1]&&R=D[0]&&P=D[0])}};function I(P,R,D){for(;P.length{Object.defineProperty(r,"__esModule",{value:!0}),r.WidthCache=void 0,r.WidthCache=class{constructor(a,c){this._flat=new Float32Array(256),this._font="",this._fontSize=0,this._weight="normal",this._weightBold="bold",this._measureElements=[],this._container=a.createElement("div"),this._container.classList.add("xterm-width-cache-measure-container"),this._container.setAttribute("aria-hidden","true"),this._container.style.whiteSpace="pre",this._container.style.fontKerning="none";let m=a.createElement("span");m.classList.add("xterm-char-measure-element");let p=a.createElement("span");p.classList.add("xterm-char-measure-element"),p.style.fontWeight="bold";let h=a.createElement("span");h.classList.add("xterm-char-measure-element"),h.style.fontStyle="italic";let g=a.createElement("span");g.classList.add("xterm-char-measure-element"),g.style.fontWeight="bold",g.style.fontStyle="italic",this._measureElements=[m,p,h,g],this._container.appendChild(m),this._container.appendChild(p),this._container.appendChild(h),this._container.appendChild(g),c.appendChild(this._container),this.clear()}dispose(){this._container.remove(),this._measureElements.length=0,this._holey=void 0}clear(){this._flat.fill(-9999),this._holey=new Map}setFont(a,c,m,p){a===this._font&&c===this._fontSize&&m===this._weight&&p===this._weightBold||(this._font=a,this._fontSize=c,this._weight=m,this._weightBold=p,this._container.style.fontFamily=this._font,this._container.style.fontSize=`${this._fontSize}px`,this._measureElements[0].style.fontWeight=`${m}`,this._measureElements[1].style.fontWeight=`${p}`,this._measureElements[2].style.fontWeight=`${m}`,this._measureElements[3].style.fontWeight=`${p}`,this.clear())}get(a,c,m){let p=0;if(!c&&!m&&a.length===1&&(p=a.charCodeAt(0))<256){if(this._flat[p]!==-9999)return this._flat[p];let S=this._measure(a,0);return S>0&&(this._flat[p]=S),S}let h=a;c&&(h+="B"),m&&(h+="I");let g=this._holey.get(h);if(g===void 0){let S=0;c&&(S|=1),m&&(S|=2),g=this._measure(a,S),g>0&&this._holey.set(h,g)}return g}_measure(a,c){let m=this._measureElements[c];return m.textContent=a.repeat(32),m.offsetWidth/32}}},2223:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.TEXT_BASELINE=r.DIM_OPACITY=r.INVERTED_DEFAULT_COLOR=void 0;let c=a(6114);r.INVERTED_DEFAULT_COLOR=257,r.DIM_OPACITY=.5,r.TEXT_BASELINE=c.isFirefox||c.isLegacyEdge?"bottom":"ideographic"},6171:(o,r)=>{function a(m){return 57508<=m&&m<=57558}function c(m){return m>=128512&&m<=128591||m>=127744&&m<=128511||m>=128640&&m<=128767||m>=9728&&m<=9983||m>=9984&&m<=10175||m>=65024&&m<=65039||m>=129280&&m<=129535||m>=127462&&m<=127487}Object.defineProperty(r,"__esModule",{value:!0}),r.computeNextVariantOffset=r.createRenderDimensions=r.treatGlyphAsBackgroundColor=r.allowRescaling=r.isEmoji=r.isRestrictedPowerlineGlyph=r.isPowerlineGlyph=r.throwIfFalsy=void 0,r.throwIfFalsy=function(m){if(!m)throw new Error("value must not be falsy");return m},r.isPowerlineGlyph=a,r.isRestrictedPowerlineGlyph=function(m){return 57520<=m&&m<=57527},r.isEmoji=c,r.allowRescaling=function(m,p,h,g){return p===1&&h>Math.ceil(1.5*g)&&m!==void 0&&m>255&&!c(m)&&!a(m)&&!(function(S){return 57344<=S&&S<=63743})(m)},r.treatGlyphAsBackgroundColor=function(m){return a(m)||(function(p){return 9472<=p&&p<=9631})(m)},r.createRenderDimensions=function(){return{css:{canvas:{width:0,height:0},cell:{width:0,height:0}},device:{canvas:{width:0,height:0},cell:{width:0,height:0},char:{width:0,height:0,left:0,top:0}}}},r.computeNextVariantOffset=function(m,p,h=0){return(m-(2*Math.round(p)-h))%(2*Math.round(p))}},6052:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.createSelectionRenderModel=void 0;class a{constructor(){this.clear()}clear(){this.hasSelection=!1,this.columnSelectMode=!1,this.viewportStartRow=0,this.viewportEndRow=0,this.viewportCappedStartRow=0,this.viewportCappedEndRow=0,this.startCol=0,this.endCol=0,this.selectionStart=void 0,this.selectionEnd=void 0}update(m,p,h,g=!1){if(this.selectionStart=p,this.selectionEnd=h,!p||!h||p[0]===h[0]&&p[1]===h[1])return void this.clear();let S=m.buffers.active.ydisp,x=p[1]-S,v=h[1]-S,M=Math.max(x,0),w=Math.min(v,m.rows-1);M>=m.rows||w<0?this.clear():(this.hasSelection=!0,this.columnSelectMode=g,this.viewportStartRow=x,this.viewportEndRow=v,this.viewportCappedStartRow=M,this.viewportCappedEndRow=w,this.startCol=p[0],this.endCol=h[0])}isCellSelected(m,p,h){return!!this.hasSelection&&(h-=m.buffer.active.viewportY,this.columnSelectMode?this.startCol<=this.endCol?p>=this.startCol&&h>=this.viewportCappedStartRow&&p=this.viewportCappedStartRow&&p>=this.endCol&&h<=this.viewportCappedEndRow:h>this.viewportStartRow&&h=this.startCol&&p=this.startCol)}}r.createSelectionRenderModel=function(){return new a}},456:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.SelectionModel=void 0,r.SelectionModel=class{constructor(a){this._bufferService=a,this.isSelectAllActive=!1,this.selectionStartLength=0}clearSelection(){this.selectionStart=void 0,this.selectionEnd=void 0,this.isSelectAllActive=!1,this.selectionStartLength=0}get finalSelectionStart(){return this.isSelectAllActive?[0,0]:this.selectionEnd&&this.selectionStart&&this.areSelectionValuesReversed()?this.selectionEnd:this.selectionStart}get finalSelectionEnd(){if(this.isSelectAllActive)return[this._bufferService.cols,this._bufferService.buffer.ybase+this._bufferService.rows-1];if(this.selectionStart){if(!this.selectionEnd||this.areSelectionValuesReversed()){let a=this.selectionStart[0]+this.selectionStartLength;return a>this._bufferService.cols?a%this._bufferService.cols==0?[this._bufferService.cols,this.selectionStart[1]+Math.floor(a/this._bufferService.cols)-1]:[a%this._bufferService.cols,this.selectionStart[1]+Math.floor(a/this._bufferService.cols)]:[a,this.selectionStart[1]]}if(this.selectionStartLength&&this.selectionEnd[1]===this.selectionStart[1]){let a=this.selectionStart[0]+this.selectionStartLength;return a>this._bufferService.cols?[a%this._bufferService.cols,this.selectionStart[1]+Math.floor(a/this._bufferService.cols)]:[Math.max(a,this.selectionEnd[0]),this.selectionEnd[1]]}return this.selectionEnd}}areSelectionValuesReversed(){let a=this.selectionStart,c=this.selectionEnd;return!(!a||!c)&&(a[1]>c[1]||a[1]===c[1]&&a[0]>c[0])}handleTrim(a){return this.selectionStart&&(this.selectionStart[1]-=a),this.selectionEnd&&(this.selectionEnd[1]-=a),this.selectionEnd&&this.selectionEnd[1]<0?(this.clearSelection(),!0):(this.selectionStart&&this.selectionStart[1]<0&&(this.selectionStart[1]=0),!1)}}},428:function(o,r,a){var c=this&&this.__decorate||function(w,y,k,I){var P,R=arguments.length,D=R<3?y:I===null?I=Object.getOwnPropertyDescriptor(y,k):I;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")D=Reflect.decorate(w,y,k,I);else for(var N=w.length-1;N>=0;N--)(P=w[N])&&(D=(R<3?P(D):R>3?P(y,k,D):P(y,k))||D);return R>3&&D&&Object.defineProperty(y,k,D),D},m=this&&this.__param||function(w,y){return function(k,I){y(k,I,w)}};Object.defineProperty(r,"__esModule",{value:!0}),r.CharSizeService=void 0;let p=a(2585),h=a(8460),g=a(844),S=r.CharSizeService=class extends g.Disposable{get hasValidSize(){return this.width>0&&this.height>0}constructor(w,y,k){super(),this._optionsService=k,this.width=0,this.height=0,this._onCharSizeChange=this.register(new h.EventEmitter),this.onCharSizeChange=this._onCharSizeChange.event;try{this._measureStrategy=this.register(new M(this._optionsService))}catch{this._measureStrategy=this.register(new v(w,y,this._optionsService))}this.register(this._optionsService.onMultipleOptionChange(["fontFamily","fontSize"],()=>this.measure()))}measure(){let w=this._measureStrategy.measure();w.width===this.width&&w.height===this.height||(this.width=w.width,this.height=w.height,this._onCharSizeChange.fire())}};r.CharSizeService=S=c([m(2,p.IOptionsService)],S);class x extends g.Disposable{constructor(){super(...arguments),this._result={width:0,height:0}}_validateAndSet(y,k){y!==void 0&&y>0&&k!==void 0&&k>0&&(this._result.width=y,this._result.height=k)}}class v extends x{constructor(y,k,I){super(),this._document=y,this._parentElement=k,this._optionsService=I,this._measureElement=this._document.createElement("span"),this._measureElement.classList.add("xterm-char-measure-element"),this._measureElement.textContent="W".repeat(32),this._measureElement.setAttribute("aria-hidden","true"),this._measureElement.style.whiteSpace="pre",this._measureElement.style.fontKerning="none",this._parentElement.appendChild(this._measureElement)}measure(){return this._measureElement.style.fontFamily=this._optionsService.rawOptions.fontFamily,this._measureElement.style.fontSize=`${this._optionsService.rawOptions.fontSize}px`,this._validateAndSet(Number(this._measureElement.offsetWidth)/32,Number(this._measureElement.offsetHeight)),this._result}}class M extends x{constructor(y){super(),this._optionsService=y,this._canvas=new OffscreenCanvas(100,100),this._ctx=this._canvas.getContext("2d");let k=this._ctx.measureText("W");if(!("width"in k&&"fontBoundingBoxAscent"in k&&"fontBoundingBoxDescent"in k))throw new Error("Required font metrics not supported")}measure(){this._ctx.font=`${this._optionsService.rawOptions.fontSize}px ${this._optionsService.rawOptions.fontFamily}`;let y=this._ctx.measureText("W");return this._validateAndSet(y.width,y.fontBoundingBoxAscent+y.fontBoundingBoxDescent),this._result}}},4269:function(o,r,a){var c=this&&this.__decorate||function(M,w,y,k){var I,P=arguments.length,R=P<3?w:k===null?k=Object.getOwnPropertyDescriptor(w,y):k;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")R=Reflect.decorate(M,w,y,k);else for(var D=M.length-1;D>=0;D--)(I=M[D])&&(R=(P<3?I(R):P>3?I(w,y,R):I(w,y))||R);return P>3&&R&&Object.defineProperty(w,y,R),R},m=this&&this.__param||function(M,w){return function(y,k){w(y,k,M)}};Object.defineProperty(r,"__esModule",{value:!0}),r.CharacterJoinerService=r.JoinedCellData=void 0;let p=a(3734),h=a(643),g=a(511),S=a(2585);class x extends p.AttributeData{constructor(w,y,k){super(),this.content=0,this.combinedData="",this.fg=w.fg,this.bg=w.bg,this.combinedData=y,this._width=k}isCombined(){return 2097152}getWidth(){return this._width}getChars(){return this.combinedData}getCode(){return 2097151}setFromCharData(w){throw new Error("not implemented")}getAsCharData(){return[this.fg,this.getChars(),this.getWidth(),this.getCode()]}}r.JoinedCellData=x;let v=r.CharacterJoinerService=class VH{constructor(w){this._bufferService=w,this._characterJoiners=[],this._nextCharacterJoinerId=0,this._workCell=new g.CellData}register(w){let y={id:this._nextCharacterJoinerId++,handler:w};return this._characterJoiners.push(y),y.id}deregister(w){for(let y=0;y1){let fe=this._getJoinedRanges(I,D,R,y,P);for(let G=0;G1){let de=this._getJoinedRanges(I,D,R,y,P);for(let fe=0;fe{Object.defineProperty(r,"__esModule",{value:!0}),r.CoreBrowserService=void 0;let c=a(844),m=a(8460),p=a(3656);class h extends c.Disposable{constructor(x,v,M){super(),this._textarea=x,this._window=v,this.mainDocument=M,this._isFocused=!1,this._cachedIsFocused=void 0,this._screenDprMonitor=new g(this._window),this._onDprChange=this.register(new m.EventEmitter),this.onDprChange=this._onDprChange.event,this._onWindowChange=this.register(new m.EventEmitter),this.onWindowChange=this._onWindowChange.event,this.register(this.onWindowChange(w=>this._screenDprMonitor.setWindow(w))),this.register((0,m.forwardEvent)(this._screenDprMonitor.onDprChange,this._onDprChange)),this._textarea.addEventListener("focus",()=>this._isFocused=!0),this._textarea.addEventListener("blur",()=>this._isFocused=!1)}get window(){return this._window}set window(x){this._window!==x&&(this._window=x,this._onWindowChange.fire(this._window))}get dpr(){return this.window.devicePixelRatio}get isFocused(){return this._cachedIsFocused===void 0&&(this._cachedIsFocused=this._isFocused&&this._textarea.ownerDocument.hasFocus(),queueMicrotask(()=>this._cachedIsFocused=void 0)),this._cachedIsFocused}}r.CoreBrowserService=h;class g extends c.Disposable{constructor(x){super(),this._parentWindow=x,this._windowResizeListener=this.register(new c.MutableDisposable),this._onDprChange=this.register(new m.EventEmitter),this.onDprChange=this._onDprChange.event,this._outerListener=()=>this._setDprAndFireIfDiffers(),this._currentDevicePixelRatio=this._parentWindow.devicePixelRatio,this._updateDpr(),this._setWindowResizeListener(),this.register((0,c.toDisposable)(()=>this.clearListener()))}setWindow(x){this._parentWindow=x,this._setWindowResizeListener(),this._setDprAndFireIfDiffers()}_setWindowResizeListener(){this._windowResizeListener.value=(0,p.addDisposableDomListener)(this._parentWindow,"resize",()=>this._setDprAndFireIfDiffers())}_setDprAndFireIfDiffers(){this._parentWindow.devicePixelRatio!==this._currentDevicePixelRatio&&this._onDprChange.fire(this._parentWindow.devicePixelRatio),this._updateDpr()}_updateDpr(){this._outerListener&&(this._resolutionMediaMatchList?.removeListener(this._outerListener),this._currentDevicePixelRatio=this._parentWindow.devicePixelRatio,this._resolutionMediaMatchList=this._parentWindow.matchMedia(`screen and (resolution: ${this._parentWindow.devicePixelRatio}dppx)`),this._resolutionMediaMatchList.addListener(this._outerListener))}clearListener(){this._resolutionMediaMatchList&&this._outerListener&&(this._resolutionMediaMatchList.removeListener(this._outerListener),this._resolutionMediaMatchList=void 0,this._outerListener=void 0)}}},779:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.LinkProviderService=void 0;let c=a(844);class m extends c.Disposable{constructor(){super(),this.linkProviders=[],this.register((0,c.toDisposable)(()=>this.linkProviders.length=0))}registerLinkProvider(h){return this.linkProviders.push(h),{dispose:()=>{let g=this.linkProviders.indexOf(h);g!==-1&&this.linkProviders.splice(g,1)}}}}r.LinkProviderService=m},8934:function(o,r,a){var c=this&&this.__decorate||function(S,x,v,M){var w,y=arguments.length,k=y<3?x:M===null?M=Object.getOwnPropertyDescriptor(x,v):M;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")k=Reflect.decorate(S,x,v,M);else for(var I=S.length-1;I>=0;I--)(w=S[I])&&(k=(y<3?w(k):y>3?w(x,v,k):w(x,v))||k);return y>3&&k&&Object.defineProperty(x,v,k),k},m=this&&this.__param||function(S,x){return function(v,M){x(v,M,S)}};Object.defineProperty(r,"__esModule",{value:!0}),r.MouseService=void 0;let p=a(4725),h=a(9806),g=r.MouseService=class{constructor(S,x){this._renderService=S,this._charSizeService=x}getCoords(S,x,v,M,w){return(0,h.getCoords)(window,S,x,v,M,this._charSizeService.hasValidSize,this._renderService.dimensions.css.cell.width,this._renderService.dimensions.css.cell.height,w)}getMouseReportCoords(S,x){let v=(0,h.getCoordsRelativeToElement)(window,S,x);if(this._charSizeService.hasValidSize)return v[0]=Math.min(Math.max(v[0],0),this._renderService.dimensions.css.canvas.width-1),v[1]=Math.min(Math.max(v[1],0),this._renderService.dimensions.css.canvas.height-1),{col:Math.floor(v[0]/this._renderService.dimensions.css.cell.width),row:Math.floor(v[1]/this._renderService.dimensions.css.cell.height),x:Math.floor(v[0]),y:Math.floor(v[1])}}};r.MouseService=g=c([m(0,p.IRenderService),m(1,p.ICharSizeService)],g)},3230:function(o,r,a){var c=this&&this.__decorate||function(w,y,k,I){var P,R=arguments.length,D=R<3?y:I===null?I=Object.getOwnPropertyDescriptor(y,k):I;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")D=Reflect.decorate(w,y,k,I);else for(var N=w.length-1;N>=0;N--)(P=w[N])&&(D=(R<3?P(D):R>3?P(y,k,D):P(y,k))||D);return R>3&&D&&Object.defineProperty(y,k,D),D},m=this&&this.__param||function(w,y){return function(k,I){y(k,I,w)}};Object.defineProperty(r,"__esModule",{value:!0}),r.RenderService=void 0;let p=a(6193),h=a(4725),g=a(8460),S=a(844),x=a(7226),v=a(2585),M=r.RenderService=class extends S.Disposable{get dimensions(){return this._renderer.value.dimensions}constructor(w,y,k,I,P,R,D,N){super(),this._rowCount=w,this._charSizeService=I,this._renderer=this.register(new S.MutableDisposable),this._pausedResizeTask=new x.DebouncedIdleTask,this._observerDisposable=this.register(new S.MutableDisposable),this._isPaused=!1,this._needsFullRefresh=!1,this._isNextRenderRedrawOnly=!0,this._needsSelectionRefresh=!1,this._canvasWidth=0,this._canvasHeight=0,this._selectionState={start:void 0,end:void 0,columnSelectMode:!1},this._onDimensionsChange=this.register(new g.EventEmitter),this.onDimensionsChange=this._onDimensionsChange.event,this._onRenderedViewportChange=this.register(new g.EventEmitter),this.onRenderedViewportChange=this._onRenderedViewportChange.event,this._onRender=this.register(new g.EventEmitter),this.onRender=this._onRender.event,this._onRefreshRequest=this.register(new g.EventEmitter),this.onRefreshRequest=this._onRefreshRequest.event,this._renderDebouncer=new p.RenderDebouncer((q,de)=>this._renderRows(q,de),D),this.register(this._renderDebouncer),this.register(D.onDprChange(()=>this.handleDevicePixelRatioChange())),this.register(R.onResize(()=>this._fullRefresh())),this.register(R.buffers.onBufferActivate(()=>this._renderer.value?.clear())),this.register(k.onOptionChange(()=>this._handleOptionsChanged())),this.register(this._charSizeService.onCharSizeChange(()=>this.handleCharSizeChanged())),this.register(P.onDecorationRegistered(()=>this._fullRefresh())),this.register(P.onDecorationRemoved(()=>this._fullRefresh())),this.register(k.onMultipleOptionChange(["customGlyphs","drawBoldTextInBrightColors","letterSpacing","lineHeight","fontFamily","fontSize","fontWeight","fontWeightBold","minimumContrastRatio","rescaleOverlappingGlyphs"],()=>{this.clear(),this.handleResize(R.cols,R.rows),this._fullRefresh()})),this.register(k.onMultipleOptionChange(["cursorBlink","cursorStyle"],()=>this.refreshRows(R.buffer.y,R.buffer.y,!0))),this.register(N.onChangeColors(()=>this._fullRefresh())),this._registerIntersectionObserver(D.window,y),this.register(D.onWindowChange(q=>this._registerIntersectionObserver(q,y)))}_registerIntersectionObserver(w,y){if("IntersectionObserver"in w){let k=new w.IntersectionObserver(I=>this._handleIntersectionChange(I[I.length-1]),{threshold:0});k.observe(y),this._observerDisposable.value=(0,S.toDisposable)(()=>k.disconnect())}}_handleIntersectionChange(w){this._isPaused=w.isIntersecting===void 0?w.intersectionRatio===0:!w.isIntersecting,this._isPaused||this._charSizeService.hasValidSize||this._charSizeService.measure(),!this._isPaused&&this._needsFullRefresh&&(this._pausedResizeTask.flush(),this.refreshRows(0,this._rowCount-1),this._needsFullRefresh=!1)}refreshRows(w,y,k=!1){this._isPaused?this._needsFullRefresh=!0:(k||(this._isNextRenderRedrawOnly=!1),this._renderDebouncer.refresh(w,y,this._rowCount))}_renderRows(w,y){this._renderer.value&&(w=Math.min(w,this._rowCount-1),y=Math.min(y,this._rowCount-1),this._renderer.value.renderRows(w,y),this._needsSelectionRefresh&&(this._renderer.value.handleSelectionChanged(this._selectionState.start,this._selectionState.end,this._selectionState.columnSelectMode),this._needsSelectionRefresh=!1),this._isNextRenderRedrawOnly||this._onRenderedViewportChange.fire({start:w,end:y}),this._onRender.fire({start:w,end:y}),this._isNextRenderRedrawOnly=!0)}resize(w,y){this._rowCount=y,this._fireOnCanvasResize()}_handleOptionsChanged(){this._renderer.value&&(this.refreshRows(0,this._rowCount-1),this._fireOnCanvasResize())}_fireOnCanvasResize(){this._renderer.value&&(this._renderer.value.dimensions.css.canvas.width===this._canvasWidth&&this._renderer.value.dimensions.css.canvas.height===this._canvasHeight||this._onDimensionsChange.fire(this._renderer.value.dimensions))}hasRenderer(){return!!this._renderer.value}setRenderer(w){this._renderer.value=w,this._renderer.value&&(this._renderer.value.onRequestRedraw(y=>this.refreshRows(y.start,y.end,!0)),this._needsSelectionRefresh=!0,this._fullRefresh())}addRefreshCallback(w){return this._renderDebouncer.addRefreshCallback(w)}_fullRefresh(){this._isPaused?this._needsFullRefresh=!0:this.refreshRows(0,this._rowCount-1)}clearTextureAtlas(){this._renderer.value&&(this._renderer.value.clearTextureAtlas?.(),this._fullRefresh())}handleDevicePixelRatioChange(){this._charSizeService.measure(),this._renderer.value&&(this._renderer.value.handleDevicePixelRatioChange(),this.refreshRows(0,this._rowCount-1))}handleResize(w,y){this._renderer.value&&(this._isPaused?this._pausedResizeTask.set(()=>this._renderer.value?.handleResize(w,y)):this._renderer.value.handleResize(w,y),this._fullRefresh())}handleCharSizeChanged(){this._renderer.value?.handleCharSizeChanged()}handleBlur(){this._renderer.value?.handleBlur()}handleFocus(){this._renderer.value?.handleFocus()}handleSelectionChanged(w,y,k){this._selectionState.start=w,this._selectionState.end=y,this._selectionState.columnSelectMode=k,this._renderer.value?.handleSelectionChanged(w,y,k)}handleCursorMove(){this._renderer.value?.handleCursorMove()}clear(){this._renderer.value?.clear()}};r.RenderService=M=c([m(2,v.IOptionsService),m(3,h.ICharSizeService),m(4,v.IDecorationService),m(5,v.IBufferService),m(6,h.ICoreBrowserService),m(7,h.IThemeService)],M)},9312:function(o,r,a){var c=this&&this.__decorate||function(D,N,q,de){var fe,G=arguments.length,ue=G<3?N:de===null?de=Object.getOwnPropertyDescriptor(N,q):de;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")ue=Reflect.decorate(D,N,q,de);else for(var be=D.length-1;be>=0;be--)(fe=D[be])&&(ue=(G<3?fe(ue):G>3?fe(N,q,ue):fe(N,q))||ue);return G>3&&ue&&Object.defineProperty(N,q,ue),ue},m=this&&this.__param||function(D,N){return function(q,de){N(q,de,D)}};Object.defineProperty(r,"__esModule",{value:!0}),r.SelectionService=void 0;let p=a(9806),h=a(9504),g=a(456),S=a(4725),x=a(8460),v=a(844),M=a(6114),w=a(4841),y=a(511),k=a(2585),I="\xA0",P=new RegExp(I,"g"),R=r.SelectionService=class extends v.Disposable{constructor(D,N,q,de,fe,G,ue,be,le){super(),this._element=D,this._screenElement=N,this._linkifier=q,this._bufferService=de,this._coreService=fe,this._mouseService=G,this._optionsService=ue,this._renderService=be,this._coreBrowserService=le,this._dragScrollAmount=0,this._enabled=!0,this._workCell=new y.CellData,this._mouseDownTimeStamp=0,this._oldHasSelection=!1,this._oldSelectionStart=void 0,this._oldSelectionEnd=void 0,this._onLinuxMouseSelection=this.register(new x.EventEmitter),this.onLinuxMouseSelection=this._onLinuxMouseSelection.event,this._onRedrawRequest=this.register(new x.EventEmitter),this.onRequestRedraw=this._onRedrawRequest.event,this._onSelectionChange=this.register(new x.EventEmitter),this.onSelectionChange=this._onSelectionChange.event,this._onRequestScrollLines=this.register(new x.EventEmitter),this.onRequestScrollLines=this._onRequestScrollLines.event,this._mouseMoveListener=De=>this._handleMouseMove(De),this._mouseUpListener=De=>this._handleMouseUp(De),this._coreService.onUserInput(()=>{this.hasSelection&&this.clearSelection()}),this._trimListener=this._bufferService.buffer.lines.onTrim(De=>this._handleTrim(De)),this.register(this._bufferService.buffers.onBufferActivate(De=>this._handleBufferActivate(De))),this.enable(),this._model=new g.SelectionModel(this._bufferService),this._activeSelectionMode=0,this.register((0,v.toDisposable)(()=>{this._removeMouseDownListeners()}))}reset(){this.clearSelection()}disable(){this.clearSelection(),this._enabled=!1}enable(){this._enabled=!0}get selectionStart(){return this._model.finalSelectionStart}get selectionEnd(){return this._model.finalSelectionEnd}get hasSelection(){let D=this._model.finalSelectionStart,N=this._model.finalSelectionEnd;return!(!D||!N||D[0]===N[0]&&D[1]===N[1])}get selectionText(){let D=this._model.finalSelectionStart,N=this._model.finalSelectionEnd;if(!D||!N)return"";let q=this._bufferService.buffer,de=[];if(this._activeSelectionMode===3){if(D[0]===N[0])return"";let fe=D[0]fe.replace(P," ")).join(M.isWindows?`\r -`:` -`)}clearSelection(){this._model.clearSelection(),this._removeMouseDownListeners(),this.refresh(),this._onSelectionChange.fire()}refresh(D){this._refreshAnimationFrame||(this._refreshAnimationFrame=this._coreBrowserService.window.requestAnimationFrame(()=>this._refresh())),M.isLinux&&D&&this.selectionText.length&&this._onLinuxMouseSelection.fire(this.selectionText)}_refresh(){this._refreshAnimationFrame=void 0,this._onRedrawRequest.fire({start:this._model.finalSelectionStart,end:this._model.finalSelectionEnd,columnSelectMode:this._activeSelectionMode===3})}_isClickInSelection(D){let N=this._getMouseBufferCoords(D),q=this._model.finalSelectionStart,de=this._model.finalSelectionEnd;return!!(q&&de&&N)&&this._areCoordsInSelection(N,q,de)}isCellInSelection(D,N){let q=this._model.finalSelectionStart,de=this._model.finalSelectionEnd;return!(!q||!de)&&this._areCoordsInSelection([D,N],q,de)}_areCoordsInSelection(D,N,q){return D[1]>N[1]&&D[1]=N[0]&&D[0]=N[0]}_selectWordAtCursor(D,N){let q=this._linkifier.currentLink?.link?.range;if(q)return this._model.selectionStart=[q.start.x-1,q.start.y-1],this._model.selectionStartLength=(0,w.getRangeLength)(q,this._bufferService.cols),this._model.selectionEnd=void 0,!0;let de=this._getMouseBufferCoords(D);return!!de&&(this._selectWordAt(de,N),this._model.selectionEnd=void 0,!0)}selectAll(){this._model.isSelectAllActive=!0,this.refresh(),this._onSelectionChange.fire()}selectLines(D,N){this._model.clearSelection(),D=Math.max(D,0),N=Math.min(N,this._bufferService.buffer.lines.length-1),this._model.selectionStart=[0,D],this._model.selectionEnd=[this._bufferService.cols,N],this.refresh(),this._onSelectionChange.fire()}_handleTrim(D){this._model.handleTrim(D)&&this.refresh()}_getMouseBufferCoords(D){let N=this._mouseService.getCoords(D,this._screenElement,this._bufferService.cols,this._bufferService.rows,!0);if(N)return N[0]--,N[1]--,N[1]+=this._bufferService.buffer.ydisp,N}_getMouseEventScrollAmount(D){let N=(0,p.getCoordsRelativeToElement)(this._coreBrowserService.window,D,this._screenElement)[1],q=this._renderService.dimensions.css.canvas.height;return N>=0&&N<=q?0:(N>q&&(N-=q),N=Math.min(Math.max(N,-50),50),N/=50,N/Math.abs(N)+Math.round(14*N))}shouldForceSelection(D){return M.isMac?D.altKey&&this._optionsService.rawOptions.macOptionClickForcesSelection:D.shiftKey}handleMouseDown(D){if(this._mouseDownTimeStamp=D.timeStamp,(D.button!==2||!this.hasSelection)&&D.button===0){if(!this._enabled){if(!this.shouldForceSelection(D))return;D.stopPropagation()}D.preventDefault(),this._dragScrollAmount=0,this._enabled&&D.shiftKey?this._handleIncrementalClick(D):D.detail===1?this._handleSingleClick(D):D.detail===2?this._handleDoubleClick(D):D.detail===3&&this._handleTripleClick(D),this._addMouseDownListeners(),this.refresh(!0)}}_addMouseDownListeners(){this._screenElement.ownerDocument&&(this._screenElement.ownerDocument.addEventListener("mousemove",this._mouseMoveListener),this._screenElement.ownerDocument.addEventListener("mouseup",this._mouseUpListener)),this._dragScrollIntervalTimer=this._coreBrowserService.window.setInterval(()=>this._dragScroll(),50)}_removeMouseDownListeners(){this._screenElement.ownerDocument&&(this._screenElement.ownerDocument.removeEventListener("mousemove",this._mouseMoveListener),this._screenElement.ownerDocument.removeEventListener("mouseup",this._mouseUpListener)),this._coreBrowserService.window.clearInterval(this._dragScrollIntervalTimer),this._dragScrollIntervalTimer=void 0}_handleIncrementalClick(D){this._model.selectionStart&&(this._model.selectionEnd=this._getMouseBufferCoords(D))}_handleSingleClick(D){if(this._model.selectionStartLength=0,this._model.isSelectAllActive=!1,this._activeSelectionMode=this.shouldColumnSelect(D)?3:0,this._model.selectionStart=this._getMouseBufferCoords(D),!this._model.selectionStart)return;this._model.selectionEnd=void 0;let N=this._bufferService.buffer.lines.get(this._model.selectionStart[1]);N&&N.length!==this._model.selectionStart[0]&&N.hasWidth(this._model.selectionStart[0])===0&&this._model.selectionStart[0]++}_handleDoubleClick(D){this._selectWordAtCursor(D,!0)&&(this._activeSelectionMode=1)}_handleTripleClick(D){let N=this._getMouseBufferCoords(D);N&&(this._activeSelectionMode=2,this._selectLineAt(N[1]))}shouldColumnSelect(D){return D.altKey&&!(M.isMac&&this._optionsService.rawOptions.macOptionClickForcesSelection)}_handleMouseMove(D){if(D.stopImmediatePropagation(),!this._model.selectionStart)return;let N=this._model.selectionEnd?[this._model.selectionEnd[0],this._model.selectionEnd[1]]:null;if(this._model.selectionEnd=this._getMouseBufferCoords(D),!this._model.selectionEnd)return void this.refresh(!0);this._activeSelectionMode===2?this._model.selectionEnd[1]0?this._model.selectionEnd[0]=this._bufferService.cols:this._dragScrollAmount<0&&(this._model.selectionEnd[0]=0));let q=this._bufferService.buffer;if(this._model.selectionEnd[1]0?(this._activeSelectionMode!==3&&(this._model.selectionEnd[0]=this._bufferService.cols),this._model.selectionEnd[1]=Math.min(D.ydisp+this._bufferService.rows,D.lines.length-1)):(this._activeSelectionMode!==3&&(this._model.selectionEnd[0]=0),this._model.selectionEnd[1]=D.ydisp),this.refresh()}}_handleMouseUp(D){let N=D.timeStamp-this._mouseDownTimeStamp;if(this._removeMouseDownListeners(),this.selectionText.length<=1&&N<500&&D.altKey&&this._optionsService.rawOptions.altClickMovesCursor){if(this._bufferService.buffer.ybase===this._bufferService.buffer.ydisp){let q=this._mouseService.getCoords(D,this._element,this._bufferService.cols,this._bufferService.rows,!1);if(q&&q[0]!==void 0&&q[1]!==void 0){let de=(0,h.moveToCellSequence)(q[0]-1,q[1]-1,this._bufferService,this._coreService.decPrivateModes.applicationCursorKeys);this._coreService.triggerDataEvent(de,!0)}}}else this._fireEventIfSelectionChanged()}_fireEventIfSelectionChanged(){let D=this._model.finalSelectionStart,N=this._model.finalSelectionEnd,q=!(!D||!N||D[0]===N[0]&&D[1]===N[1]);q?D&&N&&(this._oldSelectionStart&&this._oldSelectionEnd&&D[0]===this._oldSelectionStart[0]&&D[1]===this._oldSelectionStart[1]&&N[0]===this._oldSelectionEnd[0]&&N[1]===this._oldSelectionEnd[1]||this._fireOnSelectionChange(D,N,q)):this._oldHasSelection&&this._fireOnSelectionChange(D,N,q)}_fireOnSelectionChange(D,N,q){this._oldSelectionStart=D,this._oldSelectionEnd=N,this._oldHasSelection=q,this._onSelectionChange.fire()}_handleBufferActivate(D){this.clearSelection(),this._trimListener.dispose(),this._trimListener=D.activeBuffer.lines.onTrim(N=>this._handleTrim(N))}_convertViewportColToCharacterIndex(D,N){let q=N;for(let de=0;N>=de;de++){let fe=D.loadCell(de,this._workCell).getChars().length;this._workCell.getWidth()===0?q--:fe>1&&N!==de&&(q+=fe-1)}return q}setSelection(D,N,q){this._model.clearSelection(),this._removeMouseDownListeners(),this._model.selectionStart=[D,N],this._model.selectionStartLength=q,this.refresh(),this._fireEventIfSelectionChanged()}rightClickSelect(D){this._isClickInSelection(D)||(this._selectWordAtCursor(D,!1)&&this.refresh(!0),this._fireEventIfSelectionChanged())}_getWordAt(D,N,q=!0,de=!0){if(D[0]>=this._bufferService.cols)return;let fe=this._bufferService.buffer,G=fe.lines.get(D[1]);if(!G)return;let ue=fe.translateBufferLineToString(D[1],!1),be=this._convertViewportColToCharacterIndex(G,D[0]),le=be,De=D[0]-be,me=0,V=0,Y=0,ie=0;if(ue.charAt(be)===" "){for(;be>0&&ue.charAt(be-1)===" ";)be--;for(;le1&&(ie+=Xe-1,le+=Xe-1);Le>0&&be>0&&!this._isCharWordSeparator(G.loadCell(Le-1,this._workCell));){G.loadCell(Le-1,this._workCell);let xe=this._workCell.getChars().length;this._workCell.getWidth()===0?(me++,Le--):xe>1&&(Y+=xe-1,be-=xe-1),be--,Le--}for(;Ye1&&(ie+=xe-1,le+=xe-1),le++,Ye++}}le++;let oe=be+De-me+Y,Te=Math.min(this._bufferService.cols,le-be+me+V-Y-ie);if(N||ue.slice(be,le).trim()!==""){if(q&&oe===0&&G.getCodePoint(0)!==32){let Le=fe.lines.get(D[1]-1);if(Le&&G.isWrapped&&Le.getCodePoint(this._bufferService.cols-1)!==32){let Ye=this._getWordAt([this._bufferService.cols-1,D[1]-1],!1,!0,!1);if(Ye){let Xe=this._bufferService.cols-Ye.start;oe-=Xe,Te+=Xe}}}if(de&&oe+Te===this._bufferService.cols&&G.getCodePoint(this._bufferService.cols-1)!==32){let Le=fe.lines.get(D[1]+1);if(Le?.isWrapped&&Le.getCodePoint(0)!==32){let Ye=this._getWordAt([0,D[1]+1],!1,!1,!0);Ye&&(Te+=Ye.length)}}return{start:oe,length:Te}}}_selectWordAt(D,N){let q=this._getWordAt(D,N);if(q){for(;q.start<0;)q.start+=this._bufferService.cols,D[1]--;this._model.selectionStart=[q.start,D[1]],this._model.selectionStartLength=q.length}}_selectToWordAt(D){let N=this._getWordAt(D,!0);if(N){let q=D[1];for(;N.start<0;)N.start+=this._bufferService.cols,q--;if(!this._model.areSelectionValuesReversed())for(;N.start+N.length>this._bufferService.cols;)N.length-=this._bufferService.cols,q++;this._model.selectionEnd=[this._model.areSelectionValuesReversed()?N.start:N.start+N.length,q]}}_isCharWordSeparator(D){return D.getWidth()!==0&&this._optionsService.rawOptions.wordSeparator.indexOf(D.getChars())>=0}_selectLineAt(D){let N=this._bufferService.buffer.getWrappedRangeForLine(D),q={start:{x:0,y:N.first},end:{x:this._bufferService.cols-1,y:N.last}};this._model.selectionStart=[0,N.first],this._model.selectionEnd=void 0,this._model.selectionStartLength=(0,w.getRangeLength)(q,this._bufferService.cols)}};r.SelectionService=R=c([m(3,k.IBufferService),m(4,k.ICoreService),m(5,S.IMouseService),m(6,k.IOptionsService),m(7,S.IRenderService),m(8,S.ICoreBrowserService)],R)},4725:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.ILinkProviderService=r.IThemeService=r.ICharacterJoinerService=r.ISelectionService=r.IRenderService=r.IMouseService=r.ICoreBrowserService=r.ICharSizeService=void 0;let c=a(8343);r.ICharSizeService=(0,c.createDecorator)("CharSizeService"),r.ICoreBrowserService=(0,c.createDecorator)("CoreBrowserService"),r.IMouseService=(0,c.createDecorator)("MouseService"),r.IRenderService=(0,c.createDecorator)("RenderService"),r.ISelectionService=(0,c.createDecorator)("SelectionService"),r.ICharacterJoinerService=(0,c.createDecorator)("CharacterJoinerService"),r.IThemeService=(0,c.createDecorator)("ThemeService"),r.ILinkProviderService=(0,c.createDecorator)("LinkProviderService")},6731:function(o,r,a){var c=this&&this.__decorate||function(R,D,N,q){var de,fe=arguments.length,G=fe<3?D:q===null?q=Object.getOwnPropertyDescriptor(D,N):q;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")G=Reflect.decorate(R,D,N,q);else for(var ue=R.length-1;ue>=0;ue--)(de=R[ue])&&(G=(fe<3?de(G):fe>3?de(D,N,G):de(D,N))||G);return fe>3&&G&&Object.defineProperty(D,N,G),G},m=this&&this.__param||function(R,D){return function(N,q){D(N,q,R)}};Object.defineProperty(r,"__esModule",{value:!0}),r.ThemeService=r.DEFAULT_ANSI_COLORS=void 0;let p=a(7239),h=a(8055),g=a(8460),S=a(844),x=a(2585),v=h.css.toColor("#ffffff"),M=h.css.toColor("#000000"),w=h.css.toColor("#ffffff"),y=h.css.toColor("#000000"),k={css:"rgba(255, 255, 255, 0.3)",rgba:4294967117};r.DEFAULT_ANSI_COLORS=Object.freeze((()=>{let R=[h.css.toColor("#2e3436"),h.css.toColor("#cc0000"),h.css.toColor("#4e9a06"),h.css.toColor("#c4a000"),h.css.toColor("#3465a4"),h.css.toColor("#75507b"),h.css.toColor("#06989a"),h.css.toColor("#d3d7cf"),h.css.toColor("#555753"),h.css.toColor("#ef2929"),h.css.toColor("#8ae234"),h.css.toColor("#fce94f"),h.css.toColor("#729fcf"),h.css.toColor("#ad7fa8"),h.css.toColor("#34e2e2"),h.css.toColor("#eeeeec")],D=[0,95,135,175,215,255];for(let N=0;N<216;N++){let q=D[N/36%6|0],de=D[N/6%6|0],fe=D[N%6];R.push({css:h.channels.toCss(q,de,fe),rgba:h.channels.toRgba(q,de,fe)})}for(let N=0;N<24;N++){let q=8+10*N;R.push({css:h.channels.toCss(q,q,q),rgba:h.channels.toRgba(q,q,q)})}return R})());let I=r.ThemeService=class extends S.Disposable{get colors(){return this._colors}constructor(R){super(),this._optionsService=R,this._contrastCache=new p.ColorContrastCache,this._halfContrastCache=new p.ColorContrastCache,this._onChangeColors=this.register(new g.EventEmitter),this.onChangeColors=this._onChangeColors.event,this._colors={foreground:v,background:M,cursor:w,cursorAccent:y,selectionForeground:void 0,selectionBackgroundTransparent:k,selectionBackgroundOpaque:h.color.blend(M,k),selectionInactiveBackgroundTransparent:k,selectionInactiveBackgroundOpaque:h.color.blend(M,k),ansi:r.DEFAULT_ANSI_COLORS.slice(),contrastCache:this._contrastCache,halfContrastCache:this._halfContrastCache},this._updateRestoreColors(),this._setTheme(this._optionsService.rawOptions.theme),this.register(this._optionsService.onSpecificOptionChange("minimumContrastRatio",()=>this._contrastCache.clear())),this.register(this._optionsService.onSpecificOptionChange("theme",()=>this._setTheme(this._optionsService.rawOptions.theme)))}_setTheme(R={}){let D=this._colors;if(D.foreground=P(R.foreground,v),D.background=P(R.background,M),D.cursor=P(R.cursor,w),D.cursorAccent=P(R.cursorAccent,y),D.selectionBackgroundTransparent=P(R.selectionBackground,k),D.selectionBackgroundOpaque=h.color.blend(D.background,D.selectionBackgroundTransparent),D.selectionInactiveBackgroundTransparent=P(R.selectionInactiveBackground,D.selectionBackgroundTransparent),D.selectionInactiveBackgroundOpaque=h.color.blend(D.background,D.selectionInactiveBackgroundTransparent),D.selectionForeground=R.selectionForeground?P(R.selectionForeground,h.NULL_COLOR):void 0,D.selectionForeground===h.NULL_COLOR&&(D.selectionForeground=void 0),h.color.isOpaque(D.selectionBackgroundTransparent)&&(D.selectionBackgroundTransparent=h.color.opacity(D.selectionBackgroundTransparent,.3)),h.color.isOpaque(D.selectionInactiveBackgroundTransparent)&&(D.selectionInactiveBackgroundTransparent=h.color.opacity(D.selectionInactiveBackgroundTransparent,.3)),D.ansi=r.DEFAULT_ANSI_COLORS.slice(),D.ansi[0]=P(R.black,r.DEFAULT_ANSI_COLORS[0]),D.ansi[1]=P(R.red,r.DEFAULT_ANSI_COLORS[1]),D.ansi[2]=P(R.green,r.DEFAULT_ANSI_COLORS[2]),D.ansi[3]=P(R.yellow,r.DEFAULT_ANSI_COLORS[3]),D.ansi[4]=P(R.blue,r.DEFAULT_ANSI_COLORS[4]),D.ansi[5]=P(R.magenta,r.DEFAULT_ANSI_COLORS[5]),D.ansi[6]=P(R.cyan,r.DEFAULT_ANSI_COLORS[6]),D.ansi[7]=P(R.white,r.DEFAULT_ANSI_COLORS[7]),D.ansi[8]=P(R.brightBlack,r.DEFAULT_ANSI_COLORS[8]),D.ansi[9]=P(R.brightRed,r.DEFAULT_ANSI_COLORS[9]),D.ansi[10]=P(R.brightGreen,r.DEFAULT_ANSI_COLORS[10]),D.ansi[11]=P(R.brightYellow,r.DEFAULT_ANSI_COLORS[11]),D.ansi[12]=P(R.brightBlue,r.DEFAULT_ANSI_COLORS[12]),D.ansi[13]=P(R.brightMagenta,r.DEFAULT_ANSI_COLORS[13]),D.ansi[14]=P(R.brightCyan,r.DEFAULT_ANSI_COLORS[14]),D.ansi[15]=P(R.brightWhite,r.DEFAULT_ANSI_COLORS[15]),R.extendedAnsi){let N=Math.min(D.ansi.length-16,R.extendedAnsi.length);for(let q=0;q{Object.defineProperty(r,"__esModule",{value:!0}),r.CircularList=void 0;let c=a(8460),m=a(844);class p extends m.Disposable{constructor(g){super(),this._maxLength=g,this.onDeleteEmitter=this.register(new c.EventEmitter),this.onDelete=this.onDeleteEmitter.event,this.onInsertEmitter=this.register(new c.EventEmitter),this.onInsert=this.onInsertEmitter.event,this.onTrimEmitter=this.register(new c.EventEmitter),this.onTrim=this.onTrimEmitter.event,this._array=new Array(this._maxLength),this._startIndex=0,this._length=0}get maxLength(){return this._maxLength}set maxLength(g){if(this._maxLength===g)return;let S=new Array(g);for(let x=0;xthis._length)for(let S=this._length;S=g;v--)this._array[this._getCyclicIndex(v+x.length)]=this._array[this._getCyclicIndex(v)];for(let v=0;vthis._maxLength){let v=this._length+x.length-this._maxLength;this._startIndex+=v,this._length=this._maxLength,this.onTrimEmitter.fire(v)}else this._length+=x.length}trimStart(g){g>this._length&&(g=this._length),this._startIndex+=g,this._length-=g,this.onTrimEmitter.fire(g)}shiftElements(g,S,x){if(!(S<=0)){if(g<0||g>=this._length)throw new Error("start argument out of range");if(g+x<0)throw new Error("Cannot shift elements in list beyond index 0");if(x>0){for(let M=S-1;M>=0;M--)this.set(g+M+x,this.get(g+M));let v=g+S+x-this._length;if(v>0)for(this._length+=v;this._length>this._maxLength;)this._length--,this._startIndex++,this.onTrimEmitter.fire(1)}else for(let v=0;v{Object.defineProperty(r,"__esModule",{value:!0}),r.clone=void 0,r.clone=function a(c,m=5){if(typeof c!="object")return c;let p=Array.isArray(c)?[]:{};for(let h in c)p[h]=m<=1?c[h]:c[h]&&a(c[h],m-1);return p}},8055:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.contrastRatio=r.toPaddedHex=r.rgba=r.rgb=r.css=r.color=r.channels=r.NULL_COLOR=void 0;let a=0,c=0,m=0,p=0;var h,g,S,x,v;function M(y){let k=y.toString(16);return k.length<2?"0"+k:k}function w(y,k){return y>>0},y.toColor=function(k,I,P,R){return{css:y.toCss(k,I,P,R),rgba:y.toRgba(k,I,P,R)}}})(h||(r.channels=h={})),(function(y){function k(I,P){return p=Math.round(255*P),[a,c,m]=v.toChannels(I.rgba),{css:h.toCss(a,c,m,p),rgba:h.toRgba(a,c,m,p)}}y.blend=function(I,P){if(p=(255&P.rgba)/255,p===1)return{css:P.css,rgba:P.rgba};let R=P.rgba>>24&255,D=P.rgba>>16&255,N=P.rgba>>8&255,q=I.rgba>>24&255,de=I.rgba>>16&255,fe=I.rgba>>8&255;return a=q+Math.round((R-q)*p),c=de+Math.round((D-de)*p),m=fe+Math.round((N-fe)*p),{css:h.toCss(a,c,m),rgba:h.toRgba(a,c,m)}},y.isOpaque=function(I){return(255&I.rgba)==255},y.ensureContrastRatio=function(I,P,R){let D=v.ensureContrastRatio(I.rgba,P.rgba,R);if(D)return h.toColor(D>>24&255,D>>16&255,D>>8&255)},y.opaque=function(I){let P=(255|I.rgba)>>>0;return[a,c,m]=v.toChannels(P),{css:h.toCss(a,c,m),rgba:P}},y.opacity=k,y.multiplyOpacity=function(I,P){return p=255&I.rgba,k(I,p*P/255)},y.toColorRGB=function(I){return[I.rgba>>24&255,I.rgba>>16&255,I.rgba>>8&255]}})(g||(r.color=g={})),(function(y){let k,I;try{let P=document.createElement("canvas");P.width=1,P.height=1;let R=P.getContext("2d",{willReadFrequently:!0});R&&(k=R,k.globalCompositeOperation="copy",I=k.createLinearGradient(0,0,1,1))}catch{}y.toColor=function(P){if(P.match(/#[\da-f]{3,8}/i))switch(P.length){case 4:return a=parseInt(P.slice(1,2).repeat(2),16),c=parseInt(P.slice(2,3).repeat(2),16),m=parseInt(P.slice(3,4).repeat(2),16),h.toColor(a,c,m);case 5:return a=parseInt(P.slice(1,2).repeat(2),16),c=parseInt(P.slice(2,3).repeat(2),16),m=parseInt(P.slice(3,4).repeat(2),16),p=parseInt(P.slice(4,5).repeat(2),16),h.toColor(a,c,m,p);case 7:return{css:P,rgba:(parseInt(P.slice(1),16)<<8|255)>>>0};case 9:return{css:P,rgba:parseInt(P.slice(1),16)>>>0}}let R=P.match(/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(,\s*(0|1|\d?\.(\d+))\s*)?\)/);if(R)return a=parseInt(R[1]),c=parseInt(R[2]),m=parseInt(R[3]),p=Math.round(255*(R[5]===void 0?1:parseFloat(R[5]))),h.toColor(a,c,m,p);if(!k||!I)throw new Error("css.toColor: Unsupported css format");if(k.fillStyle=I,k.fillStyle=P,typeof k.fillStyle!="string")throw new Error("css.toColor: Unsupported css format");if(k.fillRect(0,0,1,1),[a,c,m,p]=k.getImageData(0,0,1,1).data,p!==255)throw new Error("css.toColor: Unsupported css format");return{rgba:h.toRgba(a,c,m,p),css:P}}})(S||(r.css=S={})),(function(y){function k(I,P,R){let D=I/255,N=P/255,q=R/255;return .2126*(D<=.03928?D/12.92:Math.pow((D+.055)/1.055,2.4))+.7152*(N<=.03928?N/12.92:Math.pow((N+.055)/1.055,2.4))+.0722*(q<=.03928?q/12.92:Math.pow((q+.055)/1.055,2.4))}y.relativeLuminance=function(I){return k(I>>16&255,I>>8&255,255&I)},y.relativeLuminance2=k})(x||(r.rgb=x={})),(function(y){function k(P,R,D){let N=P>>24&255,q=P>>16&255,de=P>>8&255,fe=R>>24&255,G=R>>16&255,ue=R>>8&255,be=w(x.relativeLuminance2(fe,G,ue),x.relativeLuminance2(N,q,de));for(;be0||G>0||ue>0);)fe-=Math.max(0,Math.ceil(.1*fe)),G-=Math.max(0,Math.ceil(.1*G)),ue-=Math.max(0,Math.ceil(.1*ue)),be=w(x.relativeLuminance2(fe,G,ue),x.relativeLuminance2(N,q,de));return(fe<<24|G<<16|ue<<8|255)>>>0}function I(P,R,D){let N=P>>24&255,q=P>>16&255,de=P>>8&255,fe=R>>24&255,G=R>>16&255,ue=R>>8&255,be=w(x.relativeLuminance2(fe,G,ue),x.relativeLuminance2(N,q,de));for(;be>>0}y.blend=function(P,R){if(p=(255&R)/255,p===1)return R;let D=R>>24&255,N=R>>16&255,q=R>>8&255,de=P>>24&255,fe=P>>16&255,G=P>>8&255;return a=de+Math.round((D-de)*p),c=fe+Math.round((N-fe)*p),m=G+Math.round((q-G)*p),h.toRgba(a,c,m)},y.ensureContrastRatio=function(P,R,D){let N=x.relativeLuminance(P>>8),q=x.relativeLuminance(R>>8);if(w(N,q)>8));if(uew(N,x.relativeLuminance(be>>8))?G:be}return G}let de=I(P,R,D),fe=w(N,x.relativeLuminance(de>>8));if(few(N,x.relativeLuminance(G>>8))?de:G}return de}},y.reduceLuminance=k,y.increaseLuminance=I,y.toChannels=function(P){return[P>>24&255,P>>16&255,P>>8&255,255&P]}})(v||(r.rgba=v={})),r.toPaddedHex=M,r.contrastRatio=w},8969:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.CoreTerminal=void 0;let c=a(844),m=a(2585),p=a(4348),h=a(7866),g=a(744),S=a(7302),x=a(6975),v=a(8460),M=a(1753),w=a(1480),y=a(7994),k=a(9282),I=a(5435),P=a(5981),R=a(2660),D=!1;class N extends c.Disposable{get onScroll(){return this._onScrollApi||(this._onScrollApi=this.register(new v.EventEmitter),this._onScroll.event(de=>{this._onScrollApi?.fire(de.position)})),this._onScrollApi.event}get cols(){return this._bufferService.cols}get rows(){return this._bufferService.rows}get buffers(){return this._bufferService.buffers}get options(){return this.optionsService.options}set options(de){for(let fe in de)this.optionsService.options[fe]=de[fe]}constructor(de){super(),this._windowsWrappingHeuristics=this.register(new c.MutableDisposable),this._onBinary=this.register(new v.EventEmitter),this.onBinary=this._onBinary.event,this._onData=this.register(new v.EventEmitter),this.onData=this._onData.event,this._onLineFeed=this.register(new v.EventEmitter),this.onLineFeed=this._onLineFeed.event,this._onResize=this.register(new v.EventEmitter),this.onResize=this._onResize.event,this._onWriteParsed=this.register(new v.EventEmitter),this.onWriteParsed=this._onWriteParsed.event,this._onScroll=this.register(new v.EventEmitter),this._instantiationService=new p.InstantiationService,this.optionsService=this.register(new S.OptionsService(de)),this._instantiationService.setService(m.IOptionsService,this.optionsService),this._bufferService=this.register(this._instantiationService.createInstance(g.BufferService)),this._instantiationService.setService(m.IBufferService,this._bufferService),this._logService=this.register(this._instantiationService.createInstance(h.LogService)),this._instantiationService.setService(m.ILogService,this._logService),this.coreService=this.register(this._instantiationService.createInstance(x.CoreService)),this._instantiationService.setService(m.ICoreService,this.coreService),this.coreMouseService=this.register(this._instantiationService.createInstance(M.CoreMouseService)),this._instantiationService.setService(m.ICoreMouseService,this.coreMouseService),this.unicodeService=this.register(this._instantiationService.createInstance(w.UnicodeService)),this._instantiationService.setService(m.IUnicodeService,this.unicodeService),this._charsetService=this._instantiationService.createInstance(y.CharsetService),this._instantiationService.setService(m.ICharsetService,this._charsetService),this._oscLinkService=this._instantiationService.createInstance(R.OscLinkService),this._instantiationService.setService(m.IOscLinkService,this._oscLinkService),this._inputHandler=this.register(new I.InputHandler(this._bufferService,this._charsetService,this.coreService,this._logService,this.optionsService,this._oscLinkService,this.coreMouseService,this.unicodeService)),this.register((0,v.forwardEvent)(this._inputHandler.onLineFeed,this._onLineFeed)),this.register(this._inputHandler),this.register((0,v.forwardEvent)(this._bufferService.onResize,this._onResize)),this.register((0,v.forwardEvent)(this.coreService.onData,this._onData)),this.register((0,v.forwardEvent)(this.coreService.onBinary,this._onBinary)),this.register(this.coreService.onRequestScrollToBottom(()=>this.scrollToBottom())),this.register(this.coreService.onUserInput(()=>this._writeBuffer.handleUserInput())),this.register(this.optionsService.onMultipleOptionChange(["windowsMode","windowsPty"],()=>this._handleWindowsPtyOptionChange())),this.register(this._bufferService.onScroll(fe=>{this._onScroll.fire({position:this._bufferService.buffer.ydisp,source:0}),this._inputHandler.markRangeDirty(this._bufferService.buffer.scrollTop,this._bufferService.buffer.scrollBottom)})),this.register(this._inputHandler.onScroll(fe=>{this._onScroll.fire({position:this._bufferService.buffer.ydisp,source:0}),this._inputHandler.markRangeDirty(this._bufferService.buffer.scrollTop,this._bufferService.buffer.scrollBottom)})),this._writeBuffer=this.register(new P.WriteBuffer((fe,G)=>this._inputHandler.parse(fe,G))),this.register((0,v.forwardEvent)(this._writeBuffer.onWriteParsed,this._onWriteParsed))}write(de,fe){this._writeBuffer.write(de,fe)}writeSync(de,fe){this._logService.logLevel<=m.LogLevelEnum.WARN&&!D&&(this._logService.warn("writeSync is unreliable and will be removed soon."),D=!0),this._writeBuffer.writeSync(de,fe)}input(de,fe=!0){this.coreService.triggerDataEvent(de,fe)}resize(de,fe){isNaN(de)||isNaN(fe)||(de=Math.max(de,g.MINIMUM_COLS),fe=Math.max(fe,g.MINIMUM_ROWS),this._bufferService.resize(de,fe))}scroll(de,fe=!1){this._bufferService.scroll(de,fe)}scrollLines(de,fe,G){this._bufferService.scrollLines(de,fe,G)}scrollPages(de){this.scrollLines(de*(this.rows-1))}scrollToTop(){this.scrollLines(-this._bufferService.buffer.ydisp)}scrollToBottom(){this.scrollLines(this._bufferService.buffer.ybase-this._bufferService.buffer.ydisp)}scrollToLine(de){let fe=de-this._bufferService.buffer.ydisp;fe!==0&&this.scrollLines(fe)}registerEscHandler(de,fe){return this._inputHandler.registerEscHandler(de,fe)}registerDcsHandler(de,fe){return this._inputHandler.registerDcsHandler(de,fe)}registerCsiHandler(de,fe){return this._inputHandler.registerCsiHandler(de,fe)}registerOscHandler(de,fe){return this._inputHandler.registerOscHandler(de,fe)}_setup(){this._handleWindowsPtyOptionChange()}reset(){this._inputHandler.reset(),this._bufferService.reset(),this._charsetService.reset(),this.coreService.reset(),this.coreMouseService.reset()}_handleWindowsPtyOptionChange(){let de=!1,fe=this.optionsService.rawOptions.windowsPty;fe&&fe.buildNumber!==void 0&&fe.buildNumber!==void 0?de=fe.backend==="conpty"&&fe.buildNumber<21376:this.optionsService.rawOptions.windowsMode&&(de=!0),de?this._enableWindowsWrappingHeuristics():this._windowsWrappingHeuristics.clear()}_enableWindowsWrappingHeuristics(){if(!this._windowsWrappingHeuristics.value){let de=[];de.push(this.onLineFeed(k.updateWindowsModeWrappedState.bind(null,this._bufferService))),de.push(this.registerCsiHandler({final:"H"},()=>((0,k.updateWindowsModeWrappedState)(this._bufferService),!1))),this._windowsWrappingHeuristics.value=(0,c.toDisposable)(()=>{for(let fe of de)fe.dispose()})}}}r.CoreTerminal=N},8460:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.runAndSubscribe=r.forwardEvent=r.EventEmitter=void 0,r.EventEmitter=class{constructor(){this._listeners=[],this._disposed=!1}get event(){return this._event||(this._event=a=>(this._listeners.push(a),{dispose:()=>{if(!this._disposed){for(let c=0;cc.fire(m))},r.runAndSubscribe=function(a,c){return c(void 0),a(m=>c(m))}},5435:function(o,r,a){var c=this&&this.__decorate||function(me,V,Y,ie){var oe,Te=arguments.length,Le=Te<3?V:ie===null?ie=Object.getOwnPropertyDescriptor(V,Y):ie;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")Le=Reflect.decorate(me,V,Y,ie);else for(var Ye=me.length-1;Ye>=0;Ye--)(oe=me[Ye])&&(Le=(Te<3?oe(Le):Te>3?oe(V,Y,Le):oe(V,Y))||Le);return Te>3&&Le&&Object.defineProperty(V,Y,Le),Le},m=this&&this.__param||function(me,V){return function(Y,ie){V(Y,ie,me)}};Object.defineProperty(r,"__esModule",{value:!0}),r.InputHandler=r.WindowsOptionsReportType=void 0;let p=a(2584),h=a(7116),g=a(2015),S=a(844),x=a(482),v=a(8437),M=a(8460),w=a(643),y=a(511),k=a(3734),I=a(2585),P=a(1480),R=a(6242),D=a(6351),N=a(5941),q={"(":0,")":1,"*":2,"+":3,"-":1,".":2},de=131072;function fe(me,V){if(me>24)return V.setWinLines||!1;switch(me){case 1:return!!V.restoreWin;case 2:return!!V.minimizeWin;case 3:return!!V.setWinPosition;case 4:return!!V.setWinSizePixels;case 5:return!!V.raiseWin;case 6:return!!V.lowerWin;case 7:return!!V.refreshWin;case 8:return!!V.setWinSizeChars;case 9:return!!V.maximizeWin;case 10:return!!V.fullscreenWin;case 11:return!!V.getWinState;case 13:return!!V.getWinPosition;case 14:return!!V.getWinSizePixels;case 15:return!!V.getScreenSizePixels;case 16:return!!V.getCellSizePixels;case 18:return!!V.getWinSizeChars;case 19:return!!V.getScreenSizeChars;case 20:return!!V.getIconTitle;case 21:return!!V.getWinTitle;case 22:return!!V.pushTitle;case 23:return!!V.popTitle;case 24:return!!V.setWinLines}return!1}var G;(function(me){me[me.GET_WIN_SIZE_PIXELS=0]="GET_WIN_SIZE_PIXELS",me[me.GET_CELL_SIZE_PIXELS=1]="GET_CELL_SIZE_PIXELS"})(G||(r.WindowsOptionsReportType=G={}));let ue=0;class be extends S.Disposable{getAttrData(){return this._curAttrData}constructor(V,Y,ie,oe,Te,Le,Ye,Xe,xe=new g.EscapeSequenceParser){super(),this._bufferService=V,this._charsetService=Y,this._coreService=ie,this._logService=oe,this._optionsService=Te,this._oscLinkService=Le,this._coreMouseService=Ye,this._unicodeService=Xe,this._parser=xe,this._parseBuffer=new Uint32Array(4096),this._stringDecoder=new x.StringToUtf32,this._utf8Decoder=new x.Utf8ToUtf32,this._workCell=new y.CellData,this._windowTitle="",this._iconName="",this._windowTitleStack=[],this._iconNameStack=[],this._curAttrData=v.DEFAULT_ATTR_DATA.clone(),this._eraseAttrDataInternal=v.DEFAULT_ATTR_DATA.clone(),this._onRequestBell=this.register(new M.EventEmitter),this.onRequestBell=this._onRequestBell.event,this._onRequestRefreshRows=this.register(new M.EventEmitter),this.onRequestRefreshRows=this._onRequestRefreshRows.event,this._onRequestReset=this.register(new M.EventEmitter),this.onRequestReset=this._onRequestReset.event,this._onRequestSendFocus=this.register(new M.EventEmitter),this.onRequestSendFocus=this._onRequestSendFocus.event,this._onRequestSyncScrollBar=this.register(new M.EventEmitter),this.onRequestSyncScrollBar=this._onRequestSyncScrollBar.event,this._onRequestWindowsOptionsReport=this.register(new M.EventEmitter),this.onRequestWindowsOptionsReport=this._onRequestWindowsOptionsReport.event,this._onA11yChar=this.register(new M.EventEmitter),this.onA11yChar=this._onA11yChar.event,this._onA11yTab=this.register(new M.EventEmitter),this.onA11yTab=this._onA11yTab.event,this._onCursorMove=this.register(new M.EventEmitter),this.onCursorMove=this._onCursorMove.event,this._onLineFeed=this.register(new M.EventEmitter),this.onLineFeed=this._onLineFeed.event,this._onScroll=this.register(new M.EventEmitter),this.onScroll=this._onScroll.event,this._onTitleChange=this.register(new M.EventEmitter),this.onTitleChange=this._onTitleChange.event,this._onColor=this.register(new M.EventEmitter),this.onColor=this._onColor.event,this._parseStack={paused:!1,cursorStartX:0,cursorStartY:0,decodedLength:0,position:0},this._specialColors=[256,257,258],this.register(this._parser),this._dirtyRowTracker=new le(this._bufferService),this._activeBuffer=this._bufferService.buffer,this.register(this._bufferService.buffers.onBufferActivate(Q=>this._activeBuffer=Q.activeBuffer)),this._parser.setCsiHandlerFallback((Q,Oe)=>{this._logService.debug("Unknown CSI code: ",{identifier:this._parser.identToString(Q),params:Oe.toArray()})}),this._parser.setEscHandlerFallback(Q=>{this._logService.debug("Unknown ESC code: ",{identifier:this._parser.identToString(Q)})}),this._parser.setExecuteHandlerFallback(Q=>{this._logService.debug("Unknown EXECUTE code: ",{code:Q})}),this._parser.setOscHandlerFallback((Q,Oe,Ge)=>{this._logService.debug("Unknown OSC code: ",{identifier:Q,action:Oe,data:Ge})}),this._parser.setDcsHandlerFallback((Q,Oe,Ge)=>{Oe==="HOOK"&&(Ge=Ge.toArray()),this._logService.debug("Unknown DCS code: ",{identifier:this._parser.identToString(Q),action:Oe,payload:Ge})}),this._parser.setPrintHandler((Q,Oe,Ge)=>this.print(Q,Oe,Ge)),this._parser.registerCsiHandler({final:"@"},Q=>this.insertChars(Q)),this._parser.registerCsiHandler({intermediates:" ",final:"@"},Q=>this.scrollLeft(Q)),this._parser.registerCsiHandler({final:"A"},Q=>this.cursorUp(Q)),this._parser.registerCsiHandler({intermediates:" ",final:"A"},Q=>this.scrollRight(Q)),this._parser.registerCsiHandler({final:"B"},Q=>this.cursorDown(Q)),this._parser.registerCsiHandler({final:"C"},Q=>this.cursorForward(Q)),this._parser.registerCsiHandler({final:"D"},Q=>this.cursorBackward(Q)),this._parser.registerCsiHandler({final:"E"},Q=>this.cursorNextLine(Q)),this._parser.registerCsiHandler({final:"F"},Q=>this.cursorPrecedingLine(Q)),this._parser.registerCsiHandler({final:"G"},Q=>this.cursorCharAbsolute(Q)),this._parser.registerCsiHandler({final:"H"},Q=>this.cursorPosition(Q)),this._parser.registerCsiHandler({final:"I"},Q=>this.cursorForwardTab(Q)),this._parser.registerCsiHandler({final:"J"},Q=>this.eraseInDisplay(Q,!1)),this._parser.registerCsiHandler({prefix:"?",final:"J"},Q=>this.eraseInDisplay(Q,!0)),this._parser.registerCsiHandler({final:"K"},Q=>this.eraseInLine(Q,!1)),this._parser.registerCsiHandler({prefix:"?",final:"K"},Q=>this.eraseInLine(Q,!0)),this._parser.registerCsiHandler({final:"L"},Q=>this.insertLines(Q)),this._parser.registerCsiHandler({final:"M"},Q=>this.deleteLines(Q)),this._parser.registerCsiHandler({final:"P"},Q=>this.deleteChars(Q)),this._parser.registerCsiHandler({final:"S"},Q=>this.scrollUp(Q)),this._parser.registerCsiHandler({final:"T"},Q=>this.scrollDown(Q)),this._parser.registerCsiHandler({final:"X"},Q=>this.eraseChars(Q)),this._parser.registerCsiHandler({final:"Z"},Q=>this.cursorBackwardTab(Q)),this._parser.registerCsiHandler({final:"`"},Q=>this.charPosAbsolute(Q)),this._parser.registerCsiHandler({final:"a"},Q=>this.hPositionRelative(Q)),this._parser.registerCsiHandler({final:"b"},Q=>this.repeatPrecedingCharacter(Q)),this._parser.registerCsiHandler({final:"c"},Q=>this.sendDeviceAttributesPrimary(Q)),this._parser.registerCsiHandler({prefix:">",final:"c"},Q=>this.sendDeviceAttributesSecondary(Q)),this._parser.registerCsiHandler({final:"d"},Q=>this.linePosAbsolute(Q)),this._parser.registerCsiHandler({final:"e"},Q=>this.vPositionRelative(Q)),this._parser.registerCsiHandler({final:"f"},Q=>this.hVPosition(Q)),this._parser.registerCsiHandler({final:"g"},Q=>this.tabClear(Q)),this._parser.registerCsiHandler({final:"h"},Q=>this.setMode(Q)),this._parser.registerCsiHandler({prefix:"?",final:"h"},Q=>this.setModePrivate(Q)),this._parser.registerCsiHandler({final:"l"},Q=>this.resetMode(Q)),this._parser.registerCsiHandler({prefix:"?",final:"l"},Q=>this.resetModePrivate(Q)),this._parser.registerCsiHandler({final:"m"},Q=>this.charAttributes(Q)),this._parser.registerCsiHandler({final:"n"},Q=>this.deviceStatus(Q)),this._parser.registerCsiHandler({prefix:"?",final:"n"},Q=>this.deviceStatusPrivate(Q)),this._parser.registerCsiHandler({intermediates:"!",final:"p"},Q=>this.softReset(Q)),this._parser.registerCsiHandler({intermediates:" ",final:"q"},Q=>this.setCursorStyle(Q)),this._parser.registerCsiHandler({final:"r"},Q=>this.setScrollRegion(Q)),this._parser.registerCsiHandler({final:"s"},Q=>this.saveCursor(Q)),this._parser.registerCsiHandler({final:"t"},Q=>this.windowOptions(Q)),this._parser.registerCsiHandler({final:"u"},Q=>this.restoreCursor(Q)),this._parser.registerCsiHandler({intermediates:"'",final:"}"},Q=>this.insertColumns(Q)),this._parser.registerCsiHandler({intermediates:"'",final:"~"},Q=>this.deleteColumns(Q)),this._parser.registerCsiHandler({intermediates:'"',final:"q"},Q=>this.selectProtected(Q)),this._parser.registerCsiHandler({intermediates:"$",final:"p"},Q=>this.requestMode(Q,!0)),this._parser.registerCsiHandler({prefix:"?",intermediates:"$",final:"p"},Q=>this.requestMode(Q,!1)),this._parser.setExecuteHandler(p.C0.BEL,()=>this.bell()),this._parser.setExecuteHandler(p.C0.LF,()=>this.lineFeed()),this._parser.setExecuteHandler(p.C0.VT,()=>this.lineFeed()),this._parser.setExecuteHandler(p.C0.FF,()=>this.lineFeed()),this._parser.setExecuteHandler(p.C0.CR,()=>this.carriageReturn()),this._parser.setExecuteHandler(p.C0.BS,()=>this.backspace()),this._parser.setExecuteHandler(p.C0.HT,()=>this.tab()),this._parser.setExecuteHandler(p.C0.SO,()=>this.shiftOut()),this._parser.setExecuteHandler(p.C0.SI,()=>this.shiftIn()),this._parser.setExecuteHandler(p.C1.IND,()=>this.index()),this._parser.setExecuteHandler(p.C1.NEL,()=>this.nextLine()),this._parser.setExecuteHandler(p.C1.HTS,()=>this.tabSet()),this._parser.registerOscHandler(0,new R.OscHandler(Q=>(this.setTitle(Q),this.setIconName(Q),!0))),this._parser.registerOscHandler(1,new R.OscHandler(Q=>this.setIconName(Q))),this._parser.registerOscHandler(2,new R.OscHandler(Q=>this.setTitle(Q))),this._parser.registerOscHandler(4,new R.OscHandler(Q=>this.setOrReportIndexedColor(Q))),this._parser.registerOscHandler(8,new R.OscHandler(Q=>this.setHyperlink(Q))),this._parser.registerOscHandler(10,new R.OscHandler(Q=>this.setOrReportFgColor(Q))),this._parser.registerOscHandler(11,new R.OscHandler(Q=>this.setOrReportBgColor(Q))),this._parser.registerOscHandler(12,new R.OscHandler(Q=>this.setOrReportCursorColor(Q))),this._parser.registerOscHandler(104,new R.OscHandler(Q=>this.restoreIndexedColor(Q))),this._parser.registerOscHandler(110,new R.OscHandler(Q=>this.restoreFgColor(Q))),this._parser.registerOscHandler(111,new R.OscHandler(Q=>this.restoreBgColor(Q))),this._parser.registerOscHandler(112,new R.OscHandler(Q=>this.restoreCursorColor(Q))),this._parser.registerEscHandler({final:"7"},()=>this.saveCursor()),this._parser.registerEscHandler({final:"8"},()=>this.restoreCursor()),this._parser.registerEscHandler({final:"D"},()=>this.index()),this._parser.registerEscHandler({final:"E"},()=>this.nextLine()),this._parser.registerEscHandler({final:"H"},()=>this.tabSet()),this._parser.registerEscHandler({final:"M"},()=>this.reverseIndex()),this._parser.registerEscHandler({final:"="},()=>this.keypadApplicationMode()),this._parser.registerEscHandler({final:">"},()=>this.keypadNumericMode()),this._parser.registerEscHandler({final:"c"},()=>this.fullReset()),this._parser.registerEscHandler({final:"n"},()=>this.setgLevel(2)),this._parser.registerEscHandler({final:"o"},()=>this.setgLevel(3)),this._parser.registerEscHandler({final:"|"},()=>this.setgLevel(3)),this._parser.registerEscHandler({final:"}"},()=>this.setgLevel(2)),this._parser.registerEscHandler({final:"~"},()=>this.setgLevel(1)),this._parser.registerEscHandler({intermediates:"%",final:"@"},()=>this.selectDefaultCharset()),this._parser.registerEscHandler({intermediates:"%",final:"G"},()=>this.selectDefaultCharset());for(let Q in h.CHARSETS)this._parser.registerEscHandler({intermediates:"(",final:Q},()=>this.selectCharset("("+Q)),this._parser.registerEscHandler({intermediates:")",final:Q},()=>this.selectCharset(")"+Q)),this._parser.registerEscHandler({intermediates:"*",final:Q},()=>this.selectCharset("*"+Q)),this._parser.registerEscHandler({intermediates:"+",final:Q},()=>this.selectCharset("+"+Q)),this._parser.registerEscHandler({intermediates:"-",final:Q},()=>this.selectCharset("-"+Q)),this._parser.registerEscHandler({intermediates:".",final:Q},()=>this.selectCharset("."+Q)),this._parser.registerEscHandler({intermediates:"/",final:Q},()=>this.selectCharset("/"+Q));this._parser.registerEscHandler({intermediates:"#",final:"8"},()=>this.screenAlignmentPattern()),this._parser.setErrorHandler(Q=>(this._logService.error("Parsing error: ",Q),Q)),this._parser.registerDcsHandler({intermediates:"$",final:"q"},new D.DcsHandler((Q,Oe)=>this.requestStatusString(Q,Oe)))}_preserveStack(V,Y,ie,oe){this._parseStack.paused=!0,this._parseStack.cursorStartX=V,this._parseStack.cursorStartY=Y,this._parseStack.decodedLength=ie,this._parseStack.position=oe}_logSlowResolvingAsync(V){this._logService.logLevel<=I.LogLevelEnum.WARN&&Promise.race([V,new Promise((Y,ie)=>setTimeout(()=>ie("#SLOW_TIMEOUT"),5e3))]).catch(Y=>{if(Y!=="#SLOW_TIMEOUT")throw Y;console.warn("async parser handler taking longer than 5000 ms")})}_getCurrentLinkId(){return this._curAttrData.extended.urlId}parse(V,Y){let ie,oe=this._activeBuffer.x,Te=this._activeBuffer.y,Le=0,Ye=this._parseStack.paused;if(Ye){if(ie=this._parser.parse(this._parseBuffer,this._parseStack.decodedLength,Y))return this._logSlowResolvingAsync(ie),ie;oe=this._parseStack.cursorStartX,Te=this._parseStack.cursorStartY,this._parseStack.paused=!1,V.length>de&&(Le=this._parseStack.position+de)}if(this._logService.logLevel<=I.LogLevelEnum.DEBUG&&this._logService.debug("parsing data"+(typeof V=="string"?` "${V}"`:` "${Array.prototype.map.call(V,Q=>String.fromCharCode(Q)).join("")}"`),typeof V=="string"?V.split("").map(Q=>Q.charCodeAt(0)):V),this._parseBuffer.lengthde)for(let Q=Le;Q0&&Ge.getWidth(this._activeBuffer.x-1)===2&&Ge.setCellFromCodepoint(this._activeBuffer.x-1,0,1,Oe);let ct=this._parser.precedingJoinState;for(let kt=Y;ktXe){if(xe){let jr=Ge,kn=this._activeBuffer.x-xo;for(this._activeBuffer.x=xo,this._activeBuffer.y++,this._activeBuffer.y===this._activeBuffer.scrollBottom+1?(this._activeBuffer.y--,this._bufferService.scroll(this._eraseAttrData(),!0)):(this._activeBuffer.y>=this._bufferService.rows&&(this._activeBuffer.y=this._bufferService.rows-1),this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y).isWrapped=!0),Ge=this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y),xo>0&&Ge instanceof v.BufferLine&&Ge.copyCellsFrom(jr,kn,0,xo,!1);kn=0;)Ge.setCellFromCodepoint(this._activeBuffer.x++,0,0,Oe)}else if(Q&&(Ge.insertCells(this._activeBuffer.x,Te-xo,this._activeBuffer.getNullCell(Oe)),Ge.getWidth(Xe-1)===2&&Ge.setCellFromCodepoint(Xe-1,w.NULL_CELL_CODE,w.NULL_CELL_WIDTH,Oe)),Ge.setCellFromCodepoint(this._activeBuffer.x++,oe,Te,Oe),Te>0)for(;--Te;)Ge.setCellFromCodepoint(this._activeBuffer.x++,0,0,Oe)}this._parser.precedingJoinState=ct,this._activeBuffer.x0&&Ge.getWidth(this._activeBuffer.x)===0&&!Ge.hasContent(this._activeBuffer.x)&&Ge.setCellFromCodepoint(this._activeBuffer.x,0,1,Oe),this._dirtyRowTracker.markDirty(this._activeBuffer.y)}registerCsiHandler(V,Y){return V.final!=="t"||V.prefix||V.intermediates?this._parser.registerCsiHandler(V,Y):this._parser.registerCsiHandler(V,ie=>!fe(ie.params[0],this._optionsService.rawOptions.windowOptions)||Y(ie))}registerDcsHandler(V,Y){return this._parser.registerDcsHandler(V,new D.DcsHandler(Y))}registerEscHandler(V,Y){return this._parser.registerEscHandler(V,Y)}registerOscHandler(V,Y){return this._parser.registerOscHandler(V,new R.OscHandler(Y))}bell(){return this._onRequestBell.fire(),!0}lineFeed(){return this._dirtyRowTracker.markDirty(this._activeBuffer.y),this._optionsService.rawOptions.convertEol&&(this._activeBuffer.x=0),this._activeBuffer.y++,this._activeBuffer.y===this._activeBuffer.scrollBottom+1?(this._activeBuffer.y--,this._bufferService.scroll(this._eraseAttrData())):this._activeBuffer.y>=this._bufferService.rows?this._activeBuffer.y=this._bufferService.rows-1:this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y).isWrapped=!1,this._activeBuffer.x>=this._bufferService.cols&&this._activeBuffer.x--,this._dirtyRowTracker.markDirty(this._activeBuffer.y),this._onLineFeed.fire(),!0}carriageReturn(){return this._activeBuffer.x=0,!0}backspace(){if(!this._coreService.decPrivateModes.reverseWraparound)return this._restrictCursor(),this._activeBuffer.x>0&&this._activeBuffer.x--,!0;if(this._restrictCursor(this._bufferService.cols),this._activeBuffer.x>0)this._activeBuffer.x--;else if(this._activeBuffer.x===0&&this._activeBuffer.y>this._activeBuffer.scrollTop&&this._activeBuffer.y<=this._activeBuffer.scrollBottom&&this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y)?.isWrapped){this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y).isWrapped=!1,this._activeBuffer.y--,this._activeBuffer.x=this._bufferService.cols-1;let V=this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y);V.hasWidth(this._activeBuffer.x)&&!V.hasContent(this._activeBuffer.x)&&this._activeBuffer.x--}return this._restrictCursor(),!0}tab(){if(this._activeBuffer.x>=this._bufferService.cols)return!0;let V=this._activeBuffer.x;return this._activeBuffer.x=this._activeBuffer.nextStop(),this._optionsService.rawOptions.screenReaderMode&&this._onA11yTab.fire(this._activeBuffer.x-V),!0}shiftOut(){return this._charsetService.setgLevel(1),!0}shiftIn(){return this._charsetService.setgLevel(0),!0}_restrictCursor(V=this._bufferService.cols-1){this._activeBuffer.x=Math.min(V,Math.max(0,this._activeBuffer.x)),this._activeBuffer.y=this._coreService.decPrivateModes.origin?Math.min(this._activeBuffer.scrollBottom,Math.max(this._activeBuffer.scrollTop,this._activeBuffer.y)):Math.min(this._bufferService.rows-1,Math.max(0,this._activeBuffer.y)),this._dirtyRowTracker.markDirty(this._activeBuffer.y)}_setCursor(V,Y){this._dirtyRowTracker.markDirty(this._activeBuffer.y),this._coreService.decPrivateModes.origin?(this._activeBuffer.x=V,this._activeBuffer.y=this._activeBuffer.scrollTop+Y):(this._activeBuffer.x=V,this._activeBuffer.y=Y),this._restrictCursor(),this._dirtyRowTracker.markDirty(this._activeBuffer.y)}_moveCursor(V,Y){this._restrictCursor(),this._setCursor(this._activeBuffer.x+V,this._activeBuffer.y+Y)}cursorUp(V){let Y=this._activeBuffer.y-this._activeBuffer.scrollTop;return Y>=0?this._moveCursor(0,-Math.min(Y,V.params[0]||1)):this._moveCursor(0,-(V.params[0]||1)),!0}cursorDown(V){let Y=this._activeBuffer.scrollBottom-this._activeBuffer.y;return Y>=0?this._moveCursor(0,Math.min(Y,V.params[0]||1)):this._moveCursor(0,V.params[0]||1),!0}cursorForward(V){return this._moveCursor(V.params[0]||1,0),!0}cursorBackward(V){return this._moveCursor(-(V.params[0]||1),0),!0}cursorNextLine(V){return this.cursorDown(V),this._activeBuffer.x=0,!0}cursorPrecedingLine(V){return this.cursorUp(V),this._activeBuffer.x=0,!0}cursorCharAbsolute(V){return this._setCursor((V.params[0]||1)-1,this._activeBuffer.y),!0}cursorPosition(V){return this._setCursor(V.length>=2?(V.params[1]||1)-1:0,(V.params[0]||1)-1),!0}charPosAbsolute(V){return this._setCursor((V.params[0]||1)-1,this._activeBuffer.y),!0}hPositionRelative(V){return this._moveCursor(V.params[0]||1,0),!0}linePosAbsolute(V){return this._setCursor(this._activeBuffer.x,(V.params[0]||1)-1),!0}vPositionRelative(V){return this._moveCursor(0,V.params[0]||1),!0}hVPosition(V){return this.cursorPosition(V),!0}tabClear(V){let Y=V.params[0];return Y===0?delete this._activeBuffer.tabs[this._activeBuffer.x]:Y===3&&(this._activeBuffer.tabs={}),!0}cursorForwardTab(V){if(this._activeBuffer.x>=this._bufferService.cols)return!0;let Y=V.params[0]||1;for(;Y--;)this._activeBuffer.x=this._activeBuffer.nextStop();return!0}cursorBackwardTab(V){if(this._activeBuffer.x>=this._bufferService.cols)return!0;let Y=V.params[0]||1;for(;Y--;)this._activeBuffer.x=this._activeBuffer.prevStop();return!0}selectProtected(V){let Y=V.params[0];return Y===1&&(this._curAttrData.bg|=536870912),Y!==2&&Y!==0||(this._curAttrData.bg&=-536870913),!0}_eraseInBufferLine(V,Y,ie,oe=!1,Te=!1){let Le=this._activeBuffer.lines.get(this._activeBuffer.ybase+V);Le.replaceCells(Y,ie,this._activeBuffer.getNullCell(this._eraseAttrData()),Te),oe&&(Le.isWrapped=!1)}_resetBufferLine(V,Y=!1){let ie=this._activeBuffer.lines.get(this._activeBuffer.ybase+V);ie&&(ie.fill(this._activeBuffer.getNullCell(this._eraseAttrData()),Y),this._bufferService.buffer.clearMarkers(this._activeBuffer.ybase+V),ie.isWrapped=!1)}eraseInDisplay(V,Y=!1){let ie;switch(this._restrictCursor(this._bufferService.cols),V.params[0]){case 0:for(ie=this._activeBuffer.y,this._dirtyRowTracker.markDirty(ie),this._eraseInBufferLine(ie++,this._activeBuffer.x,this._bufferService.cols,this._activeBuffer.x===0,Y);ie=this._bufferService.cols&&(this._activeBuffer.lines.get(ie+1).isWrapped=!1);ie--;)this._resetBufferLine(ie,Y);this._dirtyRowTracker.markDirty(0);break;case 2:for(ie=this._bufferService.rows,this._dirtyRowTracker.markDirty(ie-1);ie--;)this._resetBufferLine(ie,Y);this._dirtyRowTracker.markDirty(0);break;case 3:let oe=this._activeBuffer.lines.length-this._bufferService.rows;oe>0&&(this._activeBuffer.lines.trimStart(oe),this._activeBuffer.ybase=Math.max(this._activeBuffer.ybase-oe,0),this._activeBuffer.ydisp=Math.max(this._activeBuffer.ydisp-oe,0),this._onScroll.fire(0))}return!0}eraseInLine(V,Y=!1){switch(this._restrictCursor(this._bufferService.cols),V.params[0]){case 0:this._eraseInBufferLine(this._activeBuffer.y,this._activeBuffer.x,this._bufferService.cols,this._activeBuffer.x===0,Y);break;case 1:this._eraseInBufferLine(this._activeBuffer.y,0,this._activeBuffer.x+1,!1,Y);break;case 2:this._eraseInBufferLine(this._activeBuffer.y,0,this._bufferService.cols,!0,Y)}return this._dirtyRowTracker.markDirty(this._activeBuffer.y),!0}insertLines(V){this._restrictCursor();let Y=V.params[0]||1;if(this._activeBuffer.y>this._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.y65535?2:1}let xe=Xe;for(let Q=1;Q0||(this._is("xterm")||this._is("rxvt-unicode")||this._is("screen")?this._coreService.triggerDataEvent(p.C0.ESC+"[?1;2c"):this._is("linux")&&this._coreService.triggerDataEvent(p.C0.ESC+"[?6c")),!0}sendDeviceAttributesSecondary(V){return V.params[0]>0||(this._is("xterm")?this._coreService.triggerDataEvent(p.C0.ESC+"[>0;276;0c"):this._is("rxvt-unicode")?this._coreService.triggerDataEvent(p.C0.ESC+"[>85;95;0c"):this._is("linux")?this._coreService.triggerDataEvent(V.params[0]+"c"):this._is("screen")&&this._coreService.triggerDataEvent(p.C0.ESC+"[>83;40003;0c")),!0}_is(V){return(this._optionsService.rawOptions.termName+"").indexOf(V)===0}setMode(V){for(let Y=0;YFo?1:2,ct=V.params[0];return kt=ct,Xn=Y?ct===2?4:ct===4?Ge(Le.modes.insertMode):ct===12?3:ct===20?Ge(Oe.convertEol):0:ct===1?Ge(ie.applicationCursorKeys):ct===3?Oe.windowOptions.setWinLines?Xe===80?2:Xe===132?1:0:0:ct===6?Ge(ie.origin):ct===7?Ge(ie.wraparound):ct===8?3:ct===9?Ge(oe==="X10"):ct===12?Ge(Oe.cursorBlink):ct===25?Ge(!Le.isCursorHidden):ct===45?Ge(ie.reverseWraparound):ct===66?Ge(ie.applicationKeypad):ct===67?4:ct===1e3?Ge(oe==="VT200"):ct===1002?Ge(oe==="DRAG"):ct===1003?Ge(oe==="ANY"):ct===1004?Ge(ie.sendFocus):ct===1005?4:ct===1006?Ge(Te==="SGR"):ct===1015?4:ct===1016?Ge(Te==="SGR_PIXELS"):ct===1048?1:ct===47||ct===1047||ct===1049?Ge(xe===Q):ct===2004?Ge(ie.bracketedPasteMode):0,Le.triggerDataEvent(`${p.C0.ESC}[${Y?"":"?"}${kt};${Xn}$y`),!0;var kt,Xn}_updateAttrColor(V,Y,ie,oe,Te){return Y===2?(V|=50331648,V&=-16777216,V|=k.AttributeData.fromColorRGB([ie,oe,Te])):Y===5&&(V&=-50331904,V|=33554432|255&ie),V}_extractColor(V,Y,ie){let oe=[0,0,-1,0,0,0],Te=0,Le=0;do{if(oe[Le+Te]=V.params[Y+Le],V.hasSubParams(Y+Le)){let Ye=V.getSubParams(Y+Le),Xe=0;do oe[1]===5&&(Te=1),oe[Le+Xe+1+Te]=Ye[Xe];while(++Xe=2||oe[1]===2&&Le+Te>=5)break;oe[1]&&(Te=1)}while(++Le+Y5)&&(V=1),Y.extended.underlineStyle=V,Y.fg|=268435456,V===0&&(Y.fg&=-268435457),Y.updateExtended()}_processSGR0(V){V.fg=v.DEFAULT_ATTR_DATA.fg,V.bg=v.DEFAULT_ATTR_DATA.bg,V.extended=V.extended.clone(),V.extended.underlineStyle=0,V.extended.underlineColor&=-67108864,V.updateExtended()}charAttributes(V){if(V.length===1&&V.params[0]===0)return this._processSGR0(this._curAttrData),!0;let Y=V.length,ie,oe=this._curAttrData;for(let Te=0;Te=30&&ie<=37?(oe.fg&=-50331904,oe.fg|=16777216|ie-30):ie>=40&&ie<=47?(oe.bg&=-50331904,oe.bg|=16777216|ie-40):ie>=90&&ie<=97?(oe.fg&=-50331904,oe.fg|=16777224|ie-90):ie>=100&&ie<=107?(oe.bg&=-50331904,oe.bg|=16777224|ie-100):ie===0?this._processSGR0(oe):ie===1?oe.fg|=134217728:ie===3?oe.bg|=67108864:ie===4?(oe.fg|=268435456,this._processUnderline(V.hasSubParams(Te)?V.getSubParams(Te)[0]:1,oe)):ie===5?oe.fg|=536870912:ie===7?oe.fg|=67108864:ie===8?oe.fg|=1073741824:ie===9?oe.fg|=2147483648:ie===2?oe.bg|=134217728:ie===21?this._processUnderline(2,oe):ie===22?(oe.fg&=-134217729,oe.bg&=-134217729):ie===23?oe.bg&=-67108865:ie===24?(oe.fg&=-268435457,this._processUnderline(0,oe)):ie===25?oe.fg&=-536870913:ie===27?oe.fg&=-67108865:ie===28?oe.fg&=-1073741825:ie===29?oe.fg&=2147483647:ie===39?(oe.fg&=-67108864,oe.fg|=16777215&v.DEFAULT_ATTR_DATA.fg):ie===49?(oe.bg&=-67108864,oe.bg|=16777215&v.DEFAULT_ATTR_DATA.bg):ie===38||ie===48||ie===58?Te+=this._extractColor(V,Te,oe):ie===53?oe.bg|=1073741824:ie===55?oe.bg&=-1073741825:ie===59?(oe.extended=oe.extended.clone(),oe.extended.underlineColor=-1,oe.updateExtended()):ie===100?(oe.fg&=-67108864,oe.fg|=16777215&v.DEFAULT_ATTR_DATA.fg,oe.bg&=-67108864,oe.bg|=16777215&v.DEFAULT_ATTR_DATA.bg):this._logService.debug("Unknown SGR attribute: %d.",ie);return!0}deviceStatus(V){switch(V.params[0]){case 5:this._coreService.triggerDataEvent(`${p.C0.ESC}[0n`);break;case 6:let Y=this._activeBuffer.y+1,ie=this._activeBuffer.x+1;this._coreService.triggerDataEvent(`${p.C0.ESC}[${Y};${ie}R`)}return!0}deviceStatusPrivate(V){if(V.params[0]===6){let Y=this._activeBuffer.y+1,ie=this._activeBuffer.x+1;this._coreService.triggerDataEvent(`${p.C0.ESC}[?${Y};${ie}R`)}return!0}softReset(V){return this._coreService.isCursorHidden=!1,this._onRequestSyncScrollBar.fire(),this._activeBuffer.scrollTop=0,this._activeBuffer.scrollBottom=this._bufferService.rows-1,this._curAttrData=v.DEFAULT_ATTR_DATA.clone(),this._coreService.reset(),this._charsetService.reset(),this._activeBuffer.savedX=0,this._activeBuffer.savedY=this._activeBuffer.ybase,this._activeBuffer.savedCurAttrData.fg=this._curAttrData.fg,this._activeBuffer.savedCurAttrData.bg=this._curAttrData.bg,this._activeBuffer.savedCharset=this._charsetService.charset,this._coreService.decPrivateModes.origin=!1,!0}setCursorStyle(V){let Y=V.params[0]||1;switch(Y){case 1:case 2:this._optionsService.options.cursorStyle="block";break;case 3:case 4:this._optionsService.options.cursorStyle="underline";break;case 5:case 6:this._optionsService.options.cursorStyle="bar"}let ie=Y%2==1;return this._optionsService.options.cursorBlink=ie,!0}setScrollRegion(V){let Y=V.params[0]||1,ie;return(V.length<2||(ie=V.params[1])>this._bufferService.rows||ie===0)&&(ie=this._bufferService.rows),ie>Y&&(this._activeBuffer.scrollTop=Y-1,this._activeBuffer.scrollBottom=ie-1,this._setCursor(0,0)),!0}windowOptions(V){if(!fe(V.params[0],this._optionsService.rawOptions.windowOptions))return!0;let Y=V.length>1?V.params[1]:0;switch(V.params[0]){case 14:Y!==2&&this._onRequestWindowsOptionsReport.fire(G.GET_WIN_SIZE_PIXELS);break;case 16:this._onRequestWindowsOptionsReport.fire(G.GET_CELL_SIZE_PIXELS);break;case 18:this._bufferService&&this._coreService.triggerDataEvent(`${p.C0.ESC}[8;${this._bufferService.rows};${this._bufferService.cols}t`);break;case 22:Y!==0&&Y!==2||(this._windowTitleStack.push(this._windowTitle),this._windowTitleStack.length>10&&this._windowTitleStack.shift()),Y!==0&&Y!==1||(this._iconNameStack.push(this._iconName),this._iconNameStack.length>10&&this._iconNameStack.shift());break;case 23:Y!==0&&Y!==2||this._windowTitleStack.length&&this.setTitle(this._windowTitleStack.pop()),Y!==0&&Y!==1||this._iconNameStack.length&&this.setIconName(this._iconNameStack.pop())}return!0}saveCursor(V){return this._activeBuffer.savedX=this._activeBuffer.x,this._activeBuffer.savedY=this._activeBuffer.ybase+this._activeBuffer.y,this._activeBuffer.savedCurAttrData.fg=this._curAttrData.fg,this._activeBuffer.savedCurAttrData.bg=this._curAttrData.bg,this._activeBuffer.savedCharset=this._charsetService.charset,!0}restoreCursor(V){return this._activeBuffer.x=this._activeBuffer.savedX||0,this._activeBuffer.y=Math.max(this._activeBuffer.savedY-this._activeBuffer.ybase,0),this._curAttrData.fg=this._activeBuffer.savedCurAttrData.fg,this._curAttrData.bg=this._activeBuffer.savedCurAttrData.bg,this._charsetService.charset=this._savedCharset,this._activeBuffer.savedCharset&&(this._charsetService.charset=this._activeBuffer.savedCharset),this._restrictCursor(),!0}setTitle(V){return this._windowTitle=V,this._onTitleChange.fire(V),!0}setIconName(V){return this._iconName=V,!0}setOrReportIndexedColor(V){let Y=[],ie=V.split(";");for(;ie.length>1;){let oe=ie.shift(),Te=ie.shift();if(/^\d+$/.exec(oe)){let Le=parseInt(oe);if(De(Le))if(Te==="?")Y.push({type:0,index:Le});else{let Ye=(0,N.parseColor)(Te);Ye&&Y.push({type:1,index:Le,color:Ye})}}}return Y.length&&this._onColor.fire(Y),!0}setHyperlink(V){let Y=V.split(";");return!(Y.length<2)&&(Y[1]?this._createHyperlink(Y[0],Y[1]):!Y[0]&&this._finishHyperlink())}_createHyperlink(V,Y){this._getCurrentLinkId()&&this._finishHyperlink();let ie=V.split(":"),oe,Te=ie.findIndex(Le=>Le.startsWith("id="));return Te!==-1&&(oe=ie[Te].slice(3)||void 0),this._curAttrData.extended=this._curAttrData.extended.clone(),this._curAttrData.extended.urlId=this._oscLinkService.registerLink({id:oe,uri:Y}),this._curAttrData.updateExtended(),!0}_finishHyperlink(){return this._curAttrData.extended=this._curAttrData.extended.clone(),this._curAttrData.extended.urlId=0,this._curAttrData.updateExtended(),!0}_setOrReportSpecialColor(V,Y){let ie=V.split(";");for(let oe=0;oe=this._specialColors.length);++oe,++Y)if(ie[oe]==="?")this._onColor.fire([{type:0,index:this._specialColors[Y]}]);else{let Te=(0,N.parseColor)(ie[oe]);Te&&this._onColor.fire([{type:1,index:this._specialColors[Y],color:Te}])}return!0}setOrReportFgColor(V){return this._setOrReportSpecialColor(V,0)}setOrReportBgColor(V){return this._setOrReportSpecialColor(V,1)}setOrReportCursorColor(V){return this._setOrReportSpecialColor(V,2)}restoreIndexedColor(V){if(!V)return this._onColor.fire([{type:2}]),!0;let Y=[],ie=V.split(";");for(let oe=0;oe=this._bufferService.rows&&(this._activeBuffer.y=this._bufferService.rows-1),this._restrictCursor(),!0}tabSet(){return this._activeBuffer.tabs[this._activeBuffer.x]=!0,!0}reverseIndex(){if(this._restrictCursor(),this._activeBuffer.y===this._activeBuffer.scrollTop){let V=this._activeBuffer.scrollBottom-this._activeBuffer.scrollTop;this._activeBuffer.lines.shiftElements(this._activeBuffer.ybase+this._activeBuffer.y,V,1),this._activeBuffer.lines.set(this._activeBuffer.ybase+this._activeBuffer.y,this._activeBuffer.getBlankLine(this._eraseAttrData())),this._dirtyRowTracker.markRangeDirty(this._activeBuffer.scrollTop,this._activeBuffer.scrollBottom)}else this._activeBuffer.y--,this._restrictCursor();return!0}fullReset(){return this._parser.reset(),this._onRequestReset.fire(),!0}reset(){this._curAttrData=v.DEFAULT_ATTR_DATA.clone(),this._eraseAttrDataInternal=v.DEFAULT_ATTR_DATA.clone()}_eraseAttrData(){return this._eraseAttrDataInternal.bg&=-67108864,this._eraseAttrDataInternal.bg|=67108863&this._curAttrData.bg,this._eraseAttrDataInternal}setgLevel(V){return this._charsetService.setgLevel(V),!0}screenAlignmentPattern(){let V=new y.CellData;V.content=4194373,V.fg=this._curAttrData.fg,V.bg=this._curAttrData.bg,this._setCursor(0,0);for(let Y=0;Y(this._coreService.triggerDataEvent(`${p.C0.ESC}${Te}${p.C0.ESC}\\`),!0))(V==='"q'?`P1$r${this._curAttrData.isProtected()?1:0}"q`:V==='"p'?'P1$r61;1"p':V==="r"?`P1$r${ie.scrollTop+1};${ie.scrollBottom+1}r`:V==="m"?"P1$r0m":V===" q"?`P1$r${{block:2,underline:4,bar:6}[oe.cursorStyle]-(oe.cursorBlink?1:0)} q`:"P0$r")}markRangeDirty(V,Y){this._dirtyRowTracker.markRangeDirty(V,Y)}}r.InputHandler=be;let le=class{constructor(me){this._bufferService=me,this.clearRange()}clearRange(){this.start=this._bufferService.buffer.y,this.end=this._bufferService.buffer.y}markDirty(me){methis.end&&(this.end=me)}markRangeDirty(me,V){me>V&&(ue=me,me=V,V=ue),methis.end&&(this.end=V)}markAllDirty(){this.markRangeDirty(0,this._bufferService.rows-1)}};function De(me){return 0<=me&&me<256}le=c([m(0,I.IBufferService)],le)},844:(o,r)=>{function a(c){for(let m of c)m.dispose();c.length=0}Object.defineProperty(r,"__esModule",{value:!0}),r.getDisposeArrayDisposable=r.disposeArray=r.toDisposable=r.MutableDisposable=r.Disposable=void 0,r.Disposable=class{constructor(){this._disposables=[],this._isDisposed=!1}dispose(){this._isDisposed=!0;for(let c of this._disposables)c.dispose();this._disposables.length=0}register(c){return this._disposables.push(c),c}unregister(c){let m=this._disposables.indexOf(c);m!==-1&&this._disposables.splice(m,1)}},r.MutableDisposable=class{constructor(){this._isDisposed=!1}get value(){return this._isDisposed?void 0:this._value}set value(c){this._isDisposed||c===this._value||(this._value?.dispose(),this._value=c)}clear(){this.value=void 0}dispose(){this._isDisposed=!0,this._value?.dispose(),this._value=void 0}},r.toDisposable=function(c){return{dispose:c}},r.disposeArray=a,r.getDisposeArrayDisposable=function(c){return{dispose:()=>a(c)}}},1505:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.FourKeyMap=r.TwoKeyMap=void 0;class a{constructor(){this._data={}}set(m,p,h){this._data[m]||(this._data[m]={}),this._data[m][p]=h}get(m,p){return this._data[m]?this._data[m][p]:void 0}clear(){this._data={}}}r.TwoKeyMap=a,r.FourKeyMap=class{constructor(){this._data=new a}set(c,m,p,h,g){this._data.get(c,m)||this._data.set(c,m,new a),this._data.get(c,m).set(p,h,g)}get(c,m,p,h){return this._data.get(c,m)?.get(p,h)}clear(){this._data.clear()}}},6114:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.isChromeOS=r.isLinux=r.isWindows=r.isIphone=r.isIpad=r.isMac=r.getSafariVersion=r.isSafari=r.isLegacyEdge=r.isFirefox=r.isNode=void 0,r.isNode=typeof process<"u"&&"title"in process;let a=r.isNode?"node":navigator.userAgent,c=r.isNode?"node":navigator.platform;r.isFirefox=a.includes("Firefox"),r.isLegacyEdge=a.includes("Edge"),r.isSafari=/^((?!chrome|android).)*safari/i.test(a),r.getSafariVersion=function(){if(!r.isSafari)return 0;let m=a.match(/Version\/(\d+)/);return m===null||m.length<2?0:parseInt(m[1])},r.isMac=["Macintosh","MacIntel","MacPPC","Mac68K"].includes(c),r.isIpad=c==="iPad",r.isIphone=c==="iPhone",r.isWindows=["Windows","Win16","Win32","WinCE"].includes(c),r.isLinux=c.indexOf("Linux")>=0,r.isChromeOS=/\bCrOS\b/.test(a)},6106:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.SortedList=void 0;let a=0;r.SortedList=class{constructor(c){this._getKey=c,this._array=[]}clear(){this._array.length=0}insert(c){this._array.length!==0?(a=this._search(this._getKey(c)),this._array.splice(a,0,c)):this._array.push(c)}delete(c){if(this._array.length===0)return!1;let m=this._getKey(c);if(m===void 0||(a=this._search(m),a===-1)||this._getKey(this._array[a])!==m)return!1;do if(this._array[a]===c)return this._array.splice(a,1),!0;while(++a=this._array.length)&&this._getKey(this._array[a])===c))do yield this._array[a];while(++a=this._array.length)&&this._getKey(this._array[a])===c))do m(this._array[a]);while(++a=m;){let h=m+p>>1,g=this._getKey(this._array[h]);if(g>c)p=h-1;else{if(!(g0&&this._getKey(this._array[h-1])===c;)h--;return h}m=h+1}}return m}}},7226:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.DebouncedIdleTask=r.IdleTaskQueue=r.PriorityTaskQueue=void 0;let c=a(6114);class m{constructor(){this._tasks=[],this._i=0}enqueue(g){this._tasks.push(g),this._start()}flush(){for(;this._iM)return v-S<-20&&console.warn(`task queue exceeded allotted deadline by ${Math.abs(Math.round(v-S))}ms`),void this._start();v=M}this.clear()}}class p extends m{_requestCallback(g){return setTimeout(()=>g(this._createDeadline(16)))}_cancelCallback(g){clearTimeout(g)}_createDeadline(g){let S=Date.now()+g;return{timeRemaining:()=>Math.max(0,S-Date.now())}}}r.PriorityTaskQueue=p,r.IdleTaskQueue=!c.isNode&&"requestIdleCallback"in window?class extends m{_requestCallback(h){return requestIdleCallback(h)}_cancelCallback(h){cancelIdleCallback(h)}}:p,r.DebouncedIdleTask=class{constructor(){this._queue=new r.IdleTaskQueue}set(h){this._queue.clear(),this._queue.enqueue(h)}flush(){this._queue.flush()}}},9282:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.updateWindowsModeWrappedState=void 0;let c=a(643);r.updateWindowsModeWrappedState=function(m){let p=m.buffer.lines.get(m.buffer.ybase+m.buffer.y-1),h=p?.get(m.cols-1),g=m.buffer.lines.get(m.buffer.ybase+m.buffer.y);g&&h&&(g.isWrapped=h[c.CHAR_DATA_CODE_INDEX]!==c.NULL_CELL_CODE&&h[c.CHAR_DATA_CODE_INDEX]!==c.WHITESPACE_CELL_CODE)}},3734:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.ExtendedAttrs=r.AttributeData=void 0;class a{constructor(){this.fg=0,this.bg=0,this.extended=new c}static toColorRGB(p){return[p>>>16&255,p>>>8&255,255&p]}static fromColorRGB(p){return(255&p[0])<<16|(255&p[1])<<8|255&p[2]}clone(){let p=new a;return p.fg=this.fg,p.bg=this.bg,p.extended=this.extended.clone(),p}isInverse(){return 67108864&this.fg}isBold(){return 134217728&this.fg}isUnderline(){return this.hasExtendedAttrs()&&this.extended.underlineStyle!==0?1:268435456&this.fg}isBlink(){return 536870912&this.fg}isInvisible(){return 1073741824&this.fg}isItalic(){return 67108864&this.bg}isDim(){return 134217728&this.bg}isStrikethrough(){return 2147483648&this.fg}isProtected(){return 536870912&this.bg}isOverline(){return 1073741824&this.bg}getFgColorMode(){return 50331648&this.fg}getBgColorMode(){return 50331648&this.bg}isFgRGB(){return(50331648&this.fg)==50331648}isBgRGB(){return(50331648&this.bg)==50331648}isFgPalette(){return(50331648&this.fg)==16777216||(50331648&this.fg)==33554432}isBgPalette(){return(50331648&this.bg)==16777216||(50331648&this.bg)==33554432}isFgDefault(){return(50331648&this.fg)==0}isBgDefault(){return(50331648&this.bg)==0}isAttributeDefault(){return this.fg===0&&this.bg===0}getFgColor(){switch(50331648&this.fg){case 16777216:case 33554432:return 255&this.fg;case 50331648:return 16777215&this.fg;default:return-1}}getBgColor(){switch(50331648&this.bg){case 16777216:case 33554432:return 255&this.bg;case 50331648:return 16777215&this.bg;default:return-1}}hasExtendedAttrs(){return 268435456&this.bg}updateExtended(){this.extended.isEmpty()?this.bg&=-268435457:this.bg|=268435456}getUnderlineColor(){if(268435456&this.bg&&~this.extended.underlineColor)switch(50331648&this.extended.underlineColor){case 16777216:case 33554432:return 255&this.extended.underlineColor;case 50331648:return 16777215&this.extended.underlineColor;default:return this.getFgColor()}return this.getFgColor()}getUnderlineColorMode(){return 268435456&this.bg&&~this.extended.underlineColor?50331648&this.extended.underlineColor:this.getFgColorMode()}isUnderlineColorRGB(){return 268435456&this.bg&&~this.extended.underlineColor?(50331648&this.extended.underlineColor)==50331648:this.isFgRGB()}isUnderlineColorPalette(){return 268435456&this.bg&&~this.extended.underlineColor?(50331648&this.extended.underlineColor)==16777216||(50331648&this.extended.underlineColor)==33554432:this.isFgPalette()}isUnderlineColorDefault(){return 268435456&this.bg&&~this.extended.underlineColor?(50331648&this.extended.underlineColor)==0:this.isFgDefault()}getUnderlineStyle(){return 268435456&this.fg?268435456&this.bg?this.extended.underlineStyle:1:0}getUnderlineVariantOffset(){return this.extended.underlineVariantOffset}}r.AttributeData=a;class c{get ext(){return this._urlId?-469762049&this._ext|this.underlineStyle<<26:this._ext}set ext(p){this._ext=p}get underlineStyle(){return this._urlId?5:(469762048&this._ext)>>26}set underlineStyle(p){this._ext&=-469762049,this._ext|=p<<26&469762048}get underlineColor(){return 67108863&this._ext}set underlineColor(p){this._ext&=-67108864,this._ext|=67108863&p}get urlId(){return this._urlId}set urlId(p){this._urlId=p}get underlineVariantOffset(){let p=(3758096384&this._ext)>>29;return p<0?4294967288^p:p}set underlineVariantOffset(p){this._ext&=536870911,this._ext|=p<<29&3758096384}constructor(p=0,h=0){this._ext=0,this._urlId=0,this._ext=p,this._urlId=h}clone(){return new c(this._ext,this._urlId)}isEmpty(){return this.underlineStyle===0&&this._urlId===0}}r.ExtendedAttrs=c},9092:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.Buffer=r.MAX_BUFFER_SIZE=void 0;let c=a(6349),m=a(7226),p=a(3734),h=a(8437),g=a(4634),S=a(511),x=a(643),v=a(4863),M=a(7116);r.MAX_BUFFER_SIZE=4294967295,r.Buffer=class{constructor(w,y,k){this._hasScrollback=w,this._optionsService=y,this._bufferService=k,this.ydisp=0,this.ybase=0,this.y=0,this.x=0,this.tabs={},this.savedY=0,this.savedX=0,this.savedCurAttrData=h.DEFAULT_ATTR_DATA.clone(),this.savedCharset=M.DEFAULT_CHARSET,this.markers=[],this._nullCell=S.CellData.fromCharData([0,x.NULL_CELL_CHAR,x.NULL_CELL_WIDTH,x.NULL_CELL_CODE]),this._whitespaceCell=S.CellData.fromCharData([0,x.WHITESPACE_CELL_CHAR,x.WHITESPACE_CELL_WIDTH,x.WHITESPACE_CELL_CODE]),this._isClearing=!1,this._memoryCleanupQueue=new m.IdleTaskQueue,this._memoryCleanupPosition=0,this._cols=this._bufferService.cols,this._rows=this._bufferService.rows,this.lines=new c.CircularList(this._getCorrectBufferLength(this._rows)),this.scrollTop=0,this.scrollBottom=this._rows-1,this.setupTabStops()}getNullCell(w){return w?(this._nullCell.fg=w.fg,this._nullCell.bg=w.bg,this._nullCell.extended=w.extended):(this._nullCell.fg=0,this._nullCell.bg=0,this._nullCell.extended=new p.ExtendedAttrs),this._nullCell}getWhitespaceCell(w){return w?(this._whitespaceCell.fg=w.fg,this._whitespaceCell.bg=w.bg,this._whitespaceCell.extended=w.extended):(this._whitespaceCell.fg=0,this._whitespaceCell.bg=0,this._whitespaceCell.extended=new p.ExtendedAttrs),this._whitespaceCell}getBlankLine(w,y){return new h.BufferLine(this._bufferService.cols,this.getNullCell(w),y)}get hasScrollback(){return this._hasScrollback&&this.lines.maxLength>this._rows}get isCursorInViewport(){let w=this.ybase+this.y-this.ydisp;return w>=0&&wr.MAX_BUFFER_SIZE?r.MAX_BUFFER_SIZE:y}fillViewportRows(w){if(this.lines.length===0){w===void 0&&(w=h.DEFAULT_ATTR_DATA);let y=this._rows;for(;y--;)this.lines.push(this.getBlankLine(w))}}clear(){this.ydisp=0,this.ybase=0,this.y=0,this.x=0,this.lines=new c.CircularList(this._getCorrectBufferLength(this._rows)),this.scrollTop=0,this.scrollBottom=this._rows-1,this.setupTabStops()}resize(w,y){let k=this.getNullCell(h.DEFAULT_ATTR_DATA),I=0,P=this._getCorrectBufferLength(y);if(P>this.lines.maxLength&&(this.lines.maxLength=P),this.lines.length>0){if(this._cols0&&this.lines.length<=this.ybase+this.y+R+1?(this.ybase--,R++,this.ydisp>0&&this.ydisp--):this.lines.push(new h.BufferLine(w,k)));else for(let D=this._rows;D>y;D--)this.lines.length>y+this.ybase&&(this.lines.length>this.ybase+this.y+1?this.lines.pop():(this.ybase++,this.ydisp++));if(P0&&(this.lines.trimStart(D),this.ybase=Math.max(this.ybase-D,0),this.ydisp=Math.max(this.ydisp-D,0),this.savedY=Math.max(this.savedY-D,0)),this.lines.maxLength=P}this.x=Math.min(this.x,w-1),this.y=Math.min(this.y,y-1),R&&(this.y+=R),this.savedX=Math.min(this.savedX,w-1),this.scrollTop=0}if(this.scrollBottom=y-1,this._isReflowEnabled&&(this._reflow(w,y),this._cols>w))for(let R=0;R.1*this.lines.length&&(this._memoryCleanupPosition=0,this._memoryCleanupQueue.enqueue(()=>this._batchedMemoryCleanup()))}_batchedMemoryCleanup(){let w=!0;this._memoryCleanupPosition>=this.lines.length&&(this._memoryCleanupPosition=0,w=!1);let y=0;for(;this._memoryCleanupPosition100)return!0;return w}get _isReflowEnabled(){let w=this._optionsService.rawOptions.windowsPty;return w&&w.buildNumber?this._hasScrollback&&w.backend==="conpty"&&w.buildNumber>=21376:this._hasScrollback&&!this._optionsService.rawOptions.windowsMode}_reflow(w,y){this._cols!==w&&(w>this._cols?this._reflowLarger(w,y):this._reflowSmaller(w,y))}_reflowLarger(w,y){let k=(0,g.reflowLargerGetLinesToRemove)(this.lines,this._cols,w,this.ybase+this.y,this.getNullCell(h.DEFAULT_ATTR_DATA));if(k.length>0){let I=(0,g.reflowLargerCreateNewLayout)(this.lines,k);(0,g.reflowLargerApplyNewLayout)(this.lines,I.layout),this._reflowLargerAdjustViewport(w,y,I.countRemoved)}}_reflowLargerAdjustViewport(w,y,k){let I=this.getNullCell(h.DEFAULT_ATTR_DATA),P=k;for(;P-- >0;)this.ybase===0?(this.y>0&&this.y--,this.lines.length=0;R--){let D=this.lines.get(R);if(!D||!D.isWrapped&&D.getTrimmedLength()<=w)continue;let N=[D];for(;D.isWrapped&&R>0;)D=this.lines.get(--R),N.unshift(D);let q=this.ybase+this.y;if(q>=R&&q0&&(I.push({start:R+N.length+P,newLines:be}),P+=be.length),N.push(...be);let le=fe.length-1,De=fe[le];De===0&&(le--,De=fe[le]);let me=N.length-G-1,V=de;for(;me>=0;){let ie=Math.min(V,De);if(N[le]===void 0)break;if(N[le].copyCellsFrom(N[me],V-ie,De-ie,ie,!0),De-=ie,De===0&&(le--,De=fe[le]),V-=ie,V===0){me--;let oe=Math.max(me,0);V=(0,g.getWrappedLineTrimmedLength)(N,oe,this._cols)}}for(let ie=0;ie0;)this.ybase===0?this.y0){let R=[],D=[];for(let le=0;le=0;le--)if(fe&&fe.start>q+G){for(let De=fe.newLines.length-1;De>=0;De--)this.lines.set(le--,fe.newLines[De]);le++,R.push({index:q+1,amount:fe.newLines.length}),G+=fe.newLines.length,fe=I[++de]}else this.lines.set(le,D[q--]);let ue=0;for(let le=R.length-1;le>=0;le--)R[le].index+=ue,this.lines.onInsertEmitter.fire(R[le]),ue+=R[le].amount;let be=Math.max(0,N+P-this.lines.maxLength);be>0&&this.lines.onTrimEmitter.fire(be)}}translateBufferLineToString(w,y,k=0,I){let P=this.lines.get(w);return P?P.translateToString(y,k,I):""}getWrappedRangeForLine(w){let y=w,k=w;for(;y>0&&this.lines.get(y).isWrapped;)y--;for(;k+10;);return w>=this._cols?this._cols-1:w<0?0:w}nextStop(w){for(w==null&&(w=this.x);!this.tabs[++w]&&w=this._cols?this._cols-1:w<0?0:w}clearMarkers(w){this._isClearing=!0;for(let y=0;y{y.line-=k,y.line<0&&y.dispose()})),y.register(this.lines.onInsert(k=>{y.line>=k.index&&(y.line+=k.amount)})),y.register(this.lines.onDelete(k=>{y.line>=k.index&&y.linek.index&&(y.line-=k.amount)})),y.register(y.onDispose(()=>this._removeMarker(y))),y}_removeMarker(w){this._isClearing||this.markers.splice(this.markers.indexOf(w),1)}}},8437:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.BufferLine=r.DEFAULT_ATTR_DATA=void 0;let c=a(3734),m=a(511),p=a(643),h=a(482);r.DEFAULT_ATTR_DATA=Object.freeze(new c.AttributeData);let g=0;class S{constructor(v,M,w=!1){this.isWrapped=w,this._combined={},this._extendedAttrs={},this._data=new Uint32Array(3*v);let y=M||m.CellData.fromCharData([0,p.NULL_CELL_CHAR,p.NULL_CELL_WIDTH,p.NULL_CELL_CODE]);for(let k=0;k>22,2097152&M?this._combined[v].charCodeAt(this._combined[v].length-1):w]}set(v,M){this._data[3*v+1]=M[p.CHAR_DATA_ATTR_INDEX],M[p.CHAR_DATA_CHAR_INDEX].length>1?(this._combined[v]=M[1],this._data[3*v+0]=2097152|v|M[p.CHAR_DATA_WIDTH_INDEX]<<22):this._data[3*v+0]=M[p.CHAR_DATA_CHAR_INDEX].charCodeAt(0)|M[p.CHAR_DATA_WIDTH_INDEX]<<22}getWidth(v){return this._data[3*v+0]>>22}hasWidth(v){return 12582912&this._data[3*v+0]}getFg(v){return this._data[3*v+1]}getBg(v){return this._data[3*v+2]}hasContent(v){return 4194303&this._data[3*v+0]}getCodePoint(v){let M=this._data[3*v+0];return 2097152&M?this._combined[v].charCodeAt(this._combined[v].length-1):2097151&M}isCombined(v){return 2097152&this._data[3*v+0]}getString(v){let M=this._data[3*v+0];return 2097152&M?this._combined[v]:2097151&M?(0,h.stringFromCodePoint)(2097151&M):""}isProtected(v){return 536870912&this._data[3*v+2]}loadCell(v,M){return g=3*v,M.content=this._data[g+0],M.fg=this._data[g+1],M.bg=this._data[g+2],2097152&M.content&&(M.combinedData=this._combined[v]),268435456&M.bg&&(M.extended=this._extendedAttrs[v]),M}setCell(v,M){2097152&M.content&&(this._combined[v]=M.combinedData),268435456&M.bg&&(this._extendedAttrs[v]=M.extended),this._data[3*v+0]=M.content,this._data[3*v+1]=M.fg,this._data[3*v+2]=M.bg}setCellFromCodepoint(v,M,w,y){268435456&y.bg&&(this._extendedAttrs[v]=y.extended),this._data[3*v+0]=M|w<<22,this._data[3*v+1]=y.fg,this._data[3*v+2]=y.bg}addCodepointToCell(v,M,w){let y=this._data[3*v+0];2097152&y?this._combined[v]+=(0,h.stringFromCodePoint)(M):2097151&y?(this._combined[v]=(0,h.stringFromCodePoint)(2097151&y)+(0,h.stringFromCodePoint)(M),y&=-2097152,y|=2097152):y=M|4194304,w&&(y&=-12582913,y|=w<<22),this._data[3*v+0]=y}insertCells(v,M,w){if((v%=this.length)&&this.getWidth(v-1)===2&&this.setCellFromCodepoint(v-1,0,1,w),M=0;--k)this.setCell(v+M+k,this.loadCell(v+k,y));for(let k=0;kthis.length){if(this._data.buffer.byteLength>=4*w)this._data=new Uint32Array(this._data.buffer,0,w);else{let y=new Uint32Array(w);y.set(this._data),this._data=y}for(let y=this.length;y=v&&delete this._combined[P]}let k=Object.keys(this._extendedAttrs);for(let I=0;I=v&&delete this._extendedAttrs[P]}}return this.length=v,4*w*2=0;--v)if(4194303&this._data[3*v+0])return v+(this._data[3*v+0]>>22);return 0}getNoBgTrimmedLength(){for(let v=this.length-1;v>=0;--v)if(4194303&this._data[3*v+0]||50331648&this._data[3*v+2])return v+(this._data[3*v+0]>>22);return 0}copyCellsFrom(v,M,w,y,k){let I=v._data;if(k)for(let R=y-1;R>=0;R--){for(let D=0;D<3;D++)this._data[3*(w+R)+D]=I[3*(M+R)+D];268435456&I[3*(M+R)+2]&&(this._extendedAttrs[w+R]=v._extendedAttrs[M+R])}else for(let R=0;R=M&&(this._combined[D-M+w]=v._combined[D])}}translateToString(v,M,w,y){M=M??0,w=w??this.length,v&&(w=Math.min(w,this.getTrimmedLength())),y&&(y.length=0);let k="";for(;M>22||1}return y&&y.push(M),k}}r.BufferLine=S},4841:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.getRangeLength=void 0,r.getRangeLength=function(a,c){if(a.start.y>a.end.y)throw new Error(`Buffer range end (${a.end.x}, ${a.end.y}) cannot be before start (${a.start.x}, ${a.start.y})`);return c*(a.end.y-a.start.y)+(a.end.x-a.start.x+1)}},4634:(o,r)=>{function a(c,m,p){if(m===c.length-1)return c[m].getTrimmedLength();let h=!c[m].hasContent(p-1)&&c[m].getWidth(p-1)===1,g=c[m+1].getWidth(0)===2;return h&&g?p-1:p}Object.defineProperty(r,"__esModule",{value:!0}),r.getWrappedLineTrimmedLength=r.reflowSmallerGetNewLineLengths=r.reflowLargerApplyNewLayout=r.reflowLargerCreateNewLayout=r.reflowLargerGetLinesToRemove=void 0,r.reflowLargerGetLinesToRemove=function(c,m,p,h,g){let S=[];for(let x=0;x=x&&h0&&(D>y||w[D].getTrimmedLength()===0);D--)R++;R>0&&(S.push(x+w.length-R),S.push(R)),x+=w.length-1}return S},r.reflowLargerCreateNewLayout=function(c,m){let p=[],h=0,g=m[h],S=0;for(let x=0;xa(c,w,m)).reduce((M,w)=>M+w),S=0,x=0,v=0;for(;vM&&(S-=M,x++);let w=c[x].getWidth(S-1)===2;w&&S--;let y=w?p-1:p;h.push(y),v+=y}return h},r.getWrappedLineTrimmedLength=a},5295:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.BufferSet=void 0;let c=a(8460),m=a(844),p=a(9092);class h extends m.Disposable{constructor(S,x){super(),this._optionsService=S,this._bufferService=x,this._onBufferActivate=this.register(new c.EventEmitter),this.onBufferActivate=this._onBufferActivate.event,this.reset(),this.register(this._optionsService.onSpecificOptionChange("scrollback",()=>this.resize(this._bufferService.cols,this._bufferService.rows))),this.register(this._optionsService.onSpecificOptionChange("tabStopWidth",()=>this.setupTabStops()))}reset(){this._normal=new p.Buffer(!0,this._optionsService,this._bufferService),this._normal.fillViewportRows(),this._alt=new p.Buffer(!1,this._optionsService,this._bufferService),this._activeBuffer=this._normal,this._onBufferActivate.fire({activeBuffer:this._normal,inactiveBuffer:this._alt}),this.setupTabStops()}get alt(){return this._alt}get active(){return this._activeBuffer}get normal(){return this._normal}activateNormalBuffer(){this._activeBuffer!==this._normal&&(this._normal.x=this._alt.x,this._normal.y=this._alt.y,this._alt.clearAllMarkers(),this._alt.clear(),this._activeBuffer=this._normal,this._onBufferActivate.fire({activeBuffer:this._normal,inactiveBuffer:this._alt}))}activateAltBuffer(S){this._activeBuffer!==this._alt&&(this._alt.fillViewportRows(S),this._alt.x=this._normal.x,this._alt.y=this._normal.y,this._activeBuffer=this._alt,this._onBufferActivate.fire({activeBuffer:this._alt,inactiveBuffer:this._normal}))}resize(S,x){this._normal.resize(S,x),this._alt.resize(S,x),this.setupTabStops(S)}setupTabStops(S){this._normal.setupTabStops(S),this._alt.setupTabStops(S)}}r.BufferSet=h},511:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.CellData=void 0;let c=a(482),m=a(643),p=a(3734);class h extends p.AttributeData{constructor(){super(...arguments),this.content=0,this.fg=0,this.bg=0,this.extended=new p.ExtendedAttrs,this.combinedData=""}static fromCharData(S){let x=new h;return x.setFromCharData(S),x}isCombined(){return 2097152&this.content}getWidth(){return this.content>>22}getChars(){return 2097152&this.content?this.combinedData:2097151&this.content?(0,c.stringFromCodePoint)(2097151&this.content):""}getCode(){return this.isCombined()?this.combinedData.charCodeAt(this.combinedData.length-1):2097151&this.content}setFromCharData(S){this.fg=S[m.CHAR_DATA_ATTR_INDEX],this.bg=0;let x=!1;if(S[m.CHAR_DATA_CHAR_INDEX].length>2)x=!0;else if(S[m.CHAR_DATA_CHAR_INDEX].length===2){let v=S[m.CHAR_DATA_CHAR_INDEX].charCodeAt(0);if(55296<=v&&v<=56319){let M=S[m.CHAR_DATA_CHAR_INDEX].charCodeAt(1);56320<=M&&M<=57343?this.content=1024*(v-55296)+M-56320+65536|S[m.CHAR_DATA_WIDTH_INDEX]<<22:x=!0}else x=!0}else this.content=S[m.CHAR_DATA_CHAR_INDEX].charCodeAt(0)|S[m.CHAR_DATA_WIDTH_INDEX]<<22;x&&(this.combinedData=S[m.CHAR_DATA_CHAR_INDEX],this.content=2097152|S[m.CHAR_DATA_WIDTH_INDEX]<<22)}getAsCharData(){return[this.fg,this.getChars(),this.getWidth(),this.getCode()]}}r.CellData=h},643:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.WHITESPACE_CELL_CODE=r.WHITESPACE_CELL_WIDTH=r.WHITESPACE_CELL_CHAR=r.NULL_CELL_CODE=r.NULL_CELL_WIDTH=r.NULL_CELL_CHAR=r.CHAR_DATA_CODE_INDEX=r.CHAR_DATA_WIDTH_INDEX=r.CHAR_DATA_CHAR_INDEX=r.CHAR_DATA_ATTR_INDEX=r.DEFAULT_EXT=r.DEFAULT_ATTR=r.DEFAULT_COLOR=void 0,r.DEFAULT_COLOR=0,r.DEFAULT_ATTR=256|r.DEFAULT_COLOR<<9,r.DEFAULT_EXT=0,r.CHAR_DATA_ATTR_INDEX=0,r.CHAR_DATA_CHAR_INDEX=1,r.CHAR_DATA_WIDTH_INDEX=2,r.CHAR_DATA_CODE_INDEX=3,r.NULL_CELL_CHAR="",r.NULL_CELL_WIDTH=1,r.NULL_CELL_CODE=0,r.WHITESPACE_CELL_CHAR=" ",r.WHITESPACE_CELL_WIDTH=1,r.WHITESPACE_CELL_CODE=32},4863:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.Marker=void 0;let c=a(8460),m=a(844);class p{get id(){return this._id}constructor(g){this.line=g,this.isDisposed=!1,this._disposables=[],this._id=p._nextId++,this._onDispose=this.register(new c.EventEmitter),this.onDispose=this._onDispose.event}dispose(){this.isDisposed||(this.isDisposed=!0,this.line=-1,this._onDispose.fire(),(0,m.disposeArray)(this._disposables),this._disposables.length=0)}register(g){return this._disposables.push(g),g}}r.Marker=p,p._nextId=1},7116:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.DEFAULT_CHARSET=r.CHARSETS=void 0,r.CHARSETS={},r.DEFAULT_CHARSET=r.CHARSETS.B,r.CHARSETS[0]={"`":"\u25C6",a:"\u2592",b:"\u2409",c:"\u240C",d:"\u240D",e:"\u240A",f:"\xB0",g:"\xB1",h:"\u2424",i:"\u240B",j:"\u2518",k:"\u2510",l:"\u250C",m:"\u2514",n:"\u253C",o:"\u23BA",p:"\u23BB",q:"\u2500",r:"\u23BC",s:"\u23BD",t:"\u251C",u:"\u2524",v:"\u2534",w:"\u252C",x:"\u2502",y:"\u2264",z:"\u2265","{":"\u03C0","|":"\u2260","}":"\xA3","~":"\xB7"},r.CHARSETS.A={"#":"\xA3"},r.CHARSETS.B=void 0,r.CHARSETS[4]={"#":"\xA3","@":"\xBE","[":"ij","\\":"\xBD","]":"|","{":"\xA8","|":"f","}":"\xBC","~":"\xB4"},r.CHARSETS.C=r.CHARSETS[5]={"[":"\xC4","\\":"\xD6","]":"\xC5","^":"\xDC","`":"\xE9","{":"\xE4","|":"\xF6","}":"\xE5","~":"\xFC"},r.CHARSETS.R={"#":"\xA3","@":"\xE0","[":"\xB0","\\":"\xE7","]":"\xA7","{":"\xE9","|":"\xF9","}":"\xE8","~":"\xA8"},r.CHARSETS.Q={"@":"\xE0","[":"\xE2","\\":"\xE7","]":"\xEA","^":"\xEE","`":"\xF4","{":"\xE9","|":"\xF9","}":"\xE8","~":"\xFB"},r.CHARSETS.K={"@":"\xA7","[":"\xC4","\\":"\xD6","]":"\xDC","{":"\xE4","|":"\xF6","}":"\xFC","~":"\xDF"},r.CHARSETS.Y={"#":"\xA3","@":"\xA7","[":"\xB0","\\":"\xE7","]":"\xE9","`":"\xF9","{":"\xE0","|":"\xF2","}":"\xE8","~":"\xEC"},r.CHARSETS.E=r.CHARSETS[6]={"@":"\xC4","[":"\xC6","\\":"\xD8","]":"\xC5","^":"\xDC","`":"\xE4","{":"\xE6","|":"\xF8","}":"\xE5","~":"\xFC"},r.CHARSETS.Z={"#":"\xA3","@":"\xA7","[":"\xA1","\\":"\xD1","]":"\xBF","{":"\xB0","|":"\xF1","}":"\xE7"},r.CHARSETS.H=r.CHARSETS[7]={"@":"\xC9","[":"\xC4","\\":"\xD6","]":"\xC5","^":"\xDC","`":"\xE9","{":"\xE4","|":"\xF6","}":"\xE5","~":"\xFC"},r.CHARSETS["="]={"#":"\xF9","@":"\xE0","[":"\xE9","\\":"\xE7","]":"\xEA","^":"\xEE",_:"\xE8","`":"\xF4","{":"\xE4","|":"\xF6","}":"\xFC","~":"\xFB"}},2584:(o,r)=>{var a,c,m;Object.defineProperty(r,"__esModule",{value:!0}),r.C1_ESCAPED=r.C1=r.C0=void 0,(function(p){p.NUL="\0",p.SOH="",p.STX="",p.ETX="",p.EOT="",p.ENQ="",p.ACK="",p.BEL="\x07",p.BS="\b",p.HT=" ",p.LF=` -`,p.VT="\v",p.FF="\f",p.CR="\r",p.SO="",p.SI="",p.DLE="",p.DC1="",p.DC2="",p.DC3="",p.DC4="",p.NAK="",p.SYN="",p.ETB="",p.CAN="",p.EM="",p.SUB="",p.ESC="\x1B",p.FS="",p.GS="",p.RS="",p.US="",p.SP=" ",p.DEL="\x7F"})(a||(r.C0=a={})),(function(p){p.PAD="\x80",p.HOP="\x81",p.BPH="\x82",p.NBH="\x83",p.IND="\x84",p.NEL="\x85",p.SSA="\x86",p.ESA="\x87",p.HTS="\x88",p.HTJ="\x89",p.VTS="\x8A",p.PLD="\x8B",p.PLU="\x8C",p.RI="\x8D",p.SS2="\x8E",p.SS3="\x8F",p.DCS="\x90",p.PU1="\x91",p.PU2="\x92",p.STS="\x93",p.CCH="\x94",p.MW="\x95",p.SPA="\x96",p.EPA="\x97",p.SOS="\x98",p.SGCI="\x99",p.SCI="\x9A",p.CSI="\x9B",p.ST="\x9C",p.OSC="\x9D",p.PM="\x9E",p.APC="\x9F"})(c||(r.C1=c={})),(function(p){p.ST=`${a.ESC}\\`})(m||(r.C1_ESCAPED=m={}))},7399:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.evaluateKeyboardEvent=void 0;let c=a(2584),m={48:["0",")"],49:["1","!"],50:["2","@"],51:["3","#"],52:["4","$"],53:["5","%"],54:["6","^"],55:["7","&"],56:["8","*"],57:["9","("],186:[";",":"],187:["=","+"],188:[",","<"],189:["-","_"],190:[".",">"],191:["/","?"],192:["`","~"],219:["[","{"],220:["\\","|"],221:["]","}"],222:["'",'"']};r.evaluateKeyboardEvent=function(p,h,g,S){let x={type:0,cancel:!1,key:void 0},v=(p.shiftKey?1:0)|(p.altKey?2:0)|(p.ctrlKey?4:0)|(p.metaKey?8:0);switch(p.keyCode){case 0:p.key==="UIKeyInputUpArrow"?x.key=h?c.C0.ESC+"OA":c.C0.ESC+"[A":p.key==="UIKeyInputLeftArrow"?x.key=h?c.C0.ESC+"OD":c.C0.ESC+"[D":p.key==="UIKeyInputRightArrow"?x.key=h?c.C0.ESC+"OC":c.C0.ESC+"[C":p.key==="UIKeyInputDownArrow"&&(x.key=h?c.C0.ESC+"OB":c.C0.ESC+"[B");break;case 8:x.key=p.ctrlKey?"\b":c.C0.DEL,p.altKey&&(x.key=c.C0.ESC+x.key);break;case 9:if(p.shiftKey){x.key=c.C0.ESC+"[Z";break}x.key=c.C0.HT,x.cancel=!0;break;case 13:x.key=p.altKey?c.C0.ESC+c.C0.CR:c.C0.CR,x.cancel=!0;break;case 27:x.key=c.C0.ESC,p.altKey&&(x.key=c.C0.ESC+c.C0.ESC),x.cancel=!0;break;case 37:if(p.metaKey)break;v?(x.key=c.C0.ESC+"[1;"+(v+1)+"D",x.key===c.C0.ESC+"[1;3D"&&(x.key=c.C0.ESC+(g?"b":"[1;5D"))):x.key=h?c.C0.ESC+"OD":c.C0.ESC+"[D";break;case 39:if(p.metaKey)break;v?(x.key=c.C0.ESC+"[1;"+(v+1)+"C",x.key===c.C0.ESC+"[1;3C"&&(x.key=c.C0.ESC+(g?"f":"[1;5C"))):x.key=h?c.C0.ESC+"OC":c.C0.ESC+"[C";break;case 38:if(p.metaKey)break;v?(x.key=c.C0.ESC+"[1;"+(v+1)+"A",g||x.key!==c.C0.ESC+"[1;3A"||(x.key=c.C0.ESC+"[1;5A")):x.key=h?c.C0.ESC+"OA":c.C0.ESC+"[A";break;case 40:if(p.metaKey)break;v?(x.key=c.C0.ESC+"[1;"+(v+1)+"B",g||x.key!==c.C0.ESC+"[1;3B"||(x.key=c.C0.ESC+"[1;5B")):x.key=h?c.C0.ESC+"OB":c.C0.ESC+"[B";break;case 45:p.shiftKey||p.ctrlKey||(x.key=c.C0.ESC+"[2~");break;case 46:x.key=v?c.C0.ESC+"[3;"+(v+1)+"~":c.C0.ESC+"[3~";break;case 36:x.key=v?c.C0.ESC+"[1;"+(v+1)+"H":h?c.C0.ESC+"OH":c.C0.ESC+"[H";break;case 35:x.key=v?c.C0.ESC+"[1;"+(v+1)+"F":h?c.C0.ESC+"OF":c.C0.ESC+"[F";break;case 33:p.shiftKey?x.type=2:p.ctrlKey?x.key=c.C0.ESC+"[5;"+(v+1)+"~":x.key=c.C0.ESC+"[5~";break;case 34:p.shiftKey?x.type=3:p.ctrlKey?x.key=c.C0.ESC+"[6;"+(v+1)+"~":x.key=c.C0.ESC+"[6~";break;case 112:x.key=v?c.C0.ESC+"[1;"+(v+1)+"P":c.C0.ESC+"OP";break;case 113:x.key=v?c.C0.ESC+"[1;"+(v+1)+"Q":c.C0.ESC+"OQ";break;case 114:x.key=v?c.C0.ESC+"[1;"+(v+1)+"R":c.C0.ESC+"OR";break;case 115:x.key=v?c.C0.ESC+"[1;"+(v+1)+"S":c.C0.ESC+"OS";break;case 116:x.key=v?c.C0.ESC+"[15;"+(v+1)+"~":c.C0.ESC+"[15~";break;case 117:x.key=v?c.C0.ESC+"[17;"+(v+1)+"~":c.C0.ESC+"[17~";break;case 118:x.key=v?c.C0.ESC+"[18;"+(v+1)+"~":c.C0.ESC+"[18~";break;case 119:x.key=v?c.C0.ESC+"[19;"+(v+1)+"~":c.C0.ESC+"[19~";break;case 120:x.key=v?c.C0.ESC+"[20;"+(v+1)+"~":c.C0.ESC+"[20~";break;case 121:x.key=v?c.C0.ESC+"[21;"+(v+1)+"~":c.C0.ESC+"[21~";break;case 122:x.key=v?c.C0.ESC+"[23;"+(v+1)+"~":c.C0.ESC+"[23~";break;case 123:x.key=v?c.C0.ESC+"[24;"+(v+1)+"~":c.C0.ESC+"[24~";break;default:if(!p.ctrlKey||p.shiftKey||p.altKey||p.metaKey)if(g&&!S||!p.altKey||p.metaKey)!g||p.altKey||p.ctrlKey||p.shiftKey||!p.metaKey?p.key&&!p.ctrlKey&&!p.altKey&&!p.metaKey&&p.keyCode>=48&&p.key.length===1?x.key=p.key:p.key&&p.ctrlKey&&(p.key==="_"&&(x.key=c.C0.US),p.key==="@"&&(x.key=c.C0.NUL)):p.keyCode===65&&(x.type=1);else{let M=m[p.keyCode],w=M?.[p.shiftKey?1:0];if(w)x.key=c.C0.ESC+w;else if(p.keyCode>=65&&p.keyCode<=90){let y=p.ctrlKey?p.keyCode-64:p.keyCode+32,k=String.fromCharCode(y);p.shiftKey&&(k=k.toUpperCase()),x.key=c.C0.ESC+k}else if(p.keyCode===32)x.key=c.C0.ESC+(p.ctrlKey?c.C0.NUL:" ");else if(p.key==="Dead"&&p.code.startsWith("Key")){let y=p.code.slice(3,4);p.shiftKey||(y=y.toLowerCase()),x.key=c.C0.ESC+y,x.cancel=!0}}else p.keyCode>=65&&p.keyCode<=90?x.key=String.fromCharCode(p.keyCode-64):p.keyCode===32?x.key=c.C0.NUL:p.keyCode>=51&&p.keyCode<=55?x.key=String.fromCharCode(p.keyCode-51+27):p.keyCode===56?x.key=c.C0.DEL:p.keyCode===219?x.key=c.C0.ESC:p.keyCode===220?x.key=c.C0.FS:p.keyCode===221&&(x.key=c.C0.GS)}return x}},482:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.Utf8ToUtf32=r.StringToUtf32=r.utf32ToString=r.stringFromCodePoint=void 0,r.stringFromCodePoint=function(a){return a>65535?(a-=65536,String.fromCharCode(55296+(a>>10))+String.fromCharCode(a%1024+56320)):String.fromCharCode(a)},r.utf32ToString=function(a,c=0,m=a.length){let p="";for(let h=c;h65535?(g-=65536,p+=String.fromCharCode(55296+(g>>10))+String.fromCharCode(g%1024+56320)):p+=String.fromCharCode(g)}return p},r.StringToUtf32=class{constructor(){this._interim=0}clear(){this._interim=0}decode(a,c){let m=a.length;if(!m)return 0;let p=0,h=0;if(this._interim){let g=a.charCodeAt(h++);56320<=g&&g<=57343?c[p++]=1024*(this._interim-55296)+g-56320+65536:(c[p++]=this._interim,c[p++]=g),this._interim=0}for(let g=h;g=m)return this._interim=S,p;let x=a.charCodeAt(g);56320<=x&&x<=57343?c[p++]=1024*(S-55296)+x-56320+65536:(c[p++]=S,c[p++]=x)}else S!==65279&&(c[p++]=S)}return p}},r.Utf8ToUtf32=class{constructor(){this.interim=new Uint8Array(3)}clear(){this.interim.fill(0)}decode(a,c){let m=a.length;if(!m)return 0;let p,h,g,S,x=0,v=0,M=0;if(this.interim[0]){let k=!1,I=this.interim[0];I&=(224&I)==192?31:(240&I)==224?15:7;let P,R=0;for(;(P=63&this.interim[++R])&&R<4;)I<<=6,I|=P;let D=(224&this.interim[0])==192?2:(240&this.interim[0])==224?3:4,N=D-R;for(;M=m)return 0;if(P=a[M++],(192&P)!=128){M--,k=!0;break}this.interim[R++]=P,I<<=6,I|=63&P}k||(D===2?I<128?M--:c[x++]=I:D===3?I<2048||I>=55296&&I<=57343||I===65279||(c[x++]=I):I<65536||I>1114111||(c[x++]=I)),this.interim.fill(0)}let w=m-4,y=M;for(;y=m)return this.interim[0]=p,x;if(h=a[y++],(192&h)!=128){y--;continue}if(v=(31&p)<<6|63&h,v<128){y--;continue}c[x++]=v}else if((240&p)==224){if(y>=m)return this.interim[0]=p,x;if(h=a[y++],(192&h)!=128){y--;continue}if(y>=m)return this.interim[0]=p,this.interim[1]=h,x;if(g=a[y++],(192&g)!=128){y--;continue}if(v=(15&p)<<12|(63&h)<<6|63&g,v<2048||v>=55296&&v<=57343||v===65279)continue;c[x++]=v}else if((248&p)==240){if(y>=m)return this.interim[0]=p,x;if(h=a[y++],(192&h)!=128){y--;continue}if(y>=m)return this.interim[0]=p,this.interim[1]=h,x;if(g=a[y++],(192&g)!=128){y--;continue}if(y>=m)return this.interim[0]=p,this.interim[1]=h,this.interim[2]=g,x;if(S=a[y++],(192&S)!=128){y--;continue}if(v=(7&p)<<18|(63&h)<<12|(63&g)<<6|63&S,v<65536||v>1114111)continue;c[x++]=v}}return x}}},225:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.UnicodeV6=void 0;let c=a(1480),m=[[768,879],[1155,1158],[1160,1161],[1425,1469],[1471,1471],[1473,1474],[1476,1477],[1479,1479],[1536,1539],[1552,1557],[1611,1630],[1648,1648],[1750,1764],[1767,1768],[1770,1773],[1807,1807],[1809,1809],[1840,1866],[1958,1968],[2027,2035],[2305,2306],[2364,2364],[2369,2376],[2381,2381],[2385,2388],[2402,2403],[2433,2433],[2492,2492],[2497,2500],[2509,2509],[2530,2531],[2561,2562],[2620,2620],[2625,2626],[2631,2632],[2635,2637],[2672,2673],[2689,2690],[2748,2748],[2753,2757],[2759,2760],[2765,2765],[2786,2787],[2817,2817],[2876,2876],[2879,2879],[2881,2883],[2893,2893],[2902,2902],[2946,2946],[3008,3008],[3021,3021],[3134,3136],[3142,3144],[3146,3149],[3157,3158],[3260,3260],[3263,3263],[3270,3270],[3276,3277],[3298,3299],[3393,3395],[3405,3405],[3530,3530],[3538,3540],[3542,3542],[3633,3633],[3636,3642],[3655,3662],[3761,3761],[3764,3769],[3771,3772],[3784,3789],[3864,3865],[3893,3893],[3895,3895],[3897,3897],[3953,3966],[3968,3972],[3974,3975],[3984,3991],[3993,4028],[4038,4038],[4141,4144],[4146,4146],[4150,4151],[4153,4153],[4184,4185],[4448,4607],[4959,4959],[5906,5908],[5938,5940],[5970,5971],[6002,6003],[6068,6069],[6071,6077],[6086,6086],[6089,6099],[6109,6109],[6155,6157],[6313,6313],[6432,6434],[6439,6440],[6450,6450],[6457,6459],[6679,6680],[6912,6915],[6964,6964],[6966,6970],[6972,6972],[6978,6978],[7019,7027],[7616,7626],[7678,7679],[8203,8207],[8234,8238],[8288,8291],[8298,8303],[8400,8431],[12330,12335],[12441,12442],[43014,43014],[43019,43019],[43045,43046],[64286,64286],[65024,65039],[65056,65059],[65279,65279],[65529,65531]],p=[[68097,68099],[68101,68102],[68108,68111],[68152,68154],[68159,68159],[119143,119145],[119155,119170],[119173,119179],[119210,119213],[119362,119364],[917505,917505],[917536,917631],[917760,917999]],h;r.UnicodeV6=class{constructor(){if(this.version="6",!h){h=new Uint8Array(65536),h.fill(1),h[0]=0,h.fill(0,1,32),h.fill(0,127,160),h.fill(2,4352,4448),h[9001]=2,h[9002]=2,h.fill(2,11904,42192),h[12351]=1,h.fill(2,44032,55204),h.fill(2,63744,64256),h.fill(2,65040,65050),h.fill(2,65072,65136),h.fill(2,65280,65377),h.fill(2,65504,65511);for(let g=0;gx[w][1])return!1;for(;w>=M;)if(v=M+w>>1,S>x[v][1])M=v+1;else{if(!(S=131072&&g<=196605||g>=196608&&g<=262141?2:1}charProperties(g,S){let x=this.wcwidth(g),v=x===0&&S!==0;if(v){let M=c.UnicodeService.extractWidth(S);M===0?v=!1:M>x&&(x=M)}return c.UnicodeService.createPropertyValue(0,x,v)}}},5981:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.WriteBuffer=void 0;let c=a(8460),m=a(844);class p extends m.Disposable{constructor(g){super(),this._action=g,this._writeBuffer=[],this._callbacks=[],this._pendingData=0,this._bufferOffset=0,this._isSyncWriting=!1,this._syncCalls=0,this._didUserInput=!1,this._onWriteParsed=this.register(new c.EventEmitter),this.onWriteParsed=this._onWriteParsed.event}handleUserInput(){this._didUserInput=!0}writeSync(g,S){if(S!==void 0&&this._syncCalls>S)return void(this._syncCalls=0);if(this._pendingData+=g.length,this._writeBuffer.push(g),this._callbacks.push(void 0),this._syncCalls++,this._isSyncWriting)return;let x;for(this._isSyncWriting=!0;x=this._writeBuffer.shift();){this._action(x);let v=this._callbacks.shift();v&&v()}this._pendingData=0,this._bufferOffset=2147483647,this._isSyncWriting=!1,this._syncCalls=0}write(g,S){if(this._pendingData>5e7)throw new Error("write data discarded, use flow control to avoid losing data");if(!this._writeBuffer.length){if(this._bufferOffset=0,this._didUserInput)return this._didUserInput=!1,this._pendingData+=g.length,this._writeBuffer.push(g),this._callbacks.push(S),void this._innerWrite();setTimeout(()=>this._innerWrite())}this._pendingData+=g.length,this._writeBuffer.push(g),this._callbacks.push(S)}_innerWrite(g=0,S=!0){let x=g||Date.now();for(;this._writeBuffer.length>this._bufferOffset;){let v=this._writeBuffer[this._bufferOffset],M=this._action(v,S);if(M){let y=k=>Date.now()-x>=12?setTimeout(()=>this._innerWrite(0,k)):this._innerWrite(x,k);return void M.catch(k=>(queueMicrotask(()=>{throw k}),Promise.resolve(!1))).then(y)}let w=this._callbacks[this._bufferOffset];if(w&&w(),this._bufferOffset++,this._pendingData-=v.length,Date.now()-x>=12)break}this._writeBuffer.length>this._bufferOffset?(this._bufferOffset>50&&(this._writeBuffer=this._writeBuffer.slice(this._bufferOffset),this._callbacks=this._callbacks.slice(this._bufferOffset),this._bufferOffset=0),setTimeout(()=>this._innerWrite())):(this._writeBuffer.length=0,this._callbacks.length=0,this._pendingData=0,this._bufferOffset=0),this._onWriteParsed.fire()}}r.WriteBuffer=p},5941:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.toRgbString=r.parseColor=void 0;let a=/^([\da-f])\/([\da-f])\/([\da-f])$|^([\da-f]{2})\/([\da-f]{2})\/([\da-f]{2})$|^([\da-f]{3})\/([\da-f]{3})\/([\da-f]{3})$|^([\da-f]{4})\/([\da-f]{4})\/([\da-f]{4})$/,c=/^[\da-f]+$/;function m(p,h){let g=p.toString(16),S=g.length<2?"0"+g:g;switch(h){case 4:return g[0];case 8:return S;case 12:return(S+S).slice(0,3);default:return S+S}}r.parseColor=function(p){if(!p)return;let h=p.toLowerCase();if(h.indexOf("rgb:")===0){h=h.slice(4);let g=a.exec(h);if(g){let S=g[1]?15:g[4]?255:g[7]?4095:65535;return[Math.round(parseInt(g[1]||g[4]||g[7]||g[10],16)/S*255),Math.round(parseInt(g[2]||g[5]||g[8]||g[11],16)/S*255),Math.round(parseInt(g[3]||g[6]||g[9]||g[12],16)/S*255)]}}else if(h.indexOf("#")===0&&(h=h.slice(1),c.exec(h)&&[3,6,9,12].includes(h.length))){let g=h.length/3,S=[0,0,0];for(let x=0;x<3;++x){let v=parseInt(h.slice(g*x,g*x+g),16);S[x]=g===1?v<<4:g===2?v:g===3?v>>4:v>>8}return S}},r.toRgbString=function(p,h=16){let[g,S,x]=p;return`rgb:${m(g,h)}/${m(S,h)}/${m(x,h)}`}},5770:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.PAYLOAD_LIMIT=void 0,r.PAYLOAD_LIMIT=1e7},6351:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.DcsHandler=r.DcsParser=void 0;let c=a(482),m=a(8742),p=a(5770),h=[];r.DcsParser=class{constructor(){this._handlers=Object.create(null),this._active=h,this._ident=0,this._handlerFb=()=>{},this._stack={paused:!1,loopPosition:0,fallThrough:!1}}dispose(){this._handlers=Object.create(null),this._handlerFb=()=>{},this._active=h}registerHandler(S,x){this._handlers[S]===void 0&&(this._handlers[S]=[]);let v=this._handlers[S];return v.push(x),{dispose:()=>{let M=v.indexOf(x);M!==-1&&v.splice(M,1)}}}clearHandler(S){this._handlers[S]&&delete this._handlers[S]}setHandlerFallback(S){this._handlerFb=S}reset(){if(this._active.length)for(let S=this._stack.paused?this._stack.loopPosition-1:this._active.length-1;S>=0;--S)this._active[S].unhook(!1);this._stack.paused=!1,this._active=h,this._ident=0}hook(S,x){if(this.reset(),this._ident=S,this._active=this._handlers[S]||h,this._active.length)for(let v=this._active.length-1;v>=0;v--)this._active[v].hook(x);else this._handlerFb(this._ident,"HOOK",x)}put(S,x,v){if(this._active.length)for(let M=this._active.length-1;M>=0;M--)this._active[M].put(S,x,v);else this._handlerFb(this._ident,"PUT",(0,c.utf32ToString)(S,x,v))}unhook(S,x=!0){if(this._active.length){let v=!1,M=this._active.length-1,w=!1;if(this._stack.paused&&(M=this._stack.loopPosition-1,v=x,w=this._stack.fallThrough,this._stack.paused=!1),!w&&v===!1){for(;M>=0&&(v=this._active[M].unhook(S),v!==!0);M--)if(v instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=M,this._stack.fallThrough=!1,v;M--}for(;M>=0;M--)if(v=this._active[M].unhook(!1),v instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=M,this._stack.fallThrough=!0,v}else this._handlerFb(this._ident,"UNHOOK",S);this._active=h,this._ident=0}};let g=new m.Params;g.addParam(0),r.DcsHandler=class{constructor(S){this._handler=S,this._data="",this._params=g,this._hitLimit=!1}hook(S){this._params=S.length>1||S.params[0]?S.clone():g,this._data="",this._hitLimit=!1}put(S,x,v){this._hitLimit||(this._data+=(0,c.utf32ToString)(S,x,v),this._data.length>p.PAYLOAD_LIMIT&&(this._data="",this._hitLimit=!0))}unhook(S){let x=!1;if(this._hitLimit)x=!1;else if(S&&(x=this._handler(this._data,this._params),x instanceof Promise))return x.then(v=>(this._params=g,this._data="",this._hitLimit=!1,v));return this._params=g,this._data="",this._hitLimit=!1,x}}},2015:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.EscapeSequenceParser=r.VT500_TRANSITION_TABLE=r.TransitionTable=void 0;let c=a(844),m=a(8742),p=a(6242),h=a(6351);class g{constructor(M){this.table=new Uint8Array(M)}setDefault(M,w){this.table.fill(M<<4|w)}add(M,w,y,k){this.table[w<<8|M]=y<<4|k}addMany(M,w,y,k){for(let I=0;ID),w=(R,D)=>M.slice(R,D),y=w(32,127),k=w(0,24);k.push(25),k.push.apply(k,w(28,32));let I=w(0,14),P;for(P in v.setDefault(1,0),v.addMany(y,0,2,0),I)v.addMany([24,26,153,154],P,3,0),v.addMany(w(128,144),P,3,0),v.addMany(w(144,152),P,3,0),v.add(156,P,0,0),v.add(27,P,11,1),v.add(157,P,4,8),v.addMany([152,158,159],P,0,7),v.add(155,P,11,3),v.add(144,P,11,9);return v.addMany(k,0,3,0),v.addMany(k,1,3,1),v.add(127,1,0,1),v.addMany(k,8,0,8),v.addMany(k,3,3,3),v.add(127,3,0,3),v.addMany(k,4,3,4),v.add(127,4,0,4),v.addMany(k,6,3,6),v.addMany(k,5,3,5),v.add(127,5,0,5),v.addMany(k,2,3,2),v.add(127,2,0,2),v.add(93,1,4,8),v.addMany(y,8,5,8),v.add(127,8,5,8),v.addMany([156,27,24,26,7],8,6,0),v.addMany(w(28,32),8,0,8),v.addMany([88,94,95],1,0,7),v.addMany(y,7,0,7),v.addMany(k,7,0,7),v.add(156,7,0,0),v.add(127,7,0,7),v.add(91,1,11,3),v.addMany(w(64,127),3,7,0),v.addMany(w(48,60),3,8,4),v.addMany([60,61,62,63],3,9,4),v.addMany(w(48,60),4,8,4),v.addMany(w(64,127),4,7,0),v.addMany([60,61,62,63],4,0,6),v.addMany(w(32,64),6,0,6),v.add(127,6,0,6),v.addMany(w(64,127),6,0,0),v.addMany(w(32,48),3,9,5),v.addMany(w(32,48),5,9,5),v.addMany(w(48,64),5,0,6),v.addMany(w(64,127),5,7,0),v.addMany(w(32,48),4,9,5),v.addMany(w(32,48),1,9,2),v.addMany(w(32,48),2,9,2),v.addMany(w(48,127),2,10,0),v.addMany(w(48,80),1,10,0),v.addMany(w(81,88),1,10,0),v.addMany([89,90,92],1,10,0),v.addMany(w(96,127),1,10,0),v.add(80,1,11,9),v.addMany(k,9,0,9),v.add(127,9,0,9),v.addMany(w(28,32),9,0,9),v.addMany(w(32,48),9,9,12),v.addMany(w(48,60),9,8,10),v.addMany([60,61,62,63],9,9,10),v.addMany(k,11,0,11),v.addMany(w(32,128),11,0,11),v.addMany(w(28,32),11,0,11),v.addMany(k,10,0,10),v.add(127,10,0,10),v.addMany(w(28,32),10,0,10),v.addMany(w(48,60),10,8,10),v.addMany([60,61,62,63],10,0,11),v.addMany(w(32,48),10,9,12),v.addMany(k,12,0,12),v.add(127,12,0,12),v.addMany(w(28,32),12,0,12),v.addMany(w(32,48),12,9,12),v.addMany(w(48,64),12,0,11),v.addMany(w(64,127),12,12,13),v.addMany(w(64,127),10,12,13),v.addMany(w(64,127),9,12,13),v.addMany(k,13,13,13),v.addMany(y,13,13,13),v.add(127,13,0,13),v.addMany([27,156,24,26],13,14,0),v.add(S,0,2,0),v.add(S,8,5,8),v.add(S,6,0,6),v.add(S,11,0,11),v.add(S,13,13,13),v})();class x extends c.Disposable{constructor(M=r.VT500_TRANSITION_TABLE){super(),this._transitions=M,this._parseStack={state:0,handlers:[],handlerPos:0,transition:0,chunkPos:0},this.initialState=0,this.currentState=this.initialState,this._params=new m.Params,this._params.addParam(0),this._collect=0,this.precedingJoinState=0,this._printHandlerFb=(w,y,k)=>{},this._executeHandlerFb=w=>{},this._csiHandlerFb=(w,y)=>{},this._escHandlerFb=w=>{},this._errorHandlerFb=w=>w,this._printHandler=this._printHandlerFb,this._executeHandlers=Object.create(null),this._csiHandlers=Object.create(null),this._escHandlers=Object.create(null),this.register((0,c.toDisposable)(()=>{this._csiHandlers=Object.create(null),this._executeHandlers=Object.create(null),this._escHandlers=Object.create(null)})),this._oscParser=this.register(new p.OscParser),this._dcsParser=this.register(new h.DcsParser),this._errorHandler=this._errorHandlerFb,this.registerEscHandler({final:"\\"},()=>!0)}_identifier(M,w=[64,126]){let y=0;if(M.prefix){if(M.prefix.length>1)throw new Error("only one byte as prefix supported");if(y=M.prefix.charCodeAt(0),y&&60>y||y>63)throw new Error("prefix must be in range 0x3c .. 0x3f")}if(M.intermediates){if(M.intermediates.length>2)throw new Error("only two bytes as intermediates are supported");for(let I=0;IP||P>47)throw new Error("intermediate must be in range 0x20 .. 0x2f");y<<=8,y|=P}}if(M.final.length!==1)throw new Error("final must be a single byte");let k=M.final.charCodeAt(0);if(w[0]>k||k>w[1])throw new Error(`final must be in range ${w[0]} .. ${w[1]}`);return y<<=8,y|=k,y}identToString(M){let w=[];for(;M;)w.push(String.fromCharCode(255&M)),M>>=8;return w.reverse().join("")}setPrintHandler(M){this._printHandler=M}clearPrintHandler(){this._printHandler=this._printHandlerFb}registerEscHandler(M,w){let y=this._identifier(M,[48,126]);this._escHandlers[y]===void 0&&(this._escHandlers[y]=[]);let k=this._escHandlers[y];return k.push(w),{dispose:()=>{let I=k.indexOf(w);I!==-1&&k.splice(I,1)}}}clearEscHandler(M){this._escHandlers[this._identifier(M,[48,126])]&&delete this._escHandlers[this._identifier(M,[48,126])]}setEscHandlerFallback(M){this._escHandlerFb=M}setExecuteHandler(M,w){this._executeHandlers[M.charCodeAt(0)]=w}clearExecuteHandler(M){this._executeHandlers[M.charCodeAt(0)]&&delete this._executeHandlers[M.charCodeAt(0)]}setExecuteHandlerFallback(M){this._executeHandlerFb=M}registerCsiHandler(M,w){let y=this._identifier(M);this._csiHandlers[y]===void 0&&(this._csiHandlers[y]=[]);let k=this._csiHandlers[y];return k.push(w),{dispose:()=>{let I=k.indexOf(w);I!==-1&&k.splice(I,1)}}}clearCsiHandler(M){this._csiHandlers[this._identifier(M)]&&delete this._csiHandlers[this._identifier(M)]}setCsiHandlerFallback(M){this._csiHandlerFb=M}registerDcsHandler(M,w){return this._dcsParser.registerHandler(this._identifier(M),w)}clearDcsHandler(M){this._dcsParser.clearHandler(this._identifier(M))}setDcsHandlerFallback(M){this._dcsParser.setHandlerFallback(M)}registerOscHandler(M,w){return this._oscParser.registerHandler(M,w)}clearOscHandler(M){this._oscParser.clearHandler(M)}setOscHandlerFallback(M){this._oscParser.setHandlerFallback(M)}setErrorHandler(M){this._errorHandler=M}clearErrorHandler(){this._errorHandler=this._errorHandlerFb}reset(){this.currentState=this.initialState,this._oscParser.reset(),this._dcsParser.reset(),this._params.reset(),this._params.addParam(0),this._collect=0,this.precedingJoinState=0,this._parseStack.state!==0&&(this._parseStack.state=2,this._parseStack.handlers=[])}_preserveStack(M,w,y,k,I){this._parseStack.state=M,this._parseStack.handlers=w,this._parseStack.handlerPos=y,this._parseStack.transition=k,this._parseStack.chunkPos=I}parse(M,w,y){let k,I=0,P=0,R=0;if(this._parseStack.state)if(this._parseStack.state===2)this._parseStack.state=0,R=this._parseStack.chunkPos+1;else{if(y===void 0||this._parseStack.state===1)throw this._parseStack.state=1,new Error("improper continuation due to previous async handler, giving up parsing");let D=this._parseStack.handlers,N=this._parseStack.handlerPos-1;switch(this._parseStack.state){case 3:if(y===!1&&N>-1){for(;N>=0&&(k=D[N](this._params),k!==!0);N--)if(k instanceof Promise)return this._parseStack.handlerPos=N,k}this._parseStack.handlers=[];break;case 4:if(y===!1&&N>-1){for(;N>=0&&(k=D[N](),k!==!0);N--)if(k instanceof Promise)return this._parseStack.handlerPos=N,k}this._parseStack.handlers=[];break;case 6:if(I=M[this._parseStack.chunkPos],k=this._dcsParser.unhook(I!==24&&I!==26,y),k)return k;I===27&&(this._parseStack.transition|=1),this._params.reset(),this._params.addParam(0),this._collect=0;break;case 5:if(I=M[this._parseStack.chunkPos],k=this._oscParser.end(I!==24&&I!==26,y),k)return k;I===27&&(this._parseStack.transition|=1),this._params.reset(),this._params.addParam(0),this._collect=0}this._parseStack.state=0,R=this._parseStack.chunkPos+1,this.precedingJoinState=0,this.currentState=15&this._parseStack.transition}for(let D=R;D>4){case 2:for(let G=D+1;;++G){if(G>=w||(I=M[G])<32||I>126&&I=w||(I=M[G])<32||I>126&&I=w||(I=M[G])<32||I>126&&I=w||(I=M[G])<32||I>126&&I=0&&(k=N[q](this._params),k!==!0);q--)if(k instanceof Promise)return this._preserveStack(3,N,q,P,D),k;q<0&&this._csiHandlerFb(this._collect<<8|I,this._params),this.precedingJoinState=0;break;case 8:do switch(I){case 59:this._params.addParam(0);break;case 58:this._params.addSubParam(-1);break;default:this._params.addDigit(I-48)}while(++D47&&I<60);D--;break;case 9:this._collect<<=8,this._collect|=I;break;case 10:let de=this._escHandlers[this._collect<<8|I],fe=de?de.length-1:-1;for(;fe>=0&&(k=de[fe](),k!==!0);fe--)if(k instanceof Promise)return this._preserveStack(4,de,fe,P,D),k;fe<0&&this._escHandlerFb(this._collect<<8|I),this.precedingJoinState=0;break;case 11:this._params.reset(),this._params.addParam(0),this._collect=0;break;case 12:this._dcsParser.hook(this._collect<<8|I,this._params);break;case 13:for(let G=D+1;;++G)if(G>=w||(I=M[G])===24||I===26||I===27||I>127&&I=w||(I=M[G])<32||I>127&&I{Object.defineProperty(r,"__esModule",{value:!0}),r.OscHandler=r.OscParser=void 0;let c=a(5770),m=a(482),p=[];r.OscParser=class{constructor(){this._state=0,this._active=p,this._id=-1,this._handlers=Object.create(null),this._handlerFb=()=>{},this._stack={paused:!1,loopPosition:0,fallThrough:!1}}registerHandler(h,g){this._handlers[h]===void 0&&(this._handlers[h]=[]);let S=this._handlers[h];return S.push(g),{dispose:()=>{let x=S.indexOf(g);x!==-1&&S.splice(x,1)}}}clearHandler(h){this._handlers[h]&&delete this._handlers[h]}setHandlerFallback(h){this._handlerFb=h}dispose(){this._handlers=Object.create(null),this._handlerFb=()=>{},this._active=p}reset(){if(this._state===2)for(let h=this._stack.paused?this._stack.loopPosition-1:this._active.length-1;h>=0;--h)this._active[h].end(!1);this._stack.paused=!1,this._active=p,this._id=-1,this._state=0}_start(){if(this._active=this._handlers[this._id]||p,this._active.length)for(let h=this._active.length-1;h>=0;h--)this._active[h].start();else this._handlerFb(this._id,"START")}_put(h,g,S){if(this._active.length)for(let x=this._active.length-1;x>=0;x--)this._active[x].put(h,g,S);else this._handlerFb(this._id,"PUT",(0,m.utf32ToString)(h,g,S))}start(){this.reset(),this._state=1}put(h,g,S){if(this._state!==3){if(this._state===1)for(;g0&&this._put(h,g,S)}}end(h,g=!0){if(this._state!==0){if(this._state!==3)if(this._state===1&&this._start(),this._active.length){let S=!1,x=this._active.length-1,v=!1;if(this._stack.paused&&(x=this._stack.loopPosition-1,S=g,v=this._stack.fallThrough,this._stack.paused=!1),!v&&S===!1){for(;x>=0&&(S=this._active[x].end(h),S!==!0);x--)if(S instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=x,this._stack.fallThrough=!1,S;x--}for(;x>=0;x--)if(S=this._active[x].end(!1),S instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=x,this._stack.fallThrough=!0,S}else this._handlerFb(this._id,"END",h);this._active=p,this._id=-1,this._state=0}}},r.OscHandler=class{constructor(h){this._handler=h,this._data="",this._hitLimit=!1}start(){this._data="",this._hitLimit=!1}put(h,g,S){this._hitLimit||(this._data+=(0,m.utf32ToString)(h,g,S),this._data.length>c.PAYLOAD_LIMIT&&(this._data="",this._hitLimit=!0))}end(h){let g=!1;if(this._hitLimit)g=!1;else if(h&&(g=this._handler(this._data),g instanceof Promise))return g.then(S=>(this._data="",this._hitLimit=!1,S));return this._data="",this._hitLimit=!1,g}}},8742:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.Params=void 0;let a=2147483647;class c{static fromArray(p){let h=new c;if(!p.length)return h;for(let g=Array.isArray(p[0])?1:0;g256)throw new Error("maxSubParamsLength must not be greater than 256");this.params=new Int32Array(p),this.length=0,this._subParams=new Int32Array(h),this._subParamsLength=0,this._subParamsIdx=new Uint16Array(p),this._rejectDigits=!1,this._rejectSubDigits=!1,this._digitIsSub=!1}clone(){let p=new c(this.maxLength,this.maxSubParamsLength);return p.params.set(this.params),p.length=this.length,p._subParams.set(this._subParams),p._subParamsLength=this._subParamsLength,p._subParamsIdx.set(this._subParamsIdx),p._rejectDigits=this._rejectDigits,p._rejectSubDigits=this._rejectSubDigits,p._digitIsSub=this._digitIsSub,p}toArray(){let p=[];for(let h=0;h>8,S=255&this._subParamsIdx[h];S-g>0&&p.push(Array.prototype.slice.call(this._subParams,g,S))}return p}reset(){this.length=0,this._subParamsLength=0,this._rejectDigits=!1,this._rejectSubDigits=!1,this._digitIsSub=!1}addParam(p){if(this._digitIsSub=!1,this.length>=this.maxLength)this._rejectDigits=!0;else{if(p<-1)throw new Error("values lesser than -1 are not allowed");this._subParamsIdx[this.length]=this._subParamsLength<<8|this._subParamsLength,this.params[this.length++]=p>a?a:p}}addSubParam(p){if(this._digitIsSub=!0,this.length)if(this._rejectDigits||this._subParamsLength>=this.maxSubParamsLength)this._rejectSubDigits=!0;else{if(p<-1)throw new Error("values lesser than -1 are not allowed");this._subParams[this._subParamsLength++]=p>a?a:p,this._subParamsIdx[this.length-1]++}}hasSubParams(p){return(255&this._subParamsIdx[p])-(this._subParamsIdx[p]>>8)>0}getSubParams(p){let h=this._subParamsIdx[p]>>8,g=255&this._subParamsIdx[p];return g-h>0?this._subParams.subarray(h,g):null}getSubParamsAll(){let p={};for(let h=0;h>8,S=255&this._subParamsIdx[h];S-g>0&&(p[h]=this._subParams.slice(g,S))}return p}addDigit(p){let h;if(this._rejectDigits||!(h=this._digitIsSub?this._subParamsLength:this.length)||this._digitIsSub&&this._rejectSubDigits)return;let g=this._digitIsSub?this._subParams:this.params,S=g[h-1];g[h-1]=~S?Math.min(10*S+p,a):p}}r.Params=c},5741:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.AddonManager=void 0,r.AddonManager=class{constructor(){this._addons=[]}dispose(){for(let a=this._addons.length-1;a>=0;a--)this._addons[a].instance.dispose()}loadAddon(a,c){let m={instance:c,dispose:c.dispose,isDisposed:!1};this._addons.push(m),c.dispose=()=>this._wrappedAddonDispose(m),c.activate(a)}_wrappedAddonDispose(a){if(a.isDisposed)return;let c=-1;for(let m=0;m{Object.defineProperty(r,"__esModule",{value:!0}),r.BufferApiView=void 0;let c=a(3785),m=a(511);r.BufferApiView=class{constructor(p,h){this._buffer=p,this.type=h}init(p){return this._buffer=p,this}get cursorY(){return this._buffer.y}get cursorX(){return this._buffer.x}get viewportY(){return this._buffer.ydisp}get baseY(){return this._buffer.ybase}get length(){return this._buffer.lines.length}getLine(p){let h=this._buffer.lines.get(p);if(h)return new c.BufferLineApiView(h)}getNullCell(){return new m.CellData}}},3785:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.BufferLineApiView=void 0;let c=a(511);r.BufferLineApiView=class{constructor(m){this._line=m}get isWrapped(){return this._line.isWrapped}get length(){return this._line.length}getCell(m,p){if(!(m<0||m>=this._line.length))return p?(this._line.loadCell(m,p),p):this._line.loadCell(m,new c.CellData)}translateToString(m,p,h){return this._line.translateToString(m,p,h)}}},8285:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.BufferNamespaceApi=void 0;let c=a(8771),m=a(8460),p=a(844);class h extends p.Disposable{constructor(S){super(),this._core=S,this._onBufferChange=this.register(new m.EventEmitter),this.onBufferChange=this._onBufferChange.event,this._normal=new c.BufferApiView(this._core.buffers.normal,"normal"),this._alternate=new c.BufferApiView(this._core.buffers.alt,"alternate"),this._core.buffers.onBufferActivate(()=>this._onBufferChange.fire(this.active))}get active(){if(this._core.buffers.active===this._core.buffers.normal)return this.normal;if(this._core.buffers.active===this._core.buffers.alt)return this.alternate;throw new Error("Active buffer is neither normal nor alternate")}get normal(){return this._normal.init(this._core.buffers.normal)}get alternate(){return this._alternate.init(this._core.buffers.alt)}}r.BufferNamespaceApi=h},7975:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.ParserApi=void 0,r.ParserApi=class{constructor(a){this._core=a}registerCsiHandler(a,c){return this._core.registerCsiHandler(a,m=>c(m.toArray()))}addCsiHandler(a,c){return this.registerCsiHandler(a,c)}registerDcsHandler(a,c){return this._core.registerDcsHandler(a,(m,p)=>c(m,p.toArray()))}addDcsHandler(a,c){return this.registerDcsHandler(a,c)}registerEscHandler(a,c){return this._core.registerEscHandler(a,c)}addEscHandler(a,c){return this.registerEscHandler(a,c)}registerOscHandler(a,c){return this._core.registerOscHandler(a,c)}addOscHandler(a,c){return this.registerOscHandler(a,c)}}},7090:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.UnicodeApi=void 0,r.UnicodeApi=class{constructor(a){this._core=a}register(a){this._core.unicodeService.register(a)}get versions(){return this._core.unicodeService.versions}get activeVersion(){return this._core.unicodeService.activeVersion}set activeVersion(a){this._core.unicodeService.activeVersion=a}}},744:function(o,r,a){var c=this&&this.__decorate||function(v,M,w,y){var k,I=arguments.length,P=I<3?M:y===null?y=Object.getOwnPropertyDescriptor(M,w):y;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")P=Reflect.decorate(v,M,w,y);else for(var R=v.length-1;R>=0;R--)(k=v[R])&&(P=(I<3?k(P):I>3?k(M,w,P):k(M,w))||P);return I>3&&P&&Object.defineProperty(M,w,P),P},m=this&&this.__param||function(v,M){return function(w,y){M(w,y,v)}};Object.defineProperty(r,"__esModule",{value:!0}),r.BufferService=r.MINIMUM_ROWS=r.MINIMUM_COLS=void 0;let p=a(8460),h=a(844),g=a(5295),S=a(2585);r.MINIMUM_COLS=2,r.MINIMUM_ROWS=1;let x=r.BufferService=class extends h.Disposable{get buffer(){return this.buffers.active}constructor(v){super(),this.isUserScrolling=!1,this._onResize=this.register(new p.EventEmitter),this.onResize=this._onResize.event,this._onScroll=this.register(new p.EventEmitter),this.onScroll=this._onScroll.event,this.cols=Math.max(v.rawOptions.cols||0,r.MINIMUM_COLS),this.rows=Math.max(v.rawOptions.rows||0,r.MINIMUM_ROWS),this.buffers=this.register(new g.BufferSet(v,this))}resize(v,M){this.cols=v,this.rows=M,this.buffers.resize(v,M),this._onResize.fire({cols:v,rows:M})}reset(){this.buffers.reset(),this.isUserScrolling=!1}scroll(v,M=!1){let w=this.buffer,y;y=this._cachedBlankLine,y&&y.length===this.cols&&y.getFg(0)===v.fg&&y.getBg(0)===v.bg||(y=w.getBlankLine(v,M),this._cachedBlankLine=y),y.isWrapped=M;let k=w.ybase+w.scrollTop,I=w.ybase+w.scrollBottom;if(w.scrollTop===0){let P=w.lines.isFull;I===w.lines.length-1?P?w.lines.recycle().copyFrom(y):w.lines.push(y.clone()):w.lines.splice(I+1,0,y.clone()),P?this.isUserScrolling&&(w.ydisp=Math.max(w.ydisp-1,0)):(w.ybase++,this.isUserScrolling||w.ydisp++)}else{let P=I-k+1;w.lines.shiftElements(k+1,P-1,-1),w.lines.set(I,y.clone())}this.isUserScrolling||(w.ydisp=w.ybase),this._onScroll.fire(w.ydisp)}scrollLines(v,M,w){let y=this.buffer;if(v<0){if(y.ydisp===0)return;this.isUserScrolling=!0}else v+y.ydisp>=y.ybase&&(this.isUserScrolling=!1);let k=y.ydisp;y.ydisp=Math.max(Math.min(y.ydisp+v,y.ybase),0),k!==y.ydisp&&(M||this._onScroll.fire(y.ydisp))}};r.BufferService=x=c([m(0,S.IOptionsService)],x)},7994:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.CharsetService=void 0,r.CharsetService=class{constructor(){this.glevel=0,this._charsets=[]}reset(){this.charset=void 0,this._charsets=[],this.glevel=0}setgLevel(a){this.glevel=a,this.charset=this._charsets[a]}setgCharset(a,c){this._charsets[a]=c,this.glevel===a&&(this.charset=c)}}},1753:function(o,r,a){var c=this&&this.__decorate||function(y,k,I,P){var R,D=arguments.length,N=D<3?k:P===null?P=Object.getOwnPropertyDescriptor(k,I):P;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")N=Reflect.decorate(y,k,I,P);else for(var q=y.length-1;q>=0;q--)(R=y[q])&&(N=(D<3?R(N):D>3?R(k,I,N):R(k,I))||N);return D>3&&N&&Object.defineProperty(k,I,N),N},m=this&&this.__param||function(y,k){return function(I,P){k(I,P,y)}};Object.defineProperty(r,"__esModule",{value:!0}),r.CoreMouseService=void 0;let p=a(2585),h=a(8460),g=a(844),S={NONE:{events:0,restrict:()=>!1},X10:{events:1,restrict:y=>y.button!==4&&y.action===1&&(y.ctrl=!1,y.alt=!1,y.shift=!1,!0)},VT200:{events:19,restrict:y=>y.action!==32},DRAG:{events:23,restrict:y=>y.action!==32||y.button!==3},ANY:{events:31,restrict:y=>!0}};function x(y,k){let I=(y.ctrl?16:0)|(y.shift?4:0)|(y.alt?8:0);return y.button===4?(I|=64,I|=y.action):(I|=3&y.button,4&y.button&&(I|=64),8&y.button&&(I|=128),y.action===32?I|=32:y.action!==0||k||(I|=3)),I}let v=String.fromCharCode,M={DEFAULT:y=>{let k=[x(y,!1)+32,y.col+32,y.row+32];return k[0]>255||k[1]>255||k[2]>255?"":`\x1B[M${v(k[0])}${v(k[1])}${v(k[2])}`},SGR:y=>{let k=y.action===0&&y.button!==4?"m":"M";return`\x1B[<${x(y,!0)};${y.col};${y.row}${k}`},SGR_PIXELS:y=>{let k=y.action===0&&y.button!==4?"m":"M";return`\x1B[<${x(y,!0)};${y.x};${y.y}${k}`}},w=r.CoreMouseService=class extends g.Disposable{constructor(y,k){super(),this._bufferService=y,this._coreService=k,this._protocols={},this._encodings={},this._activeProtocol="",this._activeEncoding="",this._lastEvent=null,this._onProtocolChange=this.register(new h.EventEmitter),this.onProtocolChange=this._onProtocolChange.event;for(let I of Object.keys(S))this.addProtocol(I,S[I]);for(let I of Object.keys(M))this.addEncoding(I,M[I]);this.reset()}addProtocol(y,k){this._protocols[y]=k}addEncoding(y,k){this._encodings[y]=k}get activeProtocol(){return this._activeProtocol}get areMouseEventsActive(){return this._protocols[this._activeProtocol].events!==0}set activeProtocol(y){if(!this._protocols[y])throw new Error(`unknown protocol "${y}"`);this._activeProtocol=y,this._onProtocolChange.fire(this._protocols[y].events)}get activeEncoding(){return this._activeEncoding}set activeEncoding(y){if(!this._encodings[y])throw new Error(`unknown encoding "${y}"`);this._activeEncoding=y}reset(){this.activeProtocol="NONE",this.activeEncoding="DEFAULT",this._lastEvent=null}triggerMouseEvent(y){if(y.col<0||y.col>=this._bufferService.cols||y.row<0||y.row>=this._bufferService.rows||y.button===4&&y.action===32||y.button===3&&y.action!==32||y.button!==4&&(y.action===2||y.action===3)||(y.col++,y.row++,y.action===32&&this._lastEvent&&this._equalEvents(this._lastEvent,y,this._activeEncoding==="SGR_PIXELS"))||!this._protocols[this._activeProtocol].restrict(y))return!1;let k=this._encodings[this._activeEncoding](y);return k&&(this._activeEncoding==="DEFAULT"?this._coreService.triggerBinaryEvent(k):this._coreService.triggerDataEvent(k,!0)),this._lastEvent=y,!0}explainEvents(y){return{down:!!(1&y),up:!!(2&y),drag:!!(4&y),move:!!(8&y),wheel:!!(16&y)}}_equalEvents(y,k,I){if(I){if(y.x!==k.x||y.y!==k.y)return!1}else if(y.col!==k.col||y.row!==k.row)return!1;return y.button===k.button&&y.action===k.action&&y.ctrl===k.ctrl&&y.alt===k.alt&&y.shift===k.shift}};r.CoreMouseService=w=c([m(0,p.IBufferService),m(1,p.ICoreService)],w)},6975:function(o,r,a){var c=this&&this.__decorate||function(w,y,k,I){var P,R=arguments.length,D=R<3?y:I===null?I=Object.getOwnPropertyDescriptor(y,k):I;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")D=Reflect.decorate(w,y,k,I);else for(var N=w.length-1;N>=0;N--)(P=w[N])&&(D=(R<3?P(D):R>3?P(y,k,D):P(y,k))||D);return R>3&&D&&Object.defineProperty(y,k,D),D},m=this&&this.__param||function(w,y){return function(k,I){y(k,I,w)}};Object.defineProperty(r,"__esModule",{value:!0}),r.CoreService=void 0;let p=a(1439),h=a(8460),g=a(844),S=a(2585),x=Object.freeze({insertMode:!1}),v=Object.freeze({applicationCursorKeys:!1,applicationKeypad:!1,bracketedPasteMode:!1,origin:!1,reverseWraparound:!1,sendFocus:!1,wraparound:!0}),M=r.CoreService=class extends g.Disposable{constructor(w,y,k){super(),this._bufferService=w,this._logService=y,this._optionsService=k,this.isCursorInitialized=!1,this.isCursorHidden=!1,this._onData=this.register(new h.EventEmitter),this.onData=this._onData.event,this._onUserInput=this.register(new h.EventEmitter),this.onUserInput=this._onUserInput.event,this._onBinary=this.register(new h.EventEmitter),this.onBinary=this._onBinary.event,this._onRequestScrollToBottom=this.register(new h.EventEmitter),this.onRequestScrollToBottom=this._onRequestScrollToBottom.event,this.modes=(0,p.clone)(x),this.decPrivateModes=(0,p.clone)(v)}reset(){this.modes=(0,p.clone)(x),this.decPrivateModes=(0,p.clone)(v)}triggerDataEvent(w,y=!1){if(this._optionsService.rawOptions.disableStdin)return;let k=this._bufferService.buffer;y&&this._optionsService.rawOptions.scrollOnUserInput&&k.ybase!==k.ydisp&&this._onRequestScrollToBottom.fire(),y&&this._onUserInput.fire(),this._logService.debug(`sending data "${w}"`,()=>w.split("").map(I=>I.charCodeAt(0))),this._onData.fire(w)}triggerBinaryEvent(w){this._optionsService.rawOptions.disableStdin||(this._logService.debug(`sending binary "${w}"`,()=>w.split("").map(y=>y.charCodeAt(0))),this._onBinary.fire(w))}};r.CoreService=M=c([m(0,S.IBufferService),m(1,S.ILogService),m(2,S.IOptionsService)],M)},9074:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.DecorationService=void 0;let c=a(8055),m=a(8460),p=a(844),h=a(6106),g=0,S=0;class x extends p.Disposable{get decorations(){return this._decorations.values()}constructor(){super(),this._decorations=new h.SortedList(w=>w?.marker.line),this._onDecorationRegistered=this.register(new m.EventEmitter),this.onDecorationRegistered=this._onDecorationRegistered.event,this._onDecorationRemoved=this.register(new m.EventEmitter),this.onDecorationRemoved=this._onDecorationRemoved.event,this.register((0,p.toDisposable)(()=>this.reset()))}registerDecoration(w){if(w.marker.isDisposed)return;let y=new v(w);if(y){let k=y.marker.onDispose(()=>y.dispose());y.onDispose(()=>{y&&(this._decorations.delete(y)&&this._onDecorationRemoved.fire(y),k.dispose())}),this._decorations.insert(y),this._onDecorationRegistered.fire(y)}return y}reset(){for(let w of this._decorations.values())w.dispose();this._decorations.clear()}*getDecorationsAtCell(w,y,k){let I=0,P=0;for(let R of this._decorations.getKeyIterator(y))I=R.options.x??0,P=I+(R.options.width??1),w>=I&&w{g=P.options.x??0,S=g+(P.options.width??1),w>=g&&w{Object.defineProperty(r,"__esModule",{value:!0}),r.InstantiationService=r.ServiceCollection=void 0;let c=a(2585),m=a(8343);class p{constructor(...g){this._entries=new Map;for(let[S,x]of g)this.set(S,x)}set(g,S){let x=this._entries.get(g);return this._entries.set(g,S),x}forEach(g){for(let[S,x]of this._entries.entries())g(S,x)}has(g){return this._entries.has(g)}get(g){return this._entries.get(g)}}r.ServiceCollection=p,r.InstantiationService=class{constructor(){this._services=new p,this._services.set(c.IInstantiationService,this)}setService(h,g){this._services.set(h,g)}getService(h){return this._services.get(h)}createInstance(h,...g){let S=(0,m.getServiceDependencies)(h).sort((M,w)=>M.index-w.index),x=[];for(let M of S){let w=this._services.get(M.id);if(!w)throw new Error(`[createInstance] ${h.name} depends on UNKNOWN service ${M.id}.`);x.push(w)}let v=S.length>0?S[0].index:g.length;if(g.length!==v)throw new Error(`[createInstance] First service dependency of ${h.name} at position ${v+1} conflicts with ${g.length} static arguments`);return new h(...g,...x)}}},7866:function(o,r,a){var c=this&&this.__decorate||function(v,M,w,y){var k,I=arguments.length,P=I<3?M:y===null?y=Object.getOwnPropertyDescriptor(M,w):y;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")P=Reflect.decorate(v,M,w,y);else for(var R=v.length-1;R>=0;R--)(k=v[R])&&(P=(I<3?k(P):I>3?k(M,w,P):k(M,w))||P);return I>3&&P&&Object.defineProperty(M,w,P),P},m=this&&this.__param||function(v,M){return function(w,y){M(w,y,v)}};Object.defineProperty(r,"__esModule",{value:!0}),r.traceCall=r.setTraceLogger=r.LogService=void 0;let p=a(844),h=a(2585),g={trace:h.LogLevelEnum.TRACE,debug:h.LogLevelEnum.DEBUG,info:h.LogLevelEnum.INFO,warn:h.LogLevelEnum.WARN,error:h.LogLevelEnum.ERROR,off:h.LogLevelEnum.OFF},S,x=r.LogService=class extends p.Disposable{get logLevel(){return this._logLevel}constructor(v){super(),this._optionsService=v,this._logLevel=h.LogLevelEnum.OFF,this._updateLogLevel(),this.register(this._optionsService.onSpecificOptionChange("logLevel",()=>this._updateLogLevel())),S=this}_updateLogLevel(){this._logLevel=g[this._optionsService.rawOptions.logLevel]}_evalLazyOptionalParams(v){for(let M=0;MJSON.stringify(P)).join(", ")})`);let I=y.apply(this,k);return S.trace(`GlyphRenderer#${y.name} return`,I),I}}},7302:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.OptionsService=r.DEFAULT_OPTIONS=void 0;let c=a(8460),m=a(844),p=a(6114);r.DEFAULT_OPTIONS={cols:80,rows:24,cursorBlink:!1,cursorStyle:"block",cursorWidth:1,cursorInactiveStyle:"outline",customGlyphs:!0,drawBoldTextInBrightColors:!0,documentOverride:null,fastScrollModifier:"alt",fastScrollSensitivity:5,fontFamily:"courier-new, courier, monospace",fontSize:15,fontWeight:"normal",fontWeightBold:"bold",ignoreBracketedPasteMode:!1,lineHeight:1,letterSpacing:0,linkHandler:null,logLevel:"info",logger:null,scrollback:1e3,scrollOnUserInput:!0,scrollSensitivity:1,screenReaderMode:!1,smoothScrollDuration:0,macOptionIsMeta:!1,macOptionClickForcesSelection:!1,minimumContrastRatio:1,disableStdin:!1,allowProposedApi:!1,allowTransparency:!1,tabStopWidth:8,theme:{},rescaleOverlappingGlyphs:!1,rightClickSelectsWord:p.isMac,windowOptions:{},windowsMode:!1,windowsPty:{},wordSeparator:" ()[]{}',\"`",altClickMovesCursor:!0,convertEol:!1,termName:"xterm",cancelEvents:!1,overviewRulerWidth:0};let h=["normal","bold","100","200","300","400","500","600","700","800","900"];class g extends m.Disposable{constructor(x){super(),this._onOptionChange=this.register(new c.EventEmitter),this.onOptionChange=this._onOptionChange.event;let v=W({},r.DEFAULT_OPTIONS);for(let M in x)if(M in v)try{let w=x[M];v[M]=this._sanitizeAndValidateOption(M,w)}catch(w){console.error(w)}this.rawOptions=v,this.options=W({},v),this._setupOptions(),this.register((0,m.toDisposable)(()=>{this.rawOptions.linkHandler=null,this.rawOptions.documentOverride=null}))}onSpecificOptionChange(x,v){return this.onOptionChange(M=>{M===x&&v(this.rawOptions[x])})}onMultipleOptionChange(x,v){return this.onOptionChange(M=>{x.indexOf(M)!==-1&&v()})}_setupOptions(){let x=M=>{if(!(M in r.DEFAULT_OPTIONS))throw new Error(`No option with key "${M}"`);return this.rawOptions[M]},v=(M,w)=>{if(!(M in r.DEFAULT_OPTIONS))throw new Error(`No option with key "${M}"`);w=this._sanitizeAndValidateOption(M,w),this.rawOptions[M]!==w&&(this.rawOptions[M]=w,this._onOptionChange.fire(M))};for(let M in this.rawOptions){let w={get:x.bind(this,M),set:v.bind(this,M)};Object.defineProperty(this.options,M,w)}}_sanitizeAndValidateOption(x,v){switch(x){case"cursorStyle":if(v||(v=r.DEFAULT_OPTIONS[x]),!(function(M){return M==="block"||M==="underline"||M==="bar"})(v))throw new Error(`"${v}" is not a valid value for ${x}`);break;case"wordSeparator":v||(v=r.DEFAULT_OPTIONS[x]);break;case"fontWeight":case"fontWeightBold":if(typeof v=="number"&&1<=v&&v<=1e3)break;v=h.includes(v)?v:r.DEFAULT_OPTIONS[x];break;case"cursorWidth":v=Math.floor(v);case"lineHeight":case"tabStopWidth":if(v<1)throw new Error(`${x} cannot be less than 1, value: ${v}`);break;case"minimumContrastRatio":v=Math.max(1,Math.min(21,Math.round(10*v)/10));break;case"scrollback":if((v=Math.min(v,4294967295))<0)throw new Error(`${x} cannot be less than 0, value: ${v}`);break;case"fastScrollSensitivity":case"scrollSensitivity":if(v<=0)throw new Error(`${x} cannot be less than or equal to 0, value: ${v}`);break;case"rows":case"cols":if(!v&&v!==0)throw new Error(`${x} must be numeric, value: ${v}`);break;case"windowsPty":v=v??{}}return v}}r.OptionsService=g},2660:function(o,r,a){var c=this&&this.__decorate||function(g,S,x,v){var M,w=arguments.length,y=w<3?S:v===null?v=Object.getOwnPropertyDescriptor(S,x):v;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")y=Reflect.decorate(g,S,x,v);else for(var k=g.length-1;k>=0;k--)(M=g[k])&&(y=(w<3?M(y):w>3?M(S,x,y):M(S,x))||y);return w>3&&y&&Object.defineProperty(S,x,y),y},m=this&&this.__param||function(g,S){return function(x,v){S(x,v,g)}};Object.defineProperty(r,"__esModule",{value:!0}),r.OscLinkService=void 0;let p=a(2585),h=r.OscLinkService=class{constructor(g){this._bufferService=g,this._nextId=1,this._entriesWithId=new Map,this._dataByLinkId=new Map}registerLink(g){let S=this._bufferService.buffer;if(g.id===void 0){let k=S.addMarker(S.ybase+S.y),I={data:g,id:this._nextId++,lines:[k]};return k.onDispose(()=>this._removeMarkerFromLink(I,k)),this._dataByLinkId.set(I.id,I),I.id}let x=g,v=this._getEntryIdKey(x),M=this._entriesWithId.get(v);if(M)return this.addLineToLink(M.id,S.ybase+S.y),M.id;let w=S.addMarker(S.ybase+S.y),y={id:this._nextId++,key:this._getEntryIdKey(x),data:x,lines:[w]};return w.onDispose(()=>this._removeMarkerFromLink(y,w)),this._entriesWithId.set(y.key,y),this._dataByLinkId.set(y.id,y),y.id}addLineToLink(g,S){let x=this._dataByLinkId.get(g);if(x&&x.lines.every(v=>v.line!==S)){let v=this._bufferService.buffer.addMarker(S);x.lines.push(v),v.onDispose(()=>this._removeMarkerFromLink(x,v))}}getLinkData(g){return this._dataByLinkId.get(g)?.data}_getEntryIdKey(g){return`${g.id};;${g.uri}`}_removeMarkerFromLink(g,S){let x=g.lines.indexOf(S);x!==-1&&(g.lines.splice(x,1),g.lines.length===0&&(g.data.id!==void 0&&this._entriesWithId.delete(g.key),this._dataByLinkId.delete(g.id)))}};r.OscLinkService=h=c([m(0,p.IBufferService)],h)},8343:(o,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.createDecorator=r.getServiceDependencies=r.serviceRegistry=void 0;let a="di$target",c="di$dependencies";r.serviceRegistry=new Map,r.getServiceDependencies=function(m){return m[c]||[]},r.createDecorator=function(m){if(r.serviceRegistry.has(m))return r.serviceRegistry.get(m);let p=function(h,g,S){if(arguments.length!==3)throw new Error("@IServiceName-decorator can only be used to decorate a parameter");(function(x,v,M){v[a]===v?v[c].push({id:x,index:M}):(v[c]=[{id:x,index:M}],v[a]=v)})(p,h,S)};return p.toString=()=>m,r.serviceRegistry.set(m,p),p}},2585:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.IDecorationService=r.IUnicodeService=r.IOscLinkService=r.IOptionsService=r.ILogService=r.LogLevelEnum=r.IInstantiationService=r.ICharsetService=r.ICoreService=r.ICoreMouseService=r.IBufferService=void 0;let c=a(8343);var m;r.IBufferService=(0,c.createDecorator)("BufferService"),r.ICoreMouseService=(0,c.createDecorator)("CoreMouseService"),r.ICoreService=(0,c.createDecorator)("CoreService"),r.ICharsetService=(0,c.createDecorator)("CharsetService"),r.IInstantiationService=(0,c.createDecorator)("InstantiationService"),(function(p){p[p.TRACE=0]="TRACE",p[p.DEBUG=1]="DEBUG",p[p.INFO=2]="INFO",p[p.WARN=3]="WARN",p[p.ERROR=4]="ERROR",p[p.OFF=5]="OFF"})(m||(r.LogLevelEnum=m={})),r.ILogService=(0,c.createDecorator)("LogService"),r.IOptionsService=(0,c.createDecorator)("OptionsService"),r.IOscLinkService=(0,c.createDecorator)("OscLinkService"),r.IUnicodeService=(0,c.createDecorator)("UnicodeService"),r.IDecorationService=(0,c.createDecorator)("DecorationService")},1480:(o,r,a)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.UnicodeService=void 0;let c=a(8460),m=a(225);class p{static extractShouldJoin(g){return(1&g)!=0}static extractWidth(g){return g>>1&3}static extractCharKind(g){return g>>3}static createPropertyValue(g,S,x=!1){return(16777215&g)<<3|(3&S)<<1|(x?1:0)}constructor(){this._providers=Object.create(null),this._active="",this._onChange=new c.EventEmitter,this.onChange=this._onChange.event;let g=new m.UnicodeV6;this.register(g),this._active=g.version,this._activeProvider=g}dispose(){this._onChange.dispose()}get versions(){return Object.keys(this._providers)}get activeVersion(){return this._active}set activeVersion(g){if(!this._providers[g])throw new Error(`unknown Unicode version "${g}"`);this._active=g,this._activeProvider=this._providers[g],this._onChange.fire(g)}register(g){this._providers[g.version]=g}wcwidth(g){return this._activeProvider.wcwidth(g)}getStringCellWidth(g){let S=0,x=0,v=g.length;for(let M=0;M=v)return S+this.wcwidth(w);let I=g.charCodeAt(M);56320<=I&&I<=57343?w=1024*(w-55296)+I-56320+65536:S+=this.wcwidth(I)}let y=this.charProperties(w,x),k=p.extractWidth(y);p.extractShouldJoin(y)&&(k-=p.extractWidth(x)),S+=k,x=y}return S}charProperties(g,S){return this._activeProvider.charProperties(g,S)}}r.UnicodeService=p}},i={};function e(o){var r=i[o];if(r!==void 0)return r.exports;var a=i[o]={exports:{}};return n[o].call(a.exports,a,a.exports,e),a.exports}var t={};return(()=>{var o=t;Object.defineProperty(o,"__esModule",{value:!0}),o.Terminal=void 0;let r=e(9042),a=e(3236),c=e(844),m=e(5741),p=e(8285),h=e(7975),g=e(7090),S=["cols","rows"];class x extends c.Disposable{constructor(M){super(),this._core=this.register(new a.Terminal(M)),this._addonManager=this.register(new m.AddonManager),this._publicOptions=W({},this._core.options);let w=k=>this._core.options[k],y=(k,I)=>{this._checkReadonlyOptions(k),this._core.options[k]=I};for(let k in this._core.options){let I={get:w.bind(this,k),set:y.bind(this,k)};Object.defineProperty(this._publicOptions,k,I)}}_checkReadonlyOptions(M){if(S.includes(M))throw new Error(`Option "${M}" can only be set in the constructor`)}_checkProposedApi(){if(!this._core.optionsService.rawOptions.allowProposedApi)throw new Error("You must set the allowProposedApi option to true to use proposed API")}get onBell(){return this._core.onBell}get onBinary(){return this._core.onBinary}get onCursorMove(){return this._core.onCursorMove}get onData(){return this._core.onData}get onKey(){return this._core.onKey}get onLineFeed(){return this._core.onLineFeed}get onRender(){return this._core.onRender}get onResize(){return this._core.onResize}get onScroll(){return this._core.onScroll}get onSelectionChange(){return this._core.onSelectionChange}get onTitleChange(){return this._core.onTitleChange}get onWriteParsed(){return this._core.onWriteParsed}get element(){return this._core.element}get parser(){return this._parser||(this._parser=new h.ParserApi(this._core)),this._parser}get unicode(){return this._checkProposedApi(),new g.UnicodeApi(this._core)}get textarea(){return this._core.textarea}get rows(){return this._core.rows}get cols(){return this._core.cols}get buffer(){return this._buffer||(this._buffer=this.register(new p.BufferNamespaceApi(this._core))),this._buffer}get markers(){return this._checkProposedApi(),this._core.markers}get modes(){let M=this._core.coreService.decPrivateModes,w="none";switch(this._core.coreMouseService.activeProtocol){case"X10":w="x10";break;case"VT200":w="vt200";break;case"DRAG":w="drag";break;case"ANY":w="any"}return{applicationCursorKeysMode:M.applicationCursorKeys,applicationKeypadMode:M.applicationKeypad,bracketedPasteMode:M.bracketedPasteMode,insertMode:this._core.coreService.modes.insertMode,mouseTrackingMode:w,originMode:M.origin,reverseWraparoundMode:M.reverseWraparound,sendFocusMode:M.sendFocus,wraparoundMode:M.wraparound}}get options(){return this._publicOptions}set options(M){for(let w in M)this._publicOptions[w]=M[w]}blur(){this._core.blur()}focus(){this._core.focus()}input(M,w=!0){this._core.input(M,w)}resize(M,w){this._verifyIntegers(M,w),this._core.resize(M,w)}open(M){this._core.open(M)}attachCustomKeyEventHandler(M){this._core.attachCustomKeyEventHandler(M)}attachCustomWheelEventHandler(M){this._core.attachCustomWheelEventHandler(M)}registerLinkProvider(M){return this._core.registerLinkProvider(M)}registerCharacterJoiner(M){return this._checkProposedApi(),this._core.registerCharacterJoiner(M)}deregisterCharacterJoiner(M){this._checkProposedApi(),this._core.deregisterCharacterJoiner(M)}registerMarker(M=0){return this._verifyIntegers(M),this._core.registerMarker(M)}registerDecoration(M){return this._checkProposedApi(),this._verifyPositiveIntegers(M.x??0,M.width??0,M.height??0),this._core.registerDecoration(M)}hasSelection(){return this._core.hasSelection()}select(M,w,y){this._verifyIntegers(M,w,y),this._core.select(M,w,y)}getSelection(){return this._core.getSelection()}getSelectionPosition(){return this._core.getSelectionPosition()}clearSelection(){this._core.clearSelection()}selectAll(){this._core.selectAll()}selectLines(M,w){this._verifyIntegers(M,w),this._core.selectLines(M,w)}dispose(){super.dispose()}scrollLines(M){this._verifyIntegers(M),this._core.scrollLines(M)}scrollPages(M){this._verifyIntegers(M),this._core.scrollPages(M)}scrollToTop(){this._core.scrollToTop()}scrollToBottom(){this._core.scrollToBottom()}scrollToLine(M){this._verifyIntegers(M),this._core.scrollToLine(M)}clear(){this._core.clear()}write(M,w){this._core.write(M,w)}writeln(M,w){this._core.write(M),this._core.write(`\r -`,w)}paste(M){this._core.paste(M)}refresh(M,w){this._verifyIntegers(M,w),this._core.refresh(M,w)}reset(){this._core.reset()}clearTextureAtlas(){this._core.clearTextureAtlas()}loadAddon(M){this._addonManager.loadAddon(this,M)}static get strings(){return r}_verifyIntegers(...M){for(let w of M)if(w===1/0||isNaN(w)||w%1!=0)throw new Error("This API only accepts integers")}_verifyPositiveIntegers(...M){for(let w of M)if(w&&(w===1/0||isNaN(w)||w%1!=0||w<0))throw new Error("This API only accepts positive integers")}}o.Terminal=x})(),t})())});var uN=ws((lk,pN)=>{(function(n,i){typeof lk=="object"&&typeof pN=="object"?pN.exports=i():typeof define=="function"&&define.amd?define([],i):typeof lk=="object"?lk.AttachAddon=i():n.AttachAddon=i()})(self,()=>(()=>{"use strict";var n={};return(()=>{var i=n;function e(t,o,r){return t.addEventListener(o,r),{dispose:()=>{r&&t.removeEventListener(o,r)}}}Object.defineProperty(i,"__esModule",{value:!0}),i.AttachAddon=void 0,i.AttachAddon=class{constructor(t,o){this._disposables=[],this._socket=t,this._socket.binaryType="arraybuffer",this._bidirectional=!(o&&o.bidirectional===!1)}activate(t){this._disposables.push(e(this._socket,"message",o=>{let r=o.data;t.write(typeof r=="string"?r:new Uint8Array(r))})),this._bidirectional&&(this._disposables.push(t.onData(o=>this._sendData(o))),this._disposables.push(t.onBinary(o=>this._sendBinary(o)))),this._disposables.push(e(this._socket,"close",()=>this.dispose())),this._disposables.push(e(this._socket,"error",()=>this.dispose()))}dispose(){for(let t of this._disposables)t.dispose()}_sendData(t){this._checkOpenSocket()&&this._socket.send(t)}_sendBinary(t){if(!this._checkOpenSocket())return;let o=new Uint8Array(t.length);for(let r=0;r{(function(n,i){typeof ck=="object"&&typeof hN=="object"?hN.exports=i():typeof define=="function"&&define.amd?define([],i):typeof ck=="object"?ck.FitAddon=i():n.FitAddon=i()})(self,()=>(()=>{"use strict";var n={};return(()=>{var i=n;Object.defineProperty(i,"__esModule",{value:!0}),i.FitAddon=void 0,i.FitAddon=class{activate(e){this._terminal=e}dispose(){}fit(){let e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;let t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal||!this._terminal.element||!this._terminal.element.parentElement)return;let e=this._terminal._core,t=e._renderService.dimensions;if(t.css.cell.width===0||t.css.cell.height===0)return;let o=this._terminal.options.scrollback===0?0:e.viewport.scrollBarWidth,r=window.getComputedStyle(this._terminal.element.parentElement),a=parseInt(r.getPropertyValue("height")),c=Math.max(0,parseInt(r.getPropertyValue("width"))),m=window.getComputedStyle(this._terminal.element),p=a-(parseInt(m.getPropertyValue("padding-top"))+parseInt(m.getPropertyValue("padding-bottom"))),h=c-(parseInt(m.getPropertyValue("padding-right"))+parseInt(m.getPropertyValue("padding-left")))-o;return{cols:Math.max(2,Math.floor(h/t.css.cell.width)),rows:Math.max(1,Math.floor(p/t.css.cell.height))}}}})(),n})())});var aR=new RegExp(`(\\:not\\()|(([\\.\\#]?)[-\\w]+)|(?:\\[([-.\\w*\\\\$]+)(?:=(["']?)([^\\]"']*)\\5)?\\])|(\\))|(\\s*,\\s*)`,"g"),$h=class n{element=null;classNames=[];attrs=[];notSelectors=[];static parse(i){let e=[],t=(m,p)=>{p.notSelectors.length>0&&!p.element&&p.classNames.length==0&&p.attrs.length==0&&(p.element="*"),m.push(p)},o=new n,r,a=o,c=!1;for(aR.lastIndex=0;r=aR.exec(i);){if(r[1]){if(c)throw new Error("Nesting :not in a selector is not allowed");c=!0,a=new n,o.notSelectors.push(a)}let m=r[2];if(m){let h=r[3];h==="#"?a.addAttribute("id",m.slice(1)):h==="."?a.addClassName(m.slice(1)):a.setElement(m)}let p=r[4];if(p&&a.addAttribute(a.unescapeAttribute(p),r[6]),r[7]&&(c=!1,a=o),r[8]){if(c)throw new Error("Multiple selectors in :not are not supported");t(e,o),o=a=new n}}return t(e,o),e}unescapeAttribute(i){let e="",t=!1;for(let o=0;o0&&i.push("class",this.classNames.join(" ")),i.concat(this.attrs)}addAttribute(i,e=""){this.attrs.push(i,e&&e.toLowerCase()||"")}addClassName(i){this.classNames.push(i.toLowerCase())}toString(){let i=this.element||"";if(this.classNames&&this.classNames.forEach(e=>i+=`.${e}`),this.attrs)for(let e=0;ei+=`:not(${e})`),i}},J1=class n{static createNotMatcher(i){let e=new n;return e.addSelectables(i,null),e}_elementMap=new Map;_elementPartialMap=new Map;_classMap=new Map;_classPartialMap=new Map;_attrValueMap=new Map;_attrValuePartialMap=new Map;_listContexts=[];addSelectables(i,e){let t=null;i.length>1&&(t=new _E(i),this._listContexts.push(t));for(let o=0;o0&&(!this.listContext||!this.listContext.alreadyMatched)&&(t=!J1.createNotMatcher(this.notSelectors).match(i,null)),t&&e&&(!this.listContext||!this.listContext.alreadyMatched)&&(this.listContext&&(this.listContext.alreadyMatched=!0),e(this.selector,this.cbContext)),t}},eb=class{registry;constructor(i){this.registry=i}match(i){return this.registry.has(i)?this.registry.get(i):[]}};var jp=(function(n){return n[n.Emulated=0]="Emulated",n[n.None=2]="None",n[n.ShadowDom=3]="ShadowDom",n[n.ExperimentalIsolatedShadowDom=4]="ExperimentalIsolatedShadowDom",n})(jp||{}),qD=(function(n){return n[n.OnPush=0]="OnPush",n[n.Default=1]="Default",n[n.Eager=1]="Eager",n})(qD||{}),R_=(function(n){return n[n.None=0]="None",n[n.SignalBased=1]="SignalBased",n[n.HasDecoratorInputTransform=2]="HasDecoratorInputTransform",n})(R_||{}),sR={name:"custom-elements"},lR={name:"no-errors-schema"};var ro=(function(n){return n[n.NONE=0]="NONE",n[n.HTML=1]="HTML",n[n.STYLE=2]="STYLE",n[n.SCRIPT=3]="SCRIPT",n[n.URL=4]="URL",n[n.RESOURCE_URL=5]="RESOURCE_URL",n[n.ATTRIBUTE_NO_BINDING=6]="ATTRIBUTE_NO_BINDING",n})(ro||{});function nG(n){let i=n.classNames&&n.classNames.length?[8,...n.classNames]:[];return[n.element&&n.element!=="*"?n.element:"",...n.attrs,...i]}function iG(n){let i=n.classNames&&n.classNames.length?[8,...n.classNames]:[];return n.element?[5,n.element,...n.attrs,...i]:n.attrs.length?[3,...n.attrs,...i]:n.classNames&&n.classNames.length?[9,...n.classNames]:[]}function oG(n){let i=nG(n),e=n.notSelectors&&n.notSelectors.length?n.notSelectors.map(t=>iG(t)):[];return i.concat(...e)}function QD(n){return n?$h.parse(n).map(oG):[]}var Cd=(function(n){return n[n.Directive=0]="Directive",n[n.Component=1]="Component",n[n.Injectable=2]="Injectable",n[n.Pipe=3]="Pipe",n[n.NgModule=4]="NgModule",n})(Cd||{});var tb;function rG(n){return cG(lG(n.nodes).join("")+`[${n.meaning}]`)}function aG(n){return n.id||t6(n)}function t6(n){let i=new CE,e=n.nodes.map(t=>t.visit(i,null));return n6(e.join(""),n.meaning)}var nb=class{visitText(i,e){return i.value}visitContainer(i,e){return`[${i.children.map(t=>t.visit(this)).join(", ")}]`}visitIcu(i,e){let t=Object.keys(i.cases).map(o=>`${o} {${i.cases[o].visit(this)}}`);return`{${i.expression}, ${i.type}, ${t.join(", ")}}`}visitTagPlaceholder(i,e){return i.isVoid?``:`${i.children.map(t=>t.visit(this)).join(", ")}`}visitPlaceholder(i,e){return i.value?`${i.value}`:``}visitIcuPlaceholder(i,e){return`${i.value.visit(this)}`}visitBlockPlaceholder(i,e){return`${i.children.map(t=>t.visit(this)).join(", ")}`}},sG=new nb;function lG(n){return n.map(i=>i.visit(sG,null))}var CE=class extends nb{visitIcu(i){let e=Object.keys(i.cases).map(t=>`${t} {${i.cases[t].visit(this)}}`);return`{${i.type}, ${e.join(", ")}}`}};function cG(n){tb??=new TextEncoder;let i=[...tb.encode(n)],e=pG(i,XD.Big),t=i.length*8,o=new Uint32Array(80),r=1732584193,a=4023233417,c=2562383102,m=271733878,p=3285377520;e[t>>5]|=128<<24-t%32,e[(t+64>>9<<4)+15]=t;for(let h=0;h>>0).toString(16).padStart(8,"0")}function dG(n,i,e,t){return n<20?[i&e|~i&t,1518500249]:n<40?[i^e^t,1859775393]:n<60?[i&e|i&t|e&t,2400959708]:[i^e^t,3395469782]}function cR(n){tb??=new TextEncoder;let i=tb.encode(n),e=new DataView(i.buffer,i.byteOffset,i.byteLength),t=dR(e,i.length,0),o=dR(e,i.length,102072);return t==0&&(o==0||o==1)&&(t=t^319790063,o=o^-1801410264),BigInt.asUintN(32,BigInt(t))<>BigInt(63)&BigInt(1),e+=cR(i)),BigInt.asUintN(63,e).toString()}function dR(n,i,e){let t=2654435769,o=2654435769,r=0,a=i-12;for(;r<=a;r+=12){t+=n.getUint32(r,!0),o+=n.getUint32(r+4,!0),e+=n.getUint32(r+8,!0);let m=mR(t,o,e);t=m[0],o=m[1],e=m[2]}let c=i-r;return e+=i,c>=4?(t+=n.getUint32(r,!0),r+=4,c>=8?(o+=n.getUint32(r,!0),r+=4,c>=9&&(e+=n.getUint8(r++)<<8),c>=10&&(e+=n.getUint8(r++)<<16),c===11&&(e+=n.getUint8(r++)<<24)):(c>=5&&(o+=n.getUint8(r++)),c>=6&&(o+=n.getUint8(r++)<<8),c===7&&(o+=n.getUint8(r++)<<16))):(c>=1&&(t+=n.getUint8(r++)),c>=2&&(t+=n.getUint8(r++)<<8),c===3&&(t+=n.getUint8(r++)<<16)),mR(t,o,e)[2]}function mR(n,i,e){return n-=i,n-=e,n^=e>>>13,i-=e,i-=n,i^=n<<8,e-=n,e-=i,e^=i>>>13,n-=i,n-=e,n^=e>>>12,i-=e,i-=n,i^=n<<16,e-=n,e-=i,e^=i>>>5,n-=i,n-=e,n^=e>>>3,i-=e,i-=n,i^=n<<10,e-=n,e-=i,e^=i>>>15,[n,i,e]}var XD=(function(n){return n[n.Little=0]="Little",n[n.Big=1]="Big",n})(XD||{});function Mh(n,i){return mG(n,i)[1]}function mG(n,i){let e=(n&65535)+(i&65535),t=(n>>>16)+(i>>>16)+(e>>>16);return[t>>>16,t<<16|e&65535]}function XT(n,i){return n<>>32-i}function pG(n,i){let e=n.length+3>>>2,t=[];for(let o=0;o=n.length?0:n[i]}function uG(n,i,e){let t=0;if(e===XD.Big)for(let o=0;o<4;o++)t+=pR(n,i+o)<<24-8*o;else for(let o=0;o<4;o++)t+=pR(n,i+o)<<8*o;return t}var i6=(function(n){return n[n.None=0]="None",n[n.Const=1]="Const",n})(i6||{}),ib=class{modifiers;constructor(i=i6.None){this.modifiers=i}hasModifier(i){return(this.modifiers&i)!==0}},Td=(function(n){return n[n.Dynamic=0]="Dynamic",n[n.Bool=1]="Bool",n[n.String=2]="String",n[n.Int=3]="Int",n[n.Number=4]="Number",n[n.Function=5]="Function",n[n.Inferred=6]="Inferred",n[n.None=7]="None",n})(Td||{}),wc=class extends ib{name;constructor(i,e){super(e),this.name=i}visitType(i,e){return i.visitBuiltinType(this,e)}},dl=class extends ib{value;typeParams;constructor(i,e,t=null){super(e),this.value=i,this.typeParams=t}visitType(i,e){return i.visitExpressionType(this,e)}};var ls=new wc(Td.Dynamic),Ul=new wc(Td.Inferred),hG=new wc(Td.Bool),eFe=new wc(Td.Int),iu=new wc(Td.Number),YD=new wc(Td.String),tFe=new wc(Td.Function),Mc=new wc(Td.None),Y_=(function(n){return n[n.Minus=0]="Minus",n[n.Plus=1]="Plus",n})(Y_||{}),lt=(function(n){return n[n.Equals=0]="Equals",n[n.NotEquals=1]="NotEquals",n[n.Assign=2]="Assign",n[n.Identical=3]="Identical",n[n.NotIdentical=4]="NotIdentical",n[n.Minus=5]="Minus",n[n.Plus=6]="Plus",n[n.Divide=7]="Divide",n[n.Multiply=8]="Multiply",n[n.Modulo=9]="Modulo",n[n.And=10]="And",n[n.Or=11]="Or",n[n.BitwiseOr=12]="BitwiseOr",n[n.BitwiseAnd=13]="BitwiseAnd",n[n.Lower=14]="Lower",n[n.LowerEquals=15]="LowerEquals",n[n.Bigger=16]="Bigger",n[n.BiggerEquals=17]="BiggerEquals",n[n.NullishCoalesce=18]="NullishCoalesce",n[n.Exponentiation=19]="Exponentiation",n[n.In=20]="In",n[n.InstanceOf=21]="InstanceOf",n[n.AdditionAssignment=22]="AdditionAssignment",n[n.SubtractionAssignment=23]="SubtractionAssignment",n[n.MultiplicationAssignment=24]="MultiplicationAssignment",n[n.DivisionAssignment=25]="DivisionAssignment",n[n.RemainderAssignment=26]="RemainderAssignment",n[n.ExponentiationAssignment=27]="ExponentiationAssignment",n[n.AndAssignment=28]="AndAssignment",n[n.OrAssignment=29]="OrAssignment",n[n.NullishCoalesceAssignment=30]="NullishCoalesceAssignment",n})(lt||{});function fG(n,i){return n==null||i==null?n==i:n.isEquivalent(i)}function o6(n,i,e){let t=n.length;if(t!==i.length)return!1;for(let o=0;oe.isEquivalent(t))}var Li=class{type;sourceSpan;constructor(i,e){this.type=i||null,this.sourceSpan=e||null}prop(i,e){return new Rs(this,i,null,e)}key(i,e,t){return new wd(this,i,e,t)}callFn(i,e,t){return new cs(this,i,null,e,t)}instantiate(i,e,t){return new Z_(this,i,e,t)}conditional(i,e=null,t){return new kc(this,i,e,null,t)}equals(i,e){return new fi(lt.Equals,this,i,null,e)}notEquals(i,e){return new fi(lt.NotEquals,this,i,null,e)}identical(i,e){return new fi(lt.Identical,this,i,null,e)}notIdentical(i,e){return new fi(lt.NotIdentical,this,i,null,e)}minus(i,e){return new fi(lt.Minus,this,i,null,e)}plus(i,e){return new fi(lt.Plus,this,i,null,e)}divide(i,e){return new fi(lt.Divide,this,i,null,e)}multiply(i,e){return new fi(lt.Multiply,this,i,null,e)}modulo(i,e){return new fi(lt.Modulo,this,i,null,e)}power(i,e){return new fi(lt.Exponentiation,this,i,null,e)}and(i,e){return new fi(lt.And,this,i,null,e)}bitwiseOr(i,e){return new fi(lt.BitwiseOr,this,i,null,e)}bitwiseAnd(i,e){return new fi(lt.BitwiseAnd,this,i,null,e)}or(i,e){return new fi(lt.Or,this,i,null,e)}lower(i,e){return new fi(lt.Lower,this,i,null,e)}lowerEquals(i,e){return new fi(lt.LowerEquals,this,i,null,e)}bigger(i,e){return new fi(lt.Bigger,this,i,null,e)}biggerEquals(i,e){return new fi(lt.BiggerEquals,this,i,null,e)}isBlank(i){return this.equals(bG,i)}nullishCoalesce(i,e){return new fi(lt.NullishCoalesce,this,i,null,e)}toStmt(){return new ma(this,null)}},Gl=class n extends Li{name;constructor(i,e,t){super(e,t),this.name=i}isEquivalent(i){return i instanceof n&&this.name===i.name}isConstant(){return!1}visitExpression(i,e){return i.visitReadVarExpr(this,e)}clone(){return new n(this.name,this.type,this.sourceSpan)}set(i){return new fi(lt.Assign,this,i,null,this.sourceSpan)}},Hh=class n extends Li{expr;constructor(i,e,t){super(e,t),this.expr=i}visitExpression(i,e){return i.visitTypeofExpr(this,e)}isEquivalent(i){return i instanceof n&&i.expr.isEquivalent(this.expr)}isConstant(){return this.expr.isConstant()}clone(){return new n(this.expr.clone())}},ob=class n extends Li{expr;constructor(i,e,t){super(e,t),this.expr=i}visitExpression(i,e){return i.visitVoidExpr(this,e)}isEquivalent(i){return i instanceof n&&i.expr.isEquivalent(this.expr)}isConstant(){return this.expr.isConstant()}clone(){return new n(this.expr.clone())}},ri=class n extends Li{node;constructor(i,e,t){super(e,t),this.node=i}isEquivalent(i){return i instanceof n&&this.node===i.node}isConstant(){return!1}visitExpression(i,e){return i.visitWrappedNodeExpr(this,e)}clone(){return new n(this.node,this.type,this.sourceSpan)}},cs=class n extends Li{fn;args;pure;constructor(i,e,t,o,r=!1){super(t,o),this.fn=i,this.args=e,this.pure=r}get receiver(){return this.fn}isEquivalent(i){return i instanceof n&&this.fn.isEquivalent(i.fn)&&Ns(this.args,i.args)&&this.pure===i.pure}isConstant(){return!1}visitExpression(i,e){return i.visitInvokeFunctionExpr(this,e)}clone(){return new n(this.fn.clone(),this.args.map(i=>i.clone()),this.type,this.sourceSpan,this.pure)}},K_=class n extends Li{tag;template;constructor(i,e,t,o){super(t,o),this.tag=i,this.template=e}isEquivalent(i){return i instanceof n&&this.tag.isEquivalent(i.tag)&&this.template.isEquivalent(i.template)}isConstant(){return!1}visitExpression(i,e){return i.visitTaggedTemplateLiteralExpr(this,e)}clone(){return new n(this.tag.clone(),this.template.clone(),this.type,this.sourceSpan)}},Z_=class n extends Li{classExpr;args;constructor(i,e,t,o){super(t,o),this.classExpr=i,this.args=e}isEquivalent(i){return i instanceof n&&this.classExpr.isEquivalent(i.classExpr)&&Ns(this.args,i.args)}isConstant(){return!1}visitExpression(i,e){return i.visitInstantiateExpr(this,e)}clone(){return new n(this.classExpr.clone(),this.args.map(i=>i.clone()),this.type,this.sourceSpan)}},Uh=class n extends Li{body;flags;constructor(i,e,t){super(null,t),this.body=i,this.flags=e}isEquivalent(i){return i instanceof n&&this.body===i.body&&this.flags===i.flags}isConstant(){return!0}visitExpression(i,e){return i.visitRegularExpressionLiteral(this,e)}clone(){return new n(this.body,this.flags,this.sourceSpan)}},da=class n extends Li{value;constructor(i,e,t){super(e,t),this.value=i}isEquivalent(i){return i instanceof n&&this.value===i.value}isConstant(){return!0}visitExpression(i,e){return i.visitLiteralExpr(this,e)}clone(){return new n(this.value,this.type,this.sourceSpan)}},J_=class n extends Li{elements;expressions;constructor(i,e,t){super(null,t),this.elements=i,this.expressions=e}isEquivalent(i){return i instanceof n&&o6(this.elements,i.elements,(e,t)=>e.text===t.text)&&Ns(this.expressions,i.expressions)}isConstant(){return!1}visitExpression(i,e){return i.visitTemplateLiteralExpr(this,e)}clone(){return new n(this.elements.map(i=>i.clone()),this.expressions.map(i=>i.clone()))}},rb=class n extends Li{text;rawText;constructor(i,e,t){super(YD,e),this.text=i,this.rawText=t??bE(Q1(i))}visitExpression(i,e){return i.visitTemplateLiteralElementExpr(this,e)}isEquivalent(i){return i instanceof n&&i.text===this.text&&i.rawText===this.rawText}isConstant(){return!0}clone(){return new n(this.text,this.sourceSpan,this.rawText)}},Xp=class{text;sourceSpan;constructor(i,e){this.text=i,this.sourceSpan=e}},Vh=class{text;sourceSpan;associatedMessage;constructor(i,e,t){this.text=i,this.sourceSpan=e,this.associatedMessage=t}},gG="|",uR="@@",_G="\u241F",ab=class n extends Li{metaBlock;messageParts;placeHolderNames;expressions;constructor(i,e,t,o,r){super(YD,r),this.metaBlock=i,this.messageParts=e,this.placeHolderNames=t,this.expressions=o}isEquivalent(i){return!1}isConstant(){return!1}visitExpression(i,e){return i.visitLocalizedString(this,e)}clone(){return new n(this.metaBlock,this.messageParts,this.placeHolderNames,this.expressions.map(i=>i.clone()),this.sourceSpan)}serializeI18nHead(){let i=this.metaBlock.description||"";return this.metaBlock.meaning&&(i=`${this.metaBlock.meaning}${gG}${i}`),this.metaBlock.customId&&(i=`${i}${uR}${this.metaBlock.customId}`),this.metaBlock.legacyIds&&this.metaBlock.legacyIds.forEach(e=>{i=`${i}${_G}${e}`}),hR(i,this.messageParts[0].text,this.getMessagePartSourceSpan(0))}getMessagePartSourceSpan(i){return this.messageParts[i]?.sourceSpan??this.sourceSpan}getPlaceholderSourceSpan(i){return this.placeHolderNames[i]?.sourceSpan??this.expressions[i]?.sourceSpan??this.sourceSpan}serializeI18nTemplatePart(i){let e=this.placeHolderNames[i-1],t=this.messageParts[i],o=e.text;return e.associatedMessage?.legacyIds.length===0&&(o+=`${uR}${n6(e.associatedMessage.messageString,e.associatedMessage.meaning)}`),hR(o,t.text,this.getMessagePartSourceSpan(i))}},Q1=n=>n.replace(/\\/g,"\\\\"),vG=n=>n.replace(/^:/,"\\:"),CG=n=>n.replace(/:/g,"\\:"),bE=n=>n.replace(/`/g,"\\`").replace(/\${/g,"$\\{");function hR(n,i,e){return n===""?{cooked:i,raw:bE(vG(Q1(i))),range:e}:{cooked:`:${n}:${i}`,raw:bE(`:${CG(Q1(n))}:${Q1(i)}`),range:e}}var ou=class n extends Li{value;typeParams;constructor(i,e,t=null,o){super(e,o),this.value=i,this.typeParams=t}isEquivalent(i){return i instanceof n&&this.value.name===i.value.name&&this.value.moduleName===i.value.moduleName}isConstant(){return!1}visitExpression(i,e){return i.visitExternalExpr(this,e)}clone(){return new n(this.value,this.type,this.typeParams,this.sourceSpan)}};var kc=class n extends Li{condition;falseCase;trueCase;constructor(i,e,t=null,o,r){super(o||e.type,r),this.condition=i,this.falseCase=t,this.trueCase=e}isEquivalent(i){return i instanceof n&&this.condition.isEquivalent(i.condition)&&this.trueCase.isEquivalent(i.trueCase)&&fG(this.falseCase,i.falseCase)}isConstant(){return!1}visitExpression(i,e){return i.visitConditionalExpr(this,e)}clone(){return new n(this.condition.clone(),this.trueCase.clone(),this.falseCase?.clone(),this.type,this.sourceSpan)}};var e0=class n extends Li{condition;constructor(i,e){super(hG,e),this.condition=i}isEquivalent(i){return i instanceof n&&this.condition.isEquivalent(i.condition)}isConstant(){return!1}visitExpression(i,e){return i.visitNotExpr(this,e)}clone(){return new n(this.condition.clone(),this.sourceSpan)}},Sr=class n{name;type;constructor(i,e=null){this.name=i,this.type=e}isEquivalent(i){return this.name===i.name}clone(){return new n(this.name,this.type)}},vm=class n extends Li{params;statements;name;constructor(i,e,t,o,r){super(t,o),this.params=i,this.statements=e,this.name=r}isEquivalent(i){return(i instanceof n||i instanceof t0)&&Ns(this.params,i.params)&&Ns(this.statements,i.statements)}isConstant(){return!1}visitExpression(i,e){return i.visitFunctionExpr(this,e)}toDeclStmt(i,e){return new t0(i,this.params,this.statements,this.type,e,this.sourceSpan)}clone(){return new n(this.params.map(i=>i.clone()),this.statements,this.type,this.sourceSpan,this.name)}},vu=class xE extends Li{params;body;constructor(i,e,t,o){super(t,o),this.params=i,this.body=e}isEquivalent(i){return!(i instanceof xE)||!Ns(this.params,i.params)?!1:this.body instanceof Li&&i.body instanceof Li?this.body.isEquivalent(i.body):Array.isArray(this.body)&&Array.isArray(i.body)?Ns(this.body,i.body):!1}isConstant(){return!1}visitExpression(i,e){return i.visitArrowFunctionExpr(this,e)}clone(){return new xE(this.params.map(i=>i.clone()),Array.isArray(this.body)?this.body:this.body.clone(),this.type,this.sourceSpan)}toDeclStmt(i,e){return new Fr(i,this,Ul,e,this.sourceSpan)}},ru=class n extends Li{operator;expr;parens;constructor(i,e,t,o,r=!0){super(t||iu,o),this.operator=i,this.expr=e,this.parens=r}isEquivalent(i){return i instanceof n&&this.operator===i.operator&&this.expr.isEquivalent(i.expr)}isConstant(){return!1}visitExpression(i,e){return i.visitUnaryOperatorExpr(this,e)}clone(){return new n(this.operator,this.expr.clone(),this.type,this.sourceSpan,this.parens)}},Wl=class n extends Li{expr;constructor(i,e,t){super(e,t),this.expr=i}visitExpression(i,e){return i.visitParenthesizedExpr(this,e)}isEquivalent(i){return i instanceof n&&i.expr.isEquivalent(this.expr)}isConstant(){return this.expr.isConstant()}clone(){return new n(this.expr.clone())}},fi=class n extends Li{operator;rhs;lhs;constructor(i,e,t,o,r){super(o||e.type,r),this.operator=i,this.rhs=t,this.lhs=e}isEquivalent(i){return i instanceof n&&this.operator===i.operator&&this.lhs.isEquivalent(i.lhs)&&this.rhs.isEquivalent(i.rhs)}isConstant(){return!1}visitExpression(i,e){return i.visitBinaryOperatorExpr(this,e)}clone(){return new n(this.operator,this.lhs.clone(),this.rhs.clone(),this.type,this.sourceSpan)}isAssignment(){let i=this.operator;return i===lt.Assign||i===lt.AdditionAssignment||i===lt.SubtractionAssignment||i===lt.MultiplicationAssignment||i===lt.DivisionAssignment||i===lt.RemainderAssignment||i===lt.ExponentiationAssignment||i===lt.AndAssignment||i===lt.OrAssignment||i===lt.NullishCoalesceAssignment}},Rs=class n extends Li{receiver;name;constructor(i,e,t,o){super(t,o),this.receiver=i,this.name=e}get index(){return this.name}isEquivalent(i){return i instanceof n&&this.receiver.isEquivalent(i.receiver)&&this.name===i.name}isConstant(){return!1}visitExpression(i,e){return i.visitReadPropExpr(this,e)}set(i){return new fi(lt.Assign,this.receiver.prop(this.name),i,null,this.sourceSpan)}clone(){return new n(this.receiver.clone(),this.name,this.type,this.sourceSpan)}},wd=class n extends Li{receiver;index;constructor(i,e,t,o){super(t,o),this.receiver=i,this.index=e}isEquivalent(i){return i instanceof n&&this.receiver.isEquivalent(i.receiver)&&this.index.isEquivalent(i.index)}isConstant(){return!1}visitExpression(i,e){return i.visitReadKeyExpr(this,e)}set(i){return new fi(lt.Assign,this.receiver.key(this.index),i,null,this.sourceSpan)}clone(){return new n(this.receiver.clone(),this.index.clone(),this.type,this.sourceSpan)}},Tc=class n extends Li{entries;constructor(i,e,t){super(e,t),this.entries=i}isConstant(){return this.entries.every(i=>i.isConstant())}isEquivalent(i){return i instanceof n&&Ns(this.entries,i.entries)}visitExpression(i,e){return i.visitLiteralArrayExpr(this,e)}clone(){return new n(this.entries.map(i=>i.clone()),this.type,this.sourceSpan)}},Gh=class n{key;value;quoted;constructor(i,e,t){this.key=i,this.value=e,this.quoted=t}isEquivalent(i){return this.key===i.key&&this.value.isEquivalent(i.value)}clone(){return new n(this.key,this.value.clone(),this.quoted)}isConstant(){return this.value.isConstant()}},Cm=class n{expression;constructor(i){this.expression=i}isEquivalent(i){return i instanceof n&&this.expression.isEquivalent(i.expression)}clone(){return new n(this.expression.clone())}isConstant(){return this.expression.isConstant()}},ql=class n extends Li{entries;valueType=null;constructor(i,e,t){super(e,t),this.entries=i,e&&(this.valueType=e.valueType)}isEquivalent(i){return i instanceof n&&Ns(this.entries,i.entries)}isConstant(){return this.entries.every(i=>i.isConstant())}visitExpression(i,e){return i.visitLiteralMapExpr(this,e)}clone(){let i=this.entries.map(e=>e.clone());return new n(i,this.type,this.sourceSpan)}};var au=class n extends Li{expression;constructor(i,e){super(null,e),this.expression=i}isEquivalent(i){return i instanceof n&&this.expression.isEquivalent(i.expression)}isConstant(){return this.expression.isConstant()}visitExpression(i,e){return i.visitSpreadElementExpr(this,e)}clone(){return new n(this.expression.clone(),this.sourceSpan)}},Wh=new da(null,null,null),bG=new da(null,Ul,null),la=(function(n){return n[n.None=0]="None",n[n.Final=1]="Final",n[n.Private=2]="Private",n[n.Exported=4]="Exported",n[n.Static=8]="Static",n})(la||{}),yE=class{text;multiline;trailingNewline;constructor(i,e,t){this.text=i,this.multiline=e,this.trailingNewline=t}toString(){return this.multiline?` ${this.text} `:this.text}},sb=class extends yE{tags;constructor(i){super("",!0,!0),this.tags=i}toString(){return MG(this.tags)}},su=class{modifiers;sourceSpan;leadingComments;constructor(i=la.None,e=null,t){this.modifiers=i,this.sourceSpan=e,this.leadingComments=t}hasModifier(i){return(this.modifiers&i)!==0}addLeadingComment(i){this.leadingComments=this.leadingComments??[],this.leadingComments.push(i)}},Fr=class n extends su{name;value;type;constructor(i,e,t,o,r,a){super(o,r,a),this.name=i,this.value=e,this.type=t||e&&e.type||null}isEquivalent(i){return i instanceof n&&this.name===i.name&&(this.value?!!i.value&&this.value.isEquivalent(i.value):!i.value)}visitStatement(i,e){return i.visitDeclareVarStmt(this,e)}},t0=class n extends su{name;params;statements;type;constructor(i,e,t,o,r,a,c){super(r,a,c),this.name=i,this.params=e,this.statements=t,this.type=o||null}isEquivalent(i){return i instanceof n&&Ns(this.params,i.params)&&Ns(this.statements,i.statements)}visitStatement(i,e){return i.visitDeclareFunctionStmt(this,e)}},ma=class n extends su{expr;constructor(i,e,t){super(la.None,e,t),this.expr=i}isEquivalent(i){return i instanceof n&&this.expr.isEquivalent(i.expr)}visitStatement(i,e){return i.visitExpressionStmt(this,e)}},wr=class n extends su{value;constructor(i,e=null,t){super(la.None,e,t),this.value=i}isEquivalent(i){return i instanceof n&&this.value.isEquivalent(i.value)}visitStatement(i,e){return i.visitReturnStmt(this,e)}},lb=class n extends su{condition;trueCase;falseCase;constructor(i,e,t=[],o,r){super(la.None,o,r),this.condition=i,this.trueCase=e,this.falseCase=t}isEquivalent(i){return i instanceof n&&this.condition.isEquivalent(i.condition)&&Ns(this.trueCase,i.trueCase)&&Ns(this.falseCase,i.falseCase)}visitStatement(i,e){return i.visitIfStmt(this,e)}};function xG(n=[]){return new sb(n)}function Zn(n,i,e){return new Gl(n,i,e)}function Wt(n,i=null,e){return new ou(n,null,i,e)}function ca(n,i,e){return new dl(n,i,e)}function q0(n){return new Hh(n)}function Qi(n,i,e){return new Tc(n,i,e)}function ml(n,i=null){return new ql(n.map(e=>new Gh(e.key,e.value,e.quoted)),i,null)}function yG(n,i){return new e0(n,i)}function bm(n,i,e,t,o){return new vm(n,i,e,t,o)}function Fs(n,i,e,t){return new vu(n,i,e,t)}function sx(n,i,e,t,o){return new lb(n,i,e,t,o)}function SG(n,i,e,t){return new K_(n,i,e,t)}function Me(n,i,e){return new da(n,i,e)}function wG(n,i,e,t,o){return new ab(n,i,e,t,o)}function fR(n){let i="";if(n.tagName&&(i+=` @${n.tagName}`),n.text){if(n.text.match(/\/\*|\*\//))throw new Error('JSDoc text cannot contain "/*" and "*/"');i+=" "+n.text.replace(/@/g,"\\@")}return i}function MG(n){if(n.length===0)return"";if(n.length===1&&n[0].tagName&&!n[0].text)return`*${fR(n[0])} `;let i=`* -`;for(let e of n)i+=" *",i+=fR(e).replace(/\n/g,` - * `),i+=` -`;return i+=" ",i}var kG="_c",TG={},EG=50,cb=class n extends Li{resolved;original;shared=!1;constructor(i){super(i.type),this.resolved=i,this.original=i}visitExpression(i,e){return e===TG?this.original.visitExpression(i,e):this.resolved.visitExpression(i,e)}isEquivalent(i){return i instanceof n&&this.resolved.isEquivalent(i.resolved)}isConstant(){return!0}clone(){throw new Error("Not supported.")}fixup(i){this.resolved=i,this.shared=!0}},db=class{isClosureCompilerEnabled;statements=[];literals=new Map;literalFactories=new Map;sharedConstants=new Map;_claimedNames=new Map;nextNameIndex=0;constructor(i=!1){this.isClosureCompilerEnabled=i}getConstLiteral(i,e){if(i instanceof da&&!gR(i)||i instanceof cb)return i;let t=n0.INSTANCE.keyOf(i),o=this.literals.get(t),r=!1;if(o||(o=new cb(i),this.literals.set(t,o),r=!0),!r&&!o.shared||r&&e){let a=this.freshName(),c,m;this.isClosureCompilerEnabled&&gR(i)?(c=new vm([],[new wr(i)]),m=Zn(a).callFn([])):(c=i,m=Zn(a)),this.statements.push(new Fr(a,c,Ul,la.Final)),o.fixup(m)}return o}getSharedConstant(i,e){let t=i.keyOf(e);if(!this.sharedConstants.has(t)){let o=this.freshName();this.sharedConstants.set(t,Zn(o)),this.statements.push(i.toSharedConstantDeclaration(o,e))}return this.sharedConstants.get(t)}getSharedFunctionReference(i,e,t=!0){let o=i instanceof vu;for(let a of this.statements)if(o&&a instanceof Fr&&a.value?.isEquivalent(i)||!o&&a instanceof t0&&i instanceof vm&&i.isEquivalent(a))return Zn(a.name);let r=t?this.uniqueName(e):e;return this.statements.push(i instanceof vm?i.toDeclStmt(r,la.Final):new Fr(r,i,Ul,la.Final,i.sourceSpan)),Zn(r)}uniqueName(i,e=!0){let t=this._claimedNames.get(i)??0,o=t===0&&!e?`${i}`:`${i}${t}`;return this._claimedNames.set(i,t+1),o}freshName(){return this.uniqueName(kG)}},n0=class n{static INSTANCE=new n;keyOf(i){if(i instanceof da&&typeof i.value=="string")return`"${i.value}"`;if(i instanceof da)return String(i.value);if(i instanceof Uh)return`/${i.body}/${i.flags??""}`;if(i instanceof Tc){let e=[];for(let t of i.entries)e.push(this.keyOf(t));return`[${e.join(",")}]`}else if(i instanceof ql){let e=[];for(let t of i.entries)if(t instanceof Cm)e.push("..."+this.keyOf(t.expression));else{let o=t.key;t.quoted&&(o=`"${o}"`),e.push(o+":"+this.keyOf(t.value))}return`{${e.join(",")}}`}else{if(i instanceof ou)return`import("${i.value.moduleName}", ${i.value.name})`;if(i instanceof Gl)return`read(${i.name})`;if(i instanceof Hh)return`typeof(${this.keyOf(i.expr)})`;if(i instanceof au)return`...${this.keyOf(i.expression)}`;throw new Error(`${this.constructor.name} does not handle expressions of type ${i.constructor.name}`)}}};function gR(n){return n instanceof da&&typeof n.value=="string"&&n.value.length>=EG}var Ce="@angular/core",he=(()=>{class n{static core={name:null,moduleName:Ce};static namespaceHTML={name:"\u0275\u0275namespaceHTML",moduleName:Ce};static namespaceMathML={name:"\u0275\u0275namespaceMathML",moduleName:Ce};static namespaceSVG={name:"\u0275\u0275namespaceSVG",moduleName:Ce};static element={name:"\u0275\u0275element",moduleName:Ce};static elementStart={name:"\u0275\u0275elementStart",moduleName:Ce};static elementEnd={name:"\u0275\u0275elementEnd",moduleName:Ce};static domElement={name:"\u0275\u0275domElement",moduleName:Ce};static domElementStart={name:"\u0275\u0275domElementStart",moduleName:Ce};static domElementEnd={name:"\u0275\u0275domElementEnd",moduleName:Ce};static domElementContainer={name:"\u0275\u0275domElementContainer",moduleName:Ce};static domElementContainerStart={name:"\u0275\u0275domElementContainerStart",moduleName:Ce};static domElementContainerEnd={name:"\u0275\u0275domElementContainerEnd",moduleName:Ce};static domTemplate={name:"\u0275\u0275domTemplate",moduleName:Ce};static domListener={name:"\u0275\u0275domListener",moduleName:Ce};static advance={name:"\u0275\u0275advance",moduleName:Ce};static syntheticHostProperty={name:"\u0275\u0275syntheticHostProperty",moduleName:Ce};static syntheticHostListener={name:"\u0275\u0275syntheticHostListener",moduleName:Ce};static attribute={name:"\u0275\u0275attribute",moduleName:Ce};static classProp={name:"\u0275\u0275classProp",moduleName:Ce};static elementContainerStart={name:"\u0275\u0275elementContainerStart",moduleName:Ce};static elementContainerEnd={name:"\u0275\u0275elementContainerEnd",moduleName:Ce};static elementContainer={name:"\u0275\u0275elementContainer",moduleName:Ce};static styleMap={name:"\u0275\u0275styleMap",moduleName:Ce};static classMap={name:"\u0275\u0275classMap",moduleName:Ce};static styleProp={name:"\u0275\u0275styleProp",moduleName:Ce};static interpolate={name:"\u0275\u0275interpolate",moduleName:Ce};static interpolate1={name:"\u0275\u0275interpolate1",moduleName:Ce};static interpolate2={name:"\u0275\u0275interpolate2",moduleName:Ce};static interpolate3={name:"\u0275\u0275interpolate3",moduleName:Ce};static interpolate4={name:"\u0275\u0275interpolate4",moduleName:Ce};static interpolate5={name:"\u0275\u0275interpolate5",moduleName:Ce};static interpolate6={name:"\u0275\u0275interpolate6",moduleName:Ce};static interpolate7={name:"\u0275\u0275interpolate7",moduleName:Ce};static interpolate8={name:"\u0275\u0275interpolate8",moduleName:Ce};static interpolateV={name:"\u0275\u0275interpolateV",moduleName:Ce};static nextContext={name:"\u0275\u0275nextContext",moduleName:Ce};static resetView={name:"\u0275\u0275resetView",moduleName:Ce};static templateCreate={name:"\u0275\u0275template",moduleName:Ce};static defer={name:"\u0275\u0275defer",moduleName:Ce};static deferWhen={name:"\u0275\u0275deferWhen",moduleName:Ce};static deferOnIdle={name:"\u0275\u0275deferOnIdle",moduleName:Ce};static deferOnImmediate={name:"\u0275\u0275deferOnImmediate",moduleName:Ce};static deferOnTimer={name:"\u0275\u0275deferOnTimer",moduleName:Ce};static deferOnHover={name:"\u0275\u0275deferOnHover",moduleName:Ce};static deferOnInteraction={name:"\u0275\u0275deferOnInteraction",moduleName:Ce};static deferOnViewport={name:"\u0275\u0275deferOnViewport",moduleName:Ce};static deferPrefetchWhen={name:"\u0275\u0275deferPrefetchWhen",moduleName:Ce};static deferPrefetchOnIdle={name:"\u0275\u0275deferPrefetchOnIdle",moduleName:Ce};static deferPrefetchOnImmediate={name:"\u0275\u0275deferPrefetchOnImmediate",moduleName:Ce};static deferPrefetchOnTimer={name:"\u0275\u0275deferPrefetchOnTimer",moduleName:Ce};static deferPrefetchOnHover={name:"\u0275\u0275deferPrefetchOnHover",moduleName:Ce};static deferPrefetchOnInteraction={name:"\u0275\u0275deferPrefetchOnInteraction",moduleName:Ce};static deferPrefetchOnViewport={name:"\u0275\u0275deferPrefetchOnViewport",moduleName:Ce};static deferHydrateWhen={name:"\u0275\u0275deferHydrateWhen",moduleName:Ce};static deferHydrateNever={name:"\u0275\u0275deferHydrateNever",moduleName:Ce};static deferHydrateOnIdle={name:"\u0275\u0275deferHydrateOnIdle",moduleName:Ce};static deferHydrateOnImmediate={name:"\u0275\u0275deferHydrateOnImmediate",moduleName:Ce};static deferHydrateOnTimer={name:"\u0275\u0275deferHydrateOnTimer",moduleName:Ce};static deferHydrateOnHover={name:"\u0275\u0275deferHydrateOnHover",moduleName:Ce};static deferHydrateOnInteraction={name:"\u0275\u0275deferHydrateOnInteraction",moduleName:Ce};static deferHydrateOnViewport={name:"\u0275\u0275deferHydrateOnViewport",moduleName:Ce};static deferEnableTimerScheduling={name:"\u0275\u0275deferEnableTimerScheduling",moduleName:Ce};static conditionalCreate={name:"\u0275\u0275conditionalCreate",moduleName:Ce};static conditionalBranchCreate={name:"\u0275\u0275conditionalBranchCreate",moduleName:Ce};static conditional={name:"\u0275\u0275conditional",moduleName:Ce};static repeater={name:"\u0275\u0275repeater",moduleName:Ce};static repeaterCreate={name:"\u0275\u0275repeaterCreate",moduleName:Ce};static repeaterTrackByIndex={name:"\u0275\u0275repeaterTrackByIndex",moduleName:Ce};static repeaterTrackByIdentity={name:"\u0275\u0275repeaterTrackByIdentity",moduleName:Ce};static componentInstance={name:"\u0275\u0275componentInstance",moduleName:Ce};static text={name:"\u0275\u0275text",moduleName:Ce};static enableBindings={name:"\u0275\u0275enableBindings",moduleName:Ce};static disableBindings={name:"\u0275\u0275disableBindings",moduleName:Ce};static getCurrentView={name:"\u0275\u0275getCurrentView",moduleName:Ce};static textInterpolate={name:"\u0275\u0275textInterpolate",moduleName:Ce};static textInterpolate1={name:"\u0275\u0275textInterpolate1",moduleName:Ce};static textInterpolate2={name:"\u0275\u0275textInterpolate2",moduleName:Ce};static textInterpolate3={name:"\u0275\u0275textInterpolate3",moduleName:Ce};static textInterpolate4={name:"\u0275\u0275textInterpolate4",moduleName:Ce};static textInterpolate5={name:"\u0275\u0275textInterpolate5",moduleName:Ce};static textInterpolate6={name:"\u0275\u0275textInterpolate6",moduleName:Ce};static textInterpolate7={name:"\u0275\u0275textInterpolate7",moduleName:Ce};static textInterpolate8={name:"\u0275\u0275textInterpolate8",moduleName:Ce};static textInterpolateV={name:"\u0275\u0275textInterpolateV",moduleName:Ce};static restoreView={name:"\u0275\u0275restoreView",moduleName:Ce};static pureFunction0={name:"\u0275\u0275pureFunction0",moduleName:Ce};static pureFunction1={name:"\u0275\u0275pureFunction1",moduleName:Ce};static pureFunction2={name:"\u0275\u0275pureFunction2",moduleName:Ce};static pureFunction3={name:"\u0275\u0275pureFunction3",moduleName:Ce};static pureFunction4={name:"\u0275\u0275pureFunction4",moduleName:Ce};static pureFunction5={name:"\u0275\u0275pureFunction5",moduleName:Ce};static pureFunction6={name:"\u0275\u0275pureFunction6",moduleName:Ce};static pureFunction7={name:"\u0275\u0275pureFunction7",moduleName:Ce};static pureFunction8={name:"\u0275\u0275pureFunction8",moduleName:Ce};static pureFunctionV={name:"\u0275\u0275pureFunctionV",moduleName:Ce};static pipeBind1={name:"\u0275\u0275pipeBind1",moduleName:Ce};static pipeBind2={name:"\u0275\u0275pipeBind2",moduleName:Ce};static pipeBind3={name:"\u0275\u0275pipeBind3",moduleName:Ce};static pipeBind4={name:"\u0275\u0275pipeBind4",moduleName:Ce};static pipeBindV={name:"\u0275\u0275pipeBindV",moduleName:Ce};static domProperty={name:"\u0275\u0275domProperty",moduleName:Ce};static ariaProperty={name:"\u0275\u0275ariaProperty",moduleName:Ce};static property={name:"\u0275\u0275property",moduleName:Ce};static control={name:"\u0275\u0275control",moduleName:Ce};static controlCreate={name:"\u0275\u0275controlCreate",moduleName:Ce};static animationEnterListener={name:"\u0275\u0275animateEnterListener",moduleName:Ce};static animationLeaveListener={name:"\u0275\u0275animateLeaveListener",moduleName:Ce};static animationEnter={name:"\u0275\u0275animateEnter",moduleName:Ce};static animationLeave={name:"\u0275\u0275animateLeave",moduleName:Ce};static i18n={name:"\u0275\u0275i18n",moduleName:Ce};static i18nAttributes={name:"\u0275\u0275i18nAttributes",moduleName:Ce};static i18nExp={name:"\u0275\u0275i18nExp",moduleName:Ce};static i18nStart={name:"\u0275\u0275i18nStart",moduleName:Ce};static i18nEnd={name:"\u0275\u0275i18nEnd",moduleName:Ce};static i18nApply={name:"\u0275\u0275i18nApply",moduleName:Ce};static i18nPostprocess={name:"\u0275\u0275i18nPostprocess",moduleName:Ce};static pipe={name:"\u0275\u0275pipe",moduleName:Ce};static projection={name:"\u0275\u0275projection",moduleName:Ce};static projectionDef={name:"\u0275\u0275projectionDef",moduleName:Ce};static reference={name:"\u0275\u0275reference",moduleName:Ce};static inject={name:"\u0275\u0275inject",moduleName:Ce};static injectAttribute={name:"\u0275\u0275injectAttribute",moduleName:Ce};static directiveInject={name:"\u0275\u0275directiveInject",moduleName:Ce};static invalidFactory={name:"\u0275\u0275invalidFactory",moduleName:Ce};static invalidFactoryDep={name:"\u0275\u0275invalidFactoryDep",moduleName:Ce};static templateRefExtractor={name:"\u0275\u0275templateRefExtractor",moduleName:Ce};static forwardRef={name:"forwardRef",moduleName:Ce};static resolveForwardRef={name:"resolveForwardRef",moduleName:Ce};static replaceMetadata={name:"\u0275\u0275replaceMetadata",moduleName:Ce};static getReplaceMetadataURL={name:"\u0275\u0275getReplaceMetadataURL",moduleName:Ce};static \u0275\u0275defineInjectable={name:"\u0275\u0275defineInjectable",moduleName:Ce};static declareInjectable={name:"\u0275\u0275ngDeclareInjectable",moduleName:Ce};static InjectableDeclaration={name:"\u0275\u0275InjectableDeclaration",moduleName:Ce};static resolveWindow={name:"\u0275\u0275resolveWindow",moduleName:Ce};static resolveDocument={name:"\u0275\u0275resolveDocument",moduleName:Ce};static resolveBody={name:"\u0275\u0275resolveBody",moduleName:Ce};static getComponentDepsFactory={name:"\u0275\u0275getComponentDepsFactory",moduleName:Ce};static defineComponent={name:"\u0275\u0275defineComponent",moduleName:Ce};static declareComponent={name:"\u0275\u0275ngDeclareComponent",moduleName:Ce};static setComponentScope={name:"\u0275\u0275setComponentScope",moduleName:Ce};static ChangeDetectionStrategy={name:"ChangeDetectionStrategy",moduleName:Ce};static ViewEncapsulation={name:"ViewEncapsulation",moduleName:Ce};static ComponentDeclaration={name:"\u0275\u0275ComponentDeclaration",moduleName:Ce};static FactoryDeclaration={name:"\u0275\u0275FactoryDeclaration",moduleName:Ce};static declareFactory={name:"\u0275\u0275ngDeclareFactory",moduleName:Ce};static FactoryTarget={name:"\u0275\u0275FactoryTarget",moduleName:Ce};static defineDirective={name:"\u0275\u0275defineDirective",moduleName:Ce};static declareDirective={name:"\u0275\u0275ngDeclareDirective",moduleName:Ce};static DirectiveDeclaration={name:"\u0275\u0275DirectiveDeclaration",moduleName:Ce};static InjectorDef={name:"\u0275\u0275InjectorDef",moduleName:Ce};static InjectorDeclaration={name:"\u0275\u0275InjectorDeclaration",moduleName:Ce};static defineInjector={name:"\u0275\u0275defineInjector",moduleName:Ce};static declareInjector={name:"\u0275\u0275ngDeclareInjector",moduleName:Ce};static NgModuleDeclaration={name:"\u0275\u0275NgModuleDeclaration",moduleName:Ce};static ModuleWithProviders={name:"ModuleWithProviders",moduleName:Ce};static defineNgModule={name:"\u0275\u0275defineNgModule",moduleName:Ce};static declareNgModule={name:"\u0275\u0275ngDeclareNgModule",moduleName:Ce};static setNgModuleScope={name:"\u0275\u0275setNgModuleScope",moduleName:Ce};static registerNgModuleType={name:"\u0275\u0275registerNgModuleType",moduleName:Ce};static PipeDeclaration={name:"\u0275\u0275PipeDeclaration",moduleName:Ce};static definePipe={name:"\u0275\u0275definePipe",moduleName:Ce};static declarePipe={name:"\u0275\u0275ngDeclarePipe",moduleName:Ce};static declareClassMetadata={name:"\u0275\u0275ngDeclareClassMetadata",moduleName:Ce};static declareClassMetadataAsync={name:"\u0275\u0275ngDeclareClassMetadataAsync",moduleName:Ce};static setClassMetadata={name:"\u0275setClassMetadata",moduleName:Ce};static setClassMetadataAsync={name:"\u0275setClassMetadataAsync",moduleName:Ce};static setClassDebugInfo={name:"\u0275setClassDebugInfo",moduleName:Ce};static queryRefresh={name:"\u0275\u0275queryRefresh",moduleName:Ce};static viewQuery={name:"\u0275\u0275viewQuery",moduleName:Ce};static loadQuery={name:"\u0275\u0275loadQuery",moduleName:Ce};static contentQuery={name:"\u0275\u0275contentQuery",moduleName:Ce};static viewQuerySignal={name:"\u0275\u0275viewQuerySignal",moduleName:Ce};static contentQuerySignal={name:"\u0275\u0275contentQuerySignal",moduleName:Ce};static queryAdvance={name:"\u0275\u0275queryAdvance",moduleName:Ce};static twoWayProperty={name:"\u0275\u0275twoWayProperty",moduleName:Ce};static twoWayBindingSet={name:"\u0275\u0275twoWayBindingSet",moduleName:Ce};static twoWayListener={name:"\u0275\u0275twoWayListener",moduleName:Ce};static declareLet={name:"\u0275\u0275declareLet",moduleName:Ce};static storeLet={name:"\u0275\u0275storeLet",moduleName:Ce};static readContextLet={name:"\u0275\u0275readContextLet",moduleName:Ce};static arrowFunction={name:"\u0275\u0275arrowFunction",moduleName:Ce};static attachSourceLocations={name:"\u0275\u0275attachSourceLocations",moduleName:Ce};static NgOnChangesFeature={name:"\u0275\u0275NgOnChangesFeature",moduleName:Ce};static ControlFeature={name:"\u0275\u0275ControlFeature",moduleName:Ce};static InheritDefinitionFeature={name:"\u0275\u0275InheritDefinitionFeature",moduleName:Ce};static ProvidersFeature={name:"\u0275\u0275ProvidersFeature",moduleName:Ce};static HostDirectivesFeature={name:"\u0275\u0275HostDirectivesFeature",moduleName:Ce};static ExternalStylesFeature={name:"\u0275\u0275ExternalStylesFeature",moduleName:Ce};static listener={name:"\u0275\u0275listener",moduleName:Ce};static getInheritedFactory={name:"\u0275\u0275getInheritedFactory",moduleName:Ce};static sanitizeHtml={name:"\u0275\u0275sanitizeHtml",moduleName:Ce};static sanitizeStyle={name:"\u0275\u0275sanitizeStyle",moduleName:Ce};static validateAttribute={name:"\u0275\u0275validateAttribute",moduleName:Ce};static sanitizeResourceUrl={name:"\u0275\u0275sanitizeResourceUrl",moduleName:Ce};static sanitizeScript={name:"\u0275\u0275sanitizeScript",moduleName:Ce};static sanitizeUrl={name:"\u0275\u0275sanitizeUrl",moduleName:Ce};static sanitizeUrlOrResourceUrl={name:"\u0275\u0275sanitizeUrlOrResourceUrl",moduleName:Ce};static trustConstantHtml={name:"\u0275\u0275trustConstantHtml",moduleName:Ce};static trustConstantResourceUrl={name:"\u0275\u0275trustConstantResourceUrl",moduleName:Ce};static inputDecorator={name:"Input",moduleName:Ce};static outputDecorator={name:"Output",moduleName:Ce};static viewChildDecorator={name:"ViewChild",moduleName:Ce};static viewChildrenDecorator={name:"ViewChildren",moduleName:Ce};static contentChildDecorator={name:"ContentChild",moduleName:Ce};static contentChildrenDecorator={name:"ContentChildren",moduleName:Ce};static InputSignalBrandWriteType={name:"\u0275INPUT_SIGNAL_BRAND_WRITE_TYPE",moduleName:Ce};static UnwrapDirectiveSignalInputs={name:"\u0275UnwrapDirectiveSignalInputs",moduleName:Ce};static unwrapWritableSignal={name:"\u0275unwrapWritableSignal",moduleName:Ce};static assertType={name:"\u0275assertType",moduleName:Ce}}return n})(),DG=/-+([a-z0-9])/g;function PG(n){return n.replace(DG,(...i)=>i[1].toUpperCase())}function IG(n,i){return r6(n,":",i)}function AG(n,i){return r6(n,".",i)}function r6(n,i,e){let t=n.indexOf(i);return t==-1?e:[n.slice(0,t).trim(),n.slice(t+1).trim()]}function OG(n){let i=[];for(let e=0;e=55296&&t<=56319&&n.length>e+1){let o=n.charCodeAt(e+1);o>=56320&&o<=57343&&(e++,t=(t-55296<<10)+o-56320+65536)}t<=127?i.push(t):t<=2047?i.push(t>>6&31|192,t&63|128):t<=65535?i.push(t>>12|224,t>>6&63|128,t&63|128):t<=2097151&&i.push(t>>18&7|240,t>>12&63|128,t>>6&63|128,t&63|128)}return i}function a6(n){if(typeof n=="string")return n;if(Array.isArray(n))return`[${n.map(a6).join(", ")}]`;if(n==null)return""+n;let i=n.overriddenName||n.name;if(i)return`${i}`;if(!n.toString)return"object";let e=n.toString();if(e==null)return""+e;let t=e.indexOf(` -`);return t>=0?e.slice(0,t):e}var SE=class{full;major;minor;patch;constructor(i){this.full=i;let e=i.split(".");this.major=e[0],this.minor=e[1],this.patch=e.slice(2).join(".")}},j_=globalThis,NG=/^([1-9]|1[0-8])\./;function s6(n){return n.startsWith("0.")?!0:!NG.test(n)}var RG=3,FG="# sourceMappingURL=data:application/json;base64,",wE=class{file;sourcesContent=new Map;lines=[];lastCol0=0;hasMappings=!1;constructor(i=null){this.file=i}addSource(i,e=null){return this.sourcesContent.has(i)||this.sourcesContent.set(i,e),this}addLine(){return this.lines.push([]),this.lastCol0=0,this}addMapping(i,e,t,o){if(!this.currentLine)throw new Error("A line must be added before mappings can be added");if(e!=null&&!this.sourcesContent.has(e))throw new Error(`Unknown source file "${e}"`);if(i==null)throw new Error("The column in the generated code must be provided");if(i{i.set(p,h),e.push(p),t.push(this.sourcesContent.get(p)||null)});let o="",r=0,a=0,c=0,m=0;return this.lines.forEach(p=>{r=0,o+=p.map(h=>{let g=F1(h.col0-r);return r=h.col0,h.sourceUrl!=null&&(g+=F1(i.get(h.sourceUrl)-a),a=i.get(h.sourceUrl),g+=F1(h.sourceLine0-c),c=h.sourceLine0,g+=F1(h.sourceCol0-m),m=h.sourceCol0),g}).join(","),o+=";"}),o=o.slice(0,-1),{file:this.file||"",version:RG,sourceRoot:"",sources:e,sourcesContent:t,mappings:o}}toJsComment(){return this.hasMappings?"//"+FG+LG(JSON.stringify(this,null,0)):""}};function LG(n){let i="",e=OG(n);for(let t=0;t>2),i+=F_((o&3)<<4|(r===null?0:r>>4)),i+=r===null?"=":F_((r&15)<<2|(a===null?0:a>>6)),i+=r===null||a===null?"=":F_(a&63)}return i}function F1(n){n=n<0?(-n<<1)+1:n<<1;let i="";do{let e=n&31;n=n>>5,n>0&&(e=e|32),i+=F_(e)}while(n>0);return i}var BG="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";function F_(n){if(n<0||n>=64)throw new Error("Can only encode value in the range [0, 63]");return BG[n]}var VG=/'|\\|\n|\r|\$/g,zG=/^[$A-Z_][0-9A-Z_$]*$/i,ME=" ",mb=class{indent;partsLength=0;parts=[];srcSpans=[];constructor(i){this.indent=i}},jG=new Map([[lt.And,"&&"],[lt.Bigger,">"],[lt.BiggerEquals,">="],[lt.BitwiseOr,"|"],[lt.BitwiseAnd,"&"],[lt.Divide,"/"],[lt.Assign,"="],[lt.Equals,"=="],[lt.Identical,"==="],[lt.Lower,"<"],[lt.LowerEquals,"<="],[lt.Minus,"-"],[lt.Modulo,"%"],[lt.Exponentiation,"**"],[lt.Multiply,"*"],[lt.NotEquals,"!="],[lt.NotIdentical,"!=="],[lt.NullishCoalesce,"??"],[lt.Or,"||"],[lt.Plus,"+"],[lt.In,"in"],[lt.InstanceOf,"instanceof"],[lt.AdditionAssignment,"+="],[lt.SubtractionAssignment,"-="],[lt.MultiplicationAssignment,"*="],[lt.DivisionAssignment,"/="],[lt.RemainderAssignment,"%="],[lt.ExponentiationAssignment,"**="],[lt.AndAssignment,"&&="],[lt.OrAssignment,"||="],[lt.NullishCoalesceAssignment,"??="]]),kE=class n{_indent;static createRoot(){return new n(0)}_lines;constructor(i){this._indent=i,this._lines=[new mb(i)]}get _currentLine(){return this._lines[this._lines.length-1]}println(i,e=""){this.print(i||null,e,!0)}lineIsEmpty(){return this._currentLine.parts.length===0}lineLength(){return this._currentLine.indent*ME.length+this._currentLine.partsLength}print(i,e,t=!1){e.length>0&&(this._currentLine.parts.push(e),this._currentLine.partsLength+=e.length,this._currentLine.srcSpans.push(i&&i.sourceSpan||null)),t&&this._lines.push(new mb(this._indent))}removeEmptyLastLine(){this.lineIsEmpty()&&this._lines.pop()}incIndent(){this._indent++,this.lineIsEmpty()&&(this._currentLine.indent=this._indent)}decIndent(){this._indent--,this.lineIsEmpty()&&(this._currentLine.indent=this._indent)}toSource(){return this.sourceLines.map(i=>i.parts.length>0?_R(i.indent)+i.parts.join(""):"").join(` -`)}toSourceMapGenerator(i,e=0){let t=new wE(i),o=!1,r=()=>{o||(t.addSource(i," ").addMapping(0,i,0,0),o=!0)};for(let a=0;a{t.addLine();let m=a.srcSpans,p=a.parts,h=a.indent*ME.length,g=0;for(;go)return t.srcSpans[r];o-=a.length}}return null}get sourceLines(){return this._lines.length&&this._lines[this._lines.length-1].parts.length===0?this._lines.slice(0,-1):this._lines}},TE=class{_escapeDollarInStrings;lastIfCondition=null;constructor(i){this._escapeDollarInStrings=i}printLeadingComments(i,e){if(i.leadingComments!==void 0)for(let t of i.leadingComments)t instanceof sb?e.print(i,`/*${t.toString()}*/`,t.trailingNewline):t.multiline?e.print(i,`/* ${t.text} */`,t.trailingNewline):t.text.split(` -`).forEach(o=>{e.println(i,`// ${o}`)})}visitExpressionStmt(i,e){return this.printLeadingComments(i,e),i.expr.visitExpression(this,e),e.println(i,";"),null}visitReturnStmt(i,e){return this.printLeadingComments(i,e),e.print(i,"return "),i.value.visitExpression(this,e),e.println(i,";"),null}visitIfStmt(i,e){this.printLeadingComments(i,e),e.print(i,"if ("),this.lastIfCondition=i.condition,i.condition.visitExpression(this,e),this.lastIfCondition=null,e.print(i,") {");let t=i.falseCase!=null&&i.falseCase.length>0;return i.trueCase.length<=1&&!t?(e.print(i," "),this.visitAllStatements(i.trueCase,e),e.removeEmptyLastLine(),e.print(i," ")):(e.println(),e.incIndent(),this.visitAllStatements(i.trueCase,e),e.decIndent(),t&&(e.println(i,"} else {"),e.incIndent(),this.visitAllStatements(i.falseCase,e),e.decIndent())),e.println(i,"}"),null}visitInvokeFunctionExpr(i,e){let t=i.fn instanceof vu;return t&&e.print(i.fn,"("),i.fn.visitExpression(this,e),t&&e.print(i.fn,")"),e.print(i,"("),this.visitAllExpressions(i.args,e,","),e.print(i,")"),null}visitTaggedTemplateLiteralExpr(i,e){return i.tag.visitExpression(this,e),i.template.visitExpression(this,e),null}visitTemplateLiteralExpr(i,e){e.print(i,"`");for(let t=0;t{t instanceof Cm?(e.print(i,"..."),t.expression.visitExpression(this,e)):(e.print(i,`${Gp(t.key,this._escapeDollarInStrings,t.quoted)}:`),t.value.visitExpression(this,e))},i.entries,e,","),e.print(i,"}"),null}visitCommaExpr(i,e){return e.print(i,"("),this.visitAllExpressions(i.parts,e,","),e.print(i,")"),null}visitParenthesizedExpr(i,e){i.expr.visitExpression(this,e)}visitSpreadElementExpr(i,e){e.print(i,"..."),i.expression.visitExpression(this,e)}visitAllExpressions(i,e,t){this.visitAllObjects(o=>o.visitExpression(this,e),i,e,t)}visitAllObjects(i,e,t,o){let r=!1;for(let a=0;a0&&(t.lineLength()>80?(t.print(null,o,!0),r||(t.incIndent(),t.incIndent(),r=!0)):t.print(null,o,!1)),i(e[a]);r&&(t.decIndent(),t.decIndent())}visitAllStatements(i,e){i.forEach(t=>t.visitStatement(this,e))}};function Gp(n,i,e=!0){if(n==null)return null;let t=n.replace(VG,(...r)=>r[0]=="$"?i?"\\$":"$":r[0]==` -`?"\\n":r[0]=="\r"?"\\r":`\\${r[0]}`);return e||!zG.test(t)?`'${t}'`:t}function _R(n){let i="";for(let e=0;et.value));return i?Fs([],e):e}function KD(n,i){return{expression:n,forwardRef:i}}function GG({expression:n,forwardRef:i}){switch(i){case 0:case 1:return n;case 2:return WG(n)}}function WG(n){return Wt(he.forwardRef).callFn([Fs([],n)])}var pb=(function(n){return n[n.Class=0]="Class",n[n.Function=1]="Function",n})(pb||{});function $p(n){let i=Zn("__ngFactoryType__"),e=null,t=CR(n)?i:new fi(lt.Or,i,n.type.value),o=null;n.deps!==null?n.deps!=="invalid"&&(o=new Z_(t,vR(n.deps,n.target))):(e=Zn(`\u0275${n.name}_BaseFactory`),o=e.callFn([t]));let r=[],a=null;function c(p){let h=Zn("__ngConditionalFactory__");r.push(new Fr(h.name,Wh,Ul));let g=o!==null?h.set(o).toStmt():Wt(he.invalidFactory).callFn([]).toStmt();return r.push(sx(i,[g],[h.set(p).toStmt()])),h}if(CR(n)){let p=vR(n.delegateDeps,n.target),h=new(n.delegateType===pb.Class?Z_:cs)(n.delegate,p);a=c(h)}else KG(n)?a=c(n.expression):a=o;if(a===null)r.push(Wt(he.invalidFactory).callFn([]).toStmt());else if(e!==null){let p=Wt(he.getInheritedFactory).callFn([n.type.value]),h=new fi(lt.Or,e,e.set(p));r.push(new wr(h.callFn([t])))}else r.push(new wr(a));let m=bm([new Sr(i.name,ls)],r,Ul,void 0,`${n.name}_Factory`);return e!==null&&(m=Fs([],[new Fr(e.name),new wr(m)]).callFn([],void 0,!0)),{expression:m,statements:[],type:qG(n)}}function qG(n){let i=n.deps!==null&&n.deps!=="invalid"?XG(n.deps):Mc;return ca(Wt(he.FactoryDeclaration,[lx(n.type.type,n.typeArgumentCount),i]))}function vR(n,i){return n.map((e,t)=>QG(e,i,t))}function QG(n,i,e){if(n.token===null)return Wt(he.invalidFactoryDep).callFn([Me(e)]);if(n.attributeNameType===null){let t=0|(n.self?2:0)|(n.skipSelf?4:0)|(n.host?1:0)|(n.optional?8:0)|(i===Cd.Pipe?16:0),o=t!==0||n.optional?Me(t):null,r=[n.token];o&&r.push(o);let a=ZG(i);return Wt(a).callFn(r)}else return Wt(he.injectAttribute).callFn([n.token])}function XG(n){let i=!1,e=n.map(t=>{let o=YG(t);return o!==null?(i=!0,o):Me(null)});return i?ca(Qi(e)):Mc}function YG(n){let i=[];return n.attributeNameType!==null&&i.push({key:"attribute",value:n.attributeNameType,quoted:!1}),n.optional&&i.push({key:"optional",value:Me(!0),quoted:!1}),n.host&&i.push({key:"host",value:Me(!0),quoted:!1}),n.self&&i.push({key:"self",value:Me(!0),quoted:!1}),n.skipSelf&&i.push({key:"skipSelf",value:Me(!0),quoted:!1}),i.length>0?ml(i):null}function CR(n){return n.delegateType!==void 0}function KG(n){return n.expression!==void 0}function ZG(n){switch(n){case Cd.Component:case Cd.Directive:case Cd.Pipe:return he.directiveInject;case Cd.NgModule:case Cd.Injectable:default:return he.inject}}var lu=class{start;end;constructor(i,e){this.start=i,this.end=e}toAbsolute(i){return new As(i+this.start,i+this.end)}},ao=class{span;sourceSpan;constructor(i,e){this.span=i,this.sourceSpan=e}toString(){return"AST"}},i0=class extends ao{nameSpan;constructor(i,e,t){super(i,e),this.nameSpan=t}},xa=class extends ao{visit(i,e=null){return i.visitEmptyExpr?.(this,e)}},Ec=class extends ao{visit(i,e=null){return i.visitImplicitReceiver(this,e)}},o0=class extends ao{visit(i,e=null){return i.visitThisReceiver?.(this,e)}},qh=class extends ao{expressions;constructor(i,e,t){super(i,e),this.expressions=t}visit(i,e=null){return i.visitChain(this,e)}},ub=class extends ao{condition;trueExp;falseExp;constructor(i,e,t,o,r){super(i,e),this.condition=t,this.trueExp=o,this.falseExp=r}visit(i,e=null){return i.visitConditional(this,e)}},yc=class extends i0{receiver;name;constructor(i,e,t,o,r){super(i,e,t),this.receiver=o,this.name=r}visit(i,e=null){return i.visitPropertyRead(this,e)}},r0=class extends i0{receiver;name;constructor(i,e,t,o,r){super(i,e,t),this.receiver=o,this.name=r}visit(i,e=null){return i.visitSafePropertyRead(this,e)}},cu=class extends ao{receiver;key;constructor(i,e,t,o){super(i,e),this.receiver=t,this.key=o}visit(i,e=null){return i.visitKeyedRead(this,e)}},a0=class extends ao{receiver;key;constructor(i,e,t,o){super(i,e),this.receiver=t,this.key=o}visit(i,e=null){return i.visitSafeKeyedRead(this,e)}},X1=(function(n){return n[n.ReferencedByName=0]="ReferencedByName",n[n.ReferencedDirectly=1]="ReferencedDirectly",n})(X1||{}),hb=class extends i0{exp;name;args;type;constructor(i,e,t,o,r,a,c){super(i,e,c),this.exp=t,this.name=o,this.args=r,this.type=a}visit(i,e=null){return i.visitPipe(this,e)}},os=class extends ao{value;constructor(i,e,t){super(i,e),this.value=t}visit(i,e=null){return i.visitLiteralPrimitive(this,e)}},s0=class extends ao{expressions;constructor(i,e,t){super(i,e),this.expressions=t}visit(i,e=null){return i.visitLiteralArray(this,e)}},fb=class extends ao{expression;constructor(i,e,t){super(i,e),this.expression=t}visit(i,e=null){return i.visitSpreadElement(this,e)}},du=class extends ao{keys;values;constructor(i,e,t,o){super(i,e),this.keys=t,this.values=o}visit(i,e=null){return i.visitLiteralMap(this,e)}},Q0=class extends ao{strings;expressions;constructor(i,e,t,o){super(i,e),this.strings=t,this.expressions=o}visit(i,e=null){return i.visitInterpolation(this,e)}},Ba=class extends ao{operation;left;right;constructor(i,e,t,o,r){super(i,e),this.operation=t,this.left=o,this.right=r}visit(i,e=null){return i.visitBinary(this,e)}static isAssignmentOperation(i){return i==="="||i==="+="||i==="-="||i==="*="||i==="/="||i==="%="||i==="**="||i==="&&="||i==="||="||i==="??="}},zh=class n extends Ba{operator;expr;left=null;right=null;operation=null;static createMinus(i,e,t){return new n(i,e,"-",t,"-",new os(i,e,0),t)}static createPlus(i,e,t){return new n(i,e,"+",t,"-",t,new os(i,e,0))}constructor(i,e,t,o,r,a,c){super(i,e,r,a,c),this.operator=t,this.expr=o}visit(i,e=null){return i.visitUnary!==void 0?i.visitUnary(this,e):i.visitBinary(this,e)}},l0=class extends ao{expression;constructor(i,e,t){super(i,e),this.expression=t}visit(i,e=null){return i.visitPrefixNot(this,e)}},c0=class extends ao{expression;constructor(i,e,t){super(i,e),this.expression=t}visit(i,e=null){return i.visitTypeofExpression(this,e)}},d0=class extends ao{expression;constructor(i,e,t){super(i,e),this.expression=t}visit(i,e=null){return i.visitVoidExpression(this,e)}},m0=class extends ao{expression;constructor(i,e,t){super(i,e),this.expression=t}visit(i,e=null){return i.visitNonNullAssert(this,e)}},Qh=class extends ao{receiver;args;argumentSpan;constructor(i,e,t,o,r){super(i,e),this.receiver=t,this.args=o,this.argumentSpan=r}visit(i,e=null){return i.visitCall(this,e)}},gb=class extends ao{receiver;args;argumentSpan;constructor(i,e,t,o,r){super(i,e),this.receiver=t,this.args=o,this.argumentSpan=r}visit(i,e=null){return i.visitSafeCall(this,e)}},p0=class extends ao{tag;template;constructor(i,e,t,o){super(i,e),this.tag=t,this.template=o}visit(i,e){return i.visitTaggedTemplateLiteral(this,e)}},u0=class extends ao{elements;expressions;constructor(i,e,t,o){super(i,e),this.elements=t,this.expressions=o}visit(i,e){return i.visitTemplateLiteral(this,e)}},_b=class extends ao{text;constructor(i,e,t){super(i,e),this.text=t}visit(i,e){return i.visitTemplateLiteralElement(this,e)}},h0=class extends ao{expression;constructor(i,e,t){super(i,e),this.expression=t}visit(i,e){return i.visitParenthesizedExpression(this,e)}},EE=class{name;span;sourceSpan;constructor(i,e,t){this.name=i,this.span=e,this.sourceSpan=t}},vb=class extends ao{parameters;body;constructor(i,e,t,o){super(i,e),this.parameters=t,this.body=o}visit(i,e){return i.visitArrowFunction(this,e)}},Cb=class extends ao{body;flags;constructor(i,e,t,o){super(i,e),this.body=t,this.flags=o}visit(i,e){return i.visitRegularExpressionLiteral(this,e)}},As=class{start;end;constructor(i,e){this.start=i,this.end=e}},as=class extends ao{ast;source;location;errors;constructor(i,e,t,o,r){super(new lu(0,e===null?0:e.length),new As(o,e===null?o:o+e.length)),this.ast=i,this.source=e,this.location=t,this.errors=r}visit(i,e=null){return i.visitASTWithSource?i.visitASTWithSource(this,e):this.ast.visit(i,e)}toString(){return`${this.source} in ${this.location}`}},f0=class{sourceSpan;key;value;constructor(i,e,t){this.sourceSpan=i,this.key=e,this.value=t}},DE=class{sourceSpan;key;value;constructor(i,e,t){this.sourceSpan=i,this.key=e,this.value=t}},Xh=class{visit(i,e){i.visit(this,e)}visitUnary(i,e){this.visit(i.expr,e)}visitBinary(i,e){this.visit(i.left,e),this.visit(i.right,e)}visitChain(i,e){this.visitAll(i.expressions,e)}visitConditional(i,e){this.visit(i.condition,e),this.visit(i.trueExp,e),this.visit(i.falseExp,e)}visitPipe(i,e){this.visit(i.exp,e),this.visitAll(i.args,e)}visitImplicitReceiver(i,e){}visitThisReceiver(i,e){}visitInterpolation(i,e){this.visitAll(i.expressions,e)}visitKeyedRead(i,e){this.visit(i.receiver,e),this.visit(i.key,e)}visitLiteralArray(i,e){this.visitAll(i.expressions,e)}visitLiteralMap(i,e){this.visitAll(i.values,e)}visitLiteralPrimitive(i,e){}visitPrefixNot(i,e){this.visit(i.expression,e)}visitTypeofExpression(i,e){this.visit(i.expression,e)}visitVoidExpression(i,e){this.visit(i.expression,e)}visitNonNullAssert(i,e){this.visit(i.expression,e)}visitPropertyRead(i,e){this.visit(i.receiver,e)}visitSafePropertyRead(i,e){this.visit(i.receiver,e)}visitSafeKeyedRead(i,e){this.visit(i.receiver,e),this.visit(i.key,e)}visitCall(i,e){this.visit(i.receiver,e),this.visitAll(i.args,e)}visitSafeCall(i,e){this.visit(i.receiver,e),this.visitAll(i.args,e)}visitTemplateLiteral(i,e){for(let t=0;tt!==null);YT(i,e)}visitTriggers(i,e,t){YT(t,i.map(o=>e[o]))}},Mb=class extends ds{expression;groups;unknownBlocks;exhaustiveCheck;constructor(i,e,t,o,r,a,c,m){super(m,r,a,c),this.expression=i,this.groups=e,this.unknownBlocks=t,this.exhaustiveCheck=o}visit(i){return i.visitSwitchBlock(this)}},VE=class extends ds{expression;constructor(i,e,t,o,r){super(r,e,t,o),this.expression=i}visit(i){return i.visitSwitchBlockCase(this)}},b0=class extends ds{cases;children;i18n;constructor(i,e,t,o,r,a,c){super(a,t,o,r),this.cases=i,this.children=e,this.i18n=c}visit(i){return i.visitSwitchBlockCaseGroup(this)}},zE=class extends ds{constructor(i,e,t,o){super(o,i,e,t)}visit(i){return i.visitSwitchExhaustiveCheck(this)}},Zh=class extends ds{item;expression;trackBy;trackKeywordSpan;contextVariables;children;empty;mainBlockSpan;i18n;constructor(i,e,t,o,r,a,c,m,p,h,g,S,x){super(S,m,h,g),this.item=i,this.expression=e,this.trackBy=t,this.trackKeywordSpan=o,this.contextVariables=r,this.children=a,this.empty=c,this.mainBlockSpan=p,this.i18n=x}visit(i){return i.visitForLoopBlock(this)}},x0=class extends ds{children;i18n;constructor(i,e,t,o,r,a){super(r,e,t,o),this.children=i,this.i18n=a}visit(i){return i.visitForLoopBlockEmpty(this)}},kb=class extends ds{branches;constructor(i,e,t,o,r){super(r,e,t,o),this.branches=i}visit(i){return i.visitIfBlock(this)}},Yp=class extends ds{expression;children;expressionAlias;i18n;constructor(i,e,t,o,r,a,c,m){super(c,o,r,a),this.expression=i,this.children=e,this.expressionAlias=t,this.i18n=m}visit(i){return i.visitIfBlockBranch(this)}},Tb=class{name;sourceSpan;nameSpan;constructor(i,e,t){this.name=i,this.sourceSpan=e,this.nameSpan=t}visit(i){return i.visitUnknownBlock(this)}},ZD=class{name;value;sourceSpan;nameSpan;valueSpan;constructor(i,e,t,o,r){this.name=i,this.value=e,this.sourceSpan=t,this.nameSpan=o,this.valueSpan=r}visit(i){return i.visitLetDeclaration(this)}},$_=class{componentName;tagName;fullName;attributes;inputs;outputs;directives;children;references;isSelfClosing;sourceSpan;startSourceSpan;endSourceSpan;i18n;constructor(i,e,t,o,r,a,c,m,p,h,g,S,x,v){this.componentName=i,this.tagName=e,this.fullName=t,this.attributes=o,this.inputs=r,this.outputs=a,this.directives=c,this.children=m,this.references=p,this.isSelfClosing=h,this.sourceSpan=g,this.startSourceSpan=S,this.endSourceSpan=x,this.i18n=v}visit(i){return i.visitComponent(this)}},l6=class{name;attributes;inputs;outputs;references;sourceSpan;startSourceSpan;endSourceSpan;i18n;constructor(i,e,t,o,r,a,c,m,p){this.name=i,this.attributes=e,this.inputs=t,this.outputs=o,this.references=r,this.sourceSpan=a,this.startSourceSpan=c,this.endSourceSpan=m,this.i18n=p}visit(i){return i.visitDirective(this)}},Os=class{tagName;attributes;inputs;outputs;directives;templateAttrs;children;references;variables;isSelfClosing;sourceSpan;startSourceSpan;endSourceSpan;i18n;constructor(i,e,t,o,r,a,c,m,p,h,g,S,x,v){this.tagName=i,this.attributes=e,this.inputs=t,this.outputs=o,this.directives=r,this.templateAttrs=a,this.children=c,this.references=m,this.variables=p,this.isSelfClosing=h,this.sourceSpan=g,this.startSourceSpan=S,this.endSourceSpan=x,this.i18n=v}visit(i){return i.visitTemplate(this)}},Jh=class{selector;attributes;children;isSelfClosing;sourceSpan;startSourceSpan;endSourceSpan;i18n;name="ng-content";constructor(i,e,t,o,r,a,c,m){this.selector=i,this.attributes=e,this.children=t,this.isSelfClosing=o,this.sourceSpan=r,this.startSourceSpan=a,this.endSourceSpan=c,this.i18n=m}visit(i){return i.visitContent(this)}},xm=class{name;value;sourceSpan;keySpan;valueSpan;constructor(i,e,t,o,r){this.name=i,this.value=e,this.sourceSpan=t,this.keySpan=o,this.valueSpan=r}visit(i){return i.visitVariable(this)}},y0=class{name;value;sourceSpan;keySpan;valueSpan;constructor(i,e,t,o,r){this.name=i,this.value=e,this.sourceSpan=t,this.keySpan=o,this.valueSpan=r}visit(i){return i.visitReference(this)}},c6=class{vars;placeholders;sourceSpan;i18n;constructor(i,e,t,o){this.vars=i,this.placeholders=e,this.sourceSpan=t,this.i18n=o}visit(i){return i.visitIcu(this)}},S0=class{tagNames;bindings;listeners;sourceSpan;constructor(i,e,t,o){if(this.tagNames=i,this.bindings=e,this.listeners=t,this.sourceSpan=o,i.length===0)throw new Error("HostElement must have at least one tag name.")}visit(){throw new Error("HostElement cannot be visited")}};function YT(n,i){let e=[];if(n.visit)for(let t of i)n.visit(t);else for(let t of i){let o=t.visit(n);o&&e.push(o)}return e}var Ua=class{nodes;placeholders;placeholderToMessage;meaning;description;customId;sources;id;legacyIds=[];messageString;constructor(i,e,t,o,r,a){this.nodes=i,this.placeholders=e,this.placeholderToMessage=t,this.meaning=o,this.description=r,this.customId=a,this.id=this.customId,this.messageString=eW(this.nodes),i.length?this.sources=[{filePath:i[0].sourceSpan.start.file.url,startLine:i[0].sourceSpan.start.line+1,startCol:i[0].sourceSpan.start.col+1,endLine:i[i.length-1].sourceSpan.end.line+1,endCol:i[0].sourceSpan.start.col+1}]:this.sources=[]}},k_=class{value;sourceSpan;constructor(i,e){this.value=i,this.sourceSpan=e}visit(i,e){return i.visitText(this,e)}},yd=class{children;sourceSpan;constructor(i,e){this.children=i,this.sourceSpan=e}visit(i,e){return i.visitContainer(this,e)}},Eb=class{expression;type;cases;sourceSpan;expressionPlaceholder;constructor(i,e,t,o,r){this.expression=i,this.type=e,this.cases=t,this.sourceSpan=o,this.expressionPlaceholder=r}visit(i,e){return i.visitIcu(this,e)}},ym=class{tag;attrs;startName;closeName;children;isVoid;sourceSpan;startSourceSpan;endSourceSpan;constructor(i,e,t,o,r,a,c,m,p){this.tag=i,this.attrs=e,this.startName=t,this.closeName=o,this.children=r,this.isVoid=a,this.sourceSpan=c,this.startSourceSpan=m,this.endSourceSpan=p}visit(i,e){return i.visitTagPlaceholder(this,e)}},w0=class{value;name;sourceSpan;constructor(i,e,t){this.value=i,this.name=e,this.sourceSpan=t}visit(i,e){return i.visitPlaceholder(this,e)}},ef=class{value;name;sourceSpan;previousMessage;constructor(i,e,t){this.value=i,this.name=e,this.sourceSpan=t}visit(i,e){return i.visitIcuPlaceholder(this,e)}},Sm=class{name;parameters;startName;closeName;children;sourceSpan;startSourceSpan;endSourceSpan;constructor(i,e,t,o,r,a,c,m){this.name=i,this.parameters=e,this.startName=t,this.closeName=o,this.children=r,this.sourceSpan=a,this.startSourceSpan=c,this.endSourceSpan=m}visit(i,e){return i.visitBlockPlaceholder(this,e)}};function eW(n){let i=new jE;return n.map(t=>t.visit(i)).join("")}var jE=class{visitText(i){return i.value}visitContainer(i){return i.children.map(e=>e.visit(this)).join("")}visitIcu(i){let e=Object.keys(i.cases).map(t=>`${t} {${i.cases[t].visit(this)}}`);return`{${i.expressionPlaceholder}, ${i.type}, ${e.join(" ")}}`}visitTagPlaceholder(i){let e=i.children.map(t=>t.visit(this)).join("");return`{$${i.startName}}${e}{$${i.closeName}}`}visitPlaceholder(i){return`{$${i.name}}`}visitIcuPlaceholder(i){return`{$${i.name}}`}visitBlockPlaceholder(i){let e=i.children.map(t=>t.visit(this)).join("");return`{$${i.startName}}${e}{$${i.closeName}}`}};var tW=class{visitTag(i){let e=this._serializeAttributes(i.attrs);if(i.children.length==0)return`<${i.name}${e}/>`;let t=i.children.map(o=>o.visit(this));return`<${i.name}${e}>${t.join("")}`}visitText(i){return i.value}visitDeclaration(i){return``}_serializeAttributes(i){let e=Object.keys(i).map(t=>`${t}="${i[t]}"`).join(" ");return e.length>0?" "+e:""}visitDoctype(i){return``}},uFe=new tW;function nW(n){return n.toUpperCase().replace(/[^A-Z0-9_]/g,"_")}var d6="i18n",$E="i18n-",iW="VAR_";function m6(n){return n===d6||n.startsWith($E)}function oW(n){return n.attrs.some(i=>m6(i.name))}function p6(n){return n.nodes[0]}function JD(n={},i){let e={};return n&&Object.keys(n).length&&Object.keys(n).forEach(t=>e[X0(t,i)]=n[t]),e}function X0(n,i=!0){let e=nW(n);if(!i)return e;let t=e.split("_");if(t.length===1)return n.toLowerCase();let o;/^\d+$/.test(t[t.length-1])&&(o=t.pop());let r=t.shift().toLowerCase();return t.length&&(r+=t.map(a=>a.charAt(0).toUpperCase()+a.slice(1).toLowerCase()).join("")),o?`${r}_${o}`:r}var rW=/[-.]/,eP="_t",Ls="ctx",cf="rf";function u6(n,i){let e=null;return()=>(e||(n(new Fr(eP,void 0,ls)),e=Zn(i)),e)}function Rh(n){return Array.isArray(n)?Qi(n.map(Rh)):Me(n,Ul)}function xR(n,i){let e=Object.getOwnPropertyNames(n);return e.length===0?null:ml(e.map(t=>{let o=n[t],r,a,c,m;if(typeof o=="string")r=t,c=t,a=o,m=Rh(a);else{c=t,r=o.classPropertyName,a=o.bindingPropertyName;let p=a!==r,h=o.transformFunction!==null,g=R_.None;if(o.isSignal&&(g|=R_.SignalBased),h&&(g|=R_.HasDecoratorInputTransform),i&&(p||h||g!==R_.None)){let S=[Me(g),Rh(a)];(p||h)&&(S.push(Rh(r)),h&&S.push(o.transformFunction)),m=Qi(S)}else m=Rh(a)}return{key:c,quoted:rW.test(c),value:m}}))}var wm=class{values=[];set(i,e){if(e){let t=this.values.find(o=>o.key===i);t?t.value=e:this.values.push({key:i,value:e,quoted:!1})}}toLiteralMap(){return ml(this.values)}};function aW(n){let i=n instanceof Dc?n.name:"ng-template",e=sW(n),t=new $h,o=Ql(i)[1];return t.setElement(o),Object.getOwnPropertyNames(e).forEach(r=>{let a=Ql(r)[1],c=e[r];t.addAttribute(a,c),r.toLowerCase()==="class"&&c.trim().split(/\s+/).forEach(p=>t.addClassName(p))}),t}function sW(n){let i={};return n instanceof Os&&n.tagName!=="ng-template"?n.templateAttrs.forEach(e=>i[e.name]=""):(n.attributes.forEach(e=>{m6(e.name)||(i[e.name]=e.value)}),n.inputs.forEach(e=>{(e.type===Mi.Property||e.type===Mi.TwoWay)&&(i[e.name]="")}),n.outputs.forEach(e=>{i[e.name]=""})),i}function yR(n,i){let e=null,t={name:n.name,type:n.type,typeArgumentCount:n.typeArgumentCount,deps:[],target:Cd.Injectable};if(n.useClass!==void 0){let c=n.useClass.expression.isEquivalent(n.type.value),m;n.deps!==void 0&&(m=n.deps),m!==void 0?e=$p(Qe(W({},t),{delegate:n.useClass.expression,delegateDeps:m,delegateType:pb.Class})):c?e=$p(t):e={statements:[],expression:SR(n.type.value,n.useClass.expression,i)}}else n.useFactory!==void 0?n.deps!==void 0?e=$p(Qe(W({},t),{delegate:n.useFactory,delegateDeps:n.deps||[],delegateType:pb.Function})):e={statements:[],expression:Fs([],n.useFactory.callFn([]))}:n.useValue!==void 0?e=$p(Qe(W({},t),{expression:n.useValue.expression})):n.useExisting!==void 0?e=$p(Qe(W({},t),{expression:Wt(he.inject).callFn([n.useExisting.expression])})):e={statements:[],expression:SR(n.type.value,n.type.value,i)};let o=n.type.value,r=new wm;return r.set("token",o),r.set("factory",e.expression),n.providedIn.expression.value!==null&&r.set("providedIn",GG(n.providedIn)),{expression:Wt(he.\u0275\u0275defineInjectable).callFn([r.toLiteralMap()],void 0,!0),type:lW(n),statements:e.statements}}function lW(n){return new dl(Wt(he.InjectableDeclaration,[lx(n.type.type,n.typeArgumentCount)]))}function SR(n,i,e){if(n.node===i.node)return i.prop("\u0275fac");if(!e)return wR(i);let t=Wt(he.resolveForwardRef).callFn([i]);return wR(t)}function wR(n){let i=new Sr("__ngFactoryType__",ls);return Fs([i],n.prop("\u0275fac").callFn([Zn(i.name)]))}var qr=0,cW=8,tP=9,Kp=10,h6=11,f6=12,nP=13,g6=32,HE=33,M0=34,_6=35,dx=36,dW=37,Db=38,k0=39,$a=40,yr=41,MR=42,v6=43,ya=44,Pb=45,zp=46,il=47,bc=58,rs=59,jh=60,Gr=61,Ps=62,kR=63,iP=48,mW=55,C6=57,Pm=65,pW=69,uW=70,hW=88,df=90,Sc=91,Zp=92,bd=93,fW=94,Im=95,pu=97,gW=98,_W=101,oP=102,b6=110,x6=114,y6=116,S6=117,w6=118,M6=120,Y0=122,al=123,TR=124,za=125,k6=160,kh=64,UE=96;function T0(n){return n>=tP&&n<=g6||n==k6}function ol(n){return iP<=n&&n<=C6}function Mm(n){return n>=pu&&n<=Y0||n>=Pm&&n<=df}function vW(n){return n>=pu&&n<=oP||n>=Pm&&n<=uW||ol(n)}function Ib(n){return n===Kp||n===nP}function ER(n){return iP<=n&&n<=mW}function H_(n){return n===k0||n===M0||n===UE}var E0=class n{file;offset;line;col;constructor(i,e,t,o){this.file=i,this.offset=e,this.line=t,this.col=o}toString(){return this.offset!=null?`${this.file.url}@${this.line}:${this.col}`:this.file.url}moveBy(i){let e=this.file.content,t=e.length,o=this.offset,r=this.line,a=this.col;for(;o>0&&i<0;)if(o--,i++,e.charCodeAt(o)==Kp){r--;let m=e.substring(0,o-1).lastIndexOf(String.fromCharCode(Kp));a=m>0?o-m:o}else a--;for(;o0;){let c=e.charCodeAt(o);o++,i--,c==Kp?(r++,a=0):a++}return new n(this.file,o,r,a)}getContext(i,e){let t=this.file.content,o=this.offset;if(o!=null){o>t.length-1&&(o=t.length-1);let r=o,a=0,c=0;for(;a0&&(o--,a++,!(t[o]==` -`&&++c==e)););for(a=0,c=0;a]${i.after}")`:this.msg}toString(){let i=this.span.details?`, ${this.span.details}`:"";return`${this.contextualMessage()}: ${this.span.start}${i}`}};function CW(n,i,e){let t=`in ${n} ${i} in ${e}`,o=new Ab("",t);return new _n(new E0(o,-1,-1,-1),new E0(o,-1,-1,-1))}var bW=0;function xW(n){if(!n||!n.reference)return null;let i=n.reference;if(i.__anonymousType)return i.__anonymousType;if(i.__forward_ref__)return"__forward_ref__";let e=a6(i);return e.indexOf("(")>=0?(e=`anonymous_${bW++}`,i.__anonymousType=e):e=Hp(e),e}function Hp(n){return n.replace(/\W/g,"_")}var DR='(this&&this.__makeTemplateObject||function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e})',GE=class extends TE{constructor(){super(!1)}visitWrappedNodeExpr(i,e){throw new Error("Cannot emit a WrappedNodeExpr in Javascript.")}visitDeclareVarStmt(i,e){return e.print(i,`var ${i.name}`),i.value&&(e.print(i," = "),i.value.visitExpression(this,e)),e.println(i,";"),null}visitTaggedTemplateLiteralExpr(i,e){let t=i.template.elements;return i.tag.visitExpression(this,e),e.print(i,`(${DR}(`),e.print(i,`[${t.map(o=>Gp(o.text,!1)).join(", ")}], `),e.print(i,`[${t.map(o=>Gp(o.rawText,!1)).join(", ")}])`),i.template.expressions.forEach(o=>{e.print(i,", "),o.visitExpression(this,e)}),e.print(i,")"),null}visitTemplateLiteralExpr(i,e){e.print(i,"`");for(let t=0;t"),Array.isArray(i.body))e.println(i,"{"),e.incIndent(),this.visitAllStatements(i.body,e),e.decIndent(),e.print(i,"}");else{let t=i.body instanceof ql;t&&e.print(i,"("),i.body.visitExpression(this,e),t&&e.print(i,")")}return null}visitDeclareFunctionStmt(i,e){return e.print(i,`function ${i.name}(`),this._visitParams(i.params,e),e.println(i,") {"),e.incIndent(),this.visitAllStatements(i.statements,e),e.decIndent(),e.println(i,"}"),null}visitLocalizedString(i,e){e.print(i,`$localize(${DR}(`);let t=[i.serializeI18nHead()];for(let o=1;oGp(o.cooked,!1)).join(", ")}], `),e.print(i,`[${t.map(o=>Gp(o.raw,!1)).join(", ")}])`),i.expressions.forEach(o=>{e.print(i,", "),o.visitExpression(this,e)}),e.print(i,")"),null}_visitParams(i,e){this.visitAllObjects(t=>e.print(null,t.name),i,e,",")}},L1;function yW(){if(L1===void 0){let n=j_.trustedTypes;if(L1=null,n)try{L1=n.createPolicy("angular#unsafe-jit",{createScript:i=>i})}catch{}}return L1}function SW(n){return yW()?.createScript(n)||n}function PR(...n){if(!j_.trustedTypes)return new Function(...n);let i=n.slice(0,-1).join(","),e=n[n.length-1],t=`(function anonymous(${i} -) { ${e} -})`,o=j_.eval(SW(t));return o.bind===void 0?new Function(...n):(o.toString=()=>t,o.bind(j_))}var WE=class{evaluateStatements(i,e,t,o){let r=new qE(t),a=kE.createRoot();return e.length>0&&!wW(e[0])&&(e=[Me("use strict").toStmt(),...e]),r.visitAllStatements(e,a),r.createReturnStmt(a),this.evaluateCode(i,a,r.getArgs(),o)}evaluateCode(i,e,t,o){let r=`"use strict";${e.toSource()} -//# sourceURL=${i}`,a=[],c=[];for(let p in t)c.push(t[p]),a.push(p);if(o){let p=PR(...a.concat("return null;")).toString(),h=p.slice(0,p.indexOf("return null;")).split(` -`).length-1;r+=` -${e.toSourceMapGenerator(i,h).toJsComment()}`}let m=PR(...a.concat(r));return this.executeFunction(m,c)}executeFunction(i,e){return i(...e)}},qE=class extends GE{refResolver;_evalArgNames=[];_evalArgValues=[];_evalExportedVars=[];constructor(i){super(),this.refResolver=i}createReturnStmt(i){new wr(new ql(this._evalExportedVars.map(t=>new Gh(t,Zn(t),!1)))).visitStatement(this,i)}getArgs(){let i={};for(let e=0;e0&&i.set("imports",Qi(n.imports));let e=Wt(he.defineInjector).callFn([i.toLiteralMap()],void 0,!0),t=MW(n);return{expression:e,type:t,statements:[]}}function MW(n){return new dl(Wt(he.InjectorDeclaration,[new dl(n.type.type)]))}var QE=class{context;constructor(i){this.context=i}resolveExternalReference(i){if(i.moduleName!=="@angular/core")throw new Error(`Cannot resolve external reference to ${i.moduleName}, only references to @angular/core are supported.`);if(!this.context.hasOwnProperty(i.name))throw new Error(`No value provided for @angular/core symbol '${i.name}'.`);return this.context[i.name]}},Ob=(function(n){return n[n.Inline=0]="Inline",n[n.SideEffect=1]="SideEffect",n[n.Omit=2]="Omit",n})(Ob||{}),_m=(function(n){return n[n.Global=0]="Global",n[n.Local=1]="Local",n})(_m||{});function kW(n){let i=[],e=new wm;if(e.set("type",n.type.value),n.kind===_m.Global&&n.bootstrap.length>0&&e.set("bootstrap",Wp(n.bootstrap,n.containsForwardDecls)),n.selectorScopeMode===Ob.Inline)n.declarations.length>0&&e.set("declarations",Wp(n.declarations,n.containsForwardDecls)),n.imports.length>0&&e.set("imports",Wp(n.imports,n.containsForwardDecls)),n.exports.length>0&&e.set("exports",Wp(n.exports,n.containsForwardDecls));else if(n.selectorScopeMode===Ob.SideEffect){let r=DW(n);r!==null&&i.push(r)}n.schemas!==null&&n.schemas.length>0&&e.set("schemas",Qi(n.schemas.map(r=>r.value))),n.id!==null&&(e.set("id",n.id),i.push(Wt(he.registerNgModuleType).callFn([n.type.value,n.id]).toStmt()));let t=Wt(he.defineNgModule).callFn([e.toLiteralMap()],void 0,!0),o=EW(n);return{expression:t,type:o,statements:i}}function TW(n){let i=new wm;return i.set("type",new ri(n.type)),n.bootstrap!==void 0&&i.set("bootstrap",new ri(n.bootstrap)),n.declarations!==void 0&&i.set("declarations",new ri(n.declarations)),n.imports!==void 0&&i.set("imports",new ri(n.imports)),n.exports!==void 0&&i.set("exports",new ri(n.exports)),n.schemas!==void 0&&i.set("schemas",new ri(n.schemas)),n.id!==void 0&&i.set("id",new ri(n.id)),Wt(he.defineNgModule).callFn([i.toLiteralMap()])}function EW(n){if(n.kind===_m.Local)return new dl(n.type.value);let{type:i,declarations:e,exports:t,imports:o,includeImportTypes:r,publicDeclarationTypes:a}=n;return new dl(Wt(he.NgModuleDeclaration,[new dl(i.type),a===null?KT(e):PW(a),r?KT(o):Mc,KT(t)]))}function DW(n){let i=new wm;if(n.kind===_m.Global?n.declarations.length>0&&i.set("declarations",Wp(n.declarations,n.containsForwardDecls)):n.declarationsExpression&&i.set("declarations",n.declarationsExpression),n.kind===_m.Global?n.imports.length>0&&i.set("imports",Wp(n.imports,n.containsForwardDecls)):n.importsExpression&&i.set("imports",n.importsExpression),n.kind===_m.Global?n.exports.length>0&&i.set("exports",Wp(n.exports,n.containsForwardDecls)):n.exportsExpression&&i.set("exports",n.exportsExpression),n.kind===_m.Local&&n.bootstrapExpression&&i.set("bootstrap",n.bootstrapExpression),Object.keys(i.values).length===0)return null;let e=new cs(Wt(he.setNgModuleScope),[n.type.value,i.toLiteralMap()]),t=HG(e),o=new vm([],[t.toStmt()]);return new cs(o,[]).toStmt()}function KT(n){let i=n.map(e=>q0(e.type));return n.length>0?ca(Qi(i)):Mc}function PW(n){let i=n.map(e=>q0(e));return n.length>0?ca(Qi(i)):Mc}function AR(n){let i=[];i.push({key:"name",value:Me(n.pipeName??n.name),quoted:!1}),i.push({key:"type",value:n.type.value,quoted:!1}),i.push({key:"pure",value:Me(n.pure),quoted:!1}),n.isStandalone===!1&&i.push({key:"standalone",value:Me(!1),quoted:!1});let e=Wt(he.definePipe).callFn([ml(i)],void 0,!0),t=IW(n);return{expression:e,type:t,statements:[]}}function IW(n){return new dl(Wt(he.PipeDeclaration,[lx(n.type.type,n.typeArgumentCount),new dl(new da(n.pipeName)),new dl(new da(n.isStandalone))]))}var tf=(function(n){return n[n.Directive=0]="Directive",n[n.Pipe=1]="Pipe",n[n.NgModule=2]="NgModule",n})(tf||{}),AW=new Set(["inherit","initial","revert","unset","alternate","alternate-reverse","normal","reverse","backwards","both","forwards","none","paused","running","ease","ease-in","ease-in-out","ease-out","linear","step-start","step-end","end","jump-both","jump-end","jump-none","jump-start","start"]),OW=["@media","@supports","@document","@layer","@container","@scope","@starting-style"],XE=class{shimCssText(i,e,t=""){let o=[];i=i.replace(XW,c=>{if(c.match(YW))o.push(c);else{let m=c.match(QW);o.push(m?.join("")??"")}return aP}),i=this._insertDirectives(i);let r=this._scopeCssText(i,e,t),a=0;return r.replace(KW,()=>o[a++])}_insertDirectives(i){return i=this._insertPolyfillDirectivesInCssText(i),this._insertPolyfillRulesInCssText(i)}_scopeKeyframesRelatedCss(i,e){let t=new Set,o=B1(i,r=>this._scopeLocalKeyframeDeclarations(r,e,t));return B1(o,r=>this._scopeAnimationRule(r,e,t))}_scopeLocalKeyframeDeclarations(i,e,t){return Qe(W({},i),{selector:i.selector.replace(/(^@(?:-webkit-)?keyframes(?:\s+))(['"]?)(.+)\2(\s*)$/,(o,r,a,c,m)=>(t.add(RR(c,a)),`${r}${a}${e}_${c}${a}${m}`))})}_scopeAnimationKeyframe(i,e,t){return i.replace(/^(\s*)(['"]?)(.+?)\2(\s*)$/,(o,r,a,c,m)=>(c=`${t.has(RR(c,a))?e+"_":""}${c}`,`${r}${a}${c}${a}${m}`))}_animationDeclarationKeyframesRe=/(^|\s+|,)(?:(?:(['"])((?:\\\\|\\\2|(?!\2).)+)\2)|(-?[A-Za-z][\w\-]*))(?=[,\s]|$)/g;_scopeAnimationRule(i,e,t){let o=i.content.replace(/((?:^|\s+|;)(?:-webkit-)?animation\s*:\s*),*([^;]+)/g,(r,a,c)=>a+c.replace(this._animationDeclarationKeyframesRe,(m,p,h="",g,S)=>g?`${p}${this._scopeAnimationKeyframe(`${h}${g}${h}`,e,t)}`:AW.has(S)?m:`${p}${this._scopeAnimationKeyframe(S,e,t)}`));return o=o.replace(/((?:^|\s+|;)(?:-webkit-)?animation-name(?:\s*):(?:\s*))([^;]+)/g,(r,a,c)=>`${a}${c.split(",").map(m=>this._scopeAnimationKeyframe(m,e,t)).join(",")}`),Qe(W({},i),{content:o})}_insertPolyfillDirectivesInCssText(i){return i.replace(RW,function(...e){return e[2]+"{"})}_insertPolyfillRulesInCssText(i){return i.replace(FW,(...e)=>{let t=e[0].replace(e[1],"").replace(e[2],"");return e[4]+t})}_scopeCssText(i,e,t){let o=this._extractUnscopedRulesFromCssText(i);return i=this._insertPolyfillHostInCssText(i),i=this._convertColonHost(i),i=this._convertColonHostContext(i),i=this._convertShadowDOMSelectors(i),e&&(i=this._scopeKeyframesRelatedCss(i,e),i=this._scopeSelectors(i,e,t)),i=i+` -`+o,i.trim()}_extractUnscopedRulesFromCssText(i){let e="",t;for(OR.lastIndex=0;(t=OR.exec(i))!==null;){let o=t[0].replace(t[2],"").replace(t[1],t[4]);e+=o+` - -`}return e}_convertColonHost(i){return i.replace(zW,(e,t,o)=>{if(t){let r=[];for(let a of this._splitOnTopLevelCommas(t,!0)){let c=a.trim();if(!c)break;let m=um+c.replace(Nb,"")+o;r.push(m)}return r.join(",")}else return um+o})}*_splitOnTopLevelCommas(i,e){let t=i.length,o=0,r=0;for(let a=0;a{let o=[[]],r=e.indexOf(Ah);for(;r!==-1;){let a=e.substring(r+Ah.length);if(!a||a[0]!=="("){e=a,r=e.indexOf(Ah);continue}let c=[],m=0;for(let h of this._splitOnTopLevelCommas(a.substring(1),!0)){m=m+h.length+1;let g=h.trim();g&&c.push(g)}let p=o.length;lq(o,c.length);for(let h=0;hsq(a,e,t)).join(", ")})}_convertShadowDOMSelectors(i){return UW.reduce((e,t)=>e.replace(t," "),i)}_scopeSelectors(i,e,t){return B1(i,o=>{let r=o.selector,a=o.content;return o.selector[0]!=="@"?r=this._scopeSelector({selector:r,scopeSelector:e,hostSelector:t,isParentSelector:!0}):OW.some(c=>o.selector.startsWith(c))?a=this._scopeSelectors(o.content,e,t):(o.selector.startsWith("@font-face")||o.selector.startsWith("@page"))&&(a=this._stripScopingSelectors(o.content)),new D0(r,a)})}_stripScopingSelectors(i){return B1(i,e=>{let t=e.selector.replace(NR," ").replace(ZT," ");return new D0(t,e.content)})}_safeSelector;_shouldScopeIndicator;_scopeSelector({selector:i,scopeSelector:e,hostSelector:t,isParentSelector:o=!1}){let r=/ ?,(?!(?:[^)(]*(?:\([^)(]*(?:\([^)(]*(?:\([^)(]*\)[^)(]*)*\)[^)(]*)*\)[^)(]*)*\))) ?/;return i.split(r).map(a=>a.split(NR)).map(a=>{let[c,...m]=a;return[(h=>this._selectorNeedsScoping(h,e)?this._applySelectorScope({selector:h,scopeSelector:e,hostSelector:t,isParentSelector:o}):h)(c),...m].join(" ")}).join(", ")}_selectorNeedsScoping(i,e){return!this._makeScopeMatcher(e).test(i)}_makeScopeMatcher(i){let e=/\[/g,t=/\]/g;return i=i.replace(e,"\\[").replace(t,"\\]"),new RegExp("^("+i+")"+GW,"m")}_applySimpleSelectorScope(i,e,t){if(Fh.lastIndex=0,Fh.test(i)){let o=`[${t}]`,r=i;for(;r.match(ZT);)r=r.replace(ZT,(a,c)=>c.replace(/([^:\)]*)(:*)(.*)/,(m,p,h,g)=>p+o+h+g));return r.replace(Fh,o)}return e+" "+i}_applySelectorScope({selector:i,scopeSelector:e,hostSelector:t,isParentSelector:o}){let r=/\[is=([^\]]*)\]/g;e=e.replace(r,(M,...w)=>w[0]);let a=`[${e}]`,c=M=>{let w=M.trim();if(!w)return M;if(M.includes(um)){if(w=this._applySimpleSelectorScope(M,e,t),!M.match(HW)){let[y,k,I,P]=w.match(/([^:]*)(:*)([\s\S]*)/);w=k+a+I+P}}else{let y=M.replace(Fh,"");if(y.length>0){let k=y.match(/([^:]*)(:*)([\s\S]*)/);k&&(w=k[1]+a+k[2]+k[3])}}return w},m=M=>{let w="",y=[],k;for(;(k=T_.exec(M))!==null;){let I=1,P=T_.lastIndex;for(;P{let[P]=I.match(T_)??[],R=I.slice(P?.length,-1);R.includes(um)&&(this._shouldScopeIndicator=!0);let D=this._scopeSelector({selector:R,scopeSelector:e,hostSelector:t});return`${P}${D})`}).join(""):(this._shouldScopeIndicator=this._shouldScopeIndicator||M.includes(um),w=this._shouldScopeIndicator?c(M):M),w};o&&(this._safeSelector=new YE(i),i=this._safeSelector.content());let p="",h=0,g,S=/( |>|\+|~(?!=))(?!([^)(]*(?:\([^)(]*(?:\([^)(]*(?:\([^)(]*\)[^)(]*)*\)[^)(]*)*\)[^)(]*)*\)))\s*/g,x=i.includes(um);for((o||this._shouldScopeIndicator)&&(this._shouldScopeIndicator=!x);(g=S.exec(i))!==null;){let M=g[1],w=i.slice(h,g.index);if(w.match(/__esc-ph-(\d+)__/)&&i[g.index+1]?.match(/[a-fA-F\d]/))continue;let y=m(w);p+=`${y} ${M} `,h=S.lastIndex}let v=i.substring(h);return p+=m(v),this._safeSelector.restore(p)}_insertPolyfillHostInCssText(i){return i.replace(qW,Ah).replace(WW,Nb)}},YE=class{placeholders=[];index=0;_content;constructor(i){i=this._escapeRegexMatches(i,/(\[[^\]]*\])/g),i=i.replace(/(\\.)/g,(e,t)=>{let o=`__esc-ph-${this.index}__`;return this.placeholders.push(t),this.index++,o}),this._content=i.replace(VW,(e,t,o)=>{let r=`__ph-${this.index}__`;return this.placeholders.push(`(${o})`),this.index++,t+r})}restore(i){return i.replace(/__(?:ph|esc-ph)-(\d+)__/g,(e,t)=>this.placeholders[+t])}content(){return this._content}_escapeRegexMatches(i,e){return i.replace(e,(t,o)=>{let r=`__ph-${this.index}__`;return this.placeholders.push(o),this.index++,r})}},NW="(:(where|is)\\()?",T_=/:(where|is)\(/gi,RW=/polyfill-next-selector[^}]*content:[\s]*?(['"])(.*?)\1[;\s]*}([^{]*?){/gim,FW=/(polyfill-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim,OR=/(polyfill-unscoped-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim,Nb="-shadowcsshost",Ah="-shadowcsscontext",KE="[^)(]*",LW=String.raw`(?:\(${KE}\)|${KE})+?`,BW=String.raw`(?:\(${LW}\)|${KE})+?`,rP=String.raw`(?:\((${BW})\))`,VW=new RegExp(String.raw`(:nth-[-\w]+)`+rP,"g"),zW=new RegExp(Nb+rP+"?([^,{]*)","gim"),jW=Ah+rP+"?([^{]*)",$W=new RegExp(`${NW}(${jW})`,"gim"),um=Nb+"-no-combinator",HW=new RegExp(`${um}(?![^(]*\\))`,"g"),ZT=/-shadowcsshost-no-combinator([^\s,]*)/,UW=[/::shadow/g,/::content/g,/\/shadow-deep\//g,/\/shadow\//g],NR=/(?:>>>)|(?:\/deep\/)|(?:::ng-deep)/g,GW="([>\\s~+[.,{:][\\s\\S]*)?$",Fh=/-shadowcsshost/gim,WW=/:host/gim,qW=/:host-context/gim,QW=/\r?\n/g,XW=/\/\*[\s\S]*?\*\//g,YW=/\/\*\s*#\s*source(Mapping)?URL=/g,aP="%COMMENT%",KW=new RegExp(aP,"g"),JT="%BLOCK%",ZW=new RegExp(`(\\s*(?:${aP}\\s*)*)([^;\\{\\}]+?)(\\s*)((?:{%BLOCK%}?\\s*;?)|(?:\\s*;))`,"g"),JW=new Map([["{","}"]]),T6="%COMMA_IN_PLACEHOLDER%",E6="%SEMI_IN_PLACEHOLDER%",D6="%COLON_IN_PLACEHOLDER%",eq=new RegExp(T6,"g"),tq=new RegExp(E6,"g"),nq=new RegExp(D6,"g"),D0=class{selector;content;constructor(i,e){this.selector=i,this.content=e}};function B1(n,i){let e=rq(n),t=iq(e,JW,JT),o=0,r=t.escapedString.replace(ZW,(...a)=>{let c=a[2],m="",p=a[4],h="";p&&p.startsWith("{"+JT)&&(m=t.blocks[o++],p=p.substring(JT.length+1),h="{");let g=i(new D0(c,m));return`${a[1]}${g.selector}${a[3]}${h}${g.content}${p}`});return aq(r)}var ZE=class{escapedString;blocks;constructor(i,e){this.escapedString=i,this.blocks=e}};function iq(n,i,e){let t=[],o=[],r=0,a=0,c=-1,m,p;for(let h=0;h0;){let a=r.length,c=n.pop();for(let m=0;mo?`${e}${a}${i}`:`${e}${a}${t}${i}, ${e}${a} ${t}${i}`).join(",")}function lq(n,i){let e=n.length;for(let t=1;t{class n{static nextListId=0;debugListId=n.nextListId++;head={kind:L.ListEnd,next:null,prev:null,debugListId:this.debugListId};tail={kind:L.ListEnd,next:null,prev:null,debugListId:this.debugListId};constructor(){this.head.next=this.tail,this.tail.prev=this.head}push(e){if(Array.isArray(e)){for(let o of e)this.push(o);return}n.assertIsNotEnd(e),n.assertIsUnowned(e),e.debugListId=this.debugListId;let t=this.tail.prev;e.prev=t,t.next=e,e.next=this.tail,this.tail.prev=e}prepend(e){if(e.length===0)return;for(let r of e)n.assertIsNotEnd(r),n.assertIsUnowned(r),r.debugListId=this.debugListId;let t=this.head.next,o=this.head;for(let r of e)o.next=r,r.prev=o,o=r;o.next=t,t.prev=o}*[Symbol.iterator](){let e=this.head.next;for(;e!==this.tail;){n.assertIsOwned(e,this.debugListId);let t=e.next;yield e,e=t}}*reversed(){let e=this.tail.prev;for(;e!==this.head;){n.assertIsOwned(e,this.debugListId);let t=e.prev;yield e,e=t}}static replace(e,t){n.assertIsNotEnd(e),n.assertIsNotEnd(t),n.assertIsOwned(e),n.assertIsUnowned(t),t.debugListId=e.debugListId,e.prev!==null&&(e.prev.next=t,t.prev=e.prev),e.next!==null&&(e.next.prev=t,t.next=e.next),e.debugListId=null,e.prev=null,e.next=null}static replaceWithMany(e,t){if(t.length===0){n.remove(e);return}n.assertIsNotEnd(e),n.assertIsOwned(e);let o=e.debugListId;e.debugListId=null;for(let h of t)n.assertIsNotEnd(h),n.assertIsUnowned(h);let{prev:r,next:a}=e;e.prev=null,e.next=null;let c=r;for(let h of t)n.assertIsUnowned(h),h.debugListId=o,c.next=h,h.prev=c,h.next=null,c=h;let m=t[0],p=c;r!==null&&(r.next=m,m.prev=r),a!==null&&(a.prev=p,p.next=a)}static remove(e){n.assertIsNotEnd(e),n.assertIsOwned(e),e.prev.next=e.next,e.next.prev=e.prev,e.debugListId=null,e.prev=null,e.next=null}static insertBefore(e,t){if(Array.isArray(e)){for(let o of e)n.insertBefore(o,t);return}if(n.assertIsOwned(t),t.prev===null)throw new Error("AssertionError: illegal operation on list start");n.assertIsNotEnd(e),n.assertIsUnowned(e),e.debugListId=t.debugListId,e.prev=null,t.prev.next=e,e.prev=t.prev,e.next=t,t.prev=e}static insertAfter(e,t){if(n.assertIsOwned(t),t.next===null)throw new Error("AssertionError: illegal operation on list end");n.assertIsNotEnd(e),n.assertIsUnowned(e),e.debugListId=t.debugListId,t.next.prev=e,e.next=t.next,e.prev=t,t.next=e}static assertIsUnowned(e){if(e.debugListId!==null)throw new Error(`AssertionError: illegal operation on owned node: ${L[e.kind]}`)}static assertIsOwned(e,t){if(e.debugListId===null)throw new Error(`AssertionError: illegal operation on unowned node: ${L[e.kind]}`);if(t!==void 0&&e.debugListId!==t)throw new Error(`AssertionError: node belongs to the wrong list (expected ${t}, actual ${e.debugListId})`)}static assertIsNotEnd(e){if(e.kind===L.ListEnd)throw new Error("AssertionError: illegal operation on list head or tail")}}return n})();function Bs(n){return W({kind:L.Statement,statement:n},vn)}function fm(n,i,e,t){return W({kind:L.Variable,xref:n,variable:i,initializer:e,flags:t},vn)}var vn={debugListId:null,prev:null,next:null},P6=Symbol("ConsumesSlot"),sP=Symbol("DependsOnSlotContext"),Cu=Symbol("ConsumesVars"),K0=Symbol("UsesVarOffset"),pl={[P6]:!0,numSlotsUsed:1},ms={[sP]:!0},ps={[Cu]:!0};function pf(n){return n[P6]===!0}function I0(n){return n[sP]===!0}function eE(n){return n[Cu]===!0}function FR(n){return n[K0]===!0}function cq(n,i,e){return W(W(W({kind:L.InterpolateText,target:n,interpolation:i,sourceSpan:e},ms),ps),vn)}var Yo=class{strings;expressions;i18nPlaceholders;constructor(i,e,t){if(this.strings=i,this.expressions=e,this.i18nPlaceholders=t,t.length!==0&&t.length!==e.length)throw new Error(`Expected ${e.length} placeholders to match interpolation expression count, but got ${t.length}`)}};function uu(n,i,e,t,o,r,a,c,m,p,h){return W({kind:L.Binding,bindingKind:i,target:n,name:e,expression:t,unit:o,securityContext:r,isTextAttribute:a,isStructuralTemplateAttribute:c,templateKind:m,i18nContext:null,i18nMessage:p,sourceSpan:h},vn)}function dq(n,i,e,t,o,r,a,c,m,p){return W(W(W({kind:L.Property,target:n,name:i,expression:e,bindingKind:t,securityContext:o,sanitizer:null,isStructuralTemplateAttribute:r,templateKind:a,i18nContext:c,i18nMessage:m,sourceSpan:p},ms),ps),vn)}function mq(n,i,e,t,o,r,a,c,m){return W(W(W({kind:L.TwoWayProperty,target:n,name:i,expression:e,securityContext:t,sanitizer:null,isStructuralTemplateAttribute:o,templateKind:r,i18nContext:a,i18nMessage:c,sourceSpan:m},ms),ps),vn)}function pq(n,i,e,t,o){return W(W(W({kind:L.StyleProp,target:n,name:i,expression:e,unit:t,sourceSpan:o},ms),ps),vn)}function uq(n,i,e,t){return W(W(W({kind:L.ClassProp,target:n,name:i,expression:e,sourceSpan:t},ms),ps),vn)}function hq(n,i,e){return W(W(W({kind:L.StyleMap,target:n,expression:i,sourceSpan:e},ms),ps),vn)}function fq(n,i,e){return W(W(W({kind:L.ClassMap,target:n,expression:i,sourceSpan:e},ms),ps),vn)}function LR(n,i,e,t,o,r,a,c,m,p){return W(W(W({kind:L.Attribute,target:n,namespace:i,name:e,expression:t,securityContext:o,sanitizer:null,isTextAttribute:r,isStructuralTemplateAttribute:a,templateKind:c,i18nContext:null,i18nMessage:m,sourceSpan:p},ms),ps),vn)}function gq(n,i){return W({kind:L.Advance,delta:n,sourceSpan:i},vn)}function I6(n,i,e,t){return W(W(W({kind:L.Conditional,target:n,test:i,conditions:e,processed:null,sourceSpan:t,contextValue:null},vn),ms),ps)}function _q(n,i,e,t){return W(W({kind:L.Repeater,target:n,targetSlot:i,collection:e,sourceSpan:t},vn),ms)}function BR(n,i,e,t,o,r,a){return W({kind:L.AnimationBinding,name:n,target:i,animationKind:e,expression:t,i18nMessage:null,securityContext:o,sanitizer:null,sourceSpan:r,animationBindingKind:a},vn)}function vq(n,i,e,t){return W(W(W({kind:L.DeferWhen,target:n,expr:i,modifier:e,sourceSpan:t},vn),ms),ps)}function A6(n,i,e,t,o,r,a,c,m,p,h){return W(W(W({kind:L.I18nExpression,context:n,target:i,i18nOwner:e,handle:t,expression:o,icuPlaceholder:r,i18nPlaceholder:a,resolutionTime:c,usage:m,name:p,sourceSpan:h},vn),ps),ms)}function Cq(n,i,e){return W({kind:L.I18nApply,owner:n,handle:i,sourceSpan:e},vn)}function bq(n,i,e,t){return W(W(W({kind:L.StoreLet,target:n,declaredName:i,value:e,sourceSpan:t},ms),ps),vn)}function xq(n,i){return W(W({kind:L.Control,sourceSpan:i,target:n},ms),vn)}function Ic(n){return n instanceof Xi}var Xi=class extends Li{constructor(i=null){super(null,i)}},Wr=class n extends Xi{name;kind=Yt.LexicalRead;constructor(i){super(),this.name=i}visitExpression(i,e){}isEquivalent(i){return this.name===i.name}isConstant(){return!1}transformInternalExpressions(){}clone(){return new n(this.name)}},Rb=class n extends Xi{target;targetSlot;offset;kind=Yt.Reference;constructor(i,e,t){super(),this.target=i,this.targetSlot=e,this.offset=t}visitExpression(){}isEquivalent(i){return i instanceof n&&i.target===this.target}isConstant(){return!1}transformInternalExpressions(){}clone(){return new n(this.target,this.targetSlot,this.offset)}},A0=class n extends Xi{target;value;sourceSpan;kind=Yt.StoreLet;[Cu]=!0;[sP]=!0;constructor(i,e,t){super(),this.target=i,this.value=e,this.sourceSpan=t}visitExpression(){}isEquivalent(i){return i instanceof n&&i.target===this.target&&i.value.isEquivalent(this.value)}isConstant(){return!1}transformInternalExpressions(i,e){this.value=Ft(this.value,i,e)}clone(){return new n(this.target,this.value,this.sourceSpan)}},O0=class n extends Xi{target;targetSlot;kind=Yt.ContextLetReference;constructor(i,e){super(),this.target=i,this.targetSlot=e}visitExpression(){}isEquivalent(i){return i instanceof n&&i.target===this.target}isConstant(){return!1}transformInternalExpressions(){}clone(){return new n(this.target,this.targetSlot)}},km=class n extends Xi{view;kind=Yt.Context;constructor(i){super(),this.view=i}visitExpression(){}isEquivalent(i){return i instanceof n&&i.view===this.view}isConstant(){return!1}transformInternalExpressions(){}clone(){return new n(this.view)}},JE=class n extends Xi{view;kind=Yt.TrackContext;constructor(i){super(),this.view=i}visitExpression(){}isEquivalent(i){return i instanceof n&&i.view===this.view}isConstant(){return!1}transformInternalExpressions(){}clone(){return new n(this.view)}},Fb=class n extends Xi{kind=Yt.NextContext;steps=1;visitExpression(){}isEquivalent(i){return i instanceof n&&i.steps===this.steps}isConstant(){return!1}transformInternalExpressions(){}clone(){let i=new n;return i.steps=this.steps,i}},eD=class n extends Xi{kind=Yt.GetCurrentView;constructor(){super()}visitExpression(){}isEquivalent(i){return i instanceof n}isConstant(){return!1}transformInternalExpressions(){}clone(){return new n}},N0=class n extends Xi{view;kind=Yt.RestoreView;constructor(i){super(),this.view=i}visitExpression(i,e){typeof this.view!="number"&&this.view.visitExpression(i,e)}isEquivalent(i){return!(i instanceof n)||typeof i.view!=typeof this.view?!1:typeof this.view=="number"?this.view===i.view:this.view.isEquivalent(i.view)}isConstant(){return!1}transformInternalExpressions(i,e){typeof this.view!="number"&&(this.view=Ft(this.view,i,e))}clone(){return new n(this.view instanceof Li?this.view.clone():this.view)}},Lb=class n extends Xi{expr;kind=Yt.ResetView;constructor(i){super(),this.expr=i}visitExpression(i,e){this.expr.visitExpression(i,e)}isEquivalent(i){return i instanceof n&&this.expr.isEquivalent(i.expr)}isConstant(){return!1}transformInternalExpressions(i,e){this.expr=Ft(this.expr,i,e)}clone(){return new n(this.expr.clone())}},Bb=class n extends Xi{target;value;kind=Yt.TwoWayBindingSet;constructor(i,e){super(),this.target=i,this.value=e}visitExpression(i,e){this.target.visitExpression(i,e),this.value.visitExpression(i,e)}isEquivalent(i){return this.target.isEquivalent(i.target)&&this.value.isEquivalent(i.value)}isConstant(){return!1}transformInternalExpressions(i,e){this.target=Ft(this.target,i,e),this.value=Ft(this.value,i,e)}clone(){return new n(this.target,this.value)}},Sd=class n extends Xi{xref;kind=Yt.ReadVariable;name=null;constructor(i){super(),this.xref=i}visitExpression(){}isEquivalent(i){return i instanceof n&&i.xref===this.xref}isConstant(){return!1}transformInternalExpressions(){}clone(){let i=new n(this.xref);return i.name=this.name,i}},hu=class n extends Xi{kind=Yt.PureFunctionExpr;[Cu]=!0;[K0]=!0;varOffset=null;body;args;fn=null;constructor(i,e){super(),this.body=i,this.args=e}visitExpression(i,e){this.body?.visitExpression(i,e);for(let t of this.args)t.visitExpression(i,e)}isEquivalent(i){return!(i instanceof n)||i.args.length!==this.args.length?!1:i.body!==null&&this.body!==null&&i.body.isEquivalent(this.body)&&i.args.every((e,t)=>e.isEquivalent(this.args[t]))}isConstant(){return!1}transformInternalExpressions(i,e){this.body!==null?this.body=Ft(this.body,i,e|Wn.InChildOperation):this.fn!==null&&(this.fn=Ft(this.fn,i,e));for(let t=0;te.clone()));return i.fn=this.fn?.clone()??null,i.varOffset=this.varOffset,i}},Tm=class n extends Xi{index;kind=Yt.PureFunctionParameterExpr;constructor(i){super(),this.index=i}visitExpression(){}isEquivalent(i){return i instanceof n&&i.index===this.index}isConstant(){return!0}transformInternalExpressions(){}clone(){return new n(this.index)}},fu=class n extends Xi{target;targetSlot;name;args;kind=Yt.PipeBinding;[Cu]=!0;[K0]=!0;varOffset=null;constructor(i,e,t,o){super(),this.target=i,this.targetSlot=e,this.name=t,this.args=o}visitExpression(i,e){for(let t of this.args)t.visitExpression(i,e)}isEquivalent(){return!1}isConstant(){return!1}transformInternalExpressions(i,e){for(let t=0;te.clone()));return i.varOffset=this.varOffset,i}},R0=class n extends Xi{target;targetSlot;name;args;numArgs;kind=Yt.PipeBindingVariadic;[Cu]=!0;[K0]=!0;varOffset=null;constructor(i,e,t,o,r){super(),this.target=i,this.targetSlot=e,this.name=t,this.args=o,this.numArgs=r}visitExpression(i,e){this.args.visitExpression(i,e)}isEquivalent(){return!1}isConstant(){return!1}transformInternalExpressions(i,e){this.args=Ft(this.args,i,e)}clone(){let i=new n(this.target,this.targetSlot,this.name,this.args.clone(),this.numArgs);return i.varOffset=this.varOffset,i}},nf=class n extends Xi{receiver;name;kind=Yt.SafePropertyRead;constructor(i,e){super(),this.receiver=i,this.name=e}get index(){return this.name}visitExpression(i,e){this.receiver.visitExpression(i,e)}isEquivalent(){return!1}isConstant(){return!1}transformInternalExpressions(i,e){this.receiver=Ft(this.receiver,i,e)}clone(){return new n(this.receiver.clone(),this.name)}},of=class n extends Xi{receiver;index;kind=Yt.SafeKeyedRead;constructor(i,e,t){super(t),this.receiver=i,this.index=e}visitExpression(i,e){this.receiver.visitExpression(i,e),this.index.visitExpression(i,e)}isEquivalent(){return!1}isConstant(){return!1}transformInternalExpressions(i,e){this.receiver=Ft(this.receiver,i,e),this.index=Ft(this.index,i,e)}clone(){return new n(this.receiver.clone(),this.index.clone(),this.sourceSpan)}},gu=class n extends Xi{receiver;args;kind=Yt.SafeInvokeFunction;constructor(i,e){super(),this.receiver=i,this.args=e}visitExpression(i,e){this.receiver.visitExpression(i,e);for(let t of this.args)t.visitExpression(i,e)}isEquivalent(){return!1}isConstant(){return!1}transformInternalExpressions(i,e){this.receiver=Ft(this.receiver,i,e);for(let t=0;ti.clone()))}},rf=class n extends Xi{guard;expr;kind=Yt.SafeTernaryExpr;constructor(i,e){super(),this.guard=i,this.expr=e}visitExpression(i,e){this.guard.visitExpression(i,e),this.expr.visitExpression(i,e)}isEquivalent(){return!1}isConstant(){return!1}transformInternalExpressions(i,e){this.guard=Ft(this.guard,i,e),this.expr=Ft(this.expr,i,e)}clone(){return new n(this.guard.clone(),this.expr.clone())}},F0=class n extends Xi{kind=Yt.EmptyExpr;visitExpression(i,e){}isEquivalent(i){return i instanceof n}isConstant(){return!0}clone(){return new n}transformInternalExpressions(){}},Ac=class n extends Xi{expr;xref;kind=Yt.AssignTemporaryExpr;name=null;constructor(i,e){super(),this.expr=i,this.xref=e}visitExpression(i,e){this.expr.visitExpression(i,e)}isEquivalent(){return!1}isConstant(){return!1}transformInternalExpressions(i,e){this.expr=Ft(this.expr,i,e)}clone(){let i=new n(this.expr.clone(),this.xref);return i.name=this.name,i}},Em=class n extends Xi{xref;kind=Yt.ReadTemporaryExpr;name=null;constructor(i){super(),this.xref=i}visitExpression(i,e){}isEquivalent(){return this.xref===this.xref}isConstant(){return!1}transformInternalExpressions(i,e){}clone(){let i=new n(this.xref);return i.name=this.name,i}},Vb=class n extends Xi{slot;kind=Yt.SlotLiteralExpr;constructor(i){super(),this.slot=i}visitExpression(i,e){}isEquivalent(i){return i instanceof n&&i.slot===this.slot}isConstant(){return!0}clone(){return new n(this.slot)}transformInternalExpressions(){}},zb=class n extends Xi{expr;target;targetSlot;alias;kind=Yt.ConditionalCase;constructor(i,e,t,o=null){super(),this.expr=i,this.target=e,this.targetSlot=t,this.alias=o}visitExpression(i,e){this.expr!==null&&this.expr.visitExpression(i,e)}isEquivalent(i){return i instanceof n&&i.expr===this.expr}isConstant(){return!0}clone(){return new n(this.expr,this.target,this.targetSlot)}transformInternalExpressions(i,e){this.expr!==null&&(this.expr=Ft(this.expr,i,e))}},L0=class n extends Xi{expr;kind=Yt.ConstCollected;constructor(i){super(),this.expr=i}transformInternalExpressions(i,e){this.expr=i(this.expr,e)}visitExpression(i,e){this.expr.visitExpression(i,e)}isEquivalent(i){return i instanceof n?this.expr.isEquivalent(i.expr):!1}isConstant(){return this.expr.isConstant()}clone(){return new n(this.expr)}},tD=class n extends Xi{parameters;body;kind=Yt.ArrowFunction;[Cu]=!0;[K0]=!0;contextName=Ls;currentViewName="view";varOffset=null;ops;constructor(i,e){super(),this.parameters=i,this.body=e,this.ops=new We,this.ops.push([Bs(new wr(e,e.sourceSpan))])}visitExpression(i,e){for(let t of this.ops)hr(t,o=>{o.visitExpression(i,e)})}isEquivalent(i){return i instanceof n&&i.parameters.length===this.parameters.length&&i.parameters.every((e,t)=>e.isEquivalent(this.parameters[t]))&&i.body.isEquivalent(this.body)}isConstant(){return!1}transformInternalExpressions(i,e){for(let t of this.ops)Ko(t,i,e|(Wn.InChildOperation|Wn.InArrowFunctionOperation))}clone(){let i=new n(this.parameters,this.body);return i.varOffset=this.varOffset,i.ops=this.ops,i}};function hr(n,i){Ko(n,(e,t)=>(i(e,t),e),Wn.None)}var Wn=(function(n){return n[n.None=0]="None",n[n.InChildOperation=1]="InChildOperation",n[n.InArrowFunctionOperation=2]="InArrowFunctionOperation",n})(Wn||{});function tE(n,i,e){for(let t=0;tFt(t,i,e));else if(n instanceof vu)if(Array.isArray(n.body))for(let t=0;t{!a&&I0(c)&&c.target!==r.xref&&(a=!0)}),a)break;e=e.next}}}}function Qq(n){if(!(!n.enableDebugLocations||n.relativeTemplatePath===null))for(let i of n.units){let e=[];for(let t of i.create)if(t.kind===L.ElementStart||t.kind===L.Element){let o=t.startSourceSpan.start;e.push({targetSlot:t.handle,offset:o.offset,line:o.line,column:o.col})}e.length>0&&i.create.push(zq(n.relativeTemplatePath,e))}}function $6(n){let i=new Map;for(let e of n.create)pf(e)&&(i.set(e.xref,e),e.kind===L.RepeaterCreate&&e.emptyView!==null&&i.set(e.emptyView,e));return i}function Xq(n){for(let i of n.units){let e=$6(i);for(let t of i.ops())switch(t.kind){case L.Attribute:Yq(i,t,e);break;case L.Property:if(t.bindingKind!==Ht.LegacyAnimation&&t.bindingKind!==Ht.Animation){let o;t.i18nMessage!==null&&t.templateKind===null?o=Ht.I18n:t.isStructuralTemplateAttribute?o=Ht.Template:o=Ht.Property,We.insertBefore(ll(t.target,o,null,t.name,null,null,null,t.securityContext),Oh(e,t.target))}break;case L.TwoWayProperty:We.insertBefore(ll(t.target,Ht.TwoWayProperty,null,t.name,null,null,null,t.securityContext),Oh(e,t.target));break;case L.StyleProp:case L.ClassProp:t.expression instanceof F0&&We.insertBefore(ll(t.target,Ht.Property,null,t.name,null,null,null,ro.STYLE),Oh(e,t.target));break;case L.Listener:if(!t.isLegacyAnimationListener){let o=ll(t.target,Ht.Property,null,t.name,null,null,null,ro.NONE);if(n.kind===Tt.Host)break;We.insertBefore(o,Oh(e,t.target))}break;case L.TwoWayListener:if(n.kind!==Tt.Host){let o=ll(t.target,Ht.Property,null,t.name,null,null,null,ro.NONE);We.insertBefore(o,Oh(e,t.target))}break}}}function Oh(n,i){let e=n.get(i);if(e===void 0)throw new Error("All attributes should have an element-like target.");return e}function Yq(n,i,e){if(!(i.expression instanceof Yo)&&i.isTextAttribute){let t=ll(i.target,i.isStructuralTemplateAttribute?Ht.Template:Ht.Attribute,i.namespace,i.name,i.expression,i.i18nContext,i.i18nMessage,i.securityContext);if(n.job.kind===Tt.Host)n.create.push(t);else{let o=Oh(e,i.target);We.insertBefore(t,o)}We.remove(i)}}var VR="aria-";function H6(n){return n.startsWith(VR)&&n.length>VR.length}function Kq(n,i){let e=n.get(i);if(e===void 0)throw new Error("All attributes should have an element-like target.");return e}function Zq(n){let i=new Map;for(let e of n.units)for(let t of e.create)Dm(t)&&i.set(t.xref,t);for(let e of n.units)for(let t of e.ops())if(t.kind===L.Binding)switch(t.bindingKind){case Ht.Attribute:if(t.name==="ngNonBindable"){We.remove(t);let o=Kq(i,t.target);o.nonBindable=!0}else if(t.name.startsWith("animate."))We.replace(t,BR(t.name,t.target,t.name==="animate.enter"?"enter":"leave",t.expression,t.securityContext,t.sourceSpan,0));else{let[o,r]=Ql(t.name);We.replace(t,LR(t.target,o,r,t.expression,t.securityContext,t.isTextAttribute,t.isStructuralTemplateAttribute,t.templateKind,t.i18nMessage,t.sourceSpan))}break;case Ht.Animation:We.replace(t,BR(t.name,t.target,t.name==="animate.enter"?"enter":"leave",t.expression,t.securityContext,t.sourceSpan,1));break;case Ht.Property:case Ht.LegacyAnimation:n.mode===is.DomOnly&&H6(t.name)?We.replace(t,LR(t.target,null,t.name,t.expression,t.securityContext,!1,t.isStructuralTemplateAttribute,t.templateKind,t.i18nMessage,t.sourceSpan)):n.kind===Tt.Host?We.replace(t,$q(t.name,t.expression,t.bindingKind,t.i18nContext,t.securityContext,t.sourceSpan)):We.replace(t,dq(t.target,t.name,t.expression,t.bindingKind,t.securityContext,t.isStructuralTemplateAttribute,t.templateKind,t.i18nContext,t.i18nMessage,t.sourceSpan));break;case Ht.TwoWayProperty:if(!(t.expression instanceof Li))throw new Error(`Expected value of two-way property binding "${t.name}" to be an expression`);We.replace(t,mq(t.target,t.name,t.expression,t.securityContext,t.isStructuralTemplateAttribute,t.templateKind,t.i18nContext,t.i18nMessage,t.sourceSpan));break;case Ht.I18n:case Ht.ClassName:case Ht.StyleProperty:throw new Error(`Unhandled binding of kind ${Ht[t.bindingKind]}`)}}var zR=new Map([[he.ariaProperty,he.ariaProperty],[he.attribute,he.attribute],[he.classProp,he.classProp],[he.element,he.element],[he.elementContainer,he.elementContainer],[he.elementContainerEnd,he.elementContainerEnd],[he.elementContainerStart,he.elementContainerStart],[he.elementEnd,he.elementEnd],[he.elementStart,he.elementStart],[he.domProperty,he.domProperty],[he.i18nExp,he.i18nExp],[he.listener,he.listener],[he.listener,he.listener],[he.property,he.property],[he.styleProp,he.styleProp],[he.syntheticHostListener,he.syntheticHostListener],[he.syntheticHostProperty,he.syntheticHostProperty],[he.templateCreate,he.templateCreate],[he.twoWayProperty,he.twoWayProperty],[he.twoWayListener,he.twoWayListener],[he.declareLet,he.declareLet],[he.conditionalCreate,he.conditionalBranchCreate],[he.conditionalBranchCreate,he.conditionalBranchCreate],[he.domElement,he.domElement],[he.domElementStart,he.domElementStart],[he.domElementEnd,he.domElementEnd],[he.domElementContainer,he.domElementContainer],[he.domElementContainerStart,he.domElementContainerStart],[he.domElementContainerEnd,he.domElementContainerEnd],[he.domListener,he.domListener],[he.domTemplate,he.domTemplate],[he.animationEnter,he.animationEnter],[he.animationLeave,he.animationLeave],[he.animationEnterListener,he.animationEnterListener],[he.animationLeaveListener,he.animationLeaveListener]]),Jq=256;function eQ(n){for(let i of n.units)jR(i.create),jR(i.update)}function jR(n){let i=null;for(let e of n){if(e.kind!==L.Statement||!(e.statement instanceof ma)){i=null;continue}if(!(e.statement.expr instanceof cs)||!(e.statement.expr.fn instanceof ou)){i=null;continue}let t=e.statement.expr.fn.value;if(!zR.has(t)){i=null;continue}if(i!==null&&zR.get(i.instruction)===t&&i.lengtho==="")&&(e.expression=e.expression.expressions[0])}function nQ(n){for(let i of n.units)for(let e of i.ops()){if(e.kind!==L.Conditional)continue;let t,o=e.conditions.findIndex(c=>c.expr===null);if(o>=0){let c=e.conditions.splice(o,1)[0].targetSlot;t=new Vb(c)}else t=Me(-1);let r=e.test==null?null:new Ac(e.test,n.allocateXrefId()),a=null;for(let c=e.conditions.length-1;c>=0;c--){let m=e.conditions[c];if(m.expr!==null){if(r!==null){let p=c===0?r:new Em(r.xref);m.expr=new fi(lt.Identical,p,m.expr)}else m.alias!==null&&(a??=n.allocateXrefId(),m.expr=new Ac(m.expr,a),e.contextValue=new Em(a));t=new kc(m.expr,new Vb(m.targetSlot),t)}}e.processed=t,e.conditions=[]}}var iQ=new Map([["&&",lt.And],[">",lt.Bigger],[">=",lt.BiggerEquals],["|",lt.BitwiseOr],["&",lt.BitwiseAnd],["/",lt.Divide],["=",lt.Assign],["==",lt.Equals],["===",lt.Identical],["<",lt.Lower],["<=",lt.LowerEquals],["-",lt.Minus],["%",lt.Modulo],["**",lt.Exponentiation],["*",lt.Multiply],["!=",lt.NotEquals],["!==",lt.NotIdentical],["??",lt.NullishCoalesce],["||",lt.Or],["+",lt.Plus],["in",lt.In],["instanceof",lt.InstanceOf],["+=",lt.AdditionAssignment],["-=",lt.SubtractionAssignment],["*=",lt.MultiplicationAssignment],["/=",lt.DivisionAssignment],["%=",lt.RemainderAssignment],["**=",lt.ExponentiationAssignment],["&&=",lt.AndAssignment],["||=",lt.OrAssignment],["??=",lt.NullishCoalesceAssignment]]);function U6(n){let i=new Map([["svg",Sa.SVG],["math",Sa.Math]]);return n===null?Sa.HTML:i.get(n)??Sa.HTML}function oQ(n){let i=new Map([["svg",Sa.SVG],["math",Sa.Math]]);for(let[e,t]of i.entries())if(t===n)return e;return null}function rQ(n,i){return i===Sa.HTML?n:`:${oQ(i)}:${n}`}function af(n){return Array.isArray(n)?Qi(n.map(af)):Me(n)}function aQ(n){let i=new Map;for(let e of n.units)for(let t of e.create)if(t.kind===L.ExtractedAttribute){let o=i.get(t.target)||new iD;i.set(t.target,o),o.add(t.bindingKind,t.name,t.expression,t.namespace,t.trustedValueFn),We.remove(t)}if(n instanceof B0)for(let e of n.units)for(let t of e.create)if(t.kind==L.Projection){let o=i.get(t.xref);if(o!==void 0){let r=oD(o);r.entries.length>0&&(t.attributes=r)}}else Dm(t)&&(t.attributes=$R(n,i,t.xref),t.kind===L.RepeaterCreate&&t.emptyView!==null&&(t.emptyAttributes=$R(n,i,t.emptyView)));else if(n instanceof Ub)for(let[e,t]of i.entries()){if(e!==n.root.xref)throw new Error("An attribute would be const collected into the host binding's template function, but is not associated with the root xref.");let o=oD(t);o.entries.length>0&&(n.root.attributes=o)}}function $R(n,i,e){let t=i.get(e);if(t!==void 0){let o=oD(t);if(o.entries.length>0)return n.addConst(o)}return null}var Th=Object.freeze([]),iD=class{known=new Map;byKind=new Map;propertyBindings=null;projectAs=null;get attributes(){return this.byKind.get(Ht.Attribute)??Th}get classes(){return this.byKind.get(Ht.ClassName)??Th}get styles(){return this.byKind.get(Ht.StyleProperty)??Th}get bindings(){return this.propertyBindings??Th}get template(){return this.byKind.get(Ht.Template)??Th}get i18n(){return this.byKind.get(Ht.I18n)??Th}isKnown(i,e){let t=this.known.get(i)??new Set;return this.known.set(i,t),t.has(e)?!0:(t.add(e),!1)}add(i,e,t,o,r){if(!(i===Ht.Attribute||i===Ht.ClassName||i===Ht.StyleProperty)&&this.isKnown(i,e))return;if(e==="ngProjectAs"){if(t===null||!(t instanceof da)||t.value==null||typeof t.value?.toString()!="string")throw Error("ngProjectAs must have a string literal value");this.projectAs=t.value.toString()}let c=this.arrayFor(i);if(c.push(...sQ(o,e)),i===Ht.Attribute||i===Ht.StyleProperty){if(t===null)throw Error("Attribute, i18n attribute, & style element attributes must have a value");if(r!==null){if(!O6(t))throw Error("AssertionError: extracted attribute value should be string literal");c.push(SG(r,new J_([new rb(t.value)],[]),void 0,t.sourceSpan))}else c.push(t)}}arrayFor(i){return i===Ht.Property||i===Ht.TwoWayProperty?(this.propertyBindings??=[],this.propertyBindings):(this.byKind.has(i)||this.byKind.set(i,[]),this.byKind.get(i))}};function sQ(n,i){let e=Me(i);return n?[Me(0),Me(n),e]:[e]}function oD({attributes:n,bindings:i,classes:e,i18n:t,projectAs:o,styles:r,template:a}){let c=[...n];if(o!==null){let m=QD(o)[0];c.push(Me(5),af(m))}return e.length>0&&c.push(Me(1),...e),r.length>0&&c.push(Me(2),...r),i.length>0&&c.push(Me(3),...i),a.length>0&&c.push(Me(4),...a),t.length>0&&c.push(Me(6),...t),Qi(c)}function lQ(n,i){let e=n.get(i);if(e===void 0)throw new Error("All attributes should have an element-like target.");return e}function cQ(n){let i=new Map;for(let e of n.units)for(let t of e.create)Dm(t)&&i.set(t.xref,t);for(let e of n.units)for(let t of e.ops())if(t.kind===L.AnimationBinding){let o=dQ(t);n.kind===Tt.Host?e.create.push(o):We.insertAfter(o,lQ(i,t.target)),We.remove(t)}}function dQ(n){if(n.animationBindingKind===0)return Eq(n.name,n.target,n.name==="animate.enter"?"enter":"leave",n.expression,n.securityContext,n.sourceSpan);{let i=n.expression;return Dq(n.name,n.target,n.name==="animate.enter"?"enter":"leave",[Bs(new wr(i,i.sourceSpan))],n.securityContext,n.sourceSpan)}}function mQ(n){let i=new Map;for(let e of n.units){for(let t of e.create)t.kind===L.I18nAttributes&&i.set(t.target,t);for(let t of e.update)switch(t.kind){case L.Property:case L.Attribute:if(t.i18nContext===null||!(t.expression instanceof Yo))continue;let o=i.get(t.target);if(o===void 0)throw new Error("AssertionError: An i18n attribute binding instruction requires the owning element to have an I18nAttributes create instruction");if(o.target!==t.target)throw new Error("AssertionError: Expected i18nAttributes target element to match binding target element");let r=[];for(let a=0;akQ(t,{job:n}),Wn.None),Ko(e,TQ,Wn.None)}function Ds(n){return n instanceof ru?Ds(n.expr):n instanceof fi?Ds(n.lhs)||Ds(n.rhs):n instanceof kc?n.falseCase&&Ds(n.falseCase)?!0:Ds(n.condition)||Ds(n.trueCase):n instanceof e0?Ds(n.condition):n instanceof Ac?Ds(n.expr):n instanceof Rs?Ds(n.receiver):n instanceof wd?Ds(n.receiver)||Ds(n.index):n instanceof Wl?Ds(n.expr):n instanceof cs||n instanceof Tc||n instanceof ql||n instanceof gu||n instanceof fu}function xQ(n){let i=new Set;return Ft(n,e=>(e instanceof Ac&&i.add(e.xref),e),Wn.None),i}function yQ(n,i,e){return Ft(n,t=>{if(t instanceof Ac&&i.has(t.xref)){let o=new Em(t.xref);return new Ac(o,o.xref)}return t},Wn.None),n}function Eh(n,i,e){let t;if(Ds(n)){let o=e.job.allocateXrefId();t=[new Ac(n,o),new Em(o)]}else t=[n,n.clone()],yQ(t[1],xQ(t[0]));return new rf(t[0],i(t[1]))}function SQ(n){return n instanceof nf||n instanceof of||n instanceof gu}function wQ(n){return n instanceof Rs||n instanceof wd||n instanceof cs}function G6(n){return SQ(n)||wQ(n)}function MQ(n){if(G6(n)&&n.receiver instanceof rf){let i=n.receiver;for(;i.expr instanceof rf;)i=i.expr;return i}return null}function kQ(n,i){if(!G6(n))return n;let e=MQ(n);if(e){if(n instanceof cs)return e.expr=e.expr.callFn(n.args),n.receiver;if(n instanceof Rs)return e.expr=e.expr.prop(n.name),n.receiver;if(n instanceof wd)return e.expr=e.expr.key(n.index),n.receiver;if(n instanceof gu)return e.expr=Eh(e.expr,t=>t.callFn(n.args),i),n.receiver;if(n instanceof nf)return e.expr=Eh(e.expr,t=>t.prop(n.name),i),n.receiver;if(n instanceof of)return e.expr=Eh(e.expr,t=>t.key(n.index),i),n.receiver}else{if(n instanceof gu)return Eh(n.receiver,t=>t.callFn(n.args),i);if(n instanceof nf)return Eh(n.receiver,t=>t.prop(n.name),i);if(n instanceof of)return Eh(n.receiver,t=>t.key(n.index),i)}return n}function TQ(n){return n instanceof rf?new Wl(new kc(new fi(lt.Equals,n.guard,Wh),Wh,n.expr)):n}var HR="\uFFFD",EQ="#",DQ="*",PQ="/",IQ=":",AQ="[",OQ="]",NQ="|";function RQ(n){let i=new Map,e=new Map,t=new Map;for(let r of n.units)for(let a of r.create)switch(a.kind){case L.I18nContext:let c=FQ(n,a);r.create.push(c),i.set(a.xref,c),t.set(a.xref,a);break;case L.I18nStart:e.set(a.xref,a);break}let o=null;for(let r of n.units)for(let a of r.create)switch(a.kind){case L.IcuStart:o=a,We.remove(a);let c=t.get(a.context);if(c.contextKind!==Qp.Icu)continue;let m=e.get(c.i18nBlock);if(m.context===c.xref)continue;let p=e.get(m.root),h=i.get(p.context);if(h===void 0)throw Error("AssertionError: ICU sub-message should belong to a root message.");let g=i.get(c.xref);g.messagePlaceholder=a.messagePlaceholder,h.subMessages.push(g.xref);break;case L.IcuEnd:o=null,We.remove(a);break;case L.IcuPlaceholder:if(o===null||o.context==null)throw Error("AssertionError: Unexpected ICU placeholder outside of i18n context");i.get(o.context).postprocessingParams.set(a.name,Me(LQ(a))),We.remove(a);break}}function FQ(n,i,e){let t=UR(i.params),o=UR(i.postprocessingParams),r=[...i.params.values()].some(a=>a.length>1);return Fq(n.allocateXrefId(),i.xref,i.i18nBlock,i.message,null,t,o,r)}function LQ(n){if(n.strings.length!==n.expressionPlaceholders.length+1)throw Error(`AssertionError: Invalid ICU placeholder with ${n.strings.length} strings and ${n.expressionPlaceholders.length} expressions`);let i=n.expressionPlaceholders.map(Lh);return n.strings.flatMap((e,t)=>[e,i[t]||""]).join("")}function UR(n){let i=new Map;for(let[e,t]of n){let o=BQ(t);o!==null&&i.set(e,Me(o))}return i}function BQ(n){if(n.length===0)return null;let i=n.map(e=>Lh(e));return i.length===1?i[0]:`${AQ}${i.join(NQ)}${OQ}`}function Lh(n){if(n.flags&po.ElementTag&&n.flags&po.TemplateTag){if(typeof n.value!="object")throw Error("AssertionError: Expected i18n param value to have an element and template slot");let o=Lh(Qe(W({},n),{value:n.value.element,flags:n.flags&~po.TemplateTag})),r=Lh(Qe(W({},n),{value:n.value.template,flags:n.flags&~po.ElementTag}));return n.flags&po.OpenTag&&n.flags&po.CloseTag?`${r}${o}${r}`:n.flags&po.CloseTag?`${o}${r}`:`${r}${o}`}if(n.flags&po.OpenTag&&n.flags&po.CloseTag)return`${Lh(Qe(W({},n),{flags:n.flags&~po.CloseTag}))}${Lh(Qe(W({},n),{flags:n.flags&~po.OpenTag}))}`;if(n.flags===po.None)return`${n.value}`;let i="",e="";n.flags&po.ElementTag?i=EQ:n.flags&po.TemplateTag&&(i=DQ),i!==""&&(e=n.flags&po.CloseTag?PQ:"");let t=n.subTemplateIndex===null?"":`${IQ}${n.subTemplateIndex}`;return`${HR}${e}${i}${n.value}${t}${HR}`}function VQ(n){for(let i of n.units){let e=new Map;for(let o of i.create){if(pf(o)){if(o.handle.slot===null)throw new Error("AssertionError: expected slots to have been allocated before generating advance() calls")}else continue;e.set(o.xref,o.handle.slot)}let t=0;for(let o of i.update){let r=null;if(I0(o)?r=o:hr(o,c=>{r===null&&I0(c)&&(r=c)}),r===null)continue;if(!e.has(r.target))throw new Error(`AssertionError: reference to unknown slot for target ${r.target}`);let a=e.get(r.target);if(t!==a){let c=a-t;if(c<0)throw new Error("AssertionError: slot counter should never need to move backwards");We.insertBefore(gq(c,r.sourceSpan),o),t=a}}}}function zQ(n){for(let i of n.units)for(let e of i.update){if(e.kind!==L.StoreLet)continue;let t={kind:Qr.Identifier,name:null,identifier:e.declaredName,local:!0};We.replace(e,fm(n.allocateXrefId(),t,new A0(e.target,e.value,e.sourceSpan),sl.None))}}function jQ(n){let e=[],t=0;for(let o of n.units)for(let r of o.create)r.kind===L.Projection&&(e.push(r.selector),r.projectionSlotIndex=t++);if(e.length>0){let o=null;if(e.length>1||e[0]!=="*"){let r=e.map(a=>a==="*"?a:QD(a));o=n.pool.getConstLiteral(af(r),!0)}n.contentSelectors=n.pool.getConstLiteral(af(e),!0),n.root.create.prepend([Aq(o)])}}function $Q(n){L_(n.root,null)}function L_(n,i){let e=GR(n,i);for(let t of n.create)switch(t.kind){case L.ConditionalCreate:case L.ConditionalBranchCreate:case L.Template:L_(n.job.views.get(t.xref),e);break;case L.Projection:t.fallbackView!==null&&L_(n.job.views.get(t.fallbackView),e);break;case L.RepeaterCreate:L_(n.job.views.get(t.xref),e),t.emptyView&&L_(n.job.views.get(t.emptyView),e),t.trackByOps!==null&&t.trackByOps.prepend(B_(n,e,!1));break;case L.Animation:case L.AnimationListener:case L.Listener:case L.TwoWayListener:t.handlerOps.prepend(B_(n,e,!0));break}n.update.prepend(B_(n,e,!1));for(let t of n.functions)t.ops.prepend(B_(n,GR(n,i),!0))}function GR(n,i){let e={view:n.xref,viewContextVariable:{kind:Qr.Context,name:null,view:n.xref},contextVariables:new Map,aliases:n.aliases,references:[],letDeclarations:[],parent:i};for(let t of n.contextVariables.keys())e.contextVariables.set(t,{kind:Qr.Identifier,name:null,identifier:t,local:!1});for(let t of n.create)switch(t.kind){case L.ElementStart:case L.ConditionalCreate:case L.ConditionalBranchCreate:case L.Template:if(!Array.isArray(t.localRefs))throw new Error("AssertionError: expected localRefs to be an array");for(let o=0;ot instanceof L0?Me(n.addConst(t.expr)):t,Wn.None)}var WR="style.",qR="class.",UQ="style!",QR="class!",XR="!important";function GQ(n){for(let i of n.root.update)if(i.kind===L.Binding&&i.bindingKind===Ht.Property)if(i.name.endsWith(XR)&&(i.name=i.name.substring(0,i.name.length-XR.length)),i.name.startsWith(WR)){i.bindingKind=Ht.StyleProperty,i.name=i.name.substring(WR.length),WQ(i.name)||(i.name=qQ(i.name));let{property:e,suffix:t}=iE(i.name);i.name=e,i.unit=t}else i.name.startsWith(UQ)?(i.bindingKind=Ht.StyleProperty,i.name="style"):i.name.startsWith(qR)?(i.bindingKind=Ht.ClassName,i.name=iE(i.name.substring(qR.length)).property):i.name.startsWith(QR)&&(i.bindingKind=Ht.ClassName,i.name=iE(i.name.substring(QR.length)).property)}function WQ(n){return n.startsWith("--")}function qQ(n){return n.replace(/[a-z][A-Z]/g,i=>i.charAt(0)+"-"+i.charAt(1)).toLowerCase()}function iE(n){let i=n.indexOf("!important");i!==-1&&(n=i>0?n.substring(0,i):"");let e=null,t=n,o=n.lastIndexOf(".");return o>0&&(e=n.slice(o+1),t=n.substring(0,o)),{property:t,suffix:e}}function rD(n,i=!1){return ml(Object.keys(n).map(e=>({key:e,quoted:i,value:n[e]})))}var aD=class{visitText(i){return i.value}visitContainer(i){return i.children.map(e=>e.visit(this)).join("")}visitIcu(i){let e=Object.keys(i.cases).map(o=>`${o} {${i.cases[o].visit(this)}}`);return`{${i.expressionPlaceholder}, ${i.type}, ${e.join(" ")}}`}visitTagPlaceholder(i){return i.isVoid?this.formatPh(i.startName):`${this.formatPh(i.startName)}${i.children.map(e=>e.visit(this)).join("")}${this.formatPh(i.closeName)}`}visitPlaceholder(i){return this.formatPh(i.name)}visitBlockPlaceholder(i){return`${this.formatPh(i.startName)}${i.children.map(e=>e.visit(this)).join("")}${this.formatPh(i.closeName)}`}visitIcuPlaceholder(i,e){return this.formatPh(i.name)}formatPh(i){return`{${X0(i,!1)}}`}},QQ=new aD;function W6(n){return n.visit(QQ)}var Md=class{sourceSpan;i18n;constructor(i,e){this.sourceSpan=i,this.i18n=e}},_u=class extends Md{value;tokens;constructor(i,e,t,o){super(e,o),this.value=i,this.tokens=t}visit(i,e){return i.visitText(this,e)}},Jp=class extends Md{switchValue;type;cases;switchValueSourceSpan;constructor(i,e,t,o,r,a){super(o,a),this.switchValue=i,this.type=e,this.cases=t,this.switchValueSourceSpan=r}visit(i,e){return i.visitExpansion(this,e)}},Gb=class{value;expression;sourceSpan;valueSourceSpan;expSourceSpan;constructor(i,e,t,o,r){this.value=i,this.expression=e,this.sourceSpan=t,this.valueSourceSpan=o,this.expSourceSpan=r}visit(i,e){return i.visitExpansionCase(this,e)}},sD=class extends Md{name;value;keySpan;valueSpan;valueTokens;constructor(i,e,t,o,r,a,c){super(t,c),this.name=i,this.value=e,this.keySpan=o,this.valueSpan=r,this.valueTokens=a}visit(i,e){return i.visitAttribute(this,e)}},nl=class extends Md{name;attrs;directives;children;isSelfClosing;startSourceSpan;endSourceSpan;isVoid;constructor(i,e,t,o,r,a,c,m=null,p,h){super(a,h),this.name=i,this.attrs=e,this.directives=t,this.children=o,this.isSelfClosing=r,this.startSourceSpan=c,this.endSourceSpan=m,this.isVoid=p}visit(i,e){return i.visitElement(this,e)}},V0=class{value;sourceSpan;constructor(i,e){this.value=i,this.sourceSpan=e}visit(i,e){return i.visitComment(this,e)}},rl=class extends Md{name;parameters;children;nameSpan;startSourceSpan;endSourceSpan;constructor(i,e,t,o,r,a,c=null,m){super(o,m),this.name=i,this.parameters=e,this.children=t,this.nameSpan=r,this.startSourceSpan=a,this.endSourceSpan=c}visit(i,e){return i.visitBlock(this,e)}},Va=class extends Md{componentName;tagName;fullName;attrs;directives;children;isSelfClosing;startSourceSpan;endSourceSpan;constructor(i,e,t,o,r,a,c,m,p,h=null,g){super(m,g),this.componentName=i,this.tagName=e,this.fullName=t,this.attrs=o,this.directives=r,this.children=a,this.isSelfClosing=c,this.startSourceSpan=p,this.endSourceSpan=h}visit(i,e){return i.visitComponent(this,e)}},lD=class{name;attrs;sourceSpan;startSourceSpan;endSourceSpan;constructor(i,e,t,o,r=null){this.name=i,this.attrs=e,this.sourceSpan=t,this.startSourceSpan=o,this.endSourceSpan=r}visit(i,e){return i.visitDirective(this,e)}},Wb=class{expression;sourceSpan;constructor(i,e){this.expression=i,this.sourceSpan=e}visit(i,e){return i.visitBlockParameter(this,e)}},qb=class{name;value;sourceSpan;nameSpan;valueSpan;constructor(i,e,t,o,r){this.name=i,this.value=e,this.sourceSpan=t,this.nameSpan=o,this.valueSpan=r}visit(i,e){return i.visitLetDeclaration(this,e)}};function So(n,i,e=null){let t=[],o=n.visit?r=>n.visit(r,e)||r.visit(n,e):r=>r.visit(n,e);return i.forEach(r=>{let a=o(r);a&&t.push(a)}),t}var z0={AElig:"\xC6",AMP:"&",amp:"&",Aacute:"\xC1",Abreve:"\u0102",Acirc:"\xC2",Acy:"\u0410",Afr:"\u{1D504}",Agrave:"\xC0",Alpha:"\u0391",Amacr:"\u0100",And:"\u2A53",Aogon:"\u0104",Aopf:"\u{1D538}",ApplyFunction:"\u2061",af:"\u2061",Aring:"\xC5",angst:"\xC5",Ascr:"\u{1D49C}",Assign:"\u2254",colone:"\u2254",coloneq:"\u2254",Atilde:"\xC3",Auml:"\xC4",Backslash:"\u2216",setminus:"\u2216",setmn:"\u2216",smallsetminus:"\u2216",ssetmn:"\u2216",Barv:"\u2AE7",Barwed:"\u2306",doublebarwedge:"\u2306",Bcy:"\u0411",Because:"\u2235",becaus:"\u2235",because:"\u2235",Bernoullis:"\u212C",Bscr:"\u212C",bernou:"\u212C",Beta:"\u0392",Bfr:"\u{1D505}",Bopf:"\u{1D539}",Breve:"\u02D8",breve:"\u02D8",Bumpeq:"\u224E",HumpDownHump:"\u224E",bump:"\u224E",CHcy:"\u0427",COPY:"\xA9",copy:"\xA9",Cacute:"\u0106",Cap:"\u22D2",CapitalDifferentialD:"\u2145",DD:"\u2145",Cayleys:"\u212D",Cfr:"\u212D",Ccaron:"\u010C",Ccedil:"\xC7",Ccirc:"\u0108",Cconint:"\u2230",Cdot:"\u010A",Cedilla:"\xB8",cedil:"\xB8",CenterDot:"\xB7",centerdot:"\xB7",middot:"\xB7",Chi:"\u03A7",CircleDot:"\u2299",odot:"\u2299",CircleMinus:"\u2296",ominus:"\u2296",CirclePlus:"\u2295",oplus:"\u2295",CircleTimes:"\u2297",otimes:"\u2297",ClockwiseContourIntegral:"\u2232",cwconint:"\u2232",CloseCurlyDoubleQuote:"\u201D",rdquo:"\u201D",rdquor:"\u201D",CloseCurlyQuote:"\u2019",rsquo:"\u2019",rsquor:"\u2019",Colon:"\u2237",Proportion:"\u2237",Colone:"\u2A74",Congruent:"\u2261",equiv:"\u2261",Conint:"\u222F",DoubleContourIntegral:"\u222F",ContourIntegral:"\u222E",conint:"\u222E",oint:"\u222E",Copf:"\u2102",complexes:"\u2102",Coproduct:"\u2210",coprod:"\u2210",CounterClockwiseContourIntegral:"\u2233",awconint:"\u2233",Cross:"\u2A2F",Cscr:"\u{1D49E}",Cup:"\u22D3",CupCap:"\u224D",asympeq:"\u224D",DDotrahd:"\u2911",DJcy:"\u0402",DScy:"\u0405",DZcy:"\u040F",Dagger:"\u2021",ddagger:"\u2021",Darr:"\u21A1",Dashv:"\u2AE4",DoubleLeftTee:"\u2AE4",Dcaron:"\u010E",Dcy:"\u0414",Del:"\u2207",nabla:"\u2207",Delta:"\u0394",Dfr:"\u{1D507}",DiacriticalAcute:"\xB4",acute:"\xB4",DiacriticalDot:"\u02D9",dot:"\u02D9",DiacriticalDoubleAcute:"\u02DD",dblac:"\u02DD",DiacriticalGrave:"`",grave:"`",DiacriticalTilde:"\u02DC",tilde:"\u02DC",Diamond:"\u22C4",diam:"\u22C4",diamond:"\u22C4",DifferentialD:"\u2146",dd:"\u2146",Dopf:"\u{1D53B}",Dot:"\xA8",DoubleDot:"\xA8",die:"\xA8",uml:"\xA8",DotDot:"\u20DC",DotEqual:"\u2250",doteq:"\u2250",esdot:"\u2250",DoubleDownArrow:"\u21D3",Downarrow:"\u21D3",dArr:"\u21D3",DoubleLeftArrow:"\u21D0",Leftarrow:"\u21D0",lArr:"\u21D0",DoubleLeftRightArrow:"\u21D4",Leftrightarrow:"\u21D4",hArr:"\u21D4",iff:"\u21D4",DoubleLongLeftArrow:"\u27F8",Longleftarrow:"\u27F8",xlArr:"\u27F8",DoubleLongLeftRightArrow:"\u27FA",Longleftrightarrow:"\u27FA",xhArr:"\u27FA",DoubleLongRightArrow:"\u27F9",Longrightarrow:"\u27F9",xrArr:"\u27F9",DoubleRightArrow:"\u21D2",Implies:"\u21D2",Rightarrow:"\u21D2",rArr:"\u21D2",DoubleRightTee:"\u22A8",vDash:"\u22A8",DoubleUpArrow:"\u21D1",Uparrow:"\u21D1",uArr:"\u21D1",DoubleUpDownArrow:"\u21D5",Updownarrow:"\u21D5",vArr:"\u21D5",DoubleVerticalBar:"\u2225",par:"\u2225",parallel:"\u2225",shortparallel:"\u2225",spar:"\u2225",DownArrow:"\u2193",ShortDownArrow:"\u2193",darr:"\u2193",downarrow:"\u2193",DownArrowBar:"\u2913",DownArrowUpArrow:"\u21F5",duarr:"\u21F5",DownBreve:"\u0311",DownLeftRightVector:"\u2950",DownLeftTeeVector:"\u295E",DownLeftVector:"\u21BD",leftharpoondown:"\u21BD",lhard:"\u21BD",DownLeftVectorBar:"\u2956",DownRightTeeVector:"\u295F",DownRightVector:"\u21C1",rhard:"\u21C1",rightharpoondown:"\u21C1",DownRightVectorBar:"\u2957",DownTee:"\u22A4",top:"\u22A4",DownTeeArrow:"\u21A7",mapstodown:"\u21A7",Dscr:"\u{1D49F}",Dstrok:"\u0110",ENG:"\u014A",ETH:"\xD0",Eacute:"\xC9",Ecaron:"\u011A",Ecirc:"\xCA",Ecy:"\u042D",Edot:"\u0116",Efr:"\u{1D508}",Egrave:"\xC8",Element:"\u2208",in:"\u2208",isin:"\u2208",isinv:"\u2208",Emacr:"\u0112",EmptySmallSquare:"\u25FB",EmptyVerySmallSquare:"\u25AB",Eogon:"\u0118",Eopf:"\u{1D53C}",Epsilon:"\u0395",Equal:"\u2A75",EqualTilde:"\u2242",eqsim:"\u2242",esim:"\u2242",Equilibrium:"\u21CC",rightleftharpoons:"\u21CC",rlhar:"\u21CC",Escr:"\u2130",expectation:"\u2130",Esim:"\u2A73",Eta:"\u0397",Euml:"\xCB",Exists:"\u2203",exist:"\u2203",ExponentialE:"\u2147",ee:"\u2147",exponentiale:"\u2147",Fcy:"\u0424",Ffr:"\u{1D509}",FilledSmallSquare:"\u25FC",FilledVerySmallSquare:"\u25AA",blacksquare:"\u25AA",squarf:"\u25AA",squf:"\u25AA",Fopf:"\u{1D53D}",ForAll:"\u2200",forall:"\u2200",Fouriertrf:"\u2131",Fscr:"\u2131",GJcy:"\u0403",GT:">",gt:">",Gamma:"\u0393",Gammad:"\u03DC",Gbreve:"\u011E",Gcedil:"\u0122",Gcirc:"\u011C",Gcy:"\u0413",Gdot:"\u0120",Gfr:"\u{1D50A}",Gg:"\u22D9",ggg:"\u22D9",Gopf:"\u{1D53E}",GreaterEqual:"\u2265",ge:"\u2265",geq:"\u2265",GreaterEqualLess:"\u22DB",gel:"\u22DB",gtreqless:"\u22DB",GreaterFullEqual:"\u2267",gE:"\u2267",geqq:"\u2267",GreaterGreater:"\u2AA2",GreaterLess:"\u2277",gl:"\u2277",gtrless:"\u2277",GreaterSlantEqual:"\u2A7E",geqslant:"\u2A7E",ges:"\u2A7E",GreaterTilde:"\u2273",gsim:"\u2273",gtrsim:"\u2273",Gscr:"\u{1D4A2}",Gt:"\u226B",NestedGreaterGreater:"\u226B",gg:"\u226B",HARDcy:"\u042A",Hacek:"\u02C7",caron:"\u02C7",Hat:"^",Hcirc:"\u0124",Hfr:"\u210C",Poincareplane:"\u210C",HilbertSpace:"\u210B",Hscr:"\u210B",hamilt:"\u210B",Hopf:"\u210D",quaternions:"\u210D",HorizontalLine:"\u2500",boxh:"\u2500",Hstrok:"\u0126",HumpEqual:"\u224F",bumpe:"\u224F",bumpeq:"\u224F",IEcy:"\u0415",IJlig:"\u0132",IOcy:"\u0401",Iacute:"\xCD",Icirc:"\xCE",Icy:"\u0418",Idot:"\u0130",Ifr:"\u2111",Im:"\u2111",image:"\u2111",imagpart:"\u2111",Igrave:"\xCC",Imacr:"\u012A",ImaginaryI:"\u2148",ii:"\u2148",Int:"\u222C",Integral:"\u222B",int:"\u222B",Intersection:"\u22C2",bigcap:"\u22C2",xcap:"\u22C2",InvisibleComma:"\u2063",ic:"\u2063",InvisibleTimes:"\u2062",it:"\u2062",Iogon:"\u012E",Iopf:"\u{1D540}",Iota:"\u0399",Iscr:"\u2110",imagline:"\u2110",Itilde:"\u0128",Iukcy:"\u0406",Iuml:"\xCF",Jcirc:"\u0134",Jcy:"\u0419",Jfr:"\u{1D50D}",Jopf:"\u{1D541}",Jscr:"\u{1D4A5}",Jsercy:"\u0408",Jukcy:"\u0404",KHcy:"\u0425",KJcy:"\u040C",Kappa:"\u039A",Kcedil:"\u0136",Kcy:"\u041A",Kfr:"\u{1D50E}",Kopf:"\u{1D542}",Kscr:"\u{1D4A6}",LJcy:"\u0409",LT:"<",lt:"<",Lacute:"\u0139",Lambda:"\u039B",Lang:"\u27EA",Laplacetrf:"\u2112",Lscr:"\u2112",lagran:"\u2112",Larr:"\u219E",twoheadleftarrow:"\u219E",Lcaron:"\u013D",Lcedil:"\u013B",Lcy:"\u041B",LeftAngleBracket:"\u27E8",lang:"\u27E8",langle:"\u27E8",LeftArrow:"\u2190",ShortLeftArrow:"\u2190",larr:"\u2190",leftarrow:"\u2190",slarr:"\u2190",LeftArrowBar:"\u21E4",larrb:"\u21E4",LeftArrowRightArrow:"\u21C6",leftrightarrows:"\u21C6",lrarr:"\u21C6",LeftCeiling:"\u2308",lceil:"\u2308",LeftDoubleBracket:"\u27E6",lobrk:"\u27E6",LeftDownTeeVector:"\u2961",LeftDownVector:"\u21C3",dharl:"\u21C3",downharpoonleft:"\u21C3",LeftDownVectorBar:"\u2959",LeftFloor:"\u230A",lfloor:"\u230A",LeftRightArrow:"\u2194",harr:"\u2194",leftrightarrow:"\u2194",LeftRightVector:"\u294E",LeftTee:"\u22A3",dashv:"\u22A3",LeftTeeArrow:"\u21A4",mapstoleft:"\u21A4",LeftTeeVector:"\u295A",LeftTriangle:"\u22B2",vartriangleleft:"\u22B2",vltri:"\u22B2",LeftTriangleBar:"\u29CF",LeftTriangleEqual:"\u22B4",ltrie:"\u22B4",trianglelefteq:"\u22B4",LeftUpDownVector:"\u2951",LeftUpTeeVector:"\u2960",LeftUpVector:"\u21BF",uharl:"\u21BF",upharpoonleft:"\u21BF",LeftUpVectorBar:"\u2958",LeftVector:"\u21BC",leftharpoonup:"\u21BC",lharu:"\u21BC",LeftVectorBar:"\u2952",LessEqualGreater:"\u22DA",leg:"\u22DA",lesseqgtr:"\u22DA",LessFullEqual:"\u2266",lE:"\u2266",leqq:"\u2266",LessGreater:"\u2276",lessgtr:"\u2276",lg:"\u2276",LessLess:"\u2AA1",LessSlantEqual:"\u2A7D",leqslant:"\u2A7D",les:"\u2A7D",LessTilde:"\u2272",lesssim:"\u2272",lsim:"\u2272",Lfr:"\u{1D50F}",Ll:"\u22D8",Lleftarrow:"\u21DA",lAarr:"\u21DA",Lmidot:"\u013F",LongLeftArrow:"\u27F5",longleftarrow:"\u27F5",xlarr:"\u27F5",LongLeftRightArrow:"\u27F7",longleftrightarrow:"\u27F7",xharr:"\u27F7",LongRightArrow:"\u27F6",longrightarrow:"\u27F6",xrarr:"\u27F6",Lopf:"\u{1D543}",LowerLeftArrow:"\u2199",swarr:"\u2199",swarrow:"\u2199",LowerRightArrow:"\u2198",searr:"\u2198",searrow:"\u2198",Lsh:"\u21B0",lsh:"\u21B0",Lstrok:"\u0141",Lt:"\u226A",NestedLessLess:"\u226A",ll:"\u226A",Map:"\u2905",Mcy:"\u041C",MediumSpace:"\u205F",Mellintrf:"\u2133",Mscr:"\u2133",phmmat:"\u2133",Mfr:"\u{1D510}",MinusPlus:"\u2213",mnplus:"\u2213",mp:"\u2213",Mopf:"\u{1D544}",Mu:"\u039C",NJcy:"\u040A",Nacute:"\u0143",Ncaron:"\u0147",Ncedil:"\u0145",Ncy:"\u041D",NegativeMediumSpace:"\u200B",NegativeThickSpace:"\u200B",NegativeThinSpace:"\u200B",NegativeVeryThinSpace:"\u200B",ZeroWidthSpace:"\u200B",NewLine:` -`,Nfr:"\u{1D511}",NoBreak:"\u2060",NonBreakingSpace:"\xA0",nbsp:"\xA0",Nopf:"\u2115",naturals:"\u2115",Not:"\u2AEC",NotCongruent:"\u2262",nequiv:"\u2262",NotCupCap:"\u226D",NotDoubleVerticalBar:"\u2226",npar:"\u2226",nparallel:"\u2226",nshortparallel:"\u2226",nspar:"\u2226",NotElement:"\u2209",notin:"\u2209",notinva:"\u2209",NotEqual:"\u2260",ne:"\u2260",NotEqualTilde:"\u2242\u0338",nesim:"\u2242\u0338",NotExists:"\u2204",nexist:"\u2204",nexists:"\u2204",NotGreater:"\u226F",ngt:"\u226F",ngtr:"\u226F",NotGreaterEqual:"\u2271",nge:"\u2271",ngeq:"\u2271",NotGreaterFullEqual:"\u2267\u0338",ngE:"\u2267\u0338",ngeqq:"\u2267\u0338",NotGreaterGreater:"\u226B\u0338",nGtv:"\u226B\u0338",NotGreaterLess:"\u2279",ntgl:"\u2279",NotGreaterSlantEqual:"\u2A7E\u0338",ngeqslant:"\u2A7E\u0338",nges:"\u2A7E\u0338",NotGreaterTilde:"\u2275",ngsim:"\u2275",NotHumpDownHump:"\u224E\u0338",nbump:"\u224E\u0338",NotHumpEqual:"\u224F\u0338",nbumpe:"\u224F\u0338",NotLeftTriangle:"\u22EA",nltri:"\u22EA",ntriangleleft:"\u22EA",NotLeftTriangleBar:"\u29CF\u0338",NotLeftTriangleEqual:"\u22EC",nltrie:"\u22EC",ntrianglelefteq:"\u22EC",NotLess:"\u226E",nless:"\u226E",nlt:"\u226E",NotLessEqual:"\u2270",nle:"\u2270",nleq:"\u2270",NotLessGreater:"\u2278",ntlg:"\u2278",NotLessLess:"\u226A\u0338",nLtv:"\u226A\u0338",NotLessSlantEqual:"\u2A7D\u0338",nleqslant:"\u2A7D\u0338",nles:"\u2A7D\u0338",NotLessTilde:"\u2274",nlsim:"\u2274",NotNestedGreaterGreater:"\u2AA2\u0338",NotNestedLessLess:"\u2AA1\u0338",NotPrecedes:"\u2280",npr:"\u2280",nprec:"\u2280",NotPrecedesEqual:"\u2AAF\u0338",npre:"\u2AAF\u0338",npreceq:"\u2AAF\u0338",NotPrecedesSlantEqual:"\u22E0",nprcue:"\u22E0",NotReverseElement:"\u220C",notni:"\u220C",notniva:"\u220C",NotRightTriangle:"\u22EB",nrtri:"\u22EB",ntriangleright:"\u22EB",NotRightTriangleBar:"\u29D0\u0338",NotRightTriangleEqual:"\u22ED",nrtrie:"\u22ED",ntrianglerighteq:"\u22ED",NotSquareSubset:"\u228F\u0338",NotSquareSubsetEqual:"\u22E2",nsqsube:"\u22E2",NotSquareSuperset:"\u2290\u0338",NotSquareSupersetEqual:"\u22E3",nsqsupe:"\u22E3",NotSubset:"\u2282\u20D2",nsubset:"\u2282\u20D2",vnsub:"\u2282\u20D2",NotSubsetEqual:"\u2288",nsube:"\u2288",nsubseteq:"\u2288",NotSucceeds:"\u2281",nsc:"\u2281",nsucc:"\u2281",NotSucceedsEqual:"\u2AB0\u0338",nsce:"\u2AB0\u0338",nsucceq:"\u2AB0\u0338",NotSucceedsSlantEqual:"\u22E1",nsccue:"\u22E1",NotSucceedsTilde:"\u227F\u0338",NotSuperset:"\u2283\u20D2",nsupset:"\u2283\u20D2",vnsup:"\u2283\u20D2",NotSupersetEqual:"\u2289",nsupe:"\u2289",nsupseteq:"\u2289",NotTilde:"\u2241",nsim:"\u2241",NotTildeEqual:"\u2244",nsime:"\u2244",nsimeq:"\u2244",NotTildeFullEqual:"\u2247",ncong:"\u2247",NotTildeTilde:"\u2249",nap:"\u2249",napprox:"\u2249",NotVerticalBar:"\u2224",nmid:"\u2224",nshortmid:"\u2224",nsmid:"\u2224",Nscr:"\u{1D4A9}",Ntilde:"\xD1",Nu:"\u039D",OElig:"\u0152",Oacute:"\xD3",Ocirc:"\xD4",Ocy:"\u041E",Odblac:"\u0150",Ofr:"\u{1D512}",Ograve:"\xD2",Omacr:"\u014C",Omega:"\u03A9",ohm:"\u03A9",Omicron:"\u039F",Oopf:"\u{1D546}",OpenCurlyDoubleQuote:"\u201C",ldquo:"\u201C",OpenCurlyQuote:"\u2018",lsquo:"\u2018",Or:"\u2A54",Oscr:"\u{1D4AA}",Oslash:"\xD8",Otilde:"\xD5",Otimes:"\u2A37",Ouml:"\xD6",OverBar:"\u203E",oline:"\u203E",OverBrace:"\u23DE",OverBracket:"\u23B4",tbrk:"\u23B4",OverParenthesis:"\u23DC",PartialD:"\u2202",part:"\u2202",Pcy:"\u041F",Pfr:"\u{1D513}",Phi:"\u03A6",Pi:"\u03A0",PlusMinus:"\xB1",plusmn:"\xB1",pm:"\xB1",Popf:"\u2119",primes:"\u2119",Pr:"\u2ABB",Precedes:"\u227A",pr:"\u227A",prec:"\u227A",PrecedesEqual:"\u2AAF",pre:"\u2AAF",preceq:"\u2AAF",PrecedesSlantEqual:"\u227C",prcue:"\u227C",preccurlyeq:"\u227C",PrecedesTilde:"\u227E",precsim:"\u227E",prsim:"\u227E",Prime:"\u2033",Product:"\u220F",prod:"\u220F",Proportional:"\u221D",prop:"\u221D",propto:"\u221D",varpropto:"\u221D",vprop:"\u221D",Pscr:"\u{1D4AB}",Psi:"\u03A8",QUOT:'"',quot:'"',Qfr:"\u{1D514}",Qopf:"\u211A",rationals:"\u211A",Qscr:"\u{1D4AC}",RBarr:"\u2910",drbkarow:"\u2910",REG:"\xAE",circledR:"\xAE",reg:"\xAE",Racute:"\u0154",Rang:"\u27EB",Rarr:"\u21A0",twoheadrightarrow:"\u21A0",Rarrtl:"\u2916",Rcaron:"\u0158",Rcedil:"\u0156",Rcy:"\u0420",Re:"\u211C",Rfr:"\u211C",real:"\u211C",realpart:"\u211C",ReverseElement:"\u220B",SuchThat:"\u220B",ni:"\u220B",niv:"\u220B",ReverseEquilibrium:"\u21CB",leftrightharpoons:"\u21CB",lrhar:"\u21CB",ReverseUpEquilibrium:"\u296F",duhar:"\u296F",Rho:"\u03A1",RightAngleBracket:"\u27E9",rang:"\u27E9",rangle:"\u27E9",RightArrow:"\u2192",ShortRightArrow:"\u2192",rarr:"\u2192",rightarrow:"\u2192",srarr:"\u2192",RightArrowBar:"\u21E5",rarrb:"\u21E5",RightArrowLeftArrow:"\u21C4",rightleftarrows:"\u21C4",rlarr:"\u21C4",RightCeiling:"\u2309",rceil:"\u2309",RightDoubleBracket:"\u27E7",robrk:"\u27E7",RightDownTeeVector:"\u295D",RightDownVector:"\u21C2",dharr:"\u21C2",downharpoonright:"\u21C2",RightDownVectorBar:"\u2955",RightFloor:"\u230B",rfloor:"\u230B",RightTee:"\u22A2",vdash:"\u22A2",RightTeeArrow:"\u21A6",map:"\u21A6",mapsto:"\u21A6",RightTeeVector:"\u295B",RightTriangle:"\u22B3",vartriangleright:"\u22B3",vrtri:"\u22B3",RightTriangleBar:"\u29D0",RightTriangleEqual:"\u22B5",rtrie:"\u22B5",trianglerighteq:"\u22B5",RightUpDownVector:"\u294F",RightUpTeeVector:"\u295C",RightUpVector:"\u21BE",uharr:"\u21BE",upharpoonright:"\u21BE",RightUpVectorBar:"\u2954",RightVector:"\u21C0",rharu:"\u21C0",rightharpoonup:"\u21C0",RightVectorBar:"\u2953",Ropf:"\u211D",reals:"\u211D",RoundImplies:"\u2970",Rrightarrow:"\u21DB",rAarr:"\u21DB",Rscr:"\u211B",realine:"\u211B",Rsh:"\u21B1",rsh:"\u21B1",RuleDelayed:"\u29F4",SHCHcy:"\u0429",SHcy:"\u0428",SOFTcy:"\u042C",Sacute:"\u015A",Sc:"\u2ABC",Scaron:"\u0160",Scedil:"\u015E",Scirc:"\u015C",Scy:"\u0421",Sfr:"\u{1D516}",ShortUpArrow:"\u2191",UpArrow:"\u2191",uarr:"\u2191",uparrow:"\u2191",Sigma:"\u03A3",SmallCircle:"\u2218",compfn:"\u2218",Sopf:"\u{1D54A}",Sqrt:"\u221A",radic:"\u221A",Square:"\u25A1",squ:"\u25A1",square:"\u25A1",SquareIntersection:"\u2293",sqcap:"\u2293",SquareSubset:"\u228F",sqsub:"\u228F",sqsubset:"\u228F",SquareSubsetEqual:"\u2291",sqsube:"\u2291",sqsubseteq:"\u2291",SquareSuperset:"\u2290",sqsup:"\u2290",sqsupset:"\u2290",SquareSupersetEqual:"\u2292",sqsupe:"\u2292",sqsupseteq:"\u2292",SquareUnion:"\u2294",sqcup:"\u2294",Sscr:"\u{1D4AE}",Star:"\u22C6",sstarf:"\u22C6",Sub:"\u22D0",Subset:"\u22D0",SubsetEqual:"\u2286",sube:"\u2286",subseteq:"\u2286",Succeeds:"\u227B",sc:"\u227B",succ:"\u227B",SucceedsEqual:"\u2AB0",sce:"\u2AB0",succeq:"\u2AB0",SucceedsSlantEqual:"\u227D",sccue:"\u227D",succcurlyeq:"\u227D",SucceedsTilde:"\u227F",scsim:"\u227F",succsim:"\u227F",Sum:"\u2211",sum:"\u2211",Sup:"\u22D1",Supset:"\u22D1",Superset:"\u2283",sup:"\u2283",supset:"\u2283",SupersetEqual:"\u2287",supe:"\u2287",supseteq:"\u2287",THORN:"\xDE",TRADE:"\u2122",trade:"\u2122",TSHcy:"\u040B",TScy:"\u0426",Tab:" ",Tau:"\u03A4",Tcaron:"\u0164",Tcedil:"\u0162",Tcy:"\u0422",Tfr:"\u{1D517}",Therefore:"\u2234",there4:"\u2234",therefore:"\u2234",Theta:"\u0398",ThickSpace:"\u205F\u200A",ThinSpace:"\u2009",thinsp:"\u2009",Tilde:"\u223C",sim:"\u223C",thicksim:"\u223C",thksim:"\u223C",TildeEqual:"\u2243",sime:"\u2243",simeq:"\u2243",TildeFullEqual:"\u2245",cong:"\u2245",TildeTilde:"\u2248",ap:"\u2248",approx:"\u2248",asymp:"\u2248",thickapprox:"\u2248",thkap:"\u2248",Topf:"\u{1D54B}",TripleDot:"\u20DB",tdot:"\u20DB",Tscr:"\u{1D4AF}",Tstrok:"\u0166",Uacute:"\xDA",Uarr:"\u219F",Uarrocir:"\u2949",Ubrcy:"\u040E",Ubreve:"\u016C",Ucirc:"\xDB",Ucy:"\u0423",Udblac:"\u0170",Ufr:"\u{1D518}",Ugrave:"\xD9",Umacr:"\u016A",UnderBar:"_",lowbar:"_",UnderBrace:"\u23DF",UnderBracket:"\u23B5",bbrk:"\u23B5",UnderParenthesis:"\u23DD",Union:"\u22C3",bigcup:"\u22C3",xcup:"\u22C3",UnionPlus:"\u228E",uplus:"\u228E",Uogon:"\u0172",Uopf:"\u{1D54C}",UpArrowBar:"\u2912",UpArrowDownArrow:"\u21C5",udarr:"\u21C5",UpDownArrow:"\u2195",updownarrow:"\u2195",varr:"\u2195",UpEquilibrium:"\u296E",udhar:"\u296E",UpTee:"\u22A5",bot:"\u22A5",bottom:"\u22A5",perp:"\u22A5",UpTeeArrow:"\u21A5",mapstoup:"\u21A5",UpperLeftArrow:"\u2196",nwarr:"\u2196",nwarrow:"\u2196",UpperRightArrow:"\u2197",nearr:"\u2197",nearrow:"\u2197",Upsi:"\u03D2",upsih:"\u03D2",Upsilon:"\u03A5",Uring:"\u016E",Uscr:"\u{1D4B0}",Utilde:"\u0168",Uuml:"\xDC",VDash:"\u22AB",Vbar:"\u2AEB",Vcy:"\u0412",Vdash:"\u22A9",Vdashl:"\u2AE6",Vee:"\u22C1",bigvee:"\u22C1",xvee:"\u22C1",Verbar:"\u2016",Vert:"\u2016",VerticalBar:"\u2223",mid:"\u2223",shortmid:"\u2223",smid:"\u2223",VerticalLine:"|",verbar:"|",vert:"|",VerticalSeparator:"\u2758",VerticalTilde:"\u2240",wr:"\u2240",wreath:"\u2240",VeryThinSpace:"\u200A",hairsp:"\u200A",Vfr:"\u{1D519}",Vopf:"\u{1D54D}",Vscr:"\u{1D4B1}",Vvdash:"\u22AA",Wcirc:"\u0174",Wedge:"\u22C0",bigwedge:"\u22C0",xwedge:"\u22C0",Wfr:"\u{1D51A}",Wopf:"\u{1D54E}",Wscr:"\u{1D4B2}",Xfr:"\u{1D51B}",Xi:"\u039E",Xopf:"\u{1D54F}",Xscr:"\u{1D4B3}",YAcy:"\u042F",YIcy:"\u0407",YUcy:"\u042E",Yacute:"\xDD",Ycirc:"\u0176",Ycy:"\u042B",Yfr:"\u{1D51C}",Yopf:"\u{1D550}",Yscr:"\u{1D4B4}",Yuml:"\u0178",ZHcy:"\u0416",Zacute:"\u0179",Zcaron:"\u017D",Zcy:"\u0417",Zdot:"\u017B",Zeta:"\u0396",Zfr:"\u2128",zeetrf:"\u2128",Zopf:"\u2124",integers:"\u2124",Zscr:"\u{1D4B5}",aacute:"\xE1",abreve:"\u0103",ac:"\u223E",mstpos:"\u223E",acE:"\u223E\u0333",acd:"\u223F",acirc:"\xE2",acy:"\u0430",aelig:"\xE6",afr:"\u{1D51E}",agrave:"\xE0",alefsym:"\u2135",aleph:"\u2135",alpha:"\u03B1",amacr:"\u0101",amalg:"\u2A3F",and:"\u2227",wedge:"\u2227",andand:"\u2A55",andd:"\u2A5C",andslope:"\u2A58",andv:"\u2A5A",ang:"\u2220",angle:"\u2220",ange:"\u29A4",angmsd:"\u2221",measuredangle:"\u2221",angmsdaa:"\u29A8",angmsdab:"\u29A9",angmsdac:"\u29AA",angmsdad:"\u29AB",angmsdae:"\u29AC",angmsdaf:"\u29AD",angmsdag:"\u29AE",angmsdah:"\u29AF",angrt:"\u221F",angrtvb:"\u22BE",angrtvbd:"\u299D",angsph:"\u2222",angzarr:"\u237C",aogon:"\u0105",aopf:"\u{1D552}",apE:"\u2A70",apacir:"\u2A6F",ape:"\u224A",approxeq:"\u224A",apid:"\u224B",apos:"'",aring:"\xE5",ascr:"\u{1D4B6}",ast:"*",midast:"*",atilde:"\xE3",auml:"\xE4",awint:"\u2A11",bNot:"\u2AED",backcong:"\u224C",bcong:"\u224C",backepsilon:"\u03F6",bepsi:"\u03F6",backprime:"\u2035",bprime:"\u2035",backsim:"\u223D",bsim:"\u223D",backsimeq:"\u22CD",bsime:"\u22CD",barvee:"\u22BD",barwed:"\u2305",barwedge:"\u2305",bbrktbrk:"\u23B6",bcy:"\u0431",bdquo:"\u201E",ldquor:"\u201E",bemptyv:"\u29B0",beta:"\u03B2",beth:"\u2136",between:"\u226C",twixt:"\u226C",bfr:"\u{1D51F}",bigcirc:"\u25EF",xcirc:"\u25EF",bigodot:"\u2A00",xodot:"\u2A00",bigoplus:"\u2A01",xoplus:"\u2A01",bigotimes:"\u2A02",xotime:"\u2A02",bigsqcup:"\u2A06",xsqcup:"\u2A06",bigstar:"\u2605",starf:"\u2605",bigtriangledown:"\u25BD",xdtri:"\u25BD",bigtriangleup:"\u25B3",xutri:"\u25B3",biguplus:"\u2A04",xuplus:"\u2A04",bkarow:"\u290D",rbarr:"\u290D",blacklozenge:"\u29EB",lozf:"\u29EB",blacktriangle:"\u25B4",utrif:"\u25B4",blacktriangledown:"\u25BE",dtrif:"\u25BE",blacktriangleleft:"\u25C2",ltrif:"\u25C2",blacktriangleright:"\u25B8",rtrif:"\u25B8",blank:"\u2423",blk12:"\u2592",blk14:"\u2591",blk34:"\u2593",block:"\u2588",bne:"=\u20E5",bnequiv:"\u2261\u20E5",bnot:"\u2310",bopf:"\u{1D553}",bowtie:"\u22C8",boxDL:"\u2557",boxDR:"\u2554",boxDl:"\u2556",boxDr:"\u2553",boxH:"\u2550",boxHD:"\u2566",boxHU:"\u2569",boxHd:"\u2564",boxHu:"\u2567",boxUL:"\u255D",boxUR:"\u255A",boxUl:"\u255C",boxUr:"\u2559",boxV:"\u2551",boxVH:"\u256C",boxVL:"\u2563",boxVR:"\u2560",boxVh:"\u256B",boxVl:"\u2562",boxVr:"\u255F",boxbox:"\u29C9",boxdL:"\u2555",boxdR:"\u2552",boxdl:"\u2510",boxdr:"\u250C",boxhD:"\u2565",boxhU:"\u2568",boxhd:"\u252C",boxhu:"\u2534",boxminus:"\u229F",minusb:"\u229F",boxplus:"\u229E",plusb:"\u229E",boxtimes:"\u22A0",timesb:"\u22A0",boxuL:"\u255B",boxuR:"\u2558",boxul:"\u2518",boxur:"\u2514",boxv:"\u2502",boxvH:"\u256A",boxvL:"\u2561",boxvR:"\u255E",boxvh:"\u253C",boxvl:"\u2524",boxvr:"\u251C",brvbar:"\xA6",bscr:"\u{1D4B7}",bsemi:"\u204F",bsol:"\\",bsolb:"\u29C5",bsolhsub:"\u27C8",bull:"\u2022",bullet:"\u2022",bumpE:"\u2AAE",cacute:"\u0107",cap:"\u2229",capand:"\u2A44",capbrcup:"\u2A49",capcap:"\u2A4B",capcup:"\u2A47",capdot:"\u2A40",caps:"\u2229\uFE00",caret:"\u2041",ccaps:"\u2A4D",ccaron:"\u010D",ccedil:"\xE7",ccirc:"\u0109",ccups:"\u2A4C",ccupssm:"\u2A50",cdot:"\u010B",cemptyv:"\u29B2",cent:"\xA2",cfr:"\u{1D520}",chcy:"\u0447",check:"\u2713",checkmark:"\u2713",chi:"\u03C7",cir:"\u25CB",cirE:"\u29C3",circ:"\u02C6",circeq:"\u2257",cire:"\u2257",circlearrowleft:"\u21BA",olarr:"\u21BA",circlearrowright:"\u21BB",orarr:"\u21BB",circledS:"\u24C8",oS:"\u24C8",circledast:"\u229B",oast:"\u229B",circledcirc:"\u229A",ocir:"\u229A",circleddash:"\u229D",odash:"\u229D",cirfnint:"\u2A10",cirmid:"\u2AEF",cirscir:"\u29C2",clubs:"\u2663",clubsuit:"\u2663",colon:":",comma:",",commat:"@",comp:"\u2201",complement:"\u2201",congdot:"\u2A6D",copf:"\u{1D554}",copysr:"\u2117",crarr:"\u21B5",cross:"\u2717",cscr:"\u{1D4B8}",csub:"\u2ACF",csube:"\u2AD1",csup:"\u2AD0",csupe:"\u2AD2",ctdot:"\u22EF",cudarrl:"\u2938",cudarrr:"\u2935",cuepr:"\u22DE",curlyeqprec:"\u22DE",cuesc:"\u22DF",curlyeqsucc:"\u22DF",cularr:"\u21B6",curvearrowleft:"\u21B6",cularrp:"\u293D",cup:"\u222A",cupbrcap:"\u2A48",cupcap:"\u2A46",cupcup:"\u2A4A",cupdot:"\u228D",cupor:"\u2A45",cups:"\u222A\uFE00",curarr:"\u21B7",curvearrowright:"\u21B7",curarrm:"\u293C",curlyvee:"\u22CE",cuvee:"\u22CE",curlywedge:"\u22CF",cuwed:"\u22CF",curren:"\xA4",cwint:"\u2231",cylcty:"\u232D",dHar:"\u2965",dagger:"\u2020",daleth:"\u2138",dash:"\u2010",hyphen:"\u2010",dbkarow:"\u290F",rBarr:"\u290F",dcaron:"\u010F",dcy:"\u0434",ddarr:"\u21CA",downdownarrows:"\u21CA",ddotseq:"\u2A77",eDDot:"\u2A77",deg:"\xB0",delta:"\u03B4",demptyv:"\u29B1",dfisht:"\u297F",dfr:"\u{1D521}",diamondsuit:"\u2666",diams:"\u2666",digamma:"\u03DD",gammad:"\u03DD",disin:"\u22F2",div:"\xF7",divide:"\xF7",divideontimes:"\u22C7",divonx:"\u22C7",djcy:"\u0452",dlcorn:"\u231E",llcorner:"\u231E",dlcrop:"\u230D",dollar:"$",dopf:"\u{1D555}",doteqdot:"\u2251",eDot:"\u2251",dotminus:"\u2238",minusd:"\u2238",dotplus:"\u2214",plusdo:"\u2214",dotsquare:"\u22A1",sdotb:"\u22A1",drcorn:"\u231F",lrcorner:"\u231F",drcrop:"\u230C",dscr:"\u{1D4B9}",dscy:"\u0455",dsol:"\u29F6",dstrok:"\u0111",dtdot:"\u22F1",dtri:"\u25BF",triangledown:"\u25BF",dwangle:"\u29A6",dzcy:"\u045F",dzigrarr:"\u27FF",eacute:"\xE9",easter:"\u2A6E",ecaron:"\u011B",ecir:"\u2256",eqcirc:"\u2256",ecirc:"\xEA",ecolon:"\u2255",eqcolon:"\u2255",ecy:"\u044D",edot:"\u0117",efDot:"\u2252",fallingdotseq:"\u2252",efr:"\u{1D522}",eg:"\u2A9A",egrave:"\xE8",egs:"\u2A96",eqslantgtr:"\u2A96",egsdot:"\u2A98",el:"\u2A99",elinters:"\u23E7",ell:"\u2113",els:"\u2A95",eqslantless:"\u2A95",elsdot:"\u2A97",emacr:"\u0113",empty:"\u2205",emptyset:"\u2205",emptyv:"\u2205",varnothing:"\u2205",emsp13:"\u2004",emsp14:"\u2005",emsp:"\u2003",eng:"\u014B",ensp:"\u2002",eogon:"\u0119",eopf:"\u{1D556}",epar:"\u22D5",eparsl:"\u29E3",eplus:"\u2A71",epsi:"\u03B5",epsilon:"\u03B5",epsiv:"\u03F5",straightepsilon:"\u03F5",varepsilon:"\u03F5",equals:"=",equest:"\u225F",questeq:"\u225F",equivDD:"\u2A78",eqvparsl:"\u29E5",erDot:"\u2253",risingdotseq:"\u2253",erarr:"\u2971",escr:"\u212F",eta:"\u03B7",eth:"\xF0",euml:"\xEB",euro:"\u20AC",excl:"!",fcy:"\u0444",female:"\u2640",ffilig:"\uFB03",fflig:"\uFB00",ffllig:"\uFB04",ffr:"\u{1D523}",filig:"\uFB01",fjlig:"fj",flat:"\u266D",fllig:"\uFB02",fltns:"\u25B1",fnof:"\u0192",fopf:"\u{1D557}",fork:"\u22D4",pitchfork:"\u22D4",forkv:"\u2AD9",fpartint:"\u2A0D",frac12:"\xBD",half:"\xBD",frac13:"\u2153",frac14:"\xBC",frac15:"\u2155",frac16:"\u2159",frac18:"\u215B",frac23:"\u2154",frac25:"\u2156",frac34:"\xBE",frac35:"\u2157",frac38:"\u215C",frac45:"\u2158",frac56:"\u215A",frac58:"\u215D",frac78:"\u215E",frasl:"\u2044",frown:"\u2322",sfrown:"\u2322",fscr:"\u{1D4BB}",gEl:"\u2A8C",gtreqqless:"\u2A8C",gacute:"\u01F5",gamma:"\u03B3",gap:"\u2A86",gtrapprox:"\u2A86",gbreve:"\u011F",gcirc:"\u011D",gcy:"\u0433",gdot:"\u0121",gescc:"\u2AA9",gesdot:"\u2A80",gesdoto:"\u2A82",gesdotol:"\u2A84",gesl:"\u22DB\uFE00",gesles:"\u2A94",gfr:"\u{1D524}",gimel:"\u2137",gjcy:"\u0453",glE:"\u2A92",gla:"\u2AA5",glj:"\u2AA4",gnE:"\u2269",gneqq:"\u2269",gnap:"\u2A8A",gnapprox:"\u2A8A",gne:"\u2A88",gneq:"\u2A88",gnsim:"\u22E7",gopf:"\u{1D558}",gscr:"\u210A",gsime:"\u2A8E",gsiml:"\u2A90",gtcc:"\u2AA7",gtcir:"\u2A7A",gtdot:"\u22D7",gtrdot:"\u22D7",gtlPar:"\u2995",gtquest:"\u2A7C",gtrarr:"\u2978",gvertneqq:"\u2269\uFE00",gvnE:"\u2269\uFE00",hardcy:"\u044A",harrcir:"\u2948",harrw:"\u21AD",leftrightsquigarrow:"\u21AD",hbar:"\u210F",hslash:"\u210F",planck:"\u210F",plankv:"\u210F",hcirc:"\u0125",hearts:"\u2665",heartsuit:"\u2665",hellip:"\u2026",mldr:"\u2026",hercon:"\u22B9",hfr:"\u{1D525}",hksearow:"\u2925",searhk:"\u2925",hkswarow:"\u2926",swarhk:"\u2926",hoarr:"\u21FF",homtht:"\u223B",hookleftarrow:"\u21A9",larrhk:"\u21A9",hookrightarrow:"\u21AA",rarrhk:"\u21AA",hopf:"\u{1D559}",horbar:"\u2015",hscr:"\u{1D4BD}",hstrok:"\u0127",hybull:"\u2043",iacute:"\xED",icirc:"\xEE",icy:"\u0438",iecy:"\u0435",iexcl:"\xA1",ifr:"\u{1D526}",igrave:"\xEC",iiiint:"\u2A0C",qint:"\u2A0C",iiint:"\u222D",tint:"\u222D",iinfin:"\u29DC",iiota:"\u2129",ijlig:"\u0133",imacr:"\u012B",imath:"\u0131",inodot:"\u0131",imof:"\u22B7",imped:"\u01B5",incare:"\u2105",infin:"\u221E",infintie:"\u29DD",intcal:"\u22BA",intercal:"\u22BA",intlarhk:"\u2A17",intprod:"\u2A3C",iprod:"\u2A3C",iocy:"\u0451",iogon:"\u012F",iopf:"\u{1D55A}",iota:"\u03B9",iquest:"\xBF",iscr:"\u{1D4BE}",isinE:"\u22F9",isindot:"\u22F5",isins:"\u22F4",isinsv:"\u22F3",itilde:"\u0129",iukcy:"\u0456",iuml:"\xEF",jcirc:"\u0135",jcy:"\u0439",jfr:"\u{1D527}",jmath:"\u0237",jopf:"\u{1D55B}",jscr:"\u{1D4BF}",jsercy:"\u0458",jukcy:"\u0454",kappa:"\u03BA",kappav:"\u03F0",varkappa:"\u03F0",kcedil:"\u0137",kcy:"\u043A",kfr:"\u{1D528}",kgreen:"\u0138",khcy:"\u0445",kjcy:"\u045C",kopf:"\u{1D55C}",kscr:"\u{1D4C0}",lAtail:"\u291B",lBarr:"\u290E",lEg:"\u2A8B",lesseqqgtr:"\u2A8B",lHar:"\u2962",lacute:"\u013A",laemptyv:"\u29B4",lambda:"\u03BB",langd:"\u2991",lap:"\u2A85",lessapprox:"\u2A85",laquo:"\xAB",larrbfs:"\u291F",larrfs:"\u291D",larrlp:"\u21AB",looparrowleft:"\u21AB",larrpl:"\u2939",larrsim:"\u2973",larrtl:"\u21A2",leftarrowtail:"\u21A2",lat:"\u2AAB",latail:"\u2919",late:"\u2AAD",lates:"\u2AAD\uFE00",lbarr:"\u290C",lbbrk:"\u2772",lbrace:"{",lcub:"{",lbrack:"[",lsqb:"[",lbrke:"\u298B",lbrksld:"\u298F",lbrkslu:"\u298D",lcaron:"\u013E",lcedil:"\u013C",lcy:"\u043B",ldca:"\u2936",ldrdhar:"\u2967",ldrushar:"\u294B",ldsh:"\u21B2",le:"\u2264",leq:"\u2264",leftleftarrows:"\u21C7",llarr:"\u21C7",leftthreetimes:"\u22CB",lthree:"\u22CB",lescc:"\u2AA8",lesdot:"\u2A7F",lesdoto:"\u2A81",lesdotor:"\u2A83",lesg:"\u22DA\uFE00",lesges:"\u2A93",lessdot:"\u22D6",ltdot:"\u22D6",lfisht:"\u297C",lfr:"\u{1D529}",lgE:"\u2A91",lharul:"\u296A",lhblk:"\u2584",ljcy:"\u0459",llhard:"\u296B",lltri:"\u25FA",lmidot:"\u0140",lmoust:"\u23B0",lmoustache:"\u23B0",lnE:"\u2268",lneqq:"\u2268",lnap:"\u2A89",lnapprox:"\u2A89",lne:"\u2A87",lneq:"\u2A87",lnsim:"\u22E6",loang:"\u27EC",loarr:"\u21FD",longmapsto:"\u27FC",xmap:"\u27FC",looparrowright:"\u21AC",rarrlp:"\u21AC",lopar:"\u2985",lopf:"\u{1D55D}",loplus:"\u2A2D",lotimes:"\u2A34",lowast:"\u2217",loz:"\u25CA",lozenge:"\u25CA",lpar:"(",lparlt:"\u2993",lrhard:"\u296D",lrm:"\u200E",lrtri:"\u22BF",lsaquo:"\u2039",lscr:"\u{1D4C1}",lsime:"\u2A8D",lsimg:"\u2A8F",lsquor:"\u201A",sbquo:"\u201A",lstrok:"\u0142",ltcc:"\u2AA6",ltcir:"\u2A79",ltimes:"\u22C9",ltlarr:"\u2976",ltquest:"\u2A7B",ltrPar:"\u2996",ltri:"\u25C3",triangleleft:"\u25C3",lurdshar:"\u294A",luruhar:"\u2966",lvertneqq:"\u2268\uFE00",lvnE:"\u2268\uFE00",mDDot:"\u223A",macr:"\xAF",strns:"\xAF",male:"\u2642",malt:"\u2720",maltese:"\u2720",marker:"\u25AE",mcomma:"\u2A29",mcy:"\u043C",mdash:"\u2014",mfr:"\u{1D52A}",mho:"\u2127",micro:"\xB5",midcir:"\u2AF0",minus:"\u2212",minusdu:"\u2A2A",mlcp:"\u2ADB",models:"\u22A7",mopf:"\u{1D55E}",mscr:"\u{1D4C2}",mu:"\u03BC",multimap:"\u22B8",mumap:"\u22B8",nGg:"\u22D9\u0338",nGt:"\u226B\u20D2",nLeftarrow:"\u21CD",nlArr:"\u21CD",nLeftrightarrow:"\u21CE",nhArr:"\u21CE",nLl:"\u22D8\u0338",nLt:"\u226A\u20D2",nRightarrow:"\u21CF",nrArr:"\u21CF",nVDash:"\u22AF",nVdash:"\u22AE",nacute:"\u0144",nang:"\u2220\u20D2",napE:"\u2A70\u0338",napid:"\u224B\u0338",napos:"\u0149",natur:"\u266E",natural:"\u266E",ncap:"\u2A43",ncaron:"\u0148",ncedil:"\u0146",ncongdot:"\u2A6D\u0338",ncup:"\u2A42",ncy:"\u043D",ndash:"\u2013",neArr:"\u21D7",nearhk:"\u2924",nedot:"\u2250\u0338",nesear:"\u2928",toea:"\u2928",nfr:"\u{1D52B}",nharr:"\u21AE",nleftrightarrow:"\u21AE",nhpar:"\u2AF2",nis:"\u22FC",nisd:"\u22FA",njcy:"\u045A",nlE:"\u2266\u0338",nleqq:"\u2266\u0338",nlarr:"\u219A",nleftarrow:"\u219A",nldr:"\u2025",nopf:"\u{1D55F}",not:"\xAC",notinE:"\u22F9\u0338",notindot:"\u22F5\u0338",notinvb:"\u22F7",notinvc:"\u22F6",notnivb:"\u22FE",notnivc:"\u22FD",nparsl:"\u2AFD\u20E5",npart:"\u2202\u0338",npolint:"\u2A14",nrarr:"\u219B",nrightarrow:"\u219B",nrarrc:"\u2933\u0338",nrarrw:"\u219D\u0338",nscr:"\u{1D4C3}",nsub:"\u2284",nsubE:"\u2AC5\u0338",nsubseteqq:"\u2AC5\u0338",nsup:"\u2285",nsupE:"\u2AC6\u0338",nsupseteqq:"\u2AC6\u0338",ntilde:"\xF1",nu:"\u03BD",num:"#",numero:"\u2116",numsp:"\u2007",nvDash:"\u22AD",nvHarr:"\u2904",nvap:"\u224D\u20D2",nvdash:"\u22AC",nvge:"\u2265\u20D2",nvgt:">\u20D2",nvinfin:"\u29DE",nvlArr:"\u2902",nvle:"\u2264\u20D2",nvlt:"<\u20D2",nvltrie:"\u22B4\u20D2",nvrArr:"\u2903",nvrtrie:"\u22B5\u20D2",nvsim:"\u223C\u20D2",nwArr:"\u21D6",nwarhk:"\u2923",nwnear:"\u2927",oacute:"\xF3",ocirc:"\xF4",ocy:"\u043E",odblac:"\u0151",odiv:"\u2A38",odsold:"\u29BC",oelig:"\u0153",ofcir:"\u29BF",ofr:"\u{1D52C}",ogon:"\u02DB",ograve:"\xF2",ogt:"\u29C1",ohbar:"\u29B5",olcir:"\u29BE",olcross:"\u29BB",olt:"\u29C0",omacr:"\u014D",omega:"\u03C9",omicron:"\u03BF",omid:"\u29B6",oopf:"\u{1D560}",opar:"\u29B7",operp:"\u29B9",or:"\u2228",vee:"\u2228",ord:"\u2A5D",order:"\u2134",orderof:"\u2134",oscr:"\u2134",ordf:"\xAA",ordm:"\xBA",origof:"\u22B6",oror:"\u2A56",orslope:"\u2A57",orv:"\u2A5B",oslash:"\xF8",osol:"\u2298",otilde:"\xF5",otimesas:"\u2A36",ouml:"\xF6",ovbar:"\u233D",para:"\xB6",parsim:"\u2AF3",parsl:"\u2AFD",pcy:"\u043F",percnt:"%",period:".",permil:"\u2030",pertenk:"\u2031",pfr:"\u{1D52D}",phi:"\u03C6",phiv:"\u03D5",straightphi:"\u03D5",varphi:"\u03D5",phone:"\u260E",pi:"\u03C0",piv:"\u03D6",varpi:"\u03D6",planckh:"\u210E",plus:"+",plusacir:"\u2A23",pluscir:"\u2A22",plusdu:"\u2A25",pluse:"\u2A72",plussim:"\u2A26",plustwo:"\u2A27",pointint:"\u2A15",popf:"\u{1D561}",pound:"\xA3",prE:"\u2AB3",prap:"\u2AB7",precapprox:"\u2AB7",precnapprox:"\u2AB9",prnap:"\u2AB9",precneqq:"\u2AB5",prnE:"\u2AB5",precnsim:"\u22E8",prnsim:"\u22E8",prime:"\u2032",profalar:"\u232E",profline:"\u2312",profsurf:"\u2313",prurel:"\u22B0",pscr:"\u{1D4C5}",psi:"\u03C8",puncsp:"\u2008",qfr:"\u{1D52E}",qopf:"\u{1D562}",qprime:"\u2057",qscr:"\u{1D4C6}",quatint:"\u2A16",quest:"?",rAtail:"\u291C",rHar:"\u2964",race:"\u223D\u0331",racute:"\u0155",raemptyv:"\u29B3",rangd:"\u2992",range:"\u29A5",raquo:"\xBB",rarrap:"\u2975",rarrbfs:"\u2920",rarrc:"\u2933",rarrfs:"\u291E",rarrpl:"\u2945",rarrsim:"\u2974",rarrtl:"\u21A3",rightarrowtail:"\u21A3",rarrw:"\u219D",rightsquigarrow:"\u219D",ratail:"\u291A",ratio:"\u2236",rbbrk:"\u2773",rbrace:"}",rcub:"}",rbrack:"]",rsqb:"]",rbrke:"\u298C",rbrksld:"\u298E",rbrkslu:"\u2990",rcaron:"\u0159",rcedil:"\u0157",rcy:"\u0440",rdca:"\u2937",rdldhar:"\u2969",rdsh:"\u21B3",rect:"\u25AD",rfisht:"\u297D",rfr:"\u{1D52F}",rharul:"\u296C",rho:"\u03C1",rhov:"\u03F1",varrho:"\u03F1",rightrightarrows:"\u21C9",rrarr:"\u21C9",rightthreetimes:"\u22CC",rthree:"\u22CC",ring:"\u02DA",rlm:"\u200F",rmoust:"\u23B1",rmoustache:"\u23B1",rnmid:"\u2AEE",roang:"\u27ED",roarr:"\u21FE",ropar:"\u2986",ropf:"\u{1D563}",roplus:"\u2A2E",rotimes:"\u2A35",rpar:")",rpargt:"\u2994",rppolint:"\u2A12",rsaquo:"\u203A",rscr:"\u{1D4C7}",rtimes:"\u22CA",rtri:"\u25B9",triangleright:"\u25B9",rtriltri:"\u29CE",ruluhar:"\u2968",rx:"\u211E",sacute:"\u015B",scE:"\u2AB4",scap:"\u2AB8",succapprox:"\u2AB8",scaron:"\u0161",scedil:"\u015F",scirc:"\u015D",scnE:"\u2AB6",succneqq:"\u2AB6",scnap:"\u2ABA",succnapprox:"\u2ABA",scnsim:"\u22E9",succnsim:"\u22E9",scpolint:"\u2A13",scy:"\u0441",sdot:"\u22C5",sdote:"\u2A66",seArr:"\u21D8",sect:"\xA7",semi:";",seswar:"\u2929",tosa:"\u2929",sext:"\u2736",sfr:"\u{1D530}",sharp:"\u266F",shchcy:"\u0449",shcy:"\u0448",shy:"\xAD",sigma:"\u03C3",sigmaf:"\u03C2",sigmav:"\u03C2",varsigma:"\u03C2",simdot:"\u2A6A",simg:"\u2A9E",simgE:"\u2AA0",siml:"\u2A9D",simlE:"\u2A9F",simne:"\u2246",simplus:"\u2A24",simrarr:"\u2972",smashp:"\u2A33",smeparsl:"\u29E4",smile:"\u2323",ssmile:"\u2323",smt:"\u2AAA",smte:"\u2AAC",smtes:"\u2AAC\uFE00",softcy:"\u044C",sol:"/",solb:"\u29C4",solbar:"\u233F",sopf:"\u{1D564}",spades:"\u2660",spadesuit:"\u2660",sqcaps:"\u2293\uFE00",sqcups:"\u2294\uFE00",sscr:"\u{1D4C8}",star:"\u2606",sub:"\u2282",subset:"\u2282",subE:"\u2AC5",subseteqq:"\u2AC5",subdot:"\u2ABD",subedot:"\u2AC3",submult:"\u2AC1",subnE:"\u2ACB",subsetneqq:"\u2ACB",subne:"\u228A",subsetneq:"\u228A",subplus:"\u2ABF",subrarr:"\u2979",subsim:"\u2AC7",subsub:"\u2AD5",subsup:"\u2AD3",sung:"\u266A",sup1:"\xB9",sup2:"\xB2",sup3:"\xB3",supE:"\u2AC6",supseteqq:"\u2AC6",supdot:"\u2ABE",supdsub:"\u2AD8",supedot:"\u2AC4",suphsol:"\u27C9",suphsub:"\u2AD7",suplarr:"\u297B",supmult:"\u2AC2",supnE:"\u2ACC",supsetneqq:"\u2ACC",supne:"\u228B",supsetneq:"\u228B",supplus:"\u2AC0",supsim:"\u2AC8",supsub:"\u2AD4",supsup:"\u2AD6",swArr:"\u21D9",swnwar:"\u292A",szlig:"\xDF",target:"\u2316",tau:"\u03C4",tcaron:"\u0165",tcedil:"\u0163",tcy:"\u0442",telrec:"\u2315",tfr:"\u{1D531}",theta:"\u03B8",thetasym:"\u03D1",thetav:"\u03D1",vartheta:"\u03D1",thorn:"\xFE",times:"\xD7",timesbar:"\u2A31",timesd:"\u2A30",topbot:"\u2336",topcir:"\u2AF1",topf:"\u{1D565}",topfork:"\u2ADA",tprime:"\u2034",triangle:"\u25B5",utri:"\u25B5",triangleq:"\u225C",trie:"\u225C",tridot:"\u25EC",triminus:"\u2A3A",triplus:"\u2A39",trisb:"\u29CD",tritime:"\u2A3B",trpezium:"\u23E2",tscr:"\u{1D4C9}",tscy:"\u0446",tshcy:"\u045B",tstrok:"\u0167",uHar:"\u2963",uacute:"\xFA",ubrcy:"\u045E",ubreve:"\u016D",ucirc:"\xFB",ucy:"\u0443",udblac:"\u0171",ufisht:"\u297E",ufr:"\u{1D532}",ugrave:"\xF9",uhblk:"\u2580",ulcorn:"\u231C",ulcorner:"\u231C",ulcrop:"\u230F",ultri:"\u25F8",umacr:"\u016B",uogon:"\u0173",uopf:"\u{1D566}",upsi:"\u03C5",upsilon:"\u03C5",upuparrows:"\u21C8",uuarr:"\u21C8",urcorn:"\u231D",urcorner:"\u231D",urcrop:"\u230E",uring:"\u016F",urtri:"\u25F9",uscr:"\u{1D4CA}",utdot:"\u22F0",utilde:"\u0169",uuml:"\xFC",uwangle:"\u29A7",vBar:"\u2AE8",vBarv:"\u2AE9",vangrt:"\u299C",varsubsetneq:"\u228A\uFE00",vsubne:"\u228A\uFE00",varsubsetneqq:"\u2ACB\uFE00",vsubnE:"\u2ACB\uFE00",varsupsetneq:"\u228B\uFE00",vsupne:"\u228B\uFE00",varsupsetneqq:"\u2ACC\uFE00",vsupnE:"\u2ACC\uFE00",vcy:"\u0432",veebar:"\u22BB",veeeq:"\u225A",vellip:"\u22EE",vfr:"\u{1D533}",vopf:"\u{1D567}",vscr:"\u{1D4CB}",vzigzag:"\u299A",wcirc:"\u0175",wedbar:"\u2A5F",wedgeq:"\u2259",weierp:"\u2118",wp:"\u2118",wfr:"\u{1D534}",wopf:"\u{1D568}",wscr:"\u{1D4CC}",xfr:"\u{1D535}",xi:"\u03BE",xnis:"\u22FB",xopf:"\u{1D569}",xscr:"\u{1D4CD}",yacute:"\xFD",yacy:"\u044F",ycirc:"\u0177",ycy:"\u044B",yen:"\xA5",yfr:"\u{1D536}",yicy:"\u0457",yopf:"\u{1D56A}",yscr:"\u{1D4CE}",yucy:"\u044E",yuml:"\xFF",zacute:"\u017A",zcaron:"\u017E",zcy:"\u0437",zdot:"\u017C",zeta:"\u03B6",zfr:"\u{1D537}",zhcy:"\u0436",zigrarr:"\u21DD",zopf:"\u{1D56B}",zscr:"\u{1D4CF}",zwj:"\u200D",zwnj:"\u200C"},q6="\uE500";z0.ngsp=q6;var cD=class{tokens;errors;nonNormalizedIcuExpressions;constructor(i,e,t){this.tokens=i,this.errors=e,this.nonNormalizedIcuExpressions=t}};function XQ(n,i,e,t={}){let o=new mD(new Ab(n,i),e,t);return o.tokenize(),new cD(rX(o.tokens),o.errors,o.nonNormalizedIcuExpressions)}var YQ=/\r\n?/g;function Dh(n){return`Unexpected character "${n===qr?"EOF":String.fromCharCode(n)}"`}function YR(n){return`Unknown entity "${n}" - use the "&#;" or "&#x;" syntax`}function KQ(n,i){return`Unable to parse entity "${i}" - ${n} character reference entities must end with ";"`}var dD=(function(n){return n.HEX="hexadecimal",n.DEC="decimal",n})(dD||{}),ZQ=["@if","@else","@for","@switch","@case","@default","@empty","@defer","@placeholder","@loading","@error"],E_={start:"{{",end:"}}"},mD=class{_getTagDefinition;_cursor;_tokenizeIcu;_leadingTriviaCodePoints;_currentTokenStart=null;_currentTokenType=null;_expansionCaseStack=[];_openDirectiveCount=0;_inInterpolation=!1;_preserveLineEndings;_i18nNormalizeLineEndingsInICUs;_tokenizeBlocks;_tokenizeLet;_selectorlessEnabled;tokens=[];errors=[];nonNormalizedIcuExpressions=[];constructor(i,e,t){this._getTagDefinition=e,this._tokenizeIcu=t.tokenizeExpansionForms||!1,this._leadingTriviaCodePoints=t.leadingTriviaChars&&t.leadingTriviaChars.map(r=>r.codePointAt(0)||0);let o=t.range||{endPos:i.content.length,startPos:0,startLine:0,startCol:0};this._cursor=t.escapedString?new pD(i,o):new Qb(i,o),this._preserveLineEndings=t.preserveLineEndings||!1,this._i18nNormalizeLineEndingsInICUs=t.i18nNormalizeLineEndingsInICUs||!1,this._tokenizeBlocks=t.tokenizeBlocks??!0,this._tokenizeLet=t.tokenizeLet??!0,this._selectorlessEnabled=t.selectorlessEnabled??!1;try{this._cursor.init()}catch(r){this.handleError(r)}}_processCarriageReturns(i){return this._preserveLineEndings?i:i.replace(YQ,` -`)}tokenize(){for(;this._cursor.peek()!==qr;){let i=this._cursor.clone();try{this._attemptCharCode(jh)?this._attemptCharCode(HE)?this._attemptCharCode(Sc)?this._consumeCdata(i):this._attemptCharCode(Pb)?this._consumeComment(i):this._consumeDocType(i):this._attemptCharCode(il)?this._consumeTagClose(i):this._consumeTagOpen(i):this._tokenizeLet&&this._cursor.peek()===kh&&!this._inInterpolation&&this._isLetStart()?this._consumeLetDeclaration(i):this._tokenizeBlocks&&this._isBlockStart()?this._consumeBlockStart(i):this._tokenizeBlocks&&!this._inInterpolation&&!this._isInExpansionCase()&&!this._isInExpansionForm()&&this._attemptCharCode(za)?this._consumeBlockEnd(i):this._tokenizeIcu&&this._tokenizeExpansionForm()||this._consumeWithInterpolation(5,8,()=>this._isTextEnd(),()=>this._isTagStart())}catch(e){this.handleError(e)}}this._beginToken(41),this._endToken([])}_getBlockName(){let i=!1,e=this._cursor.clone();return this._attemptCharCodeUntilFn(t=>T0(t)?!i:oX(t)?(i=!0,!1):!0),this._cursor.getChars(e).trim()}_consumeBlockStart(i){this._requireCharCode(kh),this._beginToken(24,i);let e=this._endToken([this._getBlockName()]);if(e.parts[0]==="default never"&&this._attemptCharCode(rs)){this._beginToken(25),this._endToken([]),this._beginToken(26),this._endToken([]);return}if(this._cursor.peek()===$a)if(this._cursor.advance(),this._consumeBlockParameters(),this._attemptCharCodeUntilFn(Bo),this._attemptCharCode(yr))this._attemptCharCodeUntilFn(Bo);else{e.type=28;return}this._attemptCharCode(al)?(this._beginToken(25),this._endToken([])):this._isBlockStart()&&(e.parts[0]==="case"||e.parts[0]==="default")?(this._beginToken(25),this._endToken([]),this._beginToken(26),this._endToken([])):e.type=28}_consumeBlockEnd(i){this._beginToken(26,i),this._endToken([])}_consumeBlockParameters(){for(this._attemptCharCodeUntilFn(ZR);this._cursor.peek()!==yr&&this._cursor.peek()!==qr;){this._beginToken(27);let i=this._cursor.clone(),e=null,t=0;for(;this._cursor.peek()!==rs&&this._cursor.peek()!==qr||e!==null;){let o=this._cursor.peek();if(o===Zp)this._cursor.advance();else if(o===e)e=null;else if(e===null&&H_(o))e=o;else if(o===$a&&e===null)t++;else if(o===yr&&e===null){if(t===0)break;t>0&&t--}this._cursor.advance()}this._endToken([this._cursor.getChars(i)]),this._attemptCharCodeUntilFn(ZR)}}_consumeLetDeclaration(i){if(this._requireStr("@let"),this._beginToken(29,i),T0(this._cursor.peek()))this._attemptCharCodeUntilFn(Bo);else{let o=this._endToken([this._cursor.getChars(i)]);o.type=32;return}let e=this._endToken([this._getLetDeclarationName()]);if(this._attemptCharCodeUntilFn(Bo),!this._attemptCharCode(Gr)){e.type=32;return}this._attemptCharCodeUntilFn(o=>Bo(o)&&!Ib(o)),this._consumeLetDeclarationValue(),this._cursor.peek()===rs?(this._beginToken(31),this._endToken([]),this._cursor.advance()):(e.type=32,e.sourceSpan=this._cursor.getSpan(i))}_getLetDeclarationName(){let i=this._cursor.clone(),e=!1;return this._attemptCharCodeUntilFn(t=>Mm(t)||t===dx||t===Im||e&&ol(t)?(e=!0,!1):!0),this._cursor.getChars(i).trim()}_consumeLetDeclarationValue(){let i=this._cursor.clone();for(this._beginToken(30,i);this._cursor.peek()!==qr;){let e=this._cursor.peek();if(e===rs)break;H_(e)&&(this._cursor.advance(),this._attemptCharCodeUntilFn(t=>t===Zp?(this._cursor.advance(),!1):t===e)),this._cursor.advance()}this._endToken([this._cursor.getChars(i)])}_tokenizeExpansionForm(){if(this.isExpansionFormStart())return this._consumeExpansionFormStart(),!0;if(nX(this._cursor.peek())&&this._isInExpansionForm())return this._consumeExpansionCaseStart(),!0;if(this._cursor.peek()===za){if(this._isInExpansionCase())return this._consumeExpansionCaseEnd(),!0;if(this._isInExpansionForm())return this._consumeExpansionFormEnd(),!0}return!1}_beginToken(i,e=this._cursor.clone()){this._currentTokenStart=e,this._currentTokenType=i}_endToken(i,e){if(this._currentTokenStart===null)throw new rn(this._cursor.getSpan(e),"Programming error - attempted to end a token when there was no start to the token");if(this._currentTokenType===null)throw new rn(this._cursor.getSpan(this._currentTokenStart),"Programming error - attempted to end a token which has no token type");let t={type:this._currentTokenType,parts:i,sourceSpan:(e??this._cursor).getSpan(this._currentTokenStart,this._leadingTriviaCodePoints)};return this.tokens.push(t),this._currentTokenStart=null,this._currentTokenType=null,t}_createError(i,e){this._isInExpansionForm()&&(i+=` (Do you have an unescaped "{" in your template? Use "{{ '{' }}") to escape it.)`);let t=new rn(e,i);return this._currentTokenStart=null,this._currentTokenType=null,t}handleError(i){if(i instanceof j0&&(i=this._createError(i.msg,this._cursor.getSpan(i.cursor))),i instanceof rn)this.errors.push(i);else throw i}_attemptCharCode(i){return this._cursor.peek()===i?(this._cursor.advance(),!0):!1}_attemptCharCodeCaseInsensitive(i){return iX(this._cursor.peek(),i)?(this._cursor.advance(),!0):!1}_requireCharCode(i){let e=this._cursor.clone();if(!this._attemptCharCode(i))throw this._createError(Dh(this._cursor.peek()),this._cursor.getSpan(e))}_attemptStr(i){let e=i.length;if(this._cursor.charsLeft()this._peekStr(i))}_isLetStart(){return this._cursor.peek()===kh&&this._peekStr("@let")}_consumeEntity(i){this._beginToken(9);let e=this._cursor.clone();if(this._cursor.advance(),this._attemptCharCode(_6)){let t=this._attemptCharCode(M6)||this._attemptCharCode(hW),o=this._cursor.clone();if(this._attemptCharCodeUntilFn(eX),this._cursor.peek()!=rs){this._cursor.advance();let a=t?dD.HEX:dD.DEC;throw this._createError(KQ(a,this._cursor.getChars(e)),this._cursor.getSpan())}let r=this._cursor.getChars(o);this._cursor.advance();try{let a=parseInt(r,t?16:10);this._endToken([String.fromCodePoint(a),this._cursor.getChars(e)])}catch{throw this._createError(YR(this._cursor.getChars(e)),this._cursor.getSpan())}}else{let t=this._cursor.clone();if(this._attemptCharCodeUntilFn(tX),this._cursor.peek()!=rs)this._beginToken(i,e),this._cursor=t,this._endToken(["&"]);else{let o=this._cursor.getChars(t);this._cursor.advance();let r=z0.hasOwnProperty(o)&&z0[o];if(!r)throw this._createError(YR(o),this._cursor.getSpan(e));this._endToken([r,`&${o};`])}}}_consumeRawText(i,e){this._beginToken(i?6:7);let t=[];for(;;){let o=this._cursor.clone(),r=e();if(this._cursor=o,r)break;i&&this._cursor.peek()===Db?(this._endToken([this._processCarriageReturns(t.join(""))]),t.length=0,this._consumeEntity(6),this._beginToken(6)):t.push(this._readChar())}this._endToken([this._processCarriageReturns(t.join(""))])}_consumeComment(i){this._beginToken(10,i),this._requireCharCode(Pb),this._endToken([]),this._consumeRawText(!1,()=>this._attemptStr("-->")),this._beginToken(11),this._requireStr("-->"),this._endToken([])}_consumeCdata(i){this._beginToken(12,i),this._requireStr("CDATA["),this._endToken([]),this._consumeRawText(!1,()=>this._attemptStr("]]>")),this._beginToken(13),this._requireStr("]]>"),this._endToken([])}_consumeDocType(i){this._beginToken(18,i);let e=this._cursor.clone();this._attemptUntilChar(Ps);let t=this._cursor.getChars(e);this._cursor.advance(),this._endToken([t])}_consumePrefixAndName(i){let e=this._cursor.clone(),t="";for(;this._cursor.peek()!==bc&&!JQ(this._cursor.peek());)this._cursor.advance();let o;this._cursor.peek()===bc?(t=this._cursor.getChars(e),this._cursor.advance(),o=this._cursor.clone()):o=e,this._requireCharCodeUntilFn(i,t===""?0:1);let r=this._cursor.getChars(o);return[t,r]}_consumeTagOpen(i){let e,t,o,r;try{if(this._selectorlessEnabled&&V1(this._cursor.peek()))r=this._consumeComponentOpenStart(i),[o,t,e]=r.parts,t&&(o+=`:${t}`),e&&(o+=`:${e}`),this._attemptCharCodeUntilFn(Bo);else{if(!Mm(this._cursor.peek()))throw this._createError(Dh(this._cursor.peek()),this._cursor.getSpan(i));r=this._consumeTagOpenStart(i),t=r.parts[0],e=o=r.parts[1],this._attemptCharCodeUntilFn(Bo)}for(;!eF(this._cursor.peek());)if(this._selectorlessEnabled&&this._cursor.peek()===kh){let c=this._cursor.clone(),m=c.clone();m.advance(),V1(m.peek())&&this._consumeDirective(c,m)}else this._consumeAttribute();r.type===33?this._consumeComponentOpenEnd():this._consumeTagOpenEnd()}catch(c){if(c instanceof rn){r?r.type=r.type===33?37:4:(this._beginToken(5,i),this._endToken(["<"]));return}throw c}let a=this._getTagDefinition(e).getContentType(t);a===Cc.RAW_TEXT?this._consumeRawTextWithTagClose(r,o,!1):a===Cc.ESCAPABLE_RAW_TEXT&&this._consumeRawTextWithTagClose(r,o,!0)}_consumeRawTextWithTagClose(i,e,t){this._consumeRawText(t,()=>!this._attemptCharCode(jh)||!this._attemptCharCode(il)||(this._attemptCharCodeUntilFn(Bo),!this._attemptStrCaseInsensitive(e))?!1:(this._attemptCharCodeUntilFn(Bo),this._attemptCharCode(Ps))),this._beginToken(i.type===33?36:3),this._requireCharCodeUntilFn(o=>o===Ps,3),this._cursor.advance(),this._endToken(i.parts)}_consumeTagOpenStart(i){this._beginToken(0,i);let e=this._consumePrefixAndName(Vp);return this._endToken(e)}_consumeComponentOpenStart(i){this._beginToken(33,i);let e=this._consumeComponentName();return this._endToken(e)}_consumeComponentName(){let i=this._cursor.clone();for(;JR(this._cursor.peek());)this._cursor.advance();let e=this._cursor.getChars(i),t="",o="";return this._cursor.peek()===bc&&(this._cursor.advance(),[t,o]=this._consumePrefixAndName(Vp)),[e,t,o]}_consumeAttribute(){this._consumeAttributeName(),this._attemptCharCodeUntilFn(Bo),this._attemptCharCode(Gr)&&(this._attemptCharCodeUntilFn(Bo),this._consumeAttributeValue()),this._attemptCharCodeUntilFn(Bo)}_consumeAttributeName(){let i=this._cursor.peek();if(i===k0||i===M0)throw this._createError(Dh(i),this._cursor.getSpan());this._beginToken(14);let e;if(this._openDirectiveCount>0){let o=0;e=r=>{if(this._openDirectiveCount>0){if(r===$a)o++;else if(r===yr){if(o===0)return!0;o--}}return Vp(r)}}else if(i===Sc){let o=0;e=r=>(r===Sc?o++:r===bd&&o--,o<=0?Vp(r):Ib(r))}else e=Vp;let t=this._consumePrefixAndName(e);this._endToken(t)}_consumeAttributeValue(){if(this._cursor.peek()===k0||this._cursor.peek()===M0){let i=this._cursor.peek();this._consumeQuote(i);let e=()=>this._cursor.peek()===i;this._consumeWithInterpolation(16,17,e,e),this._consumeQuote(i)}else{let i=()=>Vp(this._cursor.peek());this._consumeWithInterpolation(16,17,i,i)}}_consumeQuote(i){this._beginToken(15),this._requireCharCode(i),this._endToken([String.fromCodePoint(i)])}_consumeTagOpenEnd(){let i=this._attemptCharCode(il)?2:1;this._beginToken(i),this._requireCharCode(Ps),this._endToken([])}_consumeComponentOpenEnd(){let i=this._attemptCharCode(il)?35:34;this._beginToken(i),this._requireCharCode(Ps),this._endToken([])}_consumeTagClose(i){if(this._selectorlessEnabled){let t=i.clone();for(;t.peek()!==Ps&&!V1(t.peek());)t.advance();if(V1(t.peek())){this._beginToken(36,i);let o=this._consumeComponentName();this._attemptCharCodeUntilFn(Bo),this._requireCharCode(Ps),this._endToken(o);return}}this._beginToken(3,i),this._attemptCharCodeUntilFn(Bo);let e=this._consumePrefixAndName(Vp);this._attemptCharCodeUntilFn(Bo),this._requireCharCode(Ps),this._endToken(e)}_consumeExpansionFormStart(){this._beginToken(19),this._requireCharCode(al),this._endToken([]),this._expansionCaseStack.push(19),this._beginToken(7);let i=this._readUntil(ya),e=this._processCarriageReturns(i);if(this._i18nNormalizeLineEndingsInICUs)this._endToken([e]);else{let o=this._endToken([i]);e!==i&&this.nonNormalizedIcuExpressions.push(o)}this._requireCharCode(ya),this._attemptCharCodeUntilFn(Bo),this._beginToken(7);let t=this._readUntil(ya);this._endToken([t]),this._requireCharCode(ya),this._attemptCharCodeUntilFn(Bo)}_consumeExpansionCaseStart(){this._beginToken(20);let i=this._readUntil(al).trim();this._endToken([i]),this._attemptCharCodeUntilFn(Bo),this._beginToken(21),this._requireCharCode(al),this._endToken([]),this._attemptCharCodeUntilFn(Bo),this._expansionCaseStack.push(21)}_consumeExpansionCaseEnd(){this._beginToken(22),this._requireCharCode(za),this._endToken([]),this._attemptCharCodeUntilFn(Bo),this._expansionCaseStack.pop()}_consumeExpansionFormEnd(){this._beginToken(23),this._requireCharCode(za),this._endToken([]),this._expansionCaseStack.pop()}_consumeWithInterpolation(i,e,t,o){this._beginToken(i);let r=[];for(;!t();){let a=this._cursor.clone();this._attemptStr(E_.start)?(this._endToken([this._processCarriageReturns(r.join(""))],a),r.length=0,this._consumeInterpolation(e,a,o),this._beginToken(i)):this._cursor.peek()===Db?(this._endToken([this._processCarriageReturns(r.join(""))]),r.length=0,this._consumeEntity(i),this._beginToken(i)):r.push(this._readChar())}this._inInterpolation=!1,this._endToken([this._processCarriageReturns(r.join(""))])}_consumeInterpolation(i,e,t){let o=[];this._beginToken(i,e),o.push(E_.start);let r=this._cursor.clone(),a=null,c=!1;for(;this._cursor.peek()!==qr&&(t===null||!t());){let m=this._cursor.clone();if(this._isTagStart()){this._cursor=m,o.push(this._getProcessedChars(r,m)),this._endToken(o);return}if(a===null)if(this._attemptStr(E_.end)){o.push(this._getProcessedChars(r,m)),o.push(E_.end),this._endToken(o);return}else this._attemptStr("//")&&(c=!0);let p=this._cursor.peek();this._cursor.advance(),p===Zp?this._cursor.advance():p===a?a=null:!c&&a===null&&H_(p)&&(a=p)}o.push(this._getProcessedChars(r,this._cursor)),this._endToken(o)}_consumeDirective(i,e){for(this._requireCharCode(kh),this._cursor.advance();JR(this._cursor.peek());)this._cursor.advance();this._beginToken(38,i);let t=this._cursor.getChars(e);if(this._endToken([t]),this._attemptCharCodeUntilFn(Bo),this._cursor.peek()===$a){for(this._openDirectiveCount++,this._beginToken(39),this._cursor.advance(),this._endToken([]),this._attemptCharCodeUntilFn(Bo);!eF(this._cursor.peek())&&this._cursor.peek()!==yr;)this._consumeAttribute();if(this._attemptCharCodeUntilFn(Bo),this._openDirectiveCount--,this._cursor.peek()!==yr){if(this._cursor.peek()===Ps||this._cursor.peek()===il)return;throw this._createError(Dh(this._cursor.peek()),this._cursor.getSpan(i))}this._beginToken(40),this._cursor.advance(),this._endToken([]),this._attemptCharCodeUntilFn(Bo)}}_getProcessedChars(i,e){return this._processCarriageReturns(e.getChars(i))}_isTextEnd(){return!!(this._isTagStart()||this._cursor.peek()===qr||this._tokenizeIcu&&!this._inInterpolation&&(this.isExpansionFormStart()||this._cursor.peek()===za&&this._isInExpansionCase())||this._tokenizeBlocks&&!this._inInterpolation&&!this._isInExpansion()&&(this._isBlockStart()||this._isLetStart()||this._cursor.peek()===za))}_isTagStart(){if(this._cursor.peek()===jh){let i=this._cursor.clone();i.advance();let e=i.peek();if(pu<=e&&e<=Y0||Pm<=e&&e<=df||e===il||e===HE)return!0}return!1}_readUntil(i){let e=this._cursor.clone();return this._attemptUntilChar(i),this._cursor.getChars(e)}_isInExpansion(){return this._isInExpansionCase()||this._isInExpansionForm()}_isInExpansionCase(){return this._expansionCaseStack.length>0&&this._expansionCaseStack[this._expansionCaseStack.length-1]===21}_isInExpansionForm(){return this._expansionCaseStack.length>0&&this._expansionCaseStack[this._expansionCaseStack.length-1]===19}isExpansionFormStart(){if(this._cursor.peek()!==al)return!1;let i=this._cursor.clone(),e=this._attemptStr(E_.start);return this._cursor=i,!e}};function Bo(n){return!T0(n)||n===qr}function Vp(n){return T0(n)||n===Ps||n===jh||n===il||n===k0||n===M0||n===Gr||n===qr}function JQ(n){return(nC6)}function eX(n){return n===rs||n===qr||!vW(n)}function tX(n){return n===rs||n===qr||!(Mm(n)||ol(n))}function nX(n){return n!==za}function iX(n,i){return KR(n)===KR(i)}function KR(n){return n>=pu&&n<=Y0?n-pu+Pm:n}function oX(n){return Mm(n)||ol(n)||n===Im}function ZR(n){return n!==rs&&Bo(n)}function V1(n){return n===Im||n>=Pm&&n<=df}function JR(n){return Mm(n)||ol(n)||n===Im}function eF(n){return n===il||n===Ps||n===jh||n===qr}function rX(n){let i=[],e;for(let t=0;t0&&e.indexOf(i.peek())!==-1;)t===i&&(i=i.clone()),i.advance();let o=this.locationFromCursor(i),r=this.locationFromCursor(this),a=t!==i?this.locationFromCursor(t):o;return new _n(o,r,a)}getChars(i){return this.input.substring(i.state.offset,this.state.offset)}charAt(i){return this.input.charCodeAt(i)}advanceState(i){if(i.offset>=this.end)throw this.state=i,new j0('Unexpected character "EOF"',this);let e=this.charAt(i.offset);e===Kp?(i.line++,i.column=0):Ib(e)||i.column++,i.offset++,this.updatePeek(i)}updatePeek(i){i.peek=i.offset>=this.end?qr:this.charAt(i.offset)}locationFromCursor(i){return new E0(i.file,i.state.offset,i.state.line,i.state.column)}},pD=class n extends Qb{internalState;constructor(i,e){i instanceof n?(super(i),this.internalState=W({},i.internalState)):(super(i,e),this.internalState=this.state)}advance(){this.state=this.internalState,super.advance(),this.processEscapeSequence()}init(){super.init(),this.processEscapeSequence()}clone(){return new n(this)}getChars(i){let e=i.clone(),t="";for(;e.internalState.offsetthis.internalState.peek;if(i()===Zp)if(this.internalState=W({},this.state),this.advanceState(this.internalState),i()===b6)this.state.peek=Kp;else if(i()===x6)this.state.peek=nP;else if(i()===w6)this.state.peek=h6;else if(i()===y6)this.state.peek=tP;else if(i()===gW)this.state.peek=cW;else if(i()===oP)this.state.peek=f6;else if(i()===S6)if(this.advanceState(this.internalState),i()===al){this.advanceState(this.internalState);let e=this.clone(),t=0;for(;i()!==za;)this.advanceState(this.internalState),t++;this.state.peek=this.decodeHexDigits(e,t)}else{let e=this.clone();this.advanceState(this.internalState),this.advanceState(this.internalState),this.advanceState(this.internalState),this.state.peek=this.decodeHexDigits(e,4)}else if(i()===M6){this.advanceState(this.internalState);let e=this.clone();this.advanceState(this.internalState),this.state.peek=this.decodeHexDigits(e,2)}else if(ER(i())){let e="",t=0,o=this.clone();for(;ER(i())&&t<3;)o=this.clone(),e+=String.fromCodePoint(i()),this.advanceState(this.internalState),t++;this.state.peek=parseInt(e,8),this.internalState=o.internalState}else Ib(this.internalState.peek)?(this.advanceState(this.internalState),this.state=this.internalState):this.state.peek=this.internalState.peek}decodeHexDigits(i,e){let t=this.input.slice(i.internalState.offset,i.internalState.offset+e),o=parseInt(t,16);if(isNaN(o))throw i.state=i.internalState,new j0("Invalid hexadecimal escape sequence",i);return o}},j0=class extends Error{msg;cursor;constructor(i,e){super(i),this.msg=i,this.cursor=e,Object.setPrototypeOf(this,new.target.prototype)}},ur=class n extends rn{elementName;static create(i,e,t){return new n(i,e,t)}constructor(i,e,t){super(e,t),this.elementName=i}},Xb=class{rootNodes;errors;constructor(i,e){this.rootNodes=i,this.errors=e}},aX=class{getTagDefinition;constructor(i){this.getTagDefinition=i}parse(i,e,t){let o=XQ(i,e,this.getTagDefinition,t),r=new uD(o.tokens,this.getTagDefinition);return r.build(),new Xb(r.rootNodes,[...o.errors,...r.errors])}},uD=class n{tokens;tagDefinitionResolver;_index=-1;_peek;_containerStack=[];rootNodes=[];errors=[];constructor(i,e){this.tokens=i,this.tagDefinitionResolver=e,this._advance()}build(){for(;this._peek.type!==41;)this._peek.type===0||this._peek.type===4?this._consumeElementStartTag(this._advance()):this._peek.type===3?this._consumeElementEndTag(this._advance()):this._peek.type===12?(this._closeVoidElement(),this._consumeCdata(this._advance())):this._peek.type===10?(this._closeVoidElement(),this._consumeComment(this._advance())):this._peek.type===5||this._peek.type===7||this._peek.type===6?(this._closeVoidElement(),this._consumeText(this._advance())):this._peek.type===19?this._consumeExpansion(this._advance()):this._peek.type===24?(this._closeVoidElement(),this._consumeBlockOpen(this._advance())):this._peek.type===26?(this._closeVoidElement(),this._consumeBlockClose(this._advance())):this._peek.type===28?(this._closeVoidElement(),this._consumeIncompleteBlock(this._advance())):this._peek.type===29?(this._closeVoidElement(),this._consumeLet(this._advance())):this._peek.type===32?(this._closeVoidElement(),this._consumeIncompleteLet(this._advance())):this._peek.type===33||this._peek.type===37?this._consumeComponentStartTag(this._advance()):this._peek.type===36?this._consumeComponentEndTag(this._advance()):this._advance();for(let i of this._containerStack)i instanceof rl&&this.errors.push(ur.create(i.name,i.sourceSpan,`Unclosed block "${i.name}"`))}_advance(){let i=this._peek;return this._index0)return this.errors=this.errors.concat(r.errors),null;let a=new _n(i.sourceSpan.start,o.sourceSpan.end,i.sourceSpan.fullStart),c=new _n(e.sourceSpan.start,o.sourceSpan.end,e.sourceSpan.fullStart);return new Gb(i.parts[0],r.rootNodes,a,i.sourceSpan,c)}_collectExpansionExpTokens(i){let e=[],t=[21];for(;;){if((this._peek.type===19||this._peek.type===21)&&t.push(this._peek.type),this._peek.type===22)if(tF(t,21)){if(t.pop(),t.length===0)return e}else return this.errors.push(ur.create(null,i.sourceSpan,"Invalid ICU message. Missing '}'.")),null;if(this._peek.type===23)if(tF(t,19))t.pop();else return this.errors.push(ur.create(null,i.sourceSpan,"Invalid ICU message. Missing '}'.")),null;if(this._peek.type===41)return this.errors.push(ur.create(null,i.sourceSpan,"Invalid ICU message. Missing '}'.")),null;e.push(this._advance())}}_consumeText(i){let e=[i],t=i.sourceSpan,o=i.parts[0];if(o.length>0&&o[0]===` -`){let r=this._getContainer();r!=null&&r.children.length===0&&this._getTagDefinition(r)?.ignoreFirstLf&&(o=o.substring(1),e[0]={type:i.type,sourceSpan:i.sourceSpan,parts:[o]})}for(;this._peek.type===8||this._peek.type===5||this._peek.type===9;)i=this._advance(),e.push(i),i.type===8?o+=i.parts.join("").replace(/&([^;]+);/g,nF):i.type===9?o+=i.parts[0]:o+=i.parts.join("");if(o.length>0){let r=i.sourceSpan;this._addToParent(new _u(o,new _n(t.start,r.end,t.fullStart,t.details),e))}}_closeVoidElement(){let i=this._getContainer();i!==null&&this._getTagDefinition(i)?.isVoid&&this._containerStack.pop()}_consumeElementStartTag(i){let e=[],t=[];this._consumeAttributesAndDirectives(e,t);let o=this._getElementFullName(i,this._getClosestElementLikeParent()),r=this._getTagDefinition(o),a=!1;this._peek.type===2?(this._advance(),a=!0,r?.canSelfClose||AE(o)!==null||r?.isVoid||this.errors.push(ur.create(o,i.sourceSpan,`Only void, custom and foreign elements can be self closed "${i.parts[1]}"`))):this._peek.type===1&&(this._advance(),a=!1);let c=this._peek.sourceSpan.fullStart,m=new _n(i.sourceSpan.start,c,i.sourceSpan.fullStart),p=new _n(i.sourceSpan.start,c,i.sourceSpan.fullStart),h=new nl(o,e,t,[],a,m,p,void 0,r?.isVoid??!1),g=this._getContainer(),S=g!==null&&!!this._getTagDefinition(g)?.isClosedByChild(h.name);this._pushContainer(h,S),a?this._popContainer(o,nl,m):i.type===4&&(this._popContainer(o,nl,null),this.errors.push(ur.create(o,m,`Opening tag "${o}" not terminated.`)))}_consumeComponentStartTag(i){let e=i.parts[0],t=[],o=[];this._consumeAttributesAndDirectives(t,o);let r=this._getClosestElementLikeParent(),a=this._getComponentTagName(i,r),c=this._getComponentFullName(i,r),m=this._peek.type===35;this._advance();let p=this._peek.sourceSpan.fullStart,h=new _n(i.sourceSpan.start,p,i.sourceSpan.fullStart),g=new _n(i.sourceSpan.start,p,i.sourceSpan.fullStart),S=new Va(e,a,c,t,o,[],m,h,g,void 0),x=this._getContainer(),v=x!==null&&S.tagName!==null&&!!this._getTagDefinition(x)?.isClosedByChild(S.tagName);this._pushContainer(S,v),m?this._popContainer(c,Va,h):i.type===37&&(this._popContainer(c,Va,null),this.errors.push(ur.create(c,h,`Opening tag "${c}" not terminated.`)))}_consumeAttributesAndDirectives(i,e){for(;this._peek.type===14||this._peek.type===38;)this._peek.type===38?e.push(this._consumeDirective(this._peek)):i.push(this._consumeAttr(this._advance()))}_consumeComponentEndTag(i){let e=this._getComponentFullName(i,this._getClosestElementLikeParent());if(!this._popContainer(e,Va,i.sourceSpan)){let t=this._containerStack[this._containerStack.length-1],o;t instanceof Va&&t.componentName===i.parts[0]?o=`, did you mean "${t.fullName}"?`:o=". It may happen when the tag has already been closed by another tag.";let r=`Unexpected closing tag "${e}"${o}`;this.errors.push(ur.create(e,i.sourceSpan,r))}}_getTagDefinition(i){return typeof i=="string"?this.tagDefinitionResolver(i):i instanceof nl?this.tagDefinitionResolver(i.name):i instanceof Va&&i.tagName!==null?this.tagDefinitionResolver(i.tagName):null}_pushContainer(i,e){e&&this._containerStack.pop(),this._addToParent(i),this._containerStack.push(i)}_consumeElementEndTag(i){let e=this._getElementFullName(i,this._getClosestElementLikeParent());if(this._getTagDefinition(e)?.isVoid)this.errors.push(ur.create(e,i.sourceSpan,`Void elements do not have end tags "${i.parts[1]}"`));else if(!this._popContainer(e,nl,i.sourceSpan)){let t=`Unexpected closing tag "${e}". It may happen when the tag has already been closed by another tag. For more info see https://www.w3.org/TR/html5/syntax.html#closing-elements-that-have-implied-end-tags`;this.errors.push(ur.create(e,i.sourceSpan,t))}}_popContainer(i,e,t){let o=!1;for(let r=this._containerStack.length-1;r>=0;r--){let a=this._containerStack[r];if(((a instanceof Va?a.fullName:a.name)===i||i===null)&&a instanceof e)return a.endSourceSpan=t,a.sourceSpan.end=t!==null?t.end:a.sourceSpan.end,this._containerStack.splice(r,this._containerStack.length-r),!o;(a instanceof rl||!this._getTagDefinition(a)?.closedByParent)&&(o=!0)}return!1}_consumeAttr(i){let e=Y1(i.parts[0],i.parts[1]),t=i.sourceSpan.end;this._peek.type===15&&this._advance();let o="",r=[],a,c;if(this._peek.type===16)for(a=this._peek.sourceSpan,c=this._peek.sourceSpan.end;this._peek.type===16||this._peek.type===17||this._peek.type===9;){let h=this._advance();r.push(h),h.type===17?o+=h.parts.join("").replace(/&([^;]+);/g,nF):h.type===9?o+=h.parts[0]:o+=h.parts.join(""),c=t=h.sourceSpan.end}this._peek.type===15&&(t=this._advance().sourceSpan.end);let p=a&&c&&new _n(a.start,c,a.fullStart);return new sD(e,o,new _n(i.sourceSpan.start,t,i.sourceSpan.fullStart),i.sourceSpan,p,r.length>0?r:void 0,void 0)}_consumeDirective(i){let e=[],t=i.sourceSpan.end,o=null;if(this._advance(),this._peek.type===39){for(t=this._peek.sourceSpan.end,this._advance();this._peek.type===14;)e.push(this._consumeAttr(this._advance()));this._peek.type===40?(o=this._peek.sourceSpan,this._advance()):this.errors.push(ur.create(null,i.sourceSpan,"Unterminated directive definition"))}let r=new _n(i.sourceSpan.start,t,i.sourceSpan.fullStart),a=new _n(r.start,o===null?i.sourceSpan.end:o.end,r.fullStart);return new lD(i.parts[0],e,a,r,o)}_consumeBlockOpen(i){let e=[];for(;this._peek.type===27;){let c=this._advance();e.push(new Wb(c.parts[0],c.sourceSpan))}this._peek.type===25&&this._advance();let t=this._peek.sourceSpan.fullStart,o=new _n(i.sourceSpan.start,t,i.sourceSpan.fullStart),r=new _n(i.sourceSpan.start,t,i.sourceSpan.fullStart),a=new rl(i.parts[0],e,[],o,i.sourceSpan,r);this._pushContainer(a,!1)}_consumeBlockClose(i){let e=this._containerStack.length,t=this._containerStack[e-1];if(!this._popContainer(null,rl,i.sourceSpan)){if(this._containerStack.length element? If you meant to write the \`}\` character, you should use the "}" HTML entity instead.`));return}this.errors.push(ur.create(null,i.sourceSpan,'Unexpected closing block. The block may have been closed earlier. If you meant to write the `}` character, you should use the "}" HTML entity instead.'))}}_consumeIncompleteBlock(i){let e=[];for(;this._peek.type===27;){let c=this._advance();e.push(new Wb(c.parts[0],c.sourceSpan))}let t=this._peek.sourceSpan.fullStart,o=new _n(i.sourceSpan.start,t,i.sourceSpan.fullStart),r=new _n(i.sourceSpan.start,t,i.sourceSpan.fullStart),a=new rl(i.parts[0],e,[],o,i.sourceSpan,r);this._pushContainer(a,!1),this._popContainer(null,rl,null),this.errors.push(ur.create(i.parts[0],o,`Incomplete block "${i.parts[0]}". If you meant to write the @ character, you should use the "@" HTML entity instead.`))}_consumeLet(i){let e=i.parts[0],t,o;if(this._peek.type!==30){this.errors.push(ur.create(i.parts[0],i.sourceSpan,`Invalid @let declaration "${e}". Declaration must have a value.`));return}else t=this._advance();if(this._peek.type!==31){this.errors.push(ur.create(i.parts[0],i.sourceSpan,`Unterminated @let declaration "${e}". Declaration must be terminated with a semicolon.`));return}else o=this._advance();let r=o.sourceSpan.fullStart,a=new _n(i.sourceSpan.start,r,i.sourceSpan.fullStart),c=i.sourceSpan.toString().lastIndexOf(e),m=i.sourceSpan.start.moveBy(c),p=new _n(m,i.sourceSpan.end),h=new qb(e,t.parts[0],a,p,t.sourceSpan);this._addToParent(h)}_consumeIncompleteLet(i){let e=i.parts[0]??"",t=e?` "${e}"`:"";if(e.length>0){let o=i.sourceSpan.toString().lastIndexOf(e),r=i.sourceSpan.start.moveBy(o),a=new _n(r,i.sourceSpan.end),c=new _n(i.sourceSpan.start,i.sourceSpan.start.moveBy(0)),m=new qb(e,"",i.sourceSpan,a,c);this._addToParent(m)}this.errors.push(ur.create(i.parts[0],i.sourceSpan,`Incomplete @let declaration${t}. @let declarations must be written as \`@let = ;\``))}_getContainer(){return this._containerStack.length>0?this._containerStack[this._containerStack.length-1]:null}_getClosestElementLikeParent(){for(let i=this._containerStack.length-1;i>-1;i--){let e=this._containerStack[i];if(e instanceof nl||e instanceof Va)return e}return null}_addToParent(i){let e=this._getContainer();e===null?this.rootNodes.push(i):e.children.push(i)}_getElementFullName(i,e){let t=this._getPrefix(i,e);return Y1(t,i.parts[1])}_getComponentFullName(i,e){let t=i.parts[0],o=this._getComponentTagName(i,e);return o===null?t:o.startsWith(":")?t+o:`${t}:${o}`}_getComponentTagName(i,e){let t=this._getPrefix(i,e),o=i.parts[2];return!t&&!o?null:!t&&o?o:Y1(t,o||"ng-component")}_getPrefix(i,e){let t,o;if(i.type===33||i.type===37||i.type===36?(t=i.parts[1],o=i.parts[2]):(t=i.parts[0],o=i.parts[1]),t=t||this._getTagDefinition(o)?.implicitNamespacePrefix||"",!t&&e){let r=e instanceof nl?e.name:e.tagName;if(r!==null){let a=Ql(r)[1],c=this._getTagDefinition(a);c!==null&&!c.preventNamespaceInheritance&&(t=AE(r))}}return t}};function tF(n,i){return n.length>0&&n[n.length-1]===i}function nF(n,i){return z0[i]!==void 0?z0[i]||n:/^#x[a-f0-9]+$/i.test(i)?String.fromCodePoint(parseInt(i.slice(2),16)):/^#\d+$/.test(i)?String.fromCodePoint(parseInt(i.slice(1),10)):n}var Q6="ngPreserveWhitespaces",iF=new Set(["pre","template","textarea","script","style"]),X6=` \f -\r \v\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF`,sX=new RegExp(`[^${X6}]`),lX=new RegExp(`[${X6}]{2,}`,"g");function oF(n){return n.some(i=>i.name===Q6)}function Y6(n){return n.replace(new RegExp(q6,"g")," ")}var Yb=class{preserveSignificantWhitespace;originalNodeMap;requireContext;icuExpansionDepth=0;constructor(i,e,t=!0){this.preserveSignificantWhitespace=i,this.originalNodeMap=e,this.requireContext=t}visitElement(i,e){if(iF.has(i.name)||oF(i.attrs)){let o=new nl(i.name,gc(this,i.attrs),gc(this,i.directives),i.children,i.isSelfClosing,i.sourceSpan,i.startSourceSpan,i.endSourceSpan,i.isVoid,i.i18n);return this.originalNodeMap?.set(o,i),o}let t=new nl(i.name,i.attrs,i.directives,gc(this,i.children),i.isSelfClosing,i.sourceSpan,i.startSourceSpan,i.endSourceSpan,i.isVoid,i.i18n);return this.originalNodeMap?.set(t,i),t}visitAttribute(i,e){return i.name!==Q6?i:null}visitText(i,e){let t=i.value.match(sX),o=e&&(e.prev instanceof Jp||e.next instanceof Jp);if(this.icuExpansionDepth>0&&this.preserveSignificantWhitespace)return i;if(t||o){let a=i.tokens.map(h=>h.type===5?pX(h):h);if(!this.preserveSignificantWhitespace&&a.length>0){let h=a[0];a.splice(0,1,cX(h,e));let g=a[a.length-1];a.splice(a.length-1,1,dX(g,e))}let c=Z6(i.value),m=this.preserveSignificantWhitespace?c:mX(c,e),p=new _u(m,i.sourceSpan,a,i.i18n);return this.originalNodeMap?.set(p,i),p}return null}visitComment(i,e){return i}visitExpansion(i,e){this.icuExpansionDepth++;let t;try{t=new Jp(i.switchValue,i.type,gc(this,i.cases),i.sourceSpan,i.switchValueSourceSpan,i.i18n)}finally{this.icuExpansionDepth--}return this.originalNodeMap?.set(t,i),t}visitExpansionCase(i,e){let t=new Gb(i.value,gc(this,i.expression),i.sourceSpan,i.valueSourceSpan,i.expSourceSpan);return this.originalNodeMap?.set(t,i),t}visitBlock(i,e){let t=new rl(i.name,i.parameters,gc(this,i.children),i.sourceSpan,i.nameSpan,i.startSourceSpan,i.endSourceSpan);return this.originalNodeMap?.set(t,i),t}visitBlockParameter(i,e){return i}visitLetDeclaration(i,e){return i}visitComponent(i,e){if(i.tagName&&iF.has(i.tagName)||oF(i.attrs)){let o=new Va(i.componentName,i.tagName,i.fullName,gc(this,i.attrs),gc(this,i.directives),i.children,i.isSelfClosing,i.sourceSpan,i.startSourceSpan,i.endSourceSpan,i.i18n);return this.originalNodeMap?.set(o,i),o}let t=new Va(i.componentName,i.tagName,i.fullName,i.attrs,i.directives,gc(this,i.children),i.isSelfClosing,i.sourceSpan,i.startSourceSpan,i.endSourceSpan,i.i18n);return this.originalNodeMap?.set(t,i),t}visitDirective(i,e){return i}visit(i,e){if(this.requireContext&&!e)throw new Error("WhitespaceVisitor requires context. Visit via `visitAllWithSiblings` to get this context.");return!1}};function cX(n,i){return n.type!==5||!!i?.prev?n:K6(n,t=>t.trimStart())}function dX(n,i){return n.type!==5||!!i?.next?n:K6(n,t=>t.trimEnd())}function mX(n,i){let e=!i?.prev,t=!i?.next,o=e?n.trimStart():n;return t?o.trimEnd():o}function pX({type:n,parts:i,sourceSpan:e}){return{type:n,parts:[Z6(i[0])],sourceSpan:e}}function K6({type:n,parts:i,sourceSpan:e},t){return{type:n,parts:[t(i[0])],sourceSpan:e}}function Z6(n){return Y6(n).replace(lX," ")}function gc(n,i){let e=[];return i.forEach((t,o)=>{let r={prev:i[o-1],next:i[o+1]},a=t.visit(n,r);a&&e.push(a)}),e}var un=(function(n){return n[n.Character=0]="Character",n[n.Identifier=1]="Identifier",n[n.PrivateIdentifier=2]="PrivateIdentifier",n[n.Keyword=3]="Keyword",n[n.String=4]="String",n[n.Operator=5]="Operator",n[n.Number=6]="Number",n[n.RegExpBody=7]="RegExpBody",n[n.RegExpFlags=8]="RegExpFlags",n[n.Error=9]="Error",n})(un||{}),eu=(function(n){return n[n.Plain=0]="Plain",n[n.TemplateLiteralPart=1]="TemplateLiteralPart",n[n.TemplateLiteralEnd=2]="TemplateLiteralEnd",n})(eu||{}),uX=["var","let","as","null","undefined","true","false","if","else","this","typeof","void","in","instanceof"],$0=class{tokenize(i){return new hD(i).scan()}},Vs=class{index;end;type;numValue;strValue;constructor(i,e,t,o,r){this.index=i,this.end=e,this.type=t,this.numValue=o,this.strValue=r}isCharacter(i){return this.type===un.Character&&this.numValue===i}isNumber(){return this.type===un.Number}isString(){return this.type===un.String}isOperator(i){return this.type===un.Operator&&this.strValue===i}isIdentifier(){return this.type===un.Identifier}isPrivateIdentifier(){return this.type===un.PrivateIdentifier}isKeyword(){return this.type===un.Keyword}isKeywordLet(){return this.type===un.Keyword&&this.strValue==="let"}isKeywordAs(){return this.type===un.Keyword&&this.strValue==="as"}isKeywordNull(){return this.type===un.Keyword&&this.strValue==="null"}isKeywordUndefined(){return this.type===un.Keyword&&this.strValue==="undefined"}isKeywordTrue(){return this.type===un.Keyword&&this.strValue==="true"}isKeywordFalse(){return this.type===un.Keyword&&this.strValue==="false"}isKeywordThis(){return this.type===un.Keyword&&this.strValue==="this"}isKeywordTypeof(){return this.type===un.Keyword&&this.strValue==="typeof"}isKeywordVoid(){return this.type===un.Keyword&&this.strValue==="void"}isKeywordIn(){return this.type===un.Keyword&&this.strValue==="in"}isKeywordInstanceOf(){return this.type===un.Keyword&&this.strValue==="instanceof"}isError(){return this.type===un.Error}isRegExpBody(){return this.type===un.RegExpBody}isRegExpFlags(){return this.type===un.RegExpFlags}toNumber(){return this.type===un.Number?this.numValue:-1}isTemplateLiteralPart(){return this.isString()&&this.kind===eu.TemplateLiteralPart}isTemplateLiteralEnd(){return this.isString()&&this.kind===eu.TemplateLiteralEnd}isTemplateLiteralInterpolationStart(){return this.isOperator("${")}toString(){switch(this.type){case un.Character:case un.Identifier:case un.Keyword:case un.Operator:case un.PrivateIdentifier:case un.String:case un.Error:case un.RegExpBody:case un.RegExpFlags:return this.strValue;case un.Number:return this.numValue.toString();default:return null}}},U_=class extends Vs{kind;constructor(i,e,t,o){super(i,e,un.String,0,t),this.kind=o}};function D_(n,i,e){return new Vs(n,i,un.Character,e,String.fromCharCode(e))}function hX(n,i,e){return new Vs(n,i,un.Identifier,0,e)}function fX(n,i,e){return new Vs(n,i,un.PrivateIdentifier,0,e)}function gX(n,i,e){return new Vs(n,i,un.Keyword,0,e)}function cm(n,i,e){return new Vs(n,i,un.Operator,0,e)}function _X(n,i,e){return new Vs(n,i,un.Number,e,"")}function vX(n,i,e){return new Vs(n,i,un.Error,0,e)}function CX(n,i,e){return new Vs(n,i,un.RegExpBody,0,e)}function bX(n,i,e){return new Vs(n,i,un.RegExpFlags,0,e)}var P_=new Vs(-1,-1,un.Character,0,""),hD=class{input;tokens=[];length;peek=0;index=-1;braceStack=[];constructor(i){this.input=i,this.length=i.length,this.advance()}scan(){let i=this.scanToken();for(;i!==null;)this.tokens.push(i),i=this.scanToken();return this.tokens}advance(){this.peek=++this.index>=this.length?qr:this.input.charCodeAt(this.index)}scanToken(){let i=this.input,e=this.length,t=this.peek,o=this.index;for(;t<=g6;)if(++o>=e){t=qr;break}else t=i.charCodeAt(o);if(this.peek=t,this.index=o,o>=e)return null;if(rF(t))return this.scanIdentifier();if(ol(t))return this.scanNumber(o);let r=o;switch(t){case zp:return this.advance(),ol(this.peek)?this.scanNumber(r):this.peek!==zp?D_(r,this.index,zp):(this.advance(),this.peek===zp?(this.advance(),cm(r,this.index,"...")):this.error(`Unexpected character [${String.fromCharCode(t)}]`,0));case $a:case yr:case Sc:case bd:case ya:case bc:case rs:return this.scanCharacter(r,t);case al:return this.scanOpenBrace(r,t);case za:return this.scanCloseBrace(r,t);case k0:case M0:return this.scanString();case UE:return this.advance(),this.scanTemplateLiteralPart(r);case _6:return this.scanPrivateIdentifier();case v6:return this.scanComplexOperator(r,"+",Gr,"=");case Pb:return this.scanComplexOperator(r,"-",Gr,"=");case il:return this.isStartOfRegex()?this.scanRegex(o):this.scanComplexOperator(r,"/",Gr,"=");case dW:return this.scanComplexOperator(r,"%",Gr,"=");case fW:return this.scanOperator(r,"^");case MR:return this.scanStar(r);case kR:return this.scanQuestion(r);case jh:case Ps:return this.scanComplexOperator(r,String.fromCharCode(t),Gr,"=");case HE:return this.scanComplexOperator(r,"!",Gr,"=",Gr,"=");case Gr:return this.scanEquals(r);case Db:return this.scanComplexOperator(r,"&",Db,"&",Gr,"=");case TR:return this.scanComplexOperator(r,"|",TR,"|",Gr,"=");case k6:for(;T0(this.peek);)this.advance();return this.scanToken()}return this.advance(),this.error(`Unexpected character [${String.fromCharCode(t)}]`,0)}scanCharacter(i,e){return this.advance(),D_(i,this.index,e)}scanOperator(i,e){return this.advance(),cm(i,this.index,e)}scanOpenBrace(i,e){return this.braceStack.push("expression"),this.advance(),D_(i,this.index,e)}scanCloseBrace(i,e){return this.advance(),this.braceStack.pop()==="interpolation"?(this.tokens.push(D_(i,this.index,za)),this.scanTemplateLiteralPart(this.index)):D_(i,this.index,e)}scanComplexOperator(i,e,t,o,r,a){this.advance();let c=e;return this.peek==t&&(this.advance(),c+=o),r!=null&&this.peek==r&&(this.advance(),c+=a),cm(i,this.index,c)}scanEquals(i){this.advance();let e="=";if(this.peek===Gr)this.advance(),e+="=";else if(this.peek===Ps)return this.advance(),e+=">",cm(i,this.index,e);return this.peek===Gr&&(this.advance(),e+="="),cm(i,this.index,e)}scanIdentifier(){let i=this.index;for(this.advance();aF(this.peek);)this.advance();let e=this.input.substring(i,this.index);return uX.indexOf(e)>-1?gX(i,this.index,e):hX(i,this.index,e)}scanPrivateIdentifier(){let i=this.index;if(this.advance(),!rF(this.peek))return this.error("Invalid character [#]",-1);for(;aF(this.peek);)this.advance();let e=this.input.substring(i,this.index);return fX(i,this.index,e)}scanNumber(i){let e=this.index===i,t=!1;for(this.advance();;){if(!ol(this.peek))if(this.peek===Im){if(!ol(this.input.charCodeAt(this.index-1))||!ol(this.input.charCodeAt(this.index+1)))return this.error("Invalid numeric separator",0);t=!0}else if(this.peek===zp)e=!1;else if(xX(this.peek)){if(this.advance(),yX(this.peek)&&this.advance(),!ol(this.peek))return this.error("Invalid exponent",-1);e=!1}else break;this.advance()}let o=this.input.substring(i,this.index);t&&(o=o.replace(/_/g,""));let r=e?wX(o):parseFloat(o);return _X(i,this.index,r)}scanString(){let i=this.index,e=this.peek;this.advance();let t="",o=this.index,r=this.input;for(;this.peek!=e;)if(this.peek==Zp){let c=this.scanStringBackslash(t,o);if(typeof c!="string")return c;t=c,o=this.index}else{if(this.peek==qr)return this.error("Unterminated quote",0);this.advance()}let a=r.substring(o,this.index);return this.advance(),new U_(i,this.index,t+a,eu.Plain)}scanQuestion(i){this.advance();let e="?";return this.peek===kR?(e+="?",this.advance(),this.peek===Gr&&(e+="=",this.advance())):this.peek===zp&&(e+=".",this.advance()),cm(i,this.index,e)}scanTemplateLiteralPart(i){let e="",t=this.index;for(;this.peek!==UE;)if(this.peek===Zp){let r=this.scanStringBackslash(e,t);if(typeof r!="string")return r;e=r,t=this.index}else if(this.peek===dx){let r=this.index;if(this.advance(),this.peek===al)return this.braceStack.push("interpolation"),this.tokens.push(new U_(i,r,e+this.input.substring(t,r),eu.TemplateLiteralPart)),this.advance(),cm(r,this.index,this.input.substring(r,this.index))}else{if(this.peek===qr)return this.error("Unterminated template literal",0);this.advance()}let o=this.input.substring(t,this.index);return this.advance(),new U_(i,this.index,e+o,eu.TemplateLiteralEnd)}error(i,e){let t=this.index+e;return vX(t,this.index,`Lexer Error: ${i} at column ${t} in expression [${this.input}]`)}scanStringBackslash(i,e){i+=this.input.substring(e,this.index);let t;if(this.advance(),this.peek===S6){let o=this.input.substring(this.index+1,this.index+5);if(/^[0-9a-f]+$/i.test(o))t=parseInt(o,16);else return this.error(`Invalid unicode escape [\\u${o}]`,0);for(let r=0;r<5;r++)this.advance()}else t=SX(this.peek),this.advance();return i+=String.fromCharCode(t),i}scanStar(i){this.advance();let e="*";return this.peek===MR?(e+="*",this.advance(),this.peek===Gr&&(e+="=",this.advance())):this.peek===Gr&&(e+="=",this.advance()),cm(i,this.index,e)}isStartOfRegex(){if(this.tokens.length===0)return!0;let i=this.tokens[this.tokens.length-1];if(i.isOperator("!")){let e=this.tokens.length>1?this.tokens[this.tokens.length-2]:null;return e===null||e.type!==un.Identifier&&!e.isCharacter(yr)&&!e.isCharacter(bd)}return i.type===un.Operator||i.isCharacter($a)||i.isCharacter(Sc)||i.isCharacter(ya)||i.isCharacter(bc)}scanRegex(i){this.advance();let e=this.index,t=!1,o=!1;for(;;){let m=this.peek;if(m===qr)return this.error("Unterminated regular expression",0);if(t)t=!1;else if(m===Zp)t=!0;else if(m===Sc)o=!0;else if(m===bd)o=!1;else if(m===il&&!o)break;this.advance()}let r=this.input.substring(e,this.index);this.advance();let a=CX(i,this.index,r),c=this.scanRegexFlags(this.index);return c!==null?(this.tokens.push(a),c):a}scanRegexFlags(i){if(!Mm(this.peek))return null;for(;Mm(this.peek);)this.advance();return bX(i,this.index,this.input.substring(i,this.index))}};function rF(n){return pu<=n&&n<=Y0||Pm<=n&&n<=df||n==Im||n==dx}function aF(n){return Mm(n)||ol(n)||n==Im||n==dx}function xX(n){return n==_W||n==pW}function yX(n){return n==Pb||n==v6}function SX(n){switch(n){case b6:return Kp;case oP:return f6;case x6:return nP;case y6:return tP;case w6:return h6;default:return n}}function wX(n){let i=parseInt(n);if(isNaN(i))throw new Error("Invalid integer literal when parsing "+n);return i}var fD=class{strings;expressions;offsets;constructor(i,e,t){this.strings=i,this.expressions=e,this.offsets=t}},gD=class{templateBindings;warnings;errors;constructor(i,e,t){this.templateBindings=i,this.warnings=e,this.errors=t}};function hm(n){return n.start.toString()||"(unknown)"}var Kb=class{_lexer;_supportsDirectPipeReferences;constructor(i,e=!1){this._lexer=i,this._supportsDirectPipeReferences=e}parseAction(i,e,t){let o=[];this._checkNoInterpolation(o,i,e);let{stripped:r}=this._stripComments(i),a=this._lexer.tokenize(r),c=new Up(i,e,t,a,1,o,0,this._supportsDirectPipeReferences).parseChain();return new as(c,i,hm(e),t,o)}parseBinding(i,e,t){let o=[],r=this._parseBindingAst(i,e,t,o);return new as(r,i,hm(e),t,o)}checkSimpleExpression(i){let e=new _D;return i.visit(e),e.errors}parseSimpleBinding(i,e,t){let o=[],r=this._parseBindingAst(i,e,t,o),a=this.checkSimpleExpression(r);return a.length>0&&o.push(Bh(`Host binding expression cannot contain ${a.join(" ")}`,i,"",e)),new as(r,i,hm(e),t,o)}_parseBindingAst(i,e,t,o){this._checkNoInterpolation(o,i,e);let{stripped:r}=this._stripComments(i),a=this._lexer.tokenize(r);return new Up(i,e,t,a,0,o,0,this._supportsDirectPipeReferences).parseChain()}parseTemplateBindings(i,e,t,o,r){let a=this._lexer.tokenize(e),c=[];return new Up(e,t,r,a,0,c,0,this._supportsDirectPipeReferences).parseTemplateBindings({source:i,span:new As(o,o+i.length)})}parseInterpolation(i,e,t,o){let r=[],{strings:a,expressions:c,offsets:m}=this.splitInterpolation(i,e,r,o);if(c.length===0)return null;let p=[];for(let h=0;hh.text),p,i,hm(e),t,r)}parseInterpolationExpression(i,e,t){let{stripped:o}=this._stripComments(i),r=this._lexer.tokenize(o),a=[],c=new Up(i,e,t,r,0,a,0,this._supportsDirectPipeReferences).parseChain(),m=["",""];return this.createInterpolationAst(m,[c],i,hm(e),t,a)}createInterpolationAst(i,e,t,o,r,a){let c=new lu(0,t.length),m=new Q0(c,c.toAbsolute(r),i,e);return new as(m,t,o,r,a)}splitInterpolation(i,e,t,o){let r=[],a=[],c=[],m=o?MX(o):null,p=0,h=!1,g=!1,S="{{",x="}}";for(;p-1)break;o>-1&&r>-1&&i.push(Bh("Got interpolation ({{}}) where expression was expected",e,`at column ${o} in`,t))}_getInterpolationEndIndex(i,e,t){for(let o of this._forEachUnquotedChar(i,t)){if(i.startsWith(e,o))return o;if(i.startsWith("//",o))return i.indexOf(e,o)}return-1}*_forEachUnquotedChar(i,e){let t=null,o=0;for(let r=e;r=this.tokens.length}get inputIndex(){return this.atEOF?this.currentEndIndex:this.next.index+this.offset}get currentEndIndex(){return this.index>0?this.peek(-1).end+this.offset:this.tokens.length===0?this.input.length+this.offset:this.next.index+this.offset}get currentAbsoluteOffset(){return this.absoluteOffset+this.inputIndex}span(i,e){let t=this.currentEndIndex;if(e!==void 0&&e>this.currentEndIndex&&(t=e),i>t){let o=t;t=i,i=o}return new lu(i,t)}sourceSpan(i,e){let t=`${i}@${this.inputIndex}:${e}`;return this.sourceSpanCache.has(t)||this.sourceSpanCache.set(t,this.span(i,e).toAbsolute(this.absoluteOffset)),this.sourceSpanCache.get(t)}advance(){this.index++}withContext(i,e){this.context|=i;let t=e();return this.context^=i,t}consumeOptionalCharacter(i){return this.next.isCharacter(i)?(this.advance(),!0):!1}peekKeywordLet(){return this.next.isKeywordLet()}peekKeywordAs(){return this.next.isKeywordAs()}expectCharacter(i){this.consumeOptionalCharacter(i)||this.error(`Missing expected ${String.fromCharCode(i)}`)}consumeOptionalOperator(i){return this.next.isOperator(i)?(this.advance(),!0):!1}isAssignmentOperator(i){return i.type===un.Operator&&Ba.isAssignmentOperation(i.strValue)}expectOperator(i){this.consumeOptionalOperator(i)||this.error(`Missing expected operator ${i}`)}prettyPrintToken(i){return i===P_?"end of input":`token ${i}`}expectIdentifierOrKeyword(){let i=this.next;return!i.isIdentifier()&&!i.isKeyword()?(i.isPrivateIdentifier()?this._reportErrorForPrivateIdentifier(i,"expected identifier or keyword"):this.error(`Unexpected ${this.prettyPrintToken(i)}, expected identifier or keyword`),null):(this.advance(),i.toString())}expectIdentifierOrKeywordOrString(){let i=this.next;return!i.isIdentifier()&&!i.isKeyword()&&!i.isString()?(i.isPrivateIdentifier()?this._reportErrorForPrivateIdentifier(i,"expected identifier, keyword or string"):this.error(`Unexpected ${this.prettyPrintToken(i)}, expected identifier, keyword, or string`),""):(this.advance(),i.toString())}parseChain(){let i=[],e=this.inputIndex;for(;this.index=Pm&&p<=df?X1.ReferencedDirectly:X1.ReferencedByName}else m=X1.ReferencedByName;e=new hb(this.span(i),this.sourceSpan(i,a),e,o,c,m,r)}while(this.consumeOptionalOperator("|"))}return e}parseExpression(){return this.parseConditional()}parseConditional(){let i=this.inputIndex,e=this.parseLogicalOr();if(this.consumeOptionalOperator("?")){let t=this.parsePipe(),o;if(this.consumeOptionalCharacter(bc))o=this.parsePipe();else{let r=this.inputIndex,a=this.input.substring(i,r);this.error(`Conditional expression ${a} requires all 3 expressions`),o=new xa(this.span(i),this.sourceSpan(i))}return new ub(this.span(i),this.sourceSpan(i),e,t,o)}else return e}parseLogicalOr(){let i=this.inputIndex,e=this.parseLogicalAnd();for(;this.consumeOptionalOperator("||");){let t=this.parseLogicalAnd();e=new Ba(this.span(i),this.sourceSpan(i),"||",e,t)}return e}parseLogicalAnd(){let i=this.inputIndex,e=this.parseNullishCoalescing();for(;this.consumeOptionalOperator("&&");){let t=this.parseNullishCoalescing();e=new Ba(this.span(i),this.sourceSpan(i),"&&",e,t)}return e}parseNullishCoalescing(){let i=this.inputIndex,e=this.parseEquality();for(;this.consumeOptionalOperator("??");){let t=this.parseEquality();e=new Ba(this.span(i),this.sourceSpan(i),"??",e,t)}return e}parseEquality(){let i=this.inputIndex,e=this.parseRelational();for(;this.next.type==un.Operator;){let t=this.next.strValue;switch(t){case"==":case"===":case"!=":case"!==":this.advance();let o=this.parseRelational();e=new Ba(this.span(i),this.sourceSpan(i),t,e,o);continue}break}return e}parseRelational(){let i=this.inputIndex,e=this.parseAdditive();for(;this.next.type==un.Operator||this.next.isKeywordIn()||this.next.isKeywordInstanceOf();){let t=this.next.strValue;switch(t){case"<":case">":case"<=":case">=":case"in":case"instanceof":this.advance();let o=this.parseAdditive();e=new Ba(this.span(i),this.sourceSpan(i),t,e,o);continue}break}return e}parseAdditive(){let i=this.inputIndex,e=this.parseMultiplicative();for(;this.next.type==un.Operator;){let t=this.next.strValue;switch(t){case"+":case"-":this.advance();let o=this.parseMultiplicative();e=new Ba(this.span(i),this.sourceSpan(i),t,e,o);continue}break}return e}parseMultiplicative(){let i=this.inputIndex,e=this.parseExponentiation();for(;this.next.type==un.Operator;){let t=this.next.strValue;switch(t){case"*":case"%":case"/":this.advance();let o=this.parseExponentiation();e=new Ba(this.span(i),this.sourceSpan(i),t,e,o);continue}break}return e}parseExponentiation(){let i=this.inputIndex,e=this.parsePrefix();for(;this.next.type==un.Operator&&this.next.strValue==="**";){(e instanceof zh||e instanceof l0||e instanceof c0||e instanceof d0)&&this.error("Unary operator used immediately before exponentiation expression. Parenthesis must be used to disambiguate operator precedence"),this.advance();let t=this.parseExponentiation();e=new Ba(this.span(i),this.sourceSpan(i),"**",e,t)}return e}parsePrefix(){if(this.next.type==un.Operator){let i=this.inputIndex,e=this.next.strValue,t;switch(e){case"+":return this.advance(),t=this.parsePrefix(),zh.createPlus(this.span(i),this.sourceSpan(i),t);case"-":return this.advance(),t=this.parsePrefix(),zh.createMinus(this.span(i),this.sourceSpan(i),t);case"!":return this.advance(),t=this.parsePrefix(),new l0(this.span(i),this.sourceSpan(i),t)}}else if(this.next.isKeywordTypeof()){let i=this.inputIndex;this.advance();let e=this.parsePrefix();return new c0(this.span(i),this.sourceSpan(i),e)}else if(this.next.isKeywordVoid()){let i=this.inputIndex;this.advance();let e=this.parsePrefix();return new d0(this.span(i),this.sourceSpan(i),e)}return this.parseCallChain()}parseCallChain(){let i=this.inputIndex,e=this.parsePrimary();for(;;)if(this.consumeOptionalCharacter(zp))e=this.parseAccessMember(e,i,!1);else if(this.consumeOptionalOperator("?."))this.consumeOptionalCharacter($a)?e=this.parseCall(e,i,!0):e=this.consumeOptionalCharacter(Sc)?this.parseKeyedReadOrWrite(e,i,!0):this.parseAccessMember(e,i,!0);else if(this.consumeOptionalCharacter(Sc))e=this.parseKeyedReadOrWrite(e,i,!1);else if(this.consumeOptionalCharacter($a))e=this.parseCall(e,i,!1);else if(this.consumeOptionalOperator("!"))e=new m0(this.span(i),this.sourceSpan(i),e);else if(this.next.isTemplateLiteralEnd())e=this.parseNoInterpolationTaggedTemplateLiteral(e,i);else if(this.next.isTemplateLiteralPart())e=this.parseTaggedTemplateLiteral(e,i);else return e}parsePrimary(){let i=this.inputIndex;if(this.isArrowFunction())return this.parseArrowFunction(i);if(this.consumeOptionalCharacter($a)){this.rparensExpected++;let e=this.parsePipe();return this.consumeOptionalCharacter(yr)||(this.error("Missing closing parentheses"),this.consumeOptionalCharacter(yr)),this.rparensExpected--,new h0(this.span(i),this.sourceSpan(i),e)}else{if(this.next.isKeywordNull())return this.advance(),new os(this.span(i),this.sourceSpan(i),null);if(this.next.isKeywordUndefined())return this.advance(),new os(this.span(i),this.sourceSpan(i),void 0);if(this.next.isKeywordTrue())return this.advance(),new os(this.span(i),this.sourceSpan(i),!0);if(this.next.isKeywordFalse())return this.advance(),new os(this.span(i),this.sourceSpan(i),!1);if(this.next.isKeywordIn())return this.advance(),new os(this.span(i),this.sourceSpan(i),"in");if(this.next.isKeywordThis())return this.advance(),new o0(this.span(i),this.sourceSpan(i));if(this.consumeOptionalCharacter(Sc))return this.parseLiteralArray(i);if(this.next.isCharacter(al))return this.parseLiteralMap();if(this.next.isIdentifier())return this.parseAccessMember(new Ec(this.span(i),this.sourceSpan(i)),i,!1);if(this.next.isNumber()){let e=this.next.toNumber();return this.advance(),new os(this.span(i),this.sourceSpan(i),e)}else{if(this.next.isTemplateLiteralEnd())return this.parseNoInterpolationTemplateLiteral();if(this.next.isTemplateLiteralPart())return this.parseTemplateLiteral();if(this.next.isString()&&this.next.kind===eu.Plain){let e=this.next.toString();return this.advance(),new os(this.span(i),this.sourceSpan(i),e)}else return this.next.isPrivateIdentifier()?(this._reportErrorForPrivateIdentifier(this.next,null),new xa(this.span(i),this.sourceSpan(i))):this.next.isRegExpBody()?this.parseRegularExpressionLiteral():this.index>=this.tokens.length?(this.error(`Unexpected end of expression: ${this.input}`),new xa(this.span(i),this.sourceSpan(i))):(this.error(`Unexpected token ${this.next}`),new xa(this.span(i),this.sourceSpan(i)))}}}parseLiteralArray(i){this.rbracketsExpected++;let e=[];do if(this.next.isOperator("..."))e.push(this.parseSpreadElement());else if(!this.next.isCharacter(bd))e.push(this.parsePipe());else break;while(this.consumeOptionalCharacter(ya));return this.rbracketsExpected--,this.expectCharacter(bd),new s0(this.span(i),this.sourceSpan(i),e)}parseLiteralMap(){let i=[],e=[],t=this.inputIndex;if(this.expectCharacter(al),!this.consumeOptionalCharacter(za)){this.rbracesExpected++;do{let o=this.inputIndex;if(this.next.isOperator("...")){this.advance(),i.push({kind:"spread",span:this.span(o),sourceSpan:this.sourceSpan(o)}),e.push(this.parsePipe());continue}let r=this.next.isString(),a=this.expectIdentifierOrKeywordOrString(),c=this.span(o),m=this.sourceSpan(o),p={kind:"property",key:a,quoted:r,span:c,sourceSpan:m};i.push(p),r?(this.expectCharacter(bc),e.push(this.parsePipe())):this.consumeOptionalCharacter(bc)?e.push(this.parsePipe()):(p.isShorthandInitialized=!0,e.push(new yc(c,m,m,new Ec(c,m),a)))}while(this.consumeOptionalCharacter(ya)&&!this.next.isCharacter(za));this.rbracesExpected--,this.expectCharacter(za)}return new du(this.span(t),this.sourceSpan(t),i,e)}parseAccessMember(i,e,t){let o=this.inputIndex,r=this.withContext(V_.Writable,()=>{let c=this.expectIdentifierOrKeyword()??"";return c.length===0&&this.error("Expected identifier for property access",i.span.end),c}),a=this.sourceSpan(o);if(t)return this.isAssignmentOperator(this.next)?(this.advance(),this.error("The '?.' operator cannot be used in the assignment"),new xa(this.span(e),this.sourceSpan(e))):new r0(this.span(e),this.sourceSpan(e),a,i,r);if(this.isAssignmentOperator(this.next)){let c=this.next.strValue;if(!(this.parseFlags&1))return this.advance(),this.error("Bindings cannot contain assignments"),new xa(this.span(e),this.sourceSpan(e));let m=new yc(this.span(e),this.sourceSpan(e),a,i,r);this.advance();let p=this.parseConditional();return new Ba(this.span(e),this.sourceSpan(e),c,m,p)}else return new yc(this.span(e),this.sourceSpan(e),a,i,r)}parseCall(i,e,t){let o=this.inputIndex;this.rparensExpected++;let r=this.parseCallArguments(),a=this.span(o,this.inputIndex).toAbsolute(this.absoluteOffset);this.expectCharacter(yr),this.rparensExpected--;let c=this.span(e),m=this.sourceSpan(e);return t?new gb(c,m,i,r,a):new Qh(c,m,i,r,a)}parseCallArguments(){if(this.next.isCharacter(yr))return[];let i=[];do i.push(this.next.isOperator("...")?this.parseSpreadElement():this.parsePipe());while(this.consumeOptionalCharacter(ya));return i}parseSpreadElement(){this.next.isOperator("...")||this.error("Spread element must start with '...' operator");let i=this.inputIndex;this.advance();let e=this.parsePipe(),t=this.span(i),o=this.sourceSpan(i);return new fb(t,o,e)}expectTemplateBindingKey(){let i="",e=!1,t=this.currentAbsoluteOffset;do i+=this.expectIdentifierOrKeywordOrString(),e=this.consumeOptionalOperator("-"),e&&(i+="-");while(e);return{source:i,span:new As(t,t+i.length)}}parseTemplateBindings(i){let e=[];for(e.push(...this.parseDirectiveKeywordBindings(i));this.index{this.rbracketsExpected++;let o=this.parsePipe();if(o instanceof xa&&this.error("Key access cannot be empty"),this.rbracketsExpected--,this.expectCharacter(bd),this.isAssignmentOperator(this.next)){let r=this.next.strValue;if(t)this.advance(),this.error("The '?.' operator cannot be used in the assignment");else{let a=new cu(this.span(e),this.sourceSpan(e),i,o);this.advance();let c=this.parseConditional();return new Ba(this.span(e),this.sourceSpan(e),r,a,c)}}else return t?new a0(this.span(e),this.sourceSpan(e),i,o):new cu(this.span(e),this.sourceSpan(e),i,o);return new xa(this.span(e),this.sourceSpan(e))})}parseDirectiveKeywordBindings(i){let e=[];this.consumeOptionalCharacter(bc);let t=this.getDirectiveBoundTarget(),o=this.currentAbsoluteOffset,r=this.parseAsBinding(i);r||(this.consumeStatementTerminator(),o=this.currentAbsoluteOffset);let a=new As(i.span.start,o);return e.push(new DE(a,i,t)),r&&e.push(r),e}getDirectiveBoundTarget(){if(this.next===P_||this.peekKeywordAs()||this.peekKeywordLet())return null;let i=this.parsePipe(),{start:e,end:t}=i.span,o=this.input.substring(e,t);return new as(i,o,hm(this.parseSourceSpan),this.absoluteOffset+e,this.errors)}parseAsBinding(i){if(!this.peekKeywordAs())return null;this.advance();let e=this.expectTemplateBindingKey();this.consumeStatementTerminator();let t=new As(i.span.start,this.currentAbsoluteOffset);return new f0(t,e,i)}parseLetBinding(){if(!this.peekKeywordLet())return null;let i=this.currentAbsoluteOffset;this.advance();let e=this.expectTemplateBindingKey(),t=null;this.consumeOptionalOperator("=")&&(t=this.expectTemplateBindingKey()),this.consumeStatementTerminator();let o=new As(i,this.currentAbsoluteOffset);return new f0(o,e,t)}parseNoInterpolationTaggedTemplateLiteral(i,e){let t=this.parseNoInterpolationTemplateLiteral();return new p0(this.span(e),this.sourceSpan(e),i,t)}parseNoInterpolationTemplateLiteral(){let i=this.next.strValue,e=this.inputIndex;this.advance();let t=this.span(e),o=this.sourceSpan(e);return new u0(t,o,[new _b(t,o,i)],[])}parseTaggedTemplateLiteral(i,e){let t=this.parseTemplateLiteral();return new p0(this.span(e),this.sourceSpan(e),i,t)}parseTemplateLiteral(){let i=[],e=[],t=this.inputIndex;for(;this.next!==P_;){let o=this.next;if(o.isTemplateLiteralPart()||o.isTemplateLiteralEnd()){let r=this.inputIndex;if(this.advance(),i.push(new _b(this.span(r),this.sourceSpan(r),o.strValue)),o.isTemplateLiteralEnd())break}else if(o.isTemplateLiteralInterpolationStart()){this.advance(),this.rbracesExpected++;let r=this.parsePipe();r instanceof xa?this.error("Template literal interpolation cannot be empty"):e.push(r),this.rbracesExpected--}else this.advance()}return new u0(this.span(t),this.sourceSpan(t),i,e)}parseRegularExpressionLiteral(){let i=this.next;if(this.advance(),!i.isRegExpBody())return new xa(this.span(this.inputIndex),this.sourceSpan(this.inputIndex));let e=null;if(this.next.isRegExpFlags()){e=this.next,this.advance();let r=new Set;for(let a=0;a`"${m}"`).join(", "),e.index+a)}}let t=i.index,o=e?e.end:i.end;return new Cb(this.span(t,o),this.sourceSpan(t,o),i.strValue,e?e.strValue:null)}parseArrowFunction(i){let e;if(this.next.isIdentifier()){let o=this.next;this.advance(),e=[this.getArrowFunctionIdentifierArg(o)]}else this.next.isCharacter($a)?(this.rparensExpected++,this.advance(),e=this.parseArrowFunctionParameters(),this.rparensExpected--):(e=[],this.error(`Unexpected token ${this.next}`));this.expectOperator("=>");let t;if(this.next.isCharacter(al))this.error("Multi-line arrow functions are not supported. If you meant to return an object literal, wrap it with parentheses."),t=new xa(this.span(i),this.sourceSpan(i));else{let o=this.parseFlags;this.parseFlags=1,t=this.parseExpression(),this.parseFlags=o}return new vb(this.span(i),this.sourceSpan(i),e,t)}parseArrowFunctionParameters(){let i=[];if(!this.consumeOptionalCharacter(yr))for(;this.next!==P_;)if(this.next.isIdentifier()){let e=this.next;if(this.advance(),i.push(this.getArrowFunctionIdentifierArg(e)),this.consumeOptionalCharacter(yr))break;this.expectCharacter(ya)}else{this.error(`Unexpected token ${this.next}`);break}return i}getArrowFunctionIdentifierArg(i){return new EE(i.strValue,this.span(i.index),this.sourceSpan(i.index))}isArrowFunction(){let i=this.index,e=this.tokens;if(i>e.length-2)return!1;if(e[i].isIdentifier()&&e[i+1].isOperator("=>"))return!0;if(e[i].isCharacter($a)){let t=i+1;for(t;t")}return!1}consumeStatementTerminator(){this.consumeOptionalCharacter(rs)||this.consumeOptionalCharacter(ya)}error(i,e=this.index){this.errors.push(Bh(i,this.input,this.getErrorLocationText(e),this.parseSourceSpan)),this.skip()}getErrorLocationText(i){return i0&&(e=` ${e} `);let o=hm(t),r=`Parser Error: ${n}${e}[${i}] in ${o}`;return new rn(t,r)}var _D=class extends Xh{errors=[];visitPipe(){this.errors.push("pipes")}};function MX(n){let i=new Map,e=0,t=0,o=0;for(;oc+m.length,0);t+=a,e+=a}i.set(t,e),o++}return i}function kX(n){return n.visit(new vD)}var vD=class{visitUnary(i,e){return`${i.operator}${i.expr.visit(this,e)}`}visitBinary(i,e){return`${i.left.visit(this,e)} ${i.operation} ${i.right.visit(this,e)}`}visitChain(i,e){return i.expressions.map(t=>t.visit(this,e)).join("; ")}visitConditional(i,e){return`${i.condition.visit(this,e)} ? ${i.trueExp.visit(this,e)} : ${i.falseExp.visit(this,e)}`}visitThisReceiver(){return"this"}visitImplicitReceiver(){return""}visitInterpolation(i,e){return EX(i.strings,i.expressions.map(t=>t.visit(this,e))).join("")}visitKeyedRead(i,e){return`${i.receiver.visit(this,e)}[${i.key.visit(this,e)}]`}visitLiteralArray(i,e){return`[${i.expressions.map(t=>t.visit(this,e)).join(", ")}]`}visitLiteralMap(i,e){return`{${TX(i.keys.map(t=>t.kind==="spread"?"...":t.quoted?`'${t.key}'`:t.key),i.values.map(t=>t.visit(this,e))).map(([t,o])=>`${t}: ${o}`).join(", ")}}`}visitLiteralPrimitive(i){if(i.value===null)return"null";switch(typeof i.value){case"number":case"boolean":return i.value.toString();case"undefined":return"undefined";case"string":return`'${i.value.replace(/'/g,"\\'")}'`;default:throw new Error(`Unsupported primitive type: ${i.value}`)}}visitPipe(i,e){return`${i.exp.visit(this,e)} | ${i.name}`}visitPrefixNot(i,e){return`!${i.expression.visit(this,e)}`}visitNonNullAssert(i,e){return`${i.expression.visit(this,e)}!`}visitPropertyRead(i,e){return i.receiver instanceof Ec||i.receiver instanceof o0?i.name:`${i.receiver.visit(this,e)}.${i.name}`}visitSafePropertyRead(i,e){return`${i.receiver.visit(this,e)}?.${i.name}`}visitSafeKeyedRead(i,e){return`${i.receiver.visit(this,e)}?.[${i.key.visit(this,e)}]`}visitCall(i,e){return`${i.receiver.visit(this,e)}(${i.args.map(t=>t.visit(this,e)).join(", ")})`}visitSafeCall(i,e){return`${i.receiver.visit(this,e)}?.(${i.args.map(t=>t.visit(this,e)).join(", ")})`}visitTypeofExpression(i,e){return`typeof ${i.expression.visit(this,e)}`}visitVoidExpression(i,e){return`void ${i.expression.visit(this,e)}`}visitRegularExpressionLiteral(i,e){return`/${i.body}/${i.flags||""}`}visitArrowFunction(i,e){let t;return i.parameters.length===1?t=i.parameters[0].name:t=`(${i.parameters.map(o=>o.name).join(", ")})`,`${t} => ${i.body.visit(this,e)}`}visitASTWithSource(i,e){return i.ast.visit(this,e)}visitTemplateLiteral(i,e){let t="";for(let o=0;o[e,i[t]])}function EX(n,i){let e=[];for(let t=0;t(n.set(i,e),n),new Map),sf=class extends CD{_schema=new Map;_eventSchema=new Map;constructor(){super(),OX.forEach(i=>{let e=new Map,t=new Set,[o,r]=i.split("|"),a=r.split(","),[c,m]=o.split("^");c.split(",").forEach(h=>{this._schema.set(h.toLowerCase(),e),this._eventSchema.set(h.toLowerCase(),t)});let p=m&&this._schema.get(m.toLowerCase());if(p){for(let[h,g]of p)e.set(h,g);for(let h of this._eventSchema.get(m.toLowerCase()))t.add(h)}a.forEach(h=>{if(h.length>0)switch(h[0]){case"*":t.add(h.substring(1));break;case"!":e.set(h.substring(1),DX);break;case"#":e.set(h.substring(1),PX);break;case"%":e.set(h.substring(1),AX);break;default:e.set(h,IX)}})})}hasProperty(i,e,t){if(t.some(r=>r.name===lR.name))return!0;if(i.indexOf("-")>-1){if(bR(i)||IE(i))return!1;if(t.some(r=>r.name===sR.name))return!0}return(this._schema.get(i.toLowerCase())||this._schema.get("unknown")).has(e)}hasElement(i,e){return e.some(t=>t.name===lR.name)||i.indexOf("-")>-1&&(bR(i)||IE(i)||e.some(t=>t.name===sR.name))?!0:this._schema.has(i.toLowerCase())}securityContext(i,e,t){t&&(e=this.getMappedPropName(e)),i=i.toLowerCase(),e=e.toLowerCase();let o=lF()[i+"|"+e];return o||(o=lF()["*|"+e],o||ro.NONE)}getMappedPropName(i){return J6.get(i)??i}getDefaultComponentElementName(){return"ng-component"}validateProperty(i){return i.toLowerCase().startsWith("on")?{error:!0,msg:`Binding to event property '${i}' is disallowed for security reasons, please use (${i.slice(2)})=... -If '${i}' is a directive input, make sure the directive is imported by the current module.`}:{error:!1}}validateAttribute(i){return i.toLowerCase().startsWith("on")?{error:!0,msg:`Binding to event attribute '${i}' is disallowed for security reasons, please use (${i.slice(2)})=...`}:{error:!1}}allKnownElementNames(){return Array.from(this._schema.keys())}allKnownAttributesOfElement(i){let e=this._schema.get(i.toLowerCase())||this._schema.get("unknown");return Array.from(e.keys()).map(t=>NX.get(t)??t)}allKnownEventsOfElement(i){return Array.from(this._eventSchema.get(i.toLowerCase())??[])}normalizeAnimationStyleProperty(i){return PG(i)}normalizeAnimationStyleValue(i,e,t){let o="",r=t.toString().trim(),a=null;if(RX(i)&&t!==0&&t!=="0")if(typeof t=="number")o="px";else{let c=t.match(/^[+-]?[\d\.]+([a-z]*)$/);c&&c[1].length==0&&(a=`Please provide a CSS unit value for ${e}:${t}`)}return{error:a,value:r+o}}};function RX(n){switch(n){case"width":case"height":case"minWidth":case"minHeight":case"maxWidth":case"maxHeight":case"left":case"top":case"bottom":case"right":case"fontSize":case"outlineWidth":case"outlineOffset":case"paddingTop":case"paddingLeft":case"paddingBottom":case"paddingRight":case"marginTop":case"marginLeft":case"marginBottom":case"marginRight":case"borderRadius":case"borderWidth":case"borderTopWidth":case"borderLeftWidth":case"borderRightWidth":case"borderBottomWidth":case"textIndent":return!0;default:return!1}}var Kn=class{closedByChildren={};contentType;closedByParent=!1;implicitNamespacePrefix;isVoid;ignoreFirstLf;canSelfClose;preventNamespaceInheritance;constructor({closedByChildren:i,implicitNamespacePrefix:e,contentType:t=Cc.PARSABLE_DATA,closedByParent:o=!1,isVoid:r=!1,ignoreFirstLf:a=!1,preventNamespaceInheritance:c=!1,canSelfClose:m=!1}={}){i&&i.length>0&&i.forEach(p=>this.closedByChildren[p]=!0),this.isVoid=r,this.closedByParent=o||r,this.implicitNamespacePrefix=e||null,this.contentType=t,this.ignoreFirstLf=a,this.preventNamespaceInheritance=c,this.canSelfClose=m??r}isClosedByChild(i){return this.isVoid||i.toLowerCase()in this.closedByChildren}getContentType(i){return typeof this.contentType=="object"?(i===void 0?void 0:this.contentType[i])??this.contentType.default:this.contentType}},cF,Ph;function bD(n){return Ph||(cF=new Kn({canSelfClose:!0}),Ph=Object.assign(Object.create(null),{base:new Kn({isVoid:!0}),meta:new Kn({isVoid:!0}),area:new Kn({isVoid:!0}),embed:new Kn({isVoid:!0}),link:new Kn({isVoid:!0}),img:new Kn({isVoid:!0}),input:new Kn({isVoid:!0}),param:new Kn({isVoid:!0}),hr:new Kn({isVoid:!0}),br:new Kn({isVoid:!0}),source:new Kn({isVoid:!0}),track:new Kn({isVoid:!0}),wbr:new Kn({isVoid:!0}),p:new Kn({closedByChildren:["address","article","aside","blockquote","div","dl","fieldset","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","hr","main","nav","ol","p","pre","section","table","ul"],closedByParent:!0}),thead:new Kn({closedByChildren:["tbody","tfoot"]}),tbody:new Kn({closedByChildren:["tbody","tfoot"],closedByParent:!0}),tfoot:new Kn({closedByChildren:["tbody"],closedByParent:!0}),tr:new Kn({closedByChildren:["tr"],closedByParent:!0}),td:new Kn({closedByChildren:["td","th"],closedByParent:!0}),th:new Kn({closedByChildren:["td","th"],closedByParent:!0}),col:new Kn({isVoid:!0}),svg:new Kn({implicitNamespacePrefix:"svg"}),foreignObject:new Kn({implicitNamespacePrefix:"svg",preventNamespaceInheritance:!0}),math:new Kn({implicitNamespacePrefix:"math"}),li:new Kn({closedByChildren:["li"],closedByParent:!0}),dt:new Kn({closedByChildren:["dt","dd"]}),dd:new Kn({closedByChildren:["dt","dd"],closedByParent:!0}),rb:new Kn({closedByChildren:["rb","rt","rtc","rp"],closedByParent:!0}),rt:new Kn({closedByChildren:["rb","rt","rtc","rp"],closedByParent:!0}),rtc:new Kn({closedByChildren:["rb","rtc","rp"],closedByParent:!0}),rp:new Kn({closedByChildren:["rb","rt","rtc","rp"],closedByParent:!0}),optgroup:new Kn({closedByChildren:["optgroup"],closedByParent:!0}),option:new Kn({closedByChildren:["option","optgroup"],closedByParent:!0}),pre:new Kn({ignoreFirstLf:!0}),listing:new Kn({ignoreFirstLf:!0}),style:new Kn({contentType:Cc.RAW_TEXT}),script:new Kn({contentType:Cc.RAW_TEXT}),title:new Kn({contentType:{default:Cc.ESCAPABLE_RAW_TEXT,svg:Cc.PARSABLE_DATA}}),textarea:new Kn({contentType:Cc.ESCAPABLE_RAW_TEXT,ignoreFirstLf:!0})}),new sf().allKnownElementNames().forEach(i=>{!Ph[i]&&AE(i)===null&&(Ph[i]=new Kn({canSelfClose:!1}))})),Ph[n]??Ph[n.toLowerCase()]??cF}var dF={A:"LINK",B:"BOLD_TEXT",BR:"LINE_BREAK",EM:"EMPHASISED_TEXT",H1:"HEADING_LEVEL1",H2:"HEADING_LEVEL2",H3:"HEADING_LEVEL3",H4:"HEADING_LEVEL4",H5:"HEADING_LEVEL5",H6:"HEADING_LEVEL6",HR:"HORIZONTAL_RULE",I:"ITALIC_TEXT",LI:"LIST_ITEM",LINK:"MEDIA_LINK",OL:"ORDERED_LIST",P:"PARAGRAPH",Q:"QUOTATION",S:"STRIKETHROUGH_TEXT",SMALL:"SMALL_TEXT",SUB:"SUBSTRIPT",SUP:"SUPERSCRIPT",TBODY:"TABLE_BODY",TD:"TABLE_CELL",TFOOT:"TABLE_FOOTER",TH:"TABLE_HEADER_CELL",THEAD:"TABLE_HEADER",TR:"TABLE_ROW",TT:"MONOSPACED_TEXT",U:"UNDERLINED_TEXT",UL:"UNORDERED_LIST"},xD=class{_placeHolderNameCounts={};_signatureToName={};getStartTagPlaceholderName(i,e,t){let o=this._hashTag(i,e,t);if(this._signatureToName[o])return this._signatureToName[o];let r=i.toUpperCase(),a=dF[r]||`TAG_${r}`,c=this._generateUniqueName(t?a:`START_${a}`);return this._signatureToName[o]=c,c}getCloseTagPlaceholderName(i){let e=this._hashClosingTag(i);if(this._signatureToName[e])return this._signatureToName[e];let t=i.toUpperCase(),o=dF[t]||`TAG_${t}`,r=this._generateUniqueName(`CLOSE_${o}`);return this._signatureToName[e]=r,r}getPlaceholderName(i,e){let t=i.toUpperCase(),o=`PH: ${t}=${e}`;if(this._signatureToName[o])return this._signatureToName[o];let r=this._generateUniqueName(t);return this._signatureToName[o]=r,r}getUniquePlaceholder(i){return this._generateUniqueName(i.toUpperCase())}getStartBlockPlaceholderName(i,e){let t=this._hashBlock(i,e);if(this._signatureToName[t])return this._signatureToName[t];let o=this._generateUniqueName(`START_BLOCK_${this._toSnakeCase(i)}`);return this._signatureToName[t]=o,o}getCloseBlockPlaceholderName(i){let e=this._hashClosingBlock(i);if(this._signatureToName[e])return this._signatureToName[e];let t=this._generateUniqueName(`CLOSE_BLOCK_${this._toSnakeCase(i)}`);return this._signatureToName[e]=t,t}_hashTag(i,e,t){let o=`<${i}`,r=Object.keys(e).sort().map(c=>` ${c}=${e[c]}`).join(""),a=t?"/>":`>`;return o+r+a}_hashClosingTag(i){return this._hashTag(`/${i}`,{},!1)}_hashBlock(i,e){let t=e.length===0?"":` (${e.sort().join("; ")})`;return`@${i}${t} {}`}_hashClosingBlock(i){return this._hashBlock(`close_${i}`,[])}_toSnakeCase(i){return i.toUpperCase().replace(/[^A-Z0-9]/g,"_")}_generateUniqueName(i){if(!this._placeHolderNameCounts.hasOwnProperty(i))return this._placeHolderNameCounts[i]=1,i;let t=this._placeHolderNameCounts[i];return this._placeHolderNameCounts[i]=t+1,`${i}_${t}`}},FX=new Kb(new $0);function LX(n,i){let e=new yD(FX,n,i);return(t,o,r,a,c)=>e.toI18nMessage(t,o,r,a,c)}function BX(n,i){return i}var yD=class{_expressionParser;_retainEmptyTokens;_preserveExpressionWhitespace;constructor(i,e,t){this._expressionParser=i,this._retainEmptyTokens=e,this._preserveExpressionWhitespace=t}toI18nMessage(i,e="",t="",o="",r){let a={isIcu:i.length==1&&i[0]instanceof Jp,icuDepth:0,placeholderRegistry:new xD,placeholderToContent:{},placeholderToMessage:{},visitNodeFn:r||BX},c=So(this,i,a);return new Ua(c,a.placeholderToContent,a.placeholderToMessage,e,t,o)}visitElement(i,e){return this._visitElementLike(i,e)}visitComponent(i,e){return this._visitElementLike(i,e)}visitDirective(i,e){throw new Error("Unreachable code")}visitAttribute(i,e){let t=i.valueTokens===void 0||i.valueTokens.length===1?new k_(i.value,i.valueSpan||i.sourceSpan):this._visitTextWithInterpolation(i.valueTokens,i.valueSpan||i.sourceSpan,e,i.i18n);return e.visitNodeFn(i,t)}visitText(i,e){let t=i.tokens.length===1?new k_(i.value,i.sourceSpan):this._visitTextWithInterpolation(i.tokens,i.sourceSpan,e,i.i18n);return e.visitNodeFn(i,t)}visitComment(i,e){return null}visitExpansion(i,e){e.icuDepth++;let t={},o=new Eb(i.switchValue,i.type,t,i.sourceSpan);if(i.cases.forEach(c=>{t[c.value]=new yd(c.expression.map(m=>m.visit(this,e)),c.expSourceSpan)}),e.icuDepth--,e.isIcu||e.icuDepth>0){let c=e.placeholderRegistry.getUniquePlaceholder(`VAR_${i.type}`);return o.expressionPlaceholder=c,e.placeholderToContent[c]={text:i.switchValue,sourceSpan:i.switchValueSourceSpan},e.visitNodeFn(i,o)}let r=e.placeholderRegistry.getPlaceholderName("ICU",i.sourceSpan.toString());e.placeholderToMessage[r]=this.toI18nMessage([i],"","","",void 0);let a=new ef(o,r,i.sourceSpan);return e.visitNodeFn(i,a)}visitExpansionCase(i,e){throw new Error("Unreachable code")}visitBlock(i,e){let t=So(this,i.children,e);if(i.name==="switch")return new yd(t,i.sourceSpan);let o=i.parameters.map(m=>m.expression),r=e.placeholderRegistry.getStartBlockPlaceholderName(i.name,o),a=e.placeholderRegistry.getCloseBlockPlaceholderName(i.name);e.placeholderToContent[r]={text:i.startSourceSpan.toString(),sourceSpan:i.startSourceSpan},e.placeholderToContent[a]={text:i.endSourceSpan?i.endSourceSpan.toString():"}",sourceSpan:i.endSourceSpan??i.sourceSpan};let c=new Sm(i.name,o,r,a,t,i.sourceSpan,i.startSourceSpan,i.endSourceSpan);return e.visitNodeFn(i,c)}visitBlockParameter(i,e){throw new Error("Unreachable code")}visitLetDeclaration(i,e){return null}_visitElementLike(i,e){let t=So(this,i.children,e),o={},r=g=>{o[g.name]=g.value},a,c;i instanceof nl?(a=i.name,c=bD(i.name).isVoid):(a=i.fullName,c=i.tagName?bD(i.tagName).isVoid:!1),i.attrs.forEach(r),i.directives.forEach(g=>g.attrs.forEach(r));let m=e.placeholderRegistry.getStartTagPlaceholderName(a,o,c);e.placeholderToContent[m]={text:i.startSourceSpan.toString(),sourceSpan:i.startSourceSpan};let p="";c||(p=e.placeholderRegistry.getCloseTagPlaceholderName(a),e.placeholderToContent[p]={text:``,sourceSpan:i.endSourceSpan??i.sourceSpan});let h=new ym(a,o,m,p,t,c,i.sourceSpan,i.startSourceSpan,i.endSourceSpan);return e.visitNodeFn(i,h)}_visitTextWithInterpolation(i,e,t,o){let r=[],a=!1;for(let c of i)switch(c.type){case 8:case 17:a=!0;let[m,p,h]=c.parts,g=HX(p)||"INTERPOLATION",S=t.placeholderRegistry.getPlaceholderName(g,p);if(this._preserveExpressionWhitespace)t.placeholderToContent[S]={text:c.parts.join(""),sourceSpan:c.sourceSpan},r.push(new w0(p,S,c.sourceSpan));else{let x=this.normalizeExpression(c);t.placeholderToContent[S]={text:`${m}${x}${h}`,sourceSpan:c.sourceSpan},r.push(new w0(x,S,c.sourceSpan))}break;default:if(c.parts[0].length>0||this._retainEmptyTokens){let x=r[r.length-1];x instanceof k_?(x.value+=c.parts[0],x.sourceSpan=new _n(x.sourceSpan.start,c.sourceSpan.end,x.sourceSpan.fullStart,x.sourceSpan.details)):r.push(new k_(c.parts[0],c.sourceSpan))}else this._retainEmptyTokens&&r.push(new k_(c.parts[0],c.sourceSpan));break}return a?(VX(r,o),new yd(r,e)):r[0]}normalizeExpression(i){let e=i.parts[1],t=this._expressionParser.parseBinding(e,i.sourceSpan,i.sourceSpan.start.offset);return kX(t)}};function VX(n,i){if(i instanceof Ua&&(zX(i),i=i.nodes[0]),i instanceof yd){jX(i.children,n);for(let e=0;e`"${e.sourceSpan.toString()}"`).join(` -`)} - -Second pass (${i.length} tokens): -${i.map(e=>`"${e.sourceSpan.toString()}"`).join(` -`)} - `.trim());if(n.some((e,t)=>i[t].constructor!==e.constructor))throw new Error("The types of the i18n message children changed between first and second pass.")}var $X=/\/\/[\s\S]*i18n[\s\S]*\([\s\S]*ph[\s\S]*=[\s\S]*("|')([\s\S]*?)\1[\s\S]*\)/g;function HX(n){return n.split($X)[2]}var mF=new Set(["iframe|srcdoc","*|innerhtml","*|outerhtml","embed|src","iframe|src","object|codebase","object|data"]);function pF(n,i){return n=n.toLowerCase(),i=i.toLowerCase(),mF.has(n+"|"+i)||mF.has("*|"+i)}var UX=n=>(i,e)=>{let t=n.get(i)??i;return t instanceof Md&&(e instanceof ef&&t.i18n instanceof Ua&&(e.previousMessage=t.i18n),t.i18n=e),e},Zb=class{keepI18nAttrs;enableI18nLegacyMessageIdFormat;preserveSignificantWhitespace;retainEmptyTokens;hasI18nMeta=!1;_errors=[];constructor(i=!1,e=!1,t=!0,o=!t){this.keepI18nAttrs=i,this.enableI18nLegacyMessageIdFormat=e,this.preserveSignificantWhitespace=t,this.retainEmptyTokens=o}_generateI18nMessage(i,e="",t){let{meaning:o,description:r,customId:a}=this._parseMetadata(e),m=LX(this.retainEmptyTokens,this.preserveSignificantWhitespace)(i,o,r,a,t);return this._setMessageId(m,e),this._setLegacyIds(m,e),m}visitAllWithErrors(i){let e=i.map(t=>t.visit(this,null));return new Xb(e,this._errors)}visitElement(i){return this._visitElementLike(i),i}visitComponent(i,e){return this._visitElementLike(i),i}visitExpansion(i,e){let t,o=i.i18n;if(this.hasI18nMeta=!0,o instanceof ef){let r=o.name;t=this._generateI18nMessage([i],o);let a=p6(t);a.name=r,e!==null&&(e.placeholderToMessage[r]=t)}else t=this._generateI18nMessage([i],e||o);return i.i18n=t,i}visitText(i){return i}visitAttribute(i){return i}visitComment(i){return i}visitExpansionCase(i){return i}visitBlock(i,e){return So(this,i.children,e),i}visitBlockParameter(i,e){return i}visitLetDeclaration(i,e){return i}visitDirective(i,e){return i}_visitElementLike(i){let e;if(oW(i)){this.hasI18nMeta=!0;let t=[],o={};for(let r of i.attrs)if(r.name===d6){let a=i.i18n||r.value,c=new Map,m=this.preserveSignificantWhitespace?i.children:gc(new Yb(!1,c),i.children);e=this._generateI18nMessage(m,a,UX(c)),e.nodes.length===0&&(e=void 0),i.i18n=e}else if(r.name.startsWith($E)){let a=r.name.slice($E.length),c;i instanceof Va?c=i.tagName===null?!1:pF(i.tagName,a):c=pF(i.name,a),c?this._reportError(r,`Translating attribute '${a}' is disallowed for security reasons.`):o[a]=r.value}else t.push(r);if(Object.keys(o).length)for(let r of t){let a=o[r.name];a!==void 0&&r.value&&(r.i18n=this._generateI18nMessage([r],r.i18n||a))}this.keepI18nAttrs||(i.attrs=t)}So(this,i.children,e)}_parseMetadata(i){return typeof i=="string"?qX(i):i instanceof Ua?i:{}}_setMessageId(i,e){i.id||(i.id=e instanceof Ua&&e.id||aG(i))}_setLegacyIds(i,e){if(this.enableI18nLegacyMessageIdFormat)i.legacyIds=[rG(i),t6(i)];else if(typeof e!="string"){let t=e instanceof Ua?e:e instanceof ef?e.previousMessage:void 0;i.legacyIds=t?t.legacyIds:[]}}_reportError(i,e){this._errors.push(new rn(i.sourceSpan,e))}},GX="|",WX="@@";function qX(n=""){let i,e,t;if(n=n.trim(),n){let o=n.indexOf(WX),r=n.indexOf(GX),a;[a,i]=o>-1?[n.slice(0,o),n.slice(o+2)]:[n,""],[e,t]=r>-1?[a.slice(0,r),a.slice(r+1)]:["",a]}return{customId:i,meaning:e,description:t}}function QX(n){let i=[];return n.description?i.push({tagName:"desc",text:n.description}):i.push({tagName:"suppress",text:"{msgDescriptions}"}),n.meaning&&i.push({tagName:"meaning",text:n.meaning}),xG(i)}var XX="goog.getMsg";function YX(n,i,e,t){let o=ZX(i),r=[Me(o)];Object.keys(t).length&&(r.push(rD(JD(t,!0),!0)),r.push(rD({original_code:ml(Object.keys(t).map(m=>({key:X0(m),quoted:!0,value:i.placeholders[m]?Me(i.placeholders[m].sourceSpan.toString()):Me(i.placeholderToMessage[m].nodes.map(p=>p.sourceSpan.toString()).join(""))})))})));let a=new Fr(e.name,Zn(XX).callFn(r),Ul,la.Final);a.addLeadingComment(QX(i));let c=new ma(n.set(e));return[a,c]}var SD=class{formatPh(i){return`{$${X0(i)}}`}visitText(i){return i.value}visitContainer(i){return i.children.map(e=>e.visit(this)).join("")}visitIcu(i){return W6(i)}visitTagPlaceholder(i){return i.isVoid?this.formatPh(i.startName):`${this.formatPh(i.startName)}${i.children.map(e=>e.visit(this)).join("")}${this.formatPh(i.closeName)}`}visitPlaceholder(i){return this.formatPh(i.name)}visitBlockPlaceholder(i){return`${this.formatPh(i.startName)}${i.children.map(e=>e.visit(this)).join("")}${this.formatPh(i.closeName)}`}visitIcuPlaceholder(i,e){return this.formatPh(i.name)}},KX=new SD;function ZX(n){return n.nodes.map(i=>i.visit(KX,null)).join("")}function JX(n,i,e){let{messageParts:t,placeHolders:o}=eY(i),r=tY(i),a=o.map(p=>e[p.text]),c=wG(i,t,o,a,r),m=n.set(c);return[new ma(m)]}var wD=class{placeholderToMessage;pieces;constructor(i,e){this.placeholderToMessage=i,this.pieces=e}visitText(i){if(this.pieces[this.pieces.length-1]instanceof Xp)this.pieces[this.pieces.length-1].text+=i.value;else{let e=new _n(i.sourceSpan.fullStart,i.sourceSpan.end,i.sourceSpan.fullStart,i.sourceSpan.details);this.pieces.push(new Xp(i.value,e))}}visitContainer(i){i.children.forEach(e=>e.visit(this))}visitIcu(i){this.pieces.push(new Xp(W6(i),i.sourceSpan))}visitTagPlaceholder(i){this.pieces.push(this.createPlaceholderPiece(i.startName,i.startSourceSpan??i.sourceSpan)),i.isVoid||(i.children.forEach(e=>e.visit(this)),this.pieces.push(this.createPlaceholderPiece(i.closeName,i.endSourceSpan??i.sourceSpan)))}visitPlaceholder(i){this.pieces.push(this.createPlaceholderPiece(i.name,i.sourceSpan))}visitBlockPlaceholder(i){this.pieces.push(this.createPlaceholderPiece(i.startName,i.startSourceSpan??i.sourceSpan)),i.children.forEach(e=>e.visit(this)),this.pieces.push(this.createPlaceholderPiece(i.closeName,i.endSourceSpan??i.sourceSpan))}visitIcuPlaceholder(i){this.pieces.push(this.createPlaceholderPiece(i.name,i.sourceSpan,this.placeholderToMessage[i.name]))}createPlaceholderPiece(i,e,t){return new Vh(X0(i,!1),e,t)}};function eY(n){let i=[],e=new wD(n.placeholderToMessage,i);return n.nodes.forEach(t=>t.visit(e)),nY(i)}function tY(n){let i=n.nodes[0],e=n.nodes[n.nodes.length-1];return new _n(i.sourceSpan.fullStart,e.sourceSpan.end,i.sourceSpan.fullStart,i.sourceSpan.details)}function nY(n){let i=[],e=[];n[0]instanceof Vh&&i.push(oE(n[0].sourceSpan.start));for(let t=0;t{let M=S.has(v.name);return S.add(v.name),!M});let x=g.flatMap(v=>{let M=a.get(v.context);if(M===void 0)throw new Error("AssertionError: Could not find i18n expression's value");return[Me(v.name),M]});h.i18nAttributesConfig=n.addConst(new Tc(x))}for(let m of n.units)for(let p of m.create)if(p.kind===L.I18nStart){let h=c.get(p.root);if(h===void 0)throw new Error("AssertionError: Could not find corresponding i18n block index for an i18n message op; was an i18n message incorrectly assumed to correspond to an attribute?");p.messageIndex=h}}function eL(n,i,e,t){let o=[],r=new Map;for(let p of t.subMessages){let h=e.get(p),{mainVar:g,statements:S}=eL(n,i,e,h);o.push(...S);let x=r.get(h.messagePlaceholder)??[];x.push(g),r.set(h.messagePlaceholder,x)}lY(t,r),t.params=new Map([...t.params.entries()].sort());let a=Zn(n.pool.uniqueName(iY)),c=mY(n.pool,t.message.id,i,n.i18nUseExternalIds),m;if(t.needsPostprocessing||t.postprocessingParams.size>0){let p=Object.fromEntries([...t.postprocessingParams.entries()].sort()),h=JD(p,!1),g=[];t.postprocessingParams.size>0&&g.push(rD(h,!0)),m=S=>Wt(he.i18nPostprocess).callFn([S,...g])}return o.push(...cY(t.message,a,c,t.params,m)),{mainVar:a,statements:o}}function lY(n,i){for(let[e,t]of i)t.length===1?n.params.set(e,t[0]):(n.params.set(e,Me(`${hF}${oY}${e}${hF}`)),n.postprocessingParams.set(e,Qi(t)))}function cY(n,i,e,t,o){let r=Object.fromEntries(t),a=[aY(i),sx(dY(),YX(i,n,e,r),JX(i,n,JD(r,!1)))];return o&&a.push(new ma(i.set(o(i)))),a}function dY(){return q0(Zn(uF)).notIdentical(Me("undefined",YD)).and(Zn(uF))}function mY(n,i,e,t){let o,r=e;if(t){let a=fF("EXTERNAL_"),c=n.uniqueName(r);o=`${a}${Hp(i)}$$${c}`}else{let a=fF(r);o=n.uniqueName(a)}return Zn(o)}function pY(n){for(let i of n.units){let e=null,t=null,o=new Map,r=new Map,a=new Map;for(let c of i.create)switch(c.kind){case L.I18nStart:if(c.context===null)throw Error("I18n op should have its context set.");e=c;break;case L.I18nEnd:e=null;break;case L.IcuStart:if(c.context===null)throw Error("Icu op should have its context set.");t=c;break;case L.IcuEnd:t=null;break;case L.Text:if(e!==null)if(o.set(c.xref,e),r.set(c.xref,t),c.icuPlaceholder!==null){let m=Vq(n.allocateXrefId(),c.icuPlaceholder,[c.initialValue]);We.replace(c,m),a.set(c.xref,m)}else We.remove(c);break}for(let c of i.update)switch(c.kind){case L.InterpolateText:if(!o.has(c.target))continue;let m=o.get(c.target),p=r.get(c.target),h=a.get(c.target),g=p?p.context:m.context,S=p?P0.Postproccessing:P0.Creation,x=[];for(let v=0;v0){let t=hY(e.localRefs);e.localRefs=n.addConst(t)}else e.localRefs=null;break}}function hY(n){let i=[];for(let e of n)i.push(Me(e.name),Me(e.target));return Qi(i)}function fY(n){for(let i of n.units){let e=Sa.HTML;for(let t of i.create)t.kind===L.ElementStart&&t.namespace!==e&&(We.insertBefore(Iq(t.namespace),t),e=t.namespace)}}function gY(n){let i=[],e=0,t=0,o=0,r=0,a=0,c=null;for(;e0&&t===0&&o===0){let p=n.substring(r,e-1).trim();i.push(c,p),a=e,r=0,c=null}break}if(c&&r){let m=n.slice(r).trim();i.push(c,m)}return i}function tL(n){return n.replace(/[a-z][A-Z]/g,i=>i.charAt(0)+"-"+i.charAt(1)).toLowerCase()}function _Y(n){let i=new Map;for(let e of n.units)for(let t of e.create)Dm(t)&&i.set(t.xref,t);for(let e of n.units)for(let t of e.create)if(t.kind===L.ExtractedAttribute&&t.bindingKind===Ht.Attribute&&O6(t.expression)){let o=i.get(t.target);if(o!==void 0&&(o.kind===L.Template||o.kind===L.ConditionalCreate||o.kind===L.ConditionalBranchCreate)&&o.templateKind===ss.Structural)continue;if(t.name==="style"){let r=gY(t.expression.value);for(let a=0;a{if(!(!(r instanceof Sd)||r.name!==null)){if(!t.has(r.xref))throw new Error(`Variable ${r.xref} not yet named`);r.name=t.get(r.xref)}})}function CY(n,i){if(n.name===null)switch(n.kind){case Qr.Context:n.name=`ctx_r${i.index++}`;break;case Qr.Identifier:let e=n.identifier===Ls?"i":"";n.name=`${n.identifier}_${e}r${++i.index}`;break;default:n.name=`_r${++i.index}`;break}return n.name}function bY(n){return n.startsWith("--")?n:tL(n)}function gF(n){let i=n.indexOf("!important");return i>-1?n.substring(0,i):n}function xY(n){for(let i of n.units){for(let e of i.functions)rE(e.ops);for(let e of i.create)(e.kind===L.Listener||e.kind===L.Animation||e.kind===L.AnimationListener||e.kind===L.TwoWayListener)&&rE(e.handlerOps);rE(i.update)}}function rE(n){for(let i of n){if(i.kind!==L.Statement||!(i.statement instanceof ma)||!(i.statement.expr instanceof Fb))continue;let e=i.statement.expr.steps,t=!0;for(let o=i.next;o.kind!==L.ListEnd&&t;o=o.next)hr(o,(r,a)=>{if(!Ic(r))return r;if(t&&!(a&Wn.InChildOperation))switch(r.kind){case Yt.NextContext:r.steps+=e,We.remove(i),t=!1;break;case Yt.GetCurrentView:case Yt.Reference:case Yt.ContextLetReference:t=!1;break}})}}var yY="ng-container";function SY(n){for(let i of n.units){let e=new Set;for(let t of i.create)t.kind===L.ElementStart&&t.tag===yY&&(t.kind=L.ContainerStart,e.add(t.xref)),t.kind===L.ElementEnd&&e.has(t.xref)&&(t.kind=L.ContainerEnd)}}function wY(n,i){let e=n.get(i);if(e===void 0)throw new Error("All attributes should have an element-like target.");return e}function MY(n){let i=new Map;for(let e of n.units)for(let t of e.create)Dm(t)&&i.set(t.xref,t);for(let e of n.units)for(let t of e.create)(t.kind===L.ElementStart||t.kind===L.ContainerStart)&&t.nonBindable&&We.insertAfter(kq(t.xref),t),(t.kind===L.ElementEnd||t.kind===L.ContainerEnd)&&wY(i,t.xref).nonBindable&&We.insertBefore(Tq(t.xref),t)}function xc(n){return i=>i.kind===n}function G_(n,i){return e=>e.kind===n&&i===e.expression instanceof Yo}function kY(n){return n.kind===L.Listener&&!(n.hostListener&&n.isLegacyAnimationListener)||n.kind===L.TwoWayListener||n.kind===L.Animation||n.kind===L.AnimationListener}function TY(n){return(n.kind===L.Property||n.kind===L.TwoWayProperty)&&!(n.expression instanceof Yo)}var EY=[{test:n=>n.kind===L.Listener&&n.hostListener&&n.isLegacyAnimationListener},{test:kY}],DY=[{test:xc(L.StyleMap),transform:Jb},{test:xc(L.ClassMap),transform:Jb},{test:xc(L.StyleProp)},{test:xc(L.ClassProp)},{test:G_(L.Attribute,!0)},{test:G_(L.Property,!0)},{test:TY},{test:G_(L.Attribute,!1)},{test:xc(L.Control)}],PY=[{test:G_(L.DomProperty,!0)},{test:G_(L.DomProperty,!1)},{test:xc(L.Attribute)},{test:xc(L.StyleMap),transform:Jb},{test:xc(L.ClassMap),transform:Jb},{test:xc(L.StyleProp)},{test:xc(L.ClassProp)}],_F=new Set([L.Listener,L.TwoWayListener,L.AnimationListener,L.StyleMap,L.ClassMap,L.StyleProp,L.ClassProp,L.Property,L.TwoWayProperty,L.DomProperty,L.Attribute,L.Animation,L.Control]);function IY(n){for(let i of n.units){vF(i.create,EY);let e=i.job.kind===Tt.Host?PY:DY;vF(i.update,e)}}function vF(n,i){let e=[],t=null;for(let o of n){let r=I0(o)?o.target:null;(!_F.has(o.kind)||r!==t&&t!==null&&r!==null)&&(We.insertBefore(CF(e,i),o),e=[],t=null),_F.has(o.kind)&&(e.push(o),We.remove(o),t=r??t)}n.push(CF(e,i))}function CF(n,i){let e=Array.from(i,()=>new Array);for(let t of n){let o=i.findIndex(r=>r.test(t));e[o].push(t)}return e.flatMap((t,o)=>{let r=i[o].transform;return r?r(t):t})}function Jb(n){return n.slice(n.length-1)}function AY(n){for(let i of n.units){let e=$6(i);for(let t of i.ops())if(t.kind===L.Binding){let o=NY(e,t.target);OY(t.name)&&o.kind===L.Projection&&We.remove(t)}}}function OY(n){return n.toLowerCase()==="select"}function NY(n,i){let e=n.get(i);if(e===void 0)throw new Error("All attributes should have an slottable target.");return e}function RY(n){for(let i of n.units)FY(i)}function FY(n){for(let i of n.update)hr(i,(e,t)=>{if(!Ic(e)||e.kind!==Yt.PipeBinding)return;if(t&Wn.InChildOperation)throw new Error("AssertionError: pipe bindings should not appear in child expressions");if(i.target==null)throw new Error("AssertionError: expected slot handle to be assigned for pipe creation");LY(n,i.target,e)})}function LY(n,i,e){for(let t=n.create.head.next;t.kind!==L.ListEnd;t=t.next){if(!pf(t)||t.xref!==i)continue;for(;t.next.kind===L.Pipe;)t=t.next;let o=Pq(e.target,e.targetSlot,e.name);We.insertBefore(o,t.next);return}throw new Error(`AssertionError: unable to find insertion point for pipe ${e.name}`)}function BY(n){for(let i of n.units)for(let e of i.update)Ko(e,t=>!(t instanceof fu)||t.args.length<=4?t:new R0(t.target,t.targetSlot,t.name,Qi(t.args),t.args.length),Wn.None)}function VY(n){nL(n.root,0)}function nL(n,i){let e=null;for(let t of n.create)switch(t.kind){case L.I18nStart:t.subTemplateIndex=i===0?null:i,e=t;break;case L.I18nEnd:e.subTemplateIndex===null&&(i=0),e=null;break;case L.ConditionalCreate:case L.ConditionalBranchCreate:case L.Template:i=z1(n.job.views.get(t.xref),e,t.i18nPlaceholder,i);break;case L.RepeaterCreate:let o=n.job.views.get(t.xref);i=z1(o,e,t.i18nPlaceholder,i),t.emptyView!==null&&(i=z1(n.job.views.get(t.emptyView),e,t.emptyI18nPlaceholder,i));break;case L.Projection:t.fallbackView!==null&&(i=z1(n.job.views.get(t.fallbackView),e,t.fallbackViewI18nPlaceholder,i));break}return i}function z1(n,i,e,t){if(e!==void 0){if(i===null)throw Error("Expected template with i18n placeholder to be in an i18n block.");t++,zY(n,i)}return nL(n,t)}function zY(n,i){if(n.create.head.next?.kind!==L.I18nStart){let e=n.job.allocateXrefId();We.insertAfter(mx(e,i.message,i.root,null),n.create.head),We.insertBefore(px(e,null),n.create.tail)}}function jY(n){for(let i of n.units)for(let e of i.ops())hr(e,t=>{if(!(t instanceof hu)||t.body===null)return;let o=new MD(t.args.length);t.fn=n.pool.getSharedConstant(o,t.body),t.body=null})}var MD=class extends n0{numArgs;constructor(i){super(),this.numArgs=i}keyOf(i){return i instanceof Tm?`param(${i.index})`:super.keyOf(i)}toSharedConstantDeclaration(i,e){let t=[];for(let r=0;rr instanceof Tm?Zn("a"+r.index):r,Wn.None);return new Fr(i,new vu(t,o),void 0,la.Final)}};function $Y(n){for(let i of n.units)for(let e of i.update)Ko(e,(t,o)=>o&Wn.InChildOperation?t:t instanceof Tc?HY(t):t instanceof ql?UY(t):t,Wn.None)}function HY(n){let i=[],e=[];for(let t of n.entries){if(t instanceof au){if(t.expression.isConstant())i.push(t);else{let o=e.length;e.push(t.expression),i.push(new au(new Tm(o)))}continue}if(t.isConstant())i.push(t);else{let o=e.length;e.push(t),i.push(new Tm(o))}}return new hu(Qi(i),e)}function UY(n){let i=[],e=[];for(let t of n.entries){if(t instanceof Cm){if(t.expression.isConstant())i.push(t);else{let o=e.length;e.push(t.expression),i.push(new Cm(new Tm(o)))}continue}if(t.value.isConstant())i.push(t);else{let o=e.length;e.push(t.value),i.push(new Gh(t.key,new Tm(o),t.quoted))}}return new hu(new ql(i),e)}function GY(n){for(let i of n.units)for(let e of i.ops())Ko(e,t=>t instanceof Uh&&(t.flags===null||!t.flags.includes("g"))?n.pool.getSharedConstant(new kD,t):t,Wn.None)}var kD=class extends n0{toSharedConstantDeclaration(i,e){return new Fr(i,e,void 0,la.Final)}};function WY(n,i,e,t,o){return Am(he.element,n,i,e,t,o)}function qY(n,i,e,t,o){return Am(he.elementStart,n,i,e,t,o)}function Am(n,i,e,t,o,r){let a=[Me(i)];return e!==null&&a.push(Me(e)),o!==null?a.push(Me(t),Me(o)):t!==null&&a.push(Me(t)),xn(n,a,r)}function iL(n,i,e,t,o,r,a,c,m){let p=[Me(i),e,Me(t),Me(o),Me(r),Me(a)];for(c!==null&&(p.push(Me(c)),p.push(Wt(he.templateRefExtractor)));p[p.length-1].isEquivalent(Wh);)p.pop();return xn(n,p,m)}function cP(n,i,e,t,o){let r=[Me(i)];return e instanceof Yo?r.push(uf(e,o)):r.push(e),t!==null&&r.push(t),xn(n,r,o)}function QY(n){return xn(he.elementEnd,[],n)}function XY(n,i,e,t){return Am(he.elementContainerStart,n,null,i,e,t)}function YY(n,i,e,t){return Am(he.elementContainer,n,null,i,e,t)}function KY(){return xn(he.elementContainerEnd,[],null)}function ZY(n,i,e,t,o,r,a,c){return iL(he.templateCreate,n,i,e,t,o,r,a,c)}function JY(){return xn(he.disableBindings,[],null)}function eK(){return xn(he.enableBindings,[],null)}function tK(n,i,e,t,o){let r=[Me(n),i];return e!==null&&r.push(Wt(e)),xn(t?he.syntheticHostListener:he.listener,r,o)}function bF(n,i){return Wt(he.twoWayBindingSet).callFn([n,i])}function nK(n,i,e){return xn(he.twoWayListener,[Me(n),i],e)}function iK(n,i){return xn(he.pipe,[Me(n),Me(i)],null)}function oK(){return xn(he.namespaceHTML,[],null)}function rK(){return xn(he.namespaceSVG,[],null)}function aK(){return xn(he.namespaceMathML,[],null)}function sK(n,i){return xn(he.advance,n>1?[Me(n)]:[],i)}function lK(n){return Wt(he.reference).callFn([Me(n)])}function cK(n){return Wt(he.nextContext).callFn(n===1?[]:[Me(n)])}function dK(){return Wt(he.getCurrentView).callFn([])}function mK(n){return Wt(he.restoreView).callFn([n])}function pK(n){return Wt(he.resetView).callFn([n])}function uK(n,i,e){let t=[Me(n,null)];return i!==""&&t.push(Me(i)),xn(he.text,t,e)}function hK(n,i,e,t,o,r,a,c,m,p,h){let g=[Me(n),Me(i),e??Me(null),Me(t),Me(o),Me(r),a??Me(null),c??Me(null),m?Wt(he.deferEnableTimerScheduling):Me(null),Me(h)],S;for(;(S=g[g.length-1])!==null&&S instanceof da&&S.value===null;)g.pop();return xn(he.defer,g,p)}var fK=new Map([[oo.Idle,{none:he.deferOnIdle,prefetch:he.deferPrefetchOnIdle,hydrate:he.deferHydrateOnIdle}],[oo.Immediate,{none:he.deferOnImmediate,prefetch:he.deferPrefetchOnImmediate,hydrate:he.deferHydrateOnImmediate}],[oo.Timer,{none:he.deferOnTimer,prefetch:he.deferPrefetchOnTimer,hydrate:he.deferHydrateOnTimer}],[oo.Hover,{none:he.deferOnHover,prefetch:he.deferPrefetchOnHover,hydrate:he.deferHydrateOnHover}],[oo.Interaction,{none:he.deferOnInteraction,prefetch:he.deferPrefetchOnInteraction,hydrate:he.deferHydrateOnInteraction}],[oo.Viewport,{none:he.deferOnViewport,prefetch:he.deferPrefetchOnViewport,hydrate:he.deferHydrateOnViewport}],[oo.Never,{none:he.deferHydrateNever,prefetch:he.deferHydrateNever,hydrate:he.deferHydrateNever}]]);function gK(n,i,e,t){let o=fK.get(n)?.[e];if(o===void 0)throw new Error(`Unable to determine instruction for trigger ${n}`);return xn(o,i,t)}function _K(n){return xn(he.projectionDef,n?[n]:[],null)}function vK(n,i,e,t,o,r,a){let c=[Me(n)];return(i!==0||e!==null||t!==null)&&(c.push(Me(i)),e!==null&&c.push(e),t!==null&&(e===null&&c.push(Me(null)),c.push(Zn(t),Me(o),Me(r)))),xn(he.projection,c,a)}function CK(n,i,e,t){let o=[Me(n),Me(i)];return e!==null&&o.push(Me(e)),xn(he.i18nStart,o,t)}function bK(n,i,e,t,o,r,a,c){let m=[Me(n),i,Me(e),Me(t),Me(o),Me(r)];for(a!==null&&(m.push(Me(a)),m.push(Wt(he.templateRefExtractor)));m[m.length-1].isEquivalent(Wh);)m.pop();return xn(he.conditionalCreate,m,c)}function xK(n,i,e,t,o,r,a,c){let m=[Me(n),i,Me(e),Me(t),Me(o),Me(r)];for(a!==null&&(m.push(Me(a)),m.push(Wt(he.templateRefExtractor)));m[m.length-1].isEquivalent(Wh);)m.pop();return xn(he.conditionalBranchCreate,m,c)}function yK(n,i,e,t,o,r,a,c,m,p,h,g,S,x){let v=[Me(n),Zn(i),Me(e),Me(t),Me(o),Me(r),a];return(c||m!==null)&&(v.push(Me(c)),m!==null&&(v.push(Zn(m),Me(p),Me(h)),(g!==null||S!==null)&&v.push(Me(g)),S!==null&&v.push(Me(S)))),xn(he.repeaterCreate,v,x)}function SK(n,i){return xn(he.repeater,[n],i)}function wK(n,i,e){return n==="prefetch"?xn(he.deferPrefetchWhen,[i],e):n==="hydrate"?xn(he.deferHydrateWhen,[i],e):xn(he.deferWhen,[i],e)}function MK(n,i){return xn(he.declareLet,[Me(n)],i)}function kK(n,i){return Wt(he.storeLet).callFn([n],i)}function TK(n){return Wt(he.readContextLet).callFn([Me(n)])}function EK(n,i,e,t){let o=[Me(n),Me(i)];return e&&o.push(Me(e)),xn(he.i18n,o,t)}function DK(n){return xn(he.i18nEnd,[],n)}function PK(n,i){let e=[Me(n),Me(i)];return xn(he.i18nAttributes,e,null)}function IK(n,i,e){return cP(he.ariaProperty,n,i,null,e)}function AK(n,i,e,t){return cP(he.property,n,i,e,t)}function OK(n){return xn(he.control,[],n)}function NK(n){return xn(he.controlCreate,[],n)}function RK(n,i,e,t){let o=[Me(n),i];return e!==null&&o.push(e),xn(he.twoWayProperty,o,t)}function FK(n,i,e,t,o){let r=[Me(n)];return i instanceof Yo?r.push(uf(i,o)):r.push(i),(e!==null||t!==null)&&r.push(e??Me(null)),t!==null&&r.push(Me(t)),xn(he.attribute,r,null)}function LK(n,i,e,t){let o=[Me(n)];return i instanceof Yo?o.push(uf(i,t)):o.push(i),e!==null&&o.push(Me(e)),xn(he.styleProp,o,t)}function BK(n,i,e){return xn(he.classProp,[Me(n),i],e)}function VK(n,i){let e=n instanceof Yo?uf(n,i):n;return xn(he.styleMap,[e],i)}function zK(n,i){let e=n instanceof Yo?uf(n,i):n;return xn(he.classMap,[e],i)}function jK(n,i,e,t,o){return Am(he.domElement,n,i,e,t,o)}function $K(n,i,e,t,o){return Am(he.domElementStart,n,i,e,t,o)}function HK(n){return xn(he.domElementEnd,[],n)}function UK(n,i,e,t){return Am(he.domElementContainerStart,n,null,i,e,t)}function GK(n,i,e,t){return Am(he.domElementContainer,n,null,i,e,t)}function WK(){return xn(he.domElementContainerEnd,[],null)}function qK(n,i,e,t){let o=[Me(n),i];return e!==null&&o.push(Wt(e)),xn(he.domListener,o,t)}function QK(n,i,e,t,o,r,a,c){return iL(he.domTemplate,n,i,e,t,o,r,a,c)}var xF=[he.pipeBind1,he.pipeBind2,he.pipeBind3,he.pipeBind4];function XK(n,i,e){if(e.length<1||e.length>xF.length)throw new Error("pipeBind() argument count out of bounds");let t=xF[e.length-1];return Wt(t).callFn([Me(n),Me(i),...e])}function YK(n,i,e){return Wt(he.pipeBindV).callFn([Me(n),Me(i),e])}function KK(n,i,e){let t=oL(n,i);return pZ(cZ,[],t,e)}function ZK(n,i){return xn(he.i18nExp,[n],i)}function JK(n,i){return xn(he.i18nApply,[Me(n)],i)}function eZ(n,i,e,t){return cP(he.domProperty,n,i,e,t)}function tZ(n,i,e,t){let o=[i];e!==null&&o.push(e);let r=n==="enter"?he.animationEnter:he.animationLeave;return xn(r,o,t)}function nZ(n,i,e,t){let r=[i instanceof Yo?uf(i,t):i];e!==null&&r.push(e);let a=n==="enter"?he.animationEnter:he.animationLeave;return xn(a,r,t)}function iZ(n,i,e,t){let o=[i],r=n==="enter"?he.animationEnterListener:he.animationLeaveListener;return xn(r,o,t)}function oZ(n,i,e){return xn(he.syntheticHostProperty,[Me(n),i],e)}function rZ(n,i,e){return dP(mZ,[Me(n),i],e,null)}function aZ(n,i){return xn(he.attachSourceLocations,[Me(n),i],null)}function sZ(n,i,e){return Wt(he.arrowFunction).callFn([Me(n),i,e])}function oL(n,i){if(n.length<1||i.length!==n.length-1)throw new Error("AssertionError: expected specific shape of args for strings/expressions in interpolation");let e=[];if(i.length===1&&n[0]===""&&n[1]==="")e.push(i[0]);else{let t;for(t=0;t{if(n%2===0)throw new Error("Expected odd number of arguments");return(n-1)/2}},dZ={constant:[he.interpolate,he.interpolate1,he.interpolate2,he.interpolate3,he.interpolate4,he.interpolate5,he.interpolate6,he.interpolate7,he.interpolate8],variable:he.interpolateV,mapping:n=>{if(n%2===0)throw new Error("Expected odd number of arguments");return(n-1)/2}},mZ={constant:[he.pureFunction0,he.pureFunction1,he.pureFunction2,he.pureFunction3,he.pureFunction4,he.pureFunction5,he.pureFunction6,he.pureFunction7,he.pureFunction8],variable:he.pureFunctionV,mapping:n=>n};function dP(n,i,e,t){let o=n.mapping(e.length),r=e.at(-1);if(e.length>1&&r instanceof da&&r.value===""&&e.pop(),orL(n,t),Wn.None),e.kind){case L.Text:We.replace(e,uK(e.handle.slot,e.initialValue,e.sourceSpan));break;case L.ElementStart:We.replace(e,n.job.mode===is.DomOnly?$K(e.handle.slot,e.tag,e.attributes,e.localRefs,e.startSourceSpan):qY(e.handle.slot,e.tag,e.attributes,e.localRefs,e.startSourceSpan));break;case L.Element:We.replace(e,n.job.mode===is.DomOnly?jK(e.handle.slot,e.tag,e.attributes,e.localRefs,e.wholeSourceSpan):WY(e.handle.slot,e.tag,e.attributes,e.localRefs,e.wholeSourceSpan));break;case L.ElementEnd:We.replace(e,n.job.mode===is.DomOnly?HK(e.sourceSpan):QY(e.sourceSpan));break;case L.ContainerStart:We.replace(e,n.job.mode===is.DomOnly?UK(e.handle.slot,e.attributes,e.localRefs,e.startSourceSpan):XY(e.handle.slot,e.attributes,e.localRefs,e.startSourceSpan));break;case L.Container:We.replace(e,n.job.mode===is.DomOnly?GK(e.handle.slot,e.attributes,e.localRefs,e.wholeSourceSpan):YY(e.handle.slot,e.attributes,e.localRefs,e.wholeSourceSpan));break;case L.ContainerEnd:We.replace(e,n.job.mode===is.DomOnly?WK():KY());break;case L.I18nStart:We.replace(e,CK(e.handle.slot,e.messageIndex,e.subTemplateIndex,e.sourceSpan));break;case L.I18nEnd:We.replace(e,DK(e.sourceSpan));break;case L.I18n:We.replace(e,EK(e.handle.slot,e.messageIndex,e.subTemplateIndex,e.sourceSpan));break;case L.I18nAttributes:if(e.i18nAttributesConfig===null)throw new Error("AssertionError: i18nAttributesConfig was not set");We.replace(e,PK(e.handle.slot,e.i18nAttributesConfig));break;case L.Template:if(!(n instanceof cl))throw new Error("AssertionError: must be compiling a component");if(Array.isArray(e.localRefs))throw new Error("AssertionError: local refs array should have been extracted into a constant");let t=n.job.views.get(e.xref);We.replace(e,e.templateKind===ss.Block||n.job.mode===is.DomOnly?QK(e.handle.slot,Zn(t.fnName),t.decls,t.vars,e.tag,e.attributes,e.localRefs,e.startSourceSpan):ZY(e.handle.slot,Zn(t.fnName),t.decls,t.vars,e.tag,e.attributes,e.localRefs,e.startSourceSpan));break;case L.DisableBindings:We.replace(e,JY());break;case L.EnableBindings:We.replace(e,eK());break;case L.Pipe:We.replace(e,iK(e.handle.slot,e.name));break;case L.DeclareLet:We.replace(e,MK(e.handle.slot,e.sourceSpan));break;case L.AnimationString:We.replace(e,nZ(e.animationKind,e.expression,e.sanitizer,e.sourceSpan));break;case L.Animation:let o=j1(n,e.handlerFnName,e.handlerOps,!1);We.replace(e,tZ(e.animationKind,o,e.sanitizer,e.sourceSpan));break;case L.AnimationListener:let r=j1(n,e.handlerFnName,e.handlerOps,e.consumesDollarEvent);We.replace(e,iZ(e.animationKind,r,null,e.sourceSpan));break;case L.Listener:let a=j1(n,e.handlerFnName,e.handlerOps,e.consumesDollarEvent),c=e.eventTarget?uZ.get(e.eventTarget):null;if(c===void 0)throw new Error(`Unexpected global target '${e.eventTarget}' defined for '${e.name}' event. Supported list of global targets: window,document,body.`);We.replace(e,n.job.mode===is.DomOnly&&!e.hostListener&&!e.isLegacyAnimationListener?qK(e.name,a,c,e.sourceSpan):tK(e.name,a,c,e.hostListener&&e.isLegacyAnimationListener,e.sourceSpan));break;case L.TwoWayListener:We.replace(e,nK(e.name,j1(n,e.handlerFnName,e.handlerOps,!0),e.sourceSpan));break;case L.Variable:if(e.variable.name===null)throw new Error(`AssertionError: unnamed variable ${e.xref}`);We.replace(e,Bs(new Fr(e.variable.name,e.initializer,void 0,la.Final)));break;case L.Namespace:switch(e.active){case Sa.HTML:We.replace(e,oK());break;case Sa.SVG:We.replace(e,rK());break;case Sa.Math:We.replace(e,aK());break}break;case L.Defer:let m=!!e.loadingMinimumTime||!!e.loadingAfterTime||!!e.placeholderMinimumTime;We.replace(e,hK(e.handle.slot,e.mainSlot.slot,e.resolverFn,e.loadingSlot?.slot??null,e.placeholderSlot?.slot??null,e.errorSlot?.slot??null,e.loadingConfig,e.placeholderConfig,m,e.sourceSpan,e.flags));break;case L.DeferOn:let p=[];switch(e.trigger.kind){case oo.Never:case oo.Idle:case oo.Immediate:break;case oo.Timer:p=[Me(e.trigger.delay)];break;case oo.Viewport:e.modifier==="hydrate"?p=e.trigger.options?[e.trigger.options]:[]:(p=[Me(e.trigger.targetSlot?.slot??null)],e.trigger.targetSlotViewSteps!==0?p.push(Me(e.trigger.targetSlotViewSteps)):e.trigger.options&&p.push(Me(null)),e.trigger.options&&p.push(e.trigger.options));break;case oo.Interaction:case oo.Hover:e.modifier==="hydrate"?p=[]:(p=[Me(e.trigger.targetSlot?.slot??null)],e.trigger.targetSlotViewSteps!==0&&p.push(Me(e.trigger.targetSlotViewSteps)));break;default:throw new Error(`AssertionError: Unsupported reification of defer trigger kind ${e.trigger.kind}`)}We.replace(e,gK(e.trigger.kind,p,e.modifier,e.sourceSpan));break;case L.ProjectionDef:We.replace(e,_K(e.def));break;case L.Projection:if(e.handle.slot===null)throw new Error("No slot was assigned for project instruction");let h=null,g=null,S=null;if(e.fallbackView!==null){if(!(n instanceof cl))throw new Error("AssertionError: must be compiling a component");let P=n.job.views.get(e.fallbackView);if(P===void 0)throw new Error("AssertionError: projection had fallback view xref, but fallback view was not found");if(P.fnName===null||P.decls===null||P.vars===null)throw new Error("AssertionError: expected projection fallback view to have been named and counted");h=P.fnName,g=P.decls,S=P.vars}We.replace(e,vK(e.handle.slot,e.projectionSlotIndex,e.attributes,h,g,S,e.sourceSpan));break;case L.ConditionalCreate:if(!(n instanceof cl))throw new Error("AssertionError: must be compiling a component");if(Array.isArray(e.localRefs))throw new Error("AssertionError: local refs array should have been extracted into a constant");let x=n.job.views.get(e.xref);We.replace(e,bK(e.handle.slot,Zn(x.fnName),x.decls,x.vars,e.tag,e.attributes,e.localRefs,e.startSourceSpan));break;case L.ConditionalBranchCreate:if(!(n instanceof cl))throw new Error("AssertionError: must be compiling a component");if(Array.isArray(e.localRefs))throw new Error("AssertionError: local refs array should have been extracted into a constant");let v=n.job.views.get(e.xref);We.replace(e,xK(e.handle.slot,Zn(v.fnName),v.decls,v.vars,e.tag,e.attributes,e.localRefs,e.startSourceSpan));break;case L.RepeaterCreate:if(e.handle.slot===null)throw new Error("No slot was assigned for repeater instruction");if(!(n instanceof cl))throw new Error("AssertionError: must be compiling a component");let M=n.job.views.get(e.xref);if(M.fnName===null)throw new Error("AssertionError: expected repeater primary view to have been named");let w=null,y=null,k=null;if(e.emptyView!==null){let P=n.job.views.get(e.emptyView);if(P===void 0)throw new Error("AssertionError: repeater had empty view xref, but empty view was not found");if(P.fnName===null||P.decls===null||P.vars===null)throw new Error("AssertionError: expected repeater empty view to have been named and counted");w=P.fnName,y=P.decls,k=P.vars}We.replace(e,yK(e.handle.slot,M.fnName,e.decls,e.vars,e.tag,e.attributes,CZ(n,e),e.usesComponentInstance,w,y,k,e.emptyTag,e.emptyAttributes,e.wholeSourceSpan));break;case L.SourceLocation:let I=Qi(e.locations.map(({targetSlot:P,offset:R,line:D,column:N})=>{if(P.slot===null)throw new Error("No slot was assigned for source location");return Qi([Me(P.slot),Me(R),Me(D),Me(N)])}));We.replace(e,aZ(e.templatePath,I));break;case L.ControlCreate:We.replace(e,NK(e.sourceSpan));break;case L.Statement:break;default:throw new Error(`AssertionError: Unsupported reification of create op ${L[e.kind]}`)}}function ux(n,i){for(let e of i)switch(Ko(e,t=>rL(n,t),Wn.None),e.kind){case L.Advance:We.replace(e,sK(e.delta,e.sourceSpan));break;case L.Property:We.replace(e,n.job.mode===is.DomOnly&&e.bindingKind!==Ht.LegacyAnimation&&e.bindingKind!==Ht.Animation?yF(e):_Z(e));break;case L.Control:We.replace(e,vZ(e));break;case L.TwoWayProperty:We.replace(e,RK(e.name,e.expression,e.sanitizer,e.sourceSpan));break;case L.StyleProp:We.replace(e,LK(e.name,e.expression,e.unit,e.sourceSpan));break;case L.ClassProp:We.replace(e,BK(e.name,e.expression,e.sourceSpan));break;case L.StyleMap:We.replace(e,VK(e.expression,e.sourceSpan));break;case L.ClassMap:We.replace(e,zK(e.expression,e.sourceSpan));break;case L.I18nExpression:We.replace(e,ZK(e.expression,e.sourceSpan));break;case L.I18nApply:We.replace(e,JK(e.handle.slot,e.sourceSpan));break;case L.InterpolateText:We.replace(e,KK(e.interpolation.strings,e.interpolation.expressions,e.sourceSpan));break;case L.Attribute:We.replace(e,FK(e.name,e.expression,e.sanitizer,e.namespace,e.sourceSpan));break;case L.DomProperty:if(e.expression instanceof Yo)throw new Error("not yet handled");e.bindingKind===Ht.LegacyAnimation||e.bindingKind===Ht.Animation?We.replace(e,oZ(e.name,e.expression,e.sourceSpan)):We.replace(e,yF(e));break;case L.Variable:if(e.variable.name===null)throw new Error(`AssertionError: unnamed variable ${e.xref}`);We.replace(e,Bs(new Fr(e.variable.name,e.initializer,void 0,la.Final)));break;case L.Conditional:if(e.processed===null)throw new Error("Conditional test was not set.");We.replace(e,lZ(e.processed,e.contextValue,e.sourceSpan));break;case L.Repeater:We.replace(e,SK(e.collection,e.sourceSpan));break;case L.DeferWhen:We.replace(e,wK(e.modifier,e.expr,e.sourceSpan));break;case L.StoreLet:throw new Error(`AssertionError: unexpected storeLet ${e.declaredName}`);case L.Statement:break;default:throw new Error(`AssertionError: Unsupported reification of update op ${L[e.kind]}`)}}function yF(n){return eZ(hZ.get(n.name)??n.name,n.expression,n.sanitizer,n.sourceSpan)}function _Z(n){return H6(n.name)?IK(n.name,n.expression,n.sourceSpan):AK(n.name,n.expression,n.sanitizer,n.sourceSpan)}function vZ(n){return OK(n.sourceSpan)}function rL(n,i){if(!Ic(i))return i;switch(i.kind){case Yt.NextContext:return cK(i.steps);case Yt.Reference:return lK(i.targetSlot.slot+1+i.offset);case Yt.LexicalRead:throw new Error(`AssertionError: unresolved LexicalRead of ${i.name}`);case Yt.TwoWayBindingSet:throw new Error("AssertionError: unresolved TwoWayBindingSet");case Yt.RestoreView:if(typeof i.view=="number")throw new Error("AssertionError: unresolved RestoreView");return mK(i.view);case Yt.ResetView:return pK(i.expr);case Yt.GetCurrentView:return dK();case Yt.ReadVariable:if(i.name===null)throw new Error(`Read of unnamed variable ${i.xref}`);return Zn(i.name);case Yt.ReadTemporaryExpr:if(i.name===null)throw new Error(`Read of unnamed temporary ${i.xref}`);return Zn(i.name);case Yt.AssignTemporaryExpr:if(i.name===null)throw new Error(`Assign of unnamed temporary ${i.xref}`);return Zn(i.name).set(i.expr);case Yt.PureFunctionExpr:if(i.fn===null)throw new Error("AssertionError: expected PureFunctions to have been extracted");return rZ(i.varOffset,i.fn,i.args);case Yt.PureFunctionParameterExpr:throw new Error("AssertionError: expected PureFunctionParameterExpr to have been extracted");case Yt.PipeBinding:return XK(i.targetSlot.slot,i.varOffset,i.args);case Yt.PipeBindingVariadic:return YK(i.targetSlot.slot,i.varOffset,i.args);case Yt.SlotLiteralExpr:return Me(i.slot.slot);case Yt.ContextLetReference:return TK(i.targetSlot.slot);case Yt.StoreLet:return kK(i.value,i.sourceSpan);case Yt.TrackContext:return Zn("this");case Yt.ArrowFunction:if(i.varOffset===null)throw new Error("AssertionError: variable offset was not assigned to arrow function");return sZ(i.varOffset,n.job.pool.getSharedFunctionReference(bZ(n,i),"arrowFn"),Zn(Ls));default:throw new Error(`AssertionError: Unsupported reification of ir.Expression kind: ${Yt[i.kind]}`)}}function j1(n,i,e,t){ux(n,e);let o=[];for(let a of e){if(a.kind!==L.Statement)throw new Error(`AssertionError: expected reified statements, but found op ${L[a.kind]}`);o.push(a.statement)}let r=[];return t&&r.push(new Sr("$event",ls)),bm(r,o,void 0,void 0,i)}function CZ(n,i){if(i.trackByFn!==null)return i.trackByFn;let e=[new Sr("$index",iu),new Sr("$item",ls)],t;if(i.trackByOps===null)t=i.usesComponentInstance?bm(e,[new wr(i.track)]):Fs(e,i.track);else{ux(n,i.trackByOps);let o=[];for(let r of i.trackByOps){if(r.kind!==L.Statement)throw new Error(`AssertionError: expected reified statements, but found op ${L[r.kind]}`);o.push(r.statement)}t=i.usesComponentInstance||o.length!==1||!(o[0]instanceof wr)?bm(e,o):Fs(e,o[0].value)}return i.trackByFn=n.job.pool.getSharedFunctionReference(t,"_forTrack"),i.trackByFn}function bZ(n,i){ux(n,i.ops);let e=[];for(let o of i.ops){if(o.kind!==L.Statement)throw new Error(`AssertionError: expected reified statements, but found op ${L[o.kind]}`);e.push(o.statement)}let t=e.length===1&&e[0]instanceof wr?e[0].value:e;return Fs([new Sr(i.contextName,ls),new Sr(i.currentViewName,ls)],Fs(i.parameters,t))}function xZ(n){for(let i of n.units)for(let e of i.update)switch(e.kind){case L.Attribute:case L.Binding:case L.ClassProp:case L.ClassMap:case L.Property:case L.StyleProp:case L.StyleMap:e.expression instanceof F0&&We.remove(e);break}}function yZ(n){for(let i of n.units)for(let e of i.create)switch(e.kind){case L.I18nContext:We.remove(e);break;case L.I18nStart:e.context=null;break}}function SZ(n){for(let i of n.units)for(let e of i.update){if(e.kind!==L.Variable||e.variable.kind!==Qr.Identifier||!(e.initializer instanceof A0))continue;let t=e.variable.identifier,o=e;for(;o&&o.kind!==L.ListEnd;)Ko(o,r=>r instanceof Wr&&r.name===t?Me(void 0):r,Wn.None),o=o.prev}}function wZ(n){for(let i of n.units){let e=new Set;for(let t of i.update)t.kind===L.I18nExpression&&e.add(t.i18nOwner);for(let t of i.create)switch(t.kind){case L.I18nAttributes:if(e.has(t.xref))continue;We.remove(t)}}}function MZ(n){for(let i of n.units){for(let e of i.functions)W_(i,e.ops);W_(i,i.create),W_(i,i.update)}}function W_(n,i){let e=new Map;e.set(n.xref,Zn(Ls));for(let t of i)switch(t.kind){case L.Variable:t.variable.kind===Qr.Context&&e.set(t.variable.view,new Sd(t.xref));break;case L.Animation:case L.AnimationListener:case L.Listener:case L.TwoWayListener:W_(n,t.handlerOps);break;case L.RepeaterCreate:t.trackByOps!==null&&W_(n,t.trackByOps);break}n===n.job.root&&e.set(n.xref,Zn(Ls));for(let t of i)Ko(t,o=>{if(o instanceof km){if(!e.has(o.view))throw new Error(`No context found for reference to view ${o.view} from view ${n.xref}`);return e.get(o.view)}else return o},Wn.None)}function kZ(n){for(let i of n.units)for(let e of i.create)if(e.kind===L.Defer){if(e.resolverFn!==null)continue;if(e.ownResolverFn!==null){if(e.handle.slot===null)throw new Error("AssertionError: slot must be assigned before extracting defer deps functions");let t=i.fnName?.replace("_Template","");e.resolverFn=n.pool.getSharedFunctionReference(e.ownResolverFn,`${t}_Defer_${e.handle.slot}_DepsFn`,!1)}}}function TZ(n){for(let i of n.units)SF(i.create),SF(i.update)}function SF(n){for(let i of n)(i.kind===L.Listener||i.kind===L.TwoWayListener||i.kind===L.AnimationListener)&&Ko(i,e=>e instanceof Wr&&e.name==="$event"?((i.kind===L.Listener||i.kind===L.AnimationListener)&&(i.consumesDollarEvent=!0),new Gl(e.name)):e,Wn.InChildOperation)}function EZ(n){let i=new Map,e=new Map;for(let t of n.units)for(let o of t.create)switch(o.kind){case L.I18nContext:i.set(o.xref,o);break;case L.ElementStart:e.set(o.xref,o);break}_c(n,n.root,i,e)}function _c(n,i,e,t,o){let r=null,a=new Map;for(let c of i.create)switch(c.kind){case L.I18nStart:if(!c.context)throw Error("Could not find i18n context for i18n op");r={i18nBlock:c,i18nContext:e.get(c.context)};break;case L.I18nEnd:r=null;break;case L.ElementStart:if(c.i18nPlaceholder!==void 0){if(r===null)throw Error("i18n tag placeholder should only occur inside an i18n block");wF(c,r.i18nContext,r.i18nBlock,o),o&&c.i18nPlaceholder.closeName&&a.set(c.xref,o),o=void 0}break;case L.ElementEnd:let m=t.get(c.xref);if(m&&m.i18nPlaceholder!==void 0){if(r===null)throw Error("AssertionError: i18n tag placeholder should only occur inside an i18n block");MF(m,r.i18nContext,r.i18nBlock,a.get(c.xref)),a.delete(c.xref)}break;case L.Projection:if(c.i18nPlaceholder!==void 0){if(r===null)throw Error("i18n tag placeholder should only occur inside an i18n block");wF(c,r.i18nContext,r.i18nBlock,o),MF(c,r.i18nContext,r.i18nBlock,o),o=void 0}if(c.fallbackView!==null){let S=n.views.get(c.fallbackView);if(c.fallbackViewI18nPlaceholder===void 0)_c(n,S,e,t);else{if(r===null)throw Error("i18n tag placeholder should only occur inside an i18n block");$1(n,S,c.handle.slot,c.fallbackViewI18nPlaceholder,r.i18nContext,r.i18nBlock,o),_c(n,S,e,t),H1(n,S,c.handle.slot,c.fallbackViewI18nPlaceholder,r.i18nContext,r.i18nBlock,o),o=void 0}}break;case L.ConditionalCreate:case L.ConditionalBranchCreate:case L.Template:let p=n.views.get(c.xref);if(c.i18nPlaceholder===void 0)_c(n,p,e,t);else{if(r===null)throw Error("i18n tag placeholder should only occur inside an i18n block");c.templateKind===ss.Structural?_c(n,p,e,t,c):($1(n,p,c.handle.slot,c.i18nPlaceholder,r.i18nContext,r.i18nBlock,o),_c(n,p,e,t),H1(n,p,c.handle.slot,c.i18nPlaceholder,r.i18nContext,r.i18nBlock,o),o=void 0)}break;case L.RepeaterCreate:if(o!==void 0)throw Error("AssertionError: Unexpected structural directive associated with @for block");let h=c.handle.slot+1,g=n.views.get(c.xref);if(c.i18nPlaceholder===void 0)_c(n,g,e,t);else{if(r===null)throw Error("i18n tag placeholder should only occur inside an i18n block");$1(n,g,h,c.i18nPlaceholder,r.i18nContext,r.i18nBlock,o),_c(n,g,e,t),H1(n,g,h,c.i18nPlaceholder,r.i18nContext,r.i18nBlock,o),o=void 0}if(c.emptyView!==null){let S=c.handle.slot+2,x=n.views.get(c.emptyView);if(c.emptyI18nPlaceholder===void 0)_c(n,x,e,t);else{if(r===null)throw Error("i18n tag placeholder should only occur inside an i18n block");$1(n,x,S,c.emptyI18nPlaceholder,r.i18nContext,r.i18nBlock,o),_c(n,x,e,t),H1(n,x,S,c.emptyI18nPlaceholder,r.i18nContext,r.i18nBlock,o),o=void 0}}break}}function wF(n,i,e,t){let{startName:o,closeName:r}=n.i18nPlaceholder,a=po.ElementTag|po.OpenTag,c=n.handle.slot;t!==void 0&&(a|=po.TemplateTag,c={element:c,template:t.handle.slot}),r||(a|=po.CloseTag),lf(i.params,o,c,e.subTemplateIndex,a)}function MF(n,i,e,t){let{closeName:o}=n.i18nPlaceholder;if(o){let r=po.ElementTag|po.CloseTag,a=n.handle.slot;t!==void 0&&(r|=po.TemplateTag,a={element:a,template:t.handle.slot}),lf(i.params,o,a,e.subTemplateIndex,r)}}function $1(n,i,e,t,o,r,a){let{startName:c,closeName:m}=t,p=po.TemplateTag|po.OpenTag;m||(p|=po.CloseTag),a!==void 0&&lf(o.params,c,a.handle.slot,r.subTemplateIndex,p),lf(o.params,c,e,aL(n,r,i),p)}function H1(n,i,e,t,o,r,a){let{closeName:c}=t,m=po.TemplateTag|po.CloseTag;c&&(lf(o.params,c,e,aL(n,r,i),m),a!==void 0&&lf(o.params,c,a.handle.slot,r.subTemplateIndex,m))}function aL(n,i,e){for(let t of e.create)if(t.kind===L.I18nStart)return t.subTemplateIndex;return i.subTemplateIndex}function lf(n,i,e,t,o){let r=n.get(i)??[];r.push({value:e,subTemplateIndex:t,flags:o}),n.set(i,r)}function DZ(n){let i=new Map,e=new Map,t=new Map;for(let a of n.units)for(let c of a.create)switch(c.kind){case L.I18nStart:i.set(c.xref,c.subTemplateIndex);break;case L.I18nContext:e.set(c.xref,c);break;case L.IcuPlaceholder:t.set(c.xref,c);break}let o=new Map,r=a=>a.usage===mf.I18nText?a.i18nOwner:a.context;for(let a of n.units)for(let c of a.update)if(c.kind===L.I18nExpression){let m=o.get(r(c))||0,p=i.get(c.i18nOwner)??null,h={value:m,subTemplateIndex:p,flags:po.ExpressionIndex};PZ(c,h,e,t),o.set(r(c),m+1)}}function PZ(n,i,e,t){if(n.i18nPlaceholder!==null){let o=e.get(n.context),r=n.resolutionTime===P0.Creation?o.params:o.postprocessingParams,a=r.get(n.i18nPlaceholder)||[];a.push(i),r.set(n.i18nPlaceholder,a)}n.icuPlaceholder!==null&&t.get(n.icuPlaceholder)?.expressionPlaceholders.push(i)}function IZ(n){for(let i of n.units){for(let e of i.functions)q_(i,e.ops,null);q_(i,i.create,null),q_(i,i.update,null)}}function q_(n,i,e){let t=new Map,o=new Map;for(let r of i)switch(r.kind){case L.Variable:switch(r.variable.kind){case Qr.Identifier:if(r.variable.local){if(o.has(r.variable.identifier))continue;o.set(r.variable.identifier,r.xref)}else if(t.has(r.variable.identifier))continue;t.set(r.variable.identifier,r.xref);break;case Qr.Alias:if(t.has(r.variable.identifier))continue;t.set(r.variable.identifier,r.xref);break;case Qr.SavedView:e={view:r.variable.view,variable:r.xref};break}break;case L.Animation:case L.AnimationListener:case L.Listener:case L.TwoWayListener:q_(n,r.handlerOps,e);break;case L.RepeaterCreate:r.trackByOps!==null&&q_(n,r.trackByOps,e);break}for(let r of i)r.kind===L.Listener||r.kind===L.TwoWayListener||r.kind===L.Animation||r.kind===L.AnimationListener||Ko(r,a=>{if(a instanceof Wr)return o.has(a.name)?new Sd(o.get(a.name)):t.has(a.name)?new Sd(t.get(a.name)):new Rs(new km(n.job.root.xref),a.name);if(a instanceof N0&&typeof a.view=="number"){if(e===null||e.view!==a.view)throw new Error(`AssertionError: no saved view ${a.view} from view ${n.xref}`);return a.view=new Sd(e.variable),a}else return a},Wn.None);for(let r of i)hr(r,a=>{if(a instanceof Wr)throw new Error(`AssertionError: no lexical reads should remain, but found read of ${a.name}`)})}var AZ=new Map([[ro.HTML,he.sanitizeHtml],[ro.RESOURCE_URL,he.sanitizeResourceUrl],[ro.SCRIPT,he.sanitizeScript],[ro.STYLE,he.sanitizeStyle],[ro.URL,he.sanitizeUrl],[ro.ATTRIBUTE_NO_BINDING,he.validateAttribute]]),OZ=new Map([[ro.HTML,he.trustConstantHtml],[ro.RESOURCE_URL,he.trustConstantResourceUrl]]);function NZ(n){for(let i of n.units){if(n.kind!==Tt.Host){for(let e of i.create)if(e.kind===L.ExtractedAttribute){let t=OZ.get(kF(e.securityContext))??null;e.trustedValueFn=t!==null?Wt(t):null}}for(let e of i.update)switch(e.kind){case L.Property:case L.Attribute:case L.DomProperty:let t=null;Array.isArray(e.securityContext)&&e.securityContext.length===2&&e.securityContext.includes(ro.URL)&&e.securityContext.includes(ro.RESOURCE_URL)?t=he.sanitizeUrlOrResourceUrl:t=AZ.get(kF(e.securityContext))??null,e.sanitizer=t!==null?Wt(t):null;break}}}function kF(n){if(Array.isArray(n)){if(n.length>1)throw Error("AssertionError: Ambiguous security context");return n[0]||ro.NONE}return n}function RZ(n){for(let i of n.units){for(let e of i.functions)TF(n,i,e.ops)&&EF(i,e.ops,Zn(e.currentViewName));i.create.prepend([fm(i.job.allocateXrefId(),{kind:Qr.SavedView,name:null,view:i.xref},new eD,sl.None)]);for(let e of i.create)(e.kind===L.Listener||e.kind===L.TwoWayListener||e.kind===L.Animation||e.kind===L.AnimationListener)&&TF(n,i,e.handlerOps)&&EF(i,e.handlerOps,i.xref)}}function TF(n,i,e){let t=i!==n.root;if(!t)for(let o of e)hr(o,r=>{(r instanceof Rb||r instanceof O0)&&(t=!0)});return t}function EF(n,i,e){i.prepend([fm(n.job.allocateXrefId(),{kind:Qr.Context,name:null,view:n.xref},new N0(e),sl.None)]);for(let t of i)t.kind===L.Statement&&t.statement instanceof wr&&(t.statement.value=new Lb(t.statement.value))}function FZ(n){let i=new Map;for(let e of n.units){let t=0;for(let o of e.create)pf(o)&&(o.handle.slot=t,i.set(o.xref,o.handle.slot),t+=o.numSlotsUsed);e.decls=t}for(let e of n.units)for(let t of e.ops())if(t.kind===L.Template||t.kind===L.ConditionalCreate||t.kind===L.ConditionalBranchCreate||t.kind===L.RepeaterCreate){let o=n.views.get(t.xref);t.decls=o.decls}}function LZ(n){let i=new Set,e=new Map;for(let t of n.units)for(let o of t.ops())o.kind===L.DeclareLet&&e.set(o.xref,o),hr(o,r=>{r instanceof O0&&i.add(r.target)});for(let t of n.units)for(let o of t.update)Ko(o,r=>r instanceof A0&&!i.has(r.target)?(BZ(r)||We.remove(e.get(r.target)),r.value):r,Wn.None)}function BZ(n){let i=!1;return Ft(n,e=>((e instanceof fu||e instanceof R0)&&(i=!0),e),Wn.None),i}function VZ(n){let i=new Set;for(let e of n.units)for(let t of e.ops())hr(t,o=>{if(o instanceof fi)switch(o.operator){case lt.Exponentiation:zZ(o,i);break;case lt.NullishCoalesce:jZ(o,i);break;case lt.And:case lt.Or:$Z(o,i)}});for(let e of n.units)for(let t of e.ops())Ko(t,o=>o instanceof Wl?i.has(o)?o:o.expr:o,Wn.None)}function zZ(n,i){n.lhs instanceof Wl&&n.lhs.expr instanceof ru&&i.add(n.lhs)}function jZ(n,i){n.lhs instanceof Wl&&(DF(n.lhs.expr)||n.lhs.expr instanceof kc)&&i.add(n.lhs),n.rhs instanceof Wl&&(DF(n.rhs.expr)||n.rhs.expr instanceof kc)&&i.add(n.rhs)}function $Z(n,i){n.lhs instanceof Wl&&n.lhs.expr instanceof fi&&n.lhs.expr.operator===lt.NullishCoalesce&&i.add(n.lhs)}function DF(n){return n instanceof fi&&(n.operator===lt.And||n.operator===lt.Or)}function HZ(n){for(let i of n.units)for(let e of i.update)if(e.kind===L.Binding)switch(e.bindingKind){case Ht.ClassName:if(e.expression instanceof Yo)throw new Error("Unexpected interpolation in ClassName binding");We.replace(e,uq(e.target,e.name,e.expression,e.sourceSpan));break;case Ht.StyleProperty:We.replace(e,pq(e.target,e.name,e.expression,e.unit,e.sourceSpan));break;case Ht.Property:case Ht.Template:e.name==="style"?We.replace(e,hq(e.target,e.expression,e.sourceSpan)):e.name==="class"&&We.replace(e,fq(e.target,e.expression,e.sourceSpan));break}}function UZ(n){for(let i of n.units){i.create.prepend(Q_(i.create)),i.update.prepend(Q_(i.update));for(let e of i.functions)e.ops.prepend(Q_(e.ops))}}function Q_(n){let i=0,e=[];for(let t of n){let o=new Map;hr(t,(p,h)=>{h&Wn.InChildOperation||p instanceof Em&&o.set(p.xref,p)});let r=0,a=new Set,c=new Set,m=new Map;hr(t,(p,h)=>{h&Wn.InChildOperation||(p instanceof Ac?(a.has(p.xref)||(a.add(p.xref),m.set(p.xref,`tmp_${i}_${r++}`)),PF(m,p)):p instanceof Em&&(o.get(p.xref)===p&&(c.add(p.xref),r--),PF(m,p)))}),e.push(...Array.from(new Set(m.values())).map(p=>Bs(new Fr(p)))),i++,t.kind===L.Listener||t.kind===L.Animation||t.kind===L.AnimationListener||t.kind===L.TwoWayListener?t.handlerOps.prepend(Q_(t.handlerOps)):t.kind===L.RepeaterCreate&&t.trackByOps!==null&&t.trackByOps.prepend(Q_(t.trackByOps))}return e}function PF(n,i){let e=n.get(i.xref);if(e===void 0)throw new Error(`Found xref with unassigned name: ${i.xref}`);i.name=e}function GZ(n){for(let i of n.units)for(let e of i.create)if(e.kind===L.RepeaterCreate)if(e.track instanceof Gl&&e.track.name==="$index")e.trackByFn=Wt(he.repeaterTrackByIndex);else if(e.track instanceof Gl&&e.track.name==="$item")e.trackByFn=Wt(he.repeaterTrackByIdentity);else if(WZ(n.root.xref,e.track))e.usesComponentInstance=!0,e.track.receiver.receiver.view===i.xref?e.trackByFn=e.track.receiver:(e.trackByFn=Wt(he.componentInstance).callFn([]).prop(e.track.receiver.name),e.track=e.trackByFn);else{e.track=Ft(e.track,o=>{if(o instanceof fu||o instanceof R0)throw new Error("Illegal State: Pipes are not allowed in this context");return o instanceof km?(e.usesComponentInstance=!0,new JE(o.view)):o},Wn.None);let t=new We;t.push(Bs(new wr(e.track,e.track.sourceSpan))),e.trackByOps=t}}function WZ(n,i){if(!(i instanceof cs)||i.args.length===0||i.args.length>2||!(i.receiver instanceof Rs&&i.receiver.receiver instanceof km)||i.receiver.receiver.view!==n)return!1;let[e,t]=i.args;return!(e instanceof Gl)||e.name!=="$index"?!1:i.args.length===1?!0:!(!(t instanceof Gl)||t.name!=="$item")}function qZ(n){for(let i of n.units)for(let e of i.create)e.kind===L.RepeaterCreate&&(e.track=Ft(e.track,t=>{if(t instanceof Wr){if(e.varNames.$index.has(t.name))return Zn("$index");if(t.name===e.varNames.$implicit)return Zn("$item")}return t},Wn.None))}function QZ(n){for(let i of n.units)for(let e of i.create)e.kind===L.TwoWayListener&&Ko(e,t=>{if(!(t instanceof Bb))return t;let{target:o,value:r}=t;if(o instanceof Rs||o instanceof wd)return bF(o,r).or(o.set(r));if(o instanceof Sd)return bF(o,r);throw new Error("Unsupported expression in two-way action binding.")},Wn.InChildOperation)}function XZ(n){for(let i of n.units){let e=0;for(let r of i.ops())eE(r)&&(e+=YZ(r));let t=r=>{Ic(r)&&(r instanceof hu||(FR(r)&&(r.varOffset=e),eE(r)&&(e+=IF(r))))},o=r=>{!Ic(r)||!(r instanceof hu)||(FR(r)&&(r.varOffset=e),eE(r)&&(e+=IF(r)))};for(let r of i.create)hr(r,t);for(let r of i.update)hr(r,t);for(let r of i.create)hr(r,o);for(let r of i.update)hr(r,o);i.vars=e}if(n instanceof B0)for(let i of n.units)for(let e of i.create){if(e.kind!==L.Template&&e.kind!==L.RepeaterCreate&&e.kind!==L.ConditionalCreate&&e.kind!==L.ConditionalBranchCreate)continue;let t=n.views.get(e.xref);e.vars=t.vars}}function YZ(n){let i;switch(n.kind){case L.Attribute:return i=1,n.expression instanceof Yo&&!KZ(n.expression)&&(i+=n.expression.expressions.length),i;case L.Property:case L.DomProperty:return i=1,n.expression instanceof Yo&&(i+=n.expression.expressions.length),i;case L.Control:return 2;case L.TwoWayProperty:return 1;case L.StyleProp:case L.ClassProp:case L.StyleMap:case L.ClassMap:return i=2,n.expression instanceof Yo&&(i+=n.expression.expressions.length),i;case L.InterpolateText:return n.interpolation.expressions.length;case L.I18nExpression:case L.Conditional:case L.DeferWhen:case L.StoreLet:return 1;case L.RepeaterCreate:return n.emptyView?1:0;default:throw new Error(`Unhandled op: ${L[n.kind]}`)}}function IF(n){switch(n.kind){case Yt.PureFunctionExpr:return 1+n.args.length;case Yt.PipeBinding:return 1+n.args.length;case Yt.PipeBindingVariadic:return 1+n.numArgs;case Yt.StoreLet:case Yt.ArrowFunction:return 1;default:throw new Error(`AssertionError: unhandled ConsumesVarsTrait expression ${n.constructor.name}`)}}function KZ(n){return!(n.expressions.length!==1||n.strings.length!==2||n.strings[0]!==""||n.strings[1]!=="")}function ZZ(n){for(let i of n.units){for(let e of i.functions)A_(e.ops);A_(i.create),A_(i.update);for(let e of i.create)e.kind===L.Listener||e.kind===L.Animation||e.kind===L.AnimationListener||e.kind===L.TwoWayListener?A_(e.handlerOps):e.kind===L.RepeaterCreate&&e.trackByOps!==null&&A_(e.trackByOps);for(let e of i.functions)O_(e.ops,null),AF(e.ops);for(let e of i.create)e.kind===L.Listener||e.kind===L.Animation||e.kind===L.AnimationListener||e.kind===L.TwoWayListener?(O_(e.handlerOps,U1),AF(e.handlerOps)):e.kind===L.RepeaterCreate&&e.trackByOps!==null&&O_(e.trackByOps,U1);O_(i.create,U1),O_(i.update,U1)}}var Rr=(function(n){return n[n.None=0]="None",n[n.ViewContextRead=1]="ViewContextRead",n[n.ViewContextWrite=2]="ViewContextWrite",n[n.SideEffectful=4]="SideEffectful",n})(Rr||{});function U1(n){return!(n&Wn.InArrowFunctionOperation)}function A_(n){let i=new Map;for(let e of n)e.kind===L.Variable&&e.flags&sl.AlwaysInline&&(hr(e,t=>{if(Ic(t)&&mP(t)!==Rr.None)throw new Error("AssertionError: A context-sensitive variable was marked AlwaysInline")}),i.set(e.xref,e)),Ko(e,t=>t instanceof Sd&&i.has(t.xref)?i.get(t.xref).initializer.clone():t,Wn.None);for(let e of i.values())We.remove(e)}function O_(n,i){let e=new Map,t=new Map,o=new Set,r=new Map;for(let p of n){if(p.kind===L.Variable){if(e.has(p.xref)||t.has(p.xref))throw new Error(`Should not see two declarations of the same variable: ${p.xref}`);e.set(p.xref,p),t.set(p.xref,0)}r.set(p,JZ(p,i)),eJ(p,t,o,i)}let a=!1;for(let p of n.reversed()){let h=r.get(p);if(p.kind===L.Variable&&t.get(p.xref)===0){if(a&&h.fences&Rr.ViewContextWrite||h.fences&Rr.SideEffectful){let g=Bs(p.initializer.toStmt());r.set(g,h),We.replace(p,g)}else tJ(p,t),We.remove(p);r.delete(p),e.delete(p.xref),t.delete(p.xref);continue}h.fences&Rr.ViewContextRead&&(a=!0)}let c=[];for(let[p,h]of t){let S=!!(e.get(p).flags&sl.AlwaysInline);h!==1||S||o.has(p)||c.push(p)}let m;for(;m=c.pop();){let p=e.get(m),h=r.get(p);if(!!(p.flags&sl.AlwaysInline))throw new Error("AssertionError: Found an 'AlwaysInline' variable after the always inlining pass.");for(let S=p.next;S.kind!==L.ListEnd;S=S.next){let x=r.get(S);if(x.variablesUsed.has(m)){if(!iJ(p,S))break;if(nJ(m,p.initializer,S,h.fences)){x.variablesUsed.delete(m);for(let v of h.variablesUsed)x.variablesUsed.add(v);x.fences|=h.fences,e.delete(m),t.delete(m),r.delete(p),We.remove(p)}break}if(!sL(x.fences,h.fences))break}}}function mP(n){switch(n.kind){case Yt.NextContext:return Rr.ViewContextRead|Rr.ViewContextWrite;case Yt.RestoreView:return Rr.ViewContextRead|Rr.ViewContextWrite|Rr.SideEffectful;case Yt.StoreLet:return Rr.SideEffectful;case Yt.Reference:case Yt.ContextLetReference:return Rr.ViewContextRead;default:return Rr.None}}function JZ(n,i){let e=Rr.None,t=new Set;return hr(n,(o,r)=>{!Ic(o)||i!==null&&!i(r)||(o.kind===Yt.ReadVariable?t.add(o.xref):e|=mP(o))}),{fences:e,variablesUsed:t}}function eJ(n,i,e,t){hr(n,(o,r)=>{if(!Ic(o)||t!==null&&!t(r)||o.kind!==Yt.ReadVariable)return;let a=i.get(o.xref);a!==void 0&&(i.set(o.xref,a+1),r&Wn.InChildOperation&&e.add(o.xref))})}function tJ(n,i){hr(n,e=>{if(!Ic(e)||e.kind!==Yt.ReadVariable)return;let t=i.get(e.xref);if(t!==void 0){if(t===0)throw new Error(`Inaccurate variable count: ${e.xref} - found another read but count is already 0`);i.set(e.xref,t-1)}})}function sL(n,i){if(n&Rr.ViewContextWrite){if(i&Rr.ViewContextRead)return!1}else if(n&Rr.ViewContextRead&&i&Rr.ViewContextWrite)return!1;return!0}function nJ(n,i,e,t){let o=!1,r=!0;return Ko(e,(a,c)=>{if(!Ic(a)||o||!r)return a;if(c&Wn.InChildOperation&&t&Rr.ViewContextRead)return a;switch(a.kind){case Yt.ReadVariable:if(a.xref===n)return o=!0,i;break;default:let m=mP(a);r=r&&sL(m,t);break}return a},Wn.None),o}function iJ(n,i){switch(n.variable.kind){case Qr.Identifier:return n.initializer instanceof Gl&&n.initializer.name===Ls;case Qr.Context:return i.kind===L.Variable;default:return!0}}function AF(n){let i=n.head.next,e=n.tail.prev;i!==null&&e!==null&&i.next===e&&i.kind===L.Statement&&i.statement instanceof ma&&i.statement.expr instanceof N0&&e.kind===L.Statement&&e.statement instanceof wr&&e.statement.value instanceof Lb&&(We.remove(i),e.statement.value=e.statement.value.expr)}function oJ(n){for(let i of n.units){let e=null,t=null;for(let o of i.create)switch(o.kind){case L.I18nStart:e=o;break;case L.I18nEnd:e=null;break;case L.IcuStart:e===null&&(t=n.allocateXrefId(),We.insertBefore(mx(t,o.message,void 0,null),o));break;case L.IcuEnd:t!==null&&(We.insertAfter(px(t,null),o),t=null);break}}}function rJ(n){for(let i of n.units){for(let e of i.create)e.kind!==L.Animation&&e.kind!==L.AnimationListener&&e.kind!==L.Listener&&e.kind!==L.TwoWayListener&&OF(i,e);for(let e of i.update)OF(i,e)}}function OF(n,i){Ko(i,(e,t)=>{if(!(e instanceof vu)||t&Wn.InChildOperation)return e;if(Array.isArray(e.body))throw new Error("AssertionError: unexpected multi-line arrow function");let o=new tD(e.params,e.body);return n.functions.add(o),o},Wn.None)}var aJ=new Set(["formField"]);function sJ(n){for(let i of n.units)lJ(i)}function lJ(n){for(let i of n.update)i.kind===L.Property&&aJ.has(i.name)&&pJ(n,i)}var cJ=new Set([L.Container,L.ContainerStart,L.ContainerEnd,L.Element,L.ElementStart,L.ElementEnd,L.Template]);function dJ(n){return cJ.has(n.kind)}function mJ(n,i){let e=null;for(let t of n.create)!dJ(t)||t.xref!==i||(e=t);return e}function pJ(n,i){let e=mJ(n,i.target);if(e===null)throw new Error(`No create instruction found for control target ${i.target}`);let t=jq(i.sourceSpan);We.insertAfter(t,e),We.insertAfter(xq(i.target,i.sourceSpan),i)}var uJ=[{kind:Tt.Tmpl,fn:AY},{kind:Tt.Both,fn:GY},{kind:Tt.Host,fn:GQ},{kind:Tt.Tmpl,fn:fY},{kind:Tt.Tmpl,fn:VY},{kind:Tt.Tmpl,fn:oJ},{kind:Tt.Both,fn:uQ},{kind:Tt.Both,fn:HZ},{kind:Tt.Both,fn:Zq},{kind:Tt.Tmpl,fn:sJ},{kind:Tt.Both,fn:cQ},{kind:Tt.Both,fn:Xq},{kind:Tt.Tmpl,fn:pQ},{kind:Tt.Both,fn:_Y},{kind:Tt.Tmpl,fn:xZ},{kind:Tt.Both,fn:tQ},{kind:Tt.Both,fn:IY},{kind:Tt.Tmpl,fn:nQ},{kind:Tt.Tmpl,fn:RY},{kind:Tt.Tmpl,fn:hQ},{kind:Tt.Tmpl,fn:BY},{kind:Tt.Both,fn:rJ},{kind:Tt.Both,fn:$Y},{kind:Tt.Tmpl,fn:jQ},{kind:Tt.Tmpl,fn:zQ},{kind:Tt.Tmpl,fn:$Q},{kind:Tt.Tmpl,fn:RZ},{kind:Tt.Both,fn:Hq},{kind:Tt.Both,fn:TZ},{kind:Tt.Tmpl,fn:qZ},{kind:Tt.Tmpl,fn:SZ},{kind:Tt.Both,fn:IZ},{kind:Tt.Tmpl,fn:fQ},{kind:Tt.Tmpl,fn:QZ},{kind:Tt.Tmpl,fn:GZ},{kind:Tt.Both,fn:MZ},{kind:Tt.Both,fn:NZ},{kind:Tt.Tmpl,fn:uY},{kind:Tt.Both,fn:bQ},{kind:Tt.Both,fn:VZ},{kind:Tt.Both,fn:UZ},{kind:Tt.Both,fn:ZZ},{kind:Tt.Both,fn:LZ},{kind:Tt.Tmpl,fn:pY},{kind:Tt.Tmpl,fn:mQ},{kind:Tt.Tmpl,fn:wZ},{kind:Tt.Tmpl,fn:qq},{kind:Tt.Tmpl,fn:Gq},{kind:Tt.Tmpl,fn:FZ},{kind:Tt.Tmpl,fn:EZ},{kind:Tt.Tmpl,fn:DZ},{kind:Tt.Tmpl,fn:RQ},{kind:Tt.Tmpl,fn:sY},{kind:Tt.Tmpl,fn:HQ},{kind:Tt.Both,fn:aQ},{kind:Tt.Tmpl,fn:yZ},{kind:Tt.Both,fn:XZ},{kind:Tt.Tmpl,fn:VQ},{kind:Tt.Both,fn:vY},{kind:Tt.Tmpl,fn:kZ},{kind:Tt.Tmpl,fn:xY},{kind:Tt.Tmpl,fn:SY},{kind:Tt.Tmpl,fn:CQ},{kind:Tt.Tmpl,fn:Qq},{kind:Tt.Tmpl,fn:MY},{kind:Tt.Both,fn:jY},{kind:Tt.Both,fn:fZ},{kind:Tt.Both,fn:eQ}];function lL(n,i){for(let e of uJ)(e.kind===i||e.kind===Tt.Both)&&e.fn(n)}function hJ(n,i){let e=dL(n.root);return cL(n.root,i),e}function cL(n,i){for(let e of n.job.units){if(e.parent!==n.xref)continue;cL(e,i);let t=dL(e);i.statements.push(t.toDeclStmt(t.name))}}function dL(n){if(n.fnName===null)throw new Error(`AssertionError: view ${n.xref} is unnamed`);let i=[];for(let r of n.create){if(r.kind!==L.Statement)throw new Error(`AssertionError: expected all create ops to have been compiled, but got ${L[r.kind]}`);i.push(r.statement)}let e=[];for(let r of n.update){if(r.kind!==L.Statement)throw new Error(`AssertionError: expected all update ops to have been compiled, but got ${L[r.kind]}`);e.push(r.statement)}let t=ex(1,i),o=ex(2,e);return bm([new Sr(cf,iu),new Sr(Ls,ls)],[...t,...o],void 0,void 0,n.fnName)}function ex(n,i){return i.length===0?[]:[sx(new fi(lt.BitwiseAnd,Zn(cf),Me(n)),i)]}function fJ(n){if(n.root.fnName===null)throw new Error("AssertionError: host binding function is unnamed");let i=[];for(let r of n.root.create){if(r.kind!==L.Statement)throw new Error(`AssertionError: expected all create ops to have been compiled, but got ${L[r.kind]}`);i.push(r.statement)}let e=[];for(let r of n.root.update){if(r.kind!==L.Statement)throw new Error(`AssertionError: expected all update ops to have been compiled, but got ${L[r.kind]}`);e.push(r.statement)}if(i.length===0&&e.length===0)return null;let t=ex(1,i),o=ex(2,e);return bm([new Sr(cf,iu),new Sr(Ls,ls)],[...t,...o],void 0,void 0,n.root.fnName)}var tu=new sf,nu="ng-template",gJ="animate.";function Z1(n){return n instanceof Ua}function _J(n){return Z1(n)&&n.nodes.length===1&&n.nodes[0]instanceof Eb}function vJ(n,i,e,t,o,r,a,c,m,p){let h=new B0(n,e,t,o,r,a,c,m,p);return kd(h.root,i),h}function CJ(n,i,e){let t=new Ub(n.componentName,e,is.DomOnly);for(let o of n.properties??[]){let r=Ht.Property;o.name.startsWith("attr.")&&(o.name=o.name.substring(5),r=Ht.Attribute),o.isLegacyAnimation&&(r=Ht.LegacyAnimation),o.isAnimation&&(r=Ht.Animation);let a=i.calcPossibleSecurityContexts(n.componentSelector,o.name,r===Ht.Attribute).filter(c=>c!==ro.NONE);bJ(t,o,r,a)}for(let[o,r]of Object.entries(n.attributes)??[]){let a=i.calcPossibleSecurityContexts(n.componentSelector,o,!0).filter(c=>c!==ro.NONE);xJ(t,o,r,a)}for(let o of n.events??[])yJ(t,o);return t}function bJ(n,i,e,t){let o,r=i.expression.ast;r instanceof Q0?o=new Yo(r.strings,r.expressions.map(a=>In(a,n,i.sourceSpan)),[]):o=In(r,n,i.sourceSpan),n.root.update.push(uu(n.root.xref,e,i.name,o,null,t,!1,!1,null,null,i.sourceSpan))}function xJ(n,i,e,t){let o=uu(n.root.xref,Ht.Attribute,i,e,null,t,!0,!1,null,null,e.sourceSpan);n.root.update.push(o)}function yJ(n,i){let e;if(i.type===Ha.Animation)e=B6(n.root.xref,new pa,i.name,null,H0(n.root,i.handler,i.handlerSpan),i.name.endsWith("enter")?"enter":"leave",i.targetOrPhase,!0,i.sourceSpan);else{let[t,o]=i.type!==Ha.LegacyAnimation?[null,i.targetOrPhase]:[i.targetOrPhase,null];e=lP(n.root.xref,new pa,i.name,null,H0(n.root,i.handler,i.handlerSpan),t,o,!0,i.sourceSpan)}n.root.create.push(e)}function kd(n,i){for(let e of i)if(e instanceof Dc)SJ(n,e);else if(e instanceof Os)wJ(n,e);else if(e instanceof Jh)MJ(n,e);else if(e instanceof qp)mL(n,e,null);else if(e instanceof Yh)pL(n,e,null);else if(e instanceof kb)kJ(n,e);else if(e instanceof Mb)TJ(n,e);else if(e instanceof mu)EJ(n,e);else if(e instanceof c6)PJ(n,e);else if(e instanceof Zh)IJ(n,e);else if(e instanceof ZD)OJ(n,e);else if(!(e instanceof $_))throw new Error(`Unsupported template node: ${e.constructor.name}`)}function SJ(n,i){if(i.i18n!==void 0&&!(i.i18n instanceof Ua||i.i18n instanceof ym))throw Error(`Unhandled i18n metadata type for element: ${i.i18n.constructor.name}`);let e=n.job.allocateXrefId(),[t,o]=Ql(i.name),r=Sq(o,e,U6(t),i.i18n instanceof ym?i.i18n:void 0,i.startSourceSpan,i.sourceSpan);n.create.push(r),RJ(n,r,i),fL(r,i);let a=null;i.i18n instanceof Ua&&(a=n.job.allocateXrefId(),n.create.push(mx(a,i.i18n,void 0,i.startSourceSpan))),kd(n,i.children);let c=Mq(e,i.endSourceSpan??i.startSourceSpan);n.create.push(c),a!==null&&We.insertBefore(px(a,i.endSourceSpan??i.startSourceSpan),c)}function wJ(n,i){if(i.i18n!==void 0&&!(i.i18n instanceof Ua||i.i18n instanceof ym))throw Error(`Unhandled i18n metadata type for template: ${i.i18n.constructor.name}`);let e=n.job.allocateView(n.xref),t=i.tagName,o="";i.tagName&&([o,t]=Ql(i.tagName));let r=i.i18n instanceof ym?i.i18n:void 0,a=U6(o),c=t===null?"":rQ(t,a),m=NJ(i)?ss.NgTemplate:ss.Structural,p=N6(e.xref,m,t,c,a,r,i.startSourceSpan,i.sourceSpan);n.create.push(p),FJ(n,p,i,m),fL(p,i),kd(e,i.children);for(let{name:h,value:g}of i.variables)e.contextVariables.set(h,g!==""?g:"$implicit");if(m===ss.NgTemplate&&i.i18n instanceof Ua){let h=n.job.allocateXrefId();We.insertAfter(mx(h,i.i18n,void 0,i.startSourceSpan),e.create.head),We.insertBefore(px(h,i.endSourceSpan??i.startSourceSpan),e.create.tail)}}function MJ(n,i){if(i.i18n!==void 0&&!(i.i18n instanceof ym))throw Error(`Unhandled i18n metadata type for element: ${i.i18n.constructor.name}`);let e=null;i.children.some(r=>!(r instanceof cx)&&(!(r instanceof qp)||r.value.trim().length>0))&&(e=n.job.allocateView(n.xref),kd(e,i.children));let t=n.job.allocateXrefId(),o=Oq(t,i.selector,i.i18n,e?.xref??null,i.sourceSpan);for(let r of i.attributes){let a=tu.securityContext(i.name,r.name,!0);n.update.push(uu(o.xref,Ht.Attribute,r.name,Me(r.value),null,a,!0,!1,null,xd(r.i18n),r.sourceSpan))}n.create.push(o)}function mL(n,i,e){n.create.push(L6(n.job.allocateXrefId(),i.value,e,i.sourceSpan))}function pL(n,i,e){let t=i.value;if(t instanceof as&&(t=t.ast),!(t instanceof Q0))throw new Error(`AssertionError: expected Interpolation for BoundText node, got ${t.constructor.name}`);if(i.i18n!==void 0&&!(i.i18n instanceof yd))throw Error(`Unhandled i18n metadata type for text interpolation: ${i.i18n?.constructor.name}`);let o=i.i18n instanceof yd?i.i18n.children.filter(a=>a instanceof w0).map(a=>a.name):[];if(o.length>0&&o.length!==t.expressions.length)throw Error(`Unexpected number of i18n placeholders (${t.expressions.length}) for BoundText with ${t.expressions.length} expressions`);let r=n.job.allocateXrefId();n.create.push(L6(r,"",e,i.sourceSpan)),n.update.push(cq(r,new Yo(t.strings,t.expressions.map(a=>In(a,n.job,null)),o),i.sourceSpan))}function kJ(n,i){let e=null,t=[];for(let o=0;oS.modifier==="none")||h.some(S=>S.modifier==="none")||p.push(pm(c,{kind:oo.Idle},"none",null)),n.create.push(p),n.update.push(h)}function DJ(n){return Object.keys(n.hydrateTriggers).length>0?1:null}function aE(n,i,e,t,o,r){if(i.idle!==void 0){let a=pm(r,{kind:oo.Idle},n,i.idle.sourceSpan);e.push(a)}if(i.immediate!==void 0){let a=pm(r,{kind:oo.Immediate},n,i.immediate.sourceSpan);e.push(a)}if(i.timer!==void 0){let a=pm(r,{kind:oo.Timer,delay:i.timer.delay},n,i.timer.sourceSpan);e.push(a)}if(i.hover!==void 0){let a=pm(r,{kind:oo.Hover,targetName:i.hover.reference,targetXref:null,targetSlot:null,targetView:null,targetSlotViewSteps:null},n,i.hover.sourceSpan);e.push(a)}if(i.interaction!==void 0){let a=pm(r,{kind:oo.Interaction,targetName:i.interaction.reference,targetXref:null,targetSlot:null,targetView:null,targetSlotViewSteps:null},n,i.interaction.sourceSpan);e.push(a)}if(i.viewport!==void 0){let a=pm(r,{kind:oo.Viewport,targetName:i.viewport.reference,targetXref:null,targetSlot:null,targetView:null,targetSlotViewSteps:null,options:i.viewport.options?In(i.viewport.options,o.job,i.viewport.sourceSpan):null},n,i.viewport.sourceSpan);e.push(a)}if(i.never!==void 0){let a=pm(r,{kind:oo.Never},n,i.never.sourceSpan);e.push(a)}if(i.when!==void 0){if(i.when.value instanceof Q0)throw new Error("Unexpected interpolation in defer block when trigger");let a=vq(r,In(i.when.value,o.job,i.when.sourceSpan),n,i.when.sourceSpan);t.push(a)}}function PJ(n,i){if(i.i18n instanceof Ua&&_J(i.i18n)){let e=n.job.allocateXrefId();n.create.push(Lq(e,i.i18n,p6(i.i18n).name,null));for(let[t,o]of Object.entries(W(W({},i.vars),i.placeholders)))o instanceof Yh?pL(n,o,t):mL(n,o,t);n.create.push(Bq(e))}else throw Error(`Unhandled i18n metadata type for ICU: ${i.i18n?.constructor.name}`)}function IJ(n,i){let e=n.job.allocateView(n.xref),t=`\u0275$index_${e.xref}`,o=`\u0275$count_${e.xref}`,r=new Set;e.contextVariables.set(i.item.name,i.item.value);for(let y of i.contextVariables)y.value==="$index"&&r.add(y.name),y.name==="$index"?e.contextVariables.set("$index",y.value).set(t,y.value):y.name==="$count"?e.contextVariables.set("$count",y.value).set(o,y.value):e.aliases.add({kind:Qr.Alias,name:null,identifier:y.name,expression:AJ(y,t,o)});let a=Or(i.trackBy.span,i.sourceSpan),c=In(i.trackBy,n.job,a);kd(e,i.children);let m=null,p=null;i.empty!==null&&(m=n.job.allocateView(n.xref),kd(m,i.empty.children),p=tx(n,m.xref,i.empty));let h={$index:r,$implicit:i.item.name};if(i.i18n!==void 0&&!(i.i18n instanceof Sm))throw Error("AssertionError: Unhandled i18n metadata type or @for");if(i.empty?.i18n!==void 0&&!(i.empty.i18n instanceof Sm))throw Error("AssertionError: Unhandled i18n metadata type or @empty");let g=i.i18n,S=i.empty?.i18n,x=tx(n,e.xref,i),v=wq(e.xref,m?.xref??null,x,c,h,p,g,S,i.startSourceSpan,i.sourceSpan);n.create.push(v);let M=In(i.expression,n.job,Or(i.expression.span,i.sourceSpan)),w=_q(v.xref,v.handle,M,i.sourceSpan);n.update.push(w)}function AJ(n,i,e){switch(n.value){case"$index":return new Wr(i);case"$count":return new Wr(e);case"$first":return new Wr(i).identical(Me(0));case"$last":return new Wr(i).identical(new Wr(e).minus(Me(1)));case"$even":return new Wr(i).modulo(Me(2)).identical(Me(0));case"$odd":return new Wr(i).modulo(Me(2)).notIdentical(Me(0));default:throw new Error(`AssertionError: unknown @for loop variable ${n.value}`)}}function OJ(n,i){let e=n.job.allocateXrefId();n.create.push(Rq(e,i.name,i.sourceSpan)),n.update.push(bq(e,i.name,In(i.value,n.job,i.valueSpan),i.sourceSpan))}function In(n,i,e){if(n instanceof as)return In(n.ast,i,e);if(n instanceof yc)return n.receiver instanceof Ec?new Wr(n.name):new Rs(In(n.receiver,i,e),n.name,null,Or(n.span,e));if(n instanceof Qh){if(n.receiver instanceof Ec)throw new Error("Unexpected ImplicitReceiver");return new cs(In(n.receiver,i,e),n.args.map(t=>In(t,i,e)),void 0,Or(n.span,e))}else{if(n instanceof os)return Me(n.value,void 0,Or(n.span,e));if(n instanceof zh)switch(n.operator){case"+":return new ru(Y_.Plus,In(n.expr,i,e),void 0,Or(n.span,e));case"-":return new ru(Y_.Minus,In(n.expr,i,e),void 0,Or(n.span,e));default:throw new Error(`AssertionError: unknown unary operator ${n.operator}`)}else if(n instanceof Ba){let t=iQ.get(n.operation);if(t===void 0)throw new Error(`AssertionError: unknown binary operator ${n.operation}`);return new fi(t,In(n.left,i,e),In(n.right,i,e),void 0,Or(n.span,e))}else{if(n instanceof o0)return new km(i.root.xref);if(n instanceof cu)return new wd(In(n.receiver,i,e),In(n.key,i,e),void 0,Or(n.span,e));if(n instanceof qh)throw new Error("AssertionError: Chain in unknown context");if(n instanceof du){let t=n.keys.map((o,r)=>{let a=In(n.values[r],i,e);return o.kind==="spread"?new Cm(a):new Gh(o.key,a,o.quoted)});return new ql(t,void 0,Or(n.span,e))}else{if(n instanceof s0)return new Tc(n.expressions.map(t=>In(t,i,e)));if(n instanceof ub)return new kc(In(n.condition,i,e),In(n.trueExp,i,e),In(n.falseExp,i,e),void 0,Or(n.span,e));if(n instanceof m0)return In(n.expression,i,e);if(n instanceof hb)return new fu(i.allocateXrefId(),new pa,n.name,[In(n.exp,i,e),...n.args.map(t=>In(t,i,e))]);if(n instanceof a0)return new of(In(n.receiver,i,e),In(n.key,i,e),Or(n.span,e));if(n instanceof r0)return new nf(In(n.receiver,i,e),n.name);if(n instanceof gb)return new gu(In(n.receiver,i,e),n.args.map(t=>In(t,i,e)));if(n instanceof xa)return new F0(Or(n.span,e));if(n instanceof l0)return yG(In(n.expression,i,e),Or(n.span,e));if(n instanceof c0)return q0(In(n.expression,i,e));if(n instanceof d0)return new ob(In(n.expression,i,e),void 0,Or(n.span,e));if(n instanceof u0)return NF(n,i,e);if(n instanceof p0)return new K_(In(n.tag,i,e),NF(n.template,i,e),void 0,Or(n.span,e));if(n instanceof h0)return new Wl(In(n.expression,i,e),void 0,Or(n.span,e));if(n instanceof Cb)return new Uh(n.body,n.flags,e);if(n instanceof fb)return new au(In(n.expression,i,e));if(n instanceof vb)return BJ(Fs(n.parameters.map(t=>new Sr(t.name,ls)),In(n.body,i,e)));throw new Error(`Unhandled expression type "${n.constructor.name}" in file "${e?.start.file.url}"`)}}}}function NF(n,i,e){return new J_(n.elements.map(t=>new rb(t.text,Or(t.span,e))),n.expressions.map(t=>In(t,i,e)),Or(n.span,e))}function TD(n,i,e,t){let o;return i instanceof Q0?o=new Yo(i.strings,i.expressions.map(r=>In(r,n,null)),Object.keys(xd(e)?.placeholders??{})):i instanceof ao?o=In(i,n,null):o=Me(i),o}var uL=new Map([[Mi.Property,Ht.Property],[Mi.TwoWay,Ht.TwoWayProperty],[Mi.Attribute,Ht.Attribute],[Mi.Class,Ht.ClassName],[Mi.Style,Ht.StyleProperty],[Mi.LegacyAnimation,Ht.LegacyAnimation],[Mi.Animation,Ht.Animation]]);function NJ(n){return Ql(n.tagName??"")[1]===nu}function xd(n){if(n==null)return null;if(!(n instanceof Ua))throw Error(`Expected i18n meta to be a Message, but got: ${n.constructor.name}`);return n}function RJ(n,i,e){let t=new Array,o=new Set;for(let r of e.attributes){let a=tu.securityContext(e.name,r.name,!0);t.push(uu(i.xref,Ht.Attribute,r.name,TD(n.job,r.value,r.i18n),null,a,!0,!1,null,xd(r.i18n),r.sourceSpan)),r.i18n&&o.add(r.name)}for(let r of e.inputs)o.has(r.name)&&console.error(`On component ${n.job.componentName}, the binding ${r.name} is both an i18n attribute and a property. You may want to remove the property binding. This will become a compilation error in future versions of Angular.`),t.push(uu(i.xref,uL.get(r.type),r.name,TD(n.job,U0(r.value),r.i18n),r.unit,r.securityContext,!1,!1,null,xd(r.i18n)??null,r.sourceSpan));n.create.push(t.filter(r=>r?.kind===L.ExtractedAttribute)),n.update.push(t.filter(r=>r?.kind===L.Binding));for(let r of e.outputs){if(r.type===Ha.LegacyAnimation&&r.phase===null)throw Error("Animation listener should have a phase");r.type===Ha.TwoWay?n.create.push(V6(i.xref,i.handle,r.name,i.tag,hL(n,r.handler,r.handlerSpan),r.sourceSpan)):r.type===Ha.Animation?n.create.push(B6(i.xref,i.handle,r.name,i.tag,H0(n,r.handler,r.handlerSpan),r.name.endsWith("enter")?"enter":"leave",r.target,!1,r.sourceSpan)):n.create.push(lP(i.xref,i.handle,r.name,i.tag,H0(n,r.handler,r.handlerSpan),r.phase,r.target,!1,r.sourceSpan))}t.some(r=>r?.i18nMessage)!==null&&n.create.push(z6(n.job.allocateXrefId(),new pa,i.xref))}function FJ(n,i,e,t){let o=new Array;for(let r of e.templateAttrs)if(r instanceof Kh){let a=tu.securityContext(nu,r.name,!0);o.push(W1(n,i.xref,Mi.Attribute,r.name,r.value,null,a,!0,t,xd(r.i18n),r.sourceSpan))}else o.push(W1(n,i.xref,r.type,r.name,U0(r.value),r.unit,r.securityContext,!0,t,xd(r.i18n),r.sourceSpan));for(let r of e.attributes){let a=tu.securityContext(nu,r.name,!0);o.push(W1(n,i.xref,Mi.Attribute,r.name,r.value,null,a,!1,t,xd(r.i18n),r.sourceSpan))}for(let r of e.inputs)o.push(W1(n,i.xref,r.type,r.name,U0(r.value),r.unit,r.securityContext,!1,t,xd(r.i18n),r.sourceSpan));n.create.push(o.filter(r=>r?.kind===L.ExtractedAttribute)),n.update.push(o.filter(r=>r?.kind===L.Binding));for(let r of e.outputs){if(r.type===Ha.LegacyAnimation&&r.phase===null)throw Error("Animation listener should have a phase");if(t===ss.NgTemplate&&(r.type===Ha.TwoWay?n.create.push(V6(i.xref,i.handle,r.name,i.tag,hL(n,r.handler,r.handlerSpan),r.sourceSpan)):n.create.push(lP(i.xref,i.handle,r.name,i.tag,H0(n,r.handler,r.handlerSpan),r.phase,r.target,!1,r.sourceSpan))),t===ss.Structural&&r.type!==Ha.LegacyAnimation){let a=tu.securityContext(nu,r.name,!1);n.create.push(ll(i.xref,Ht.Property,null,r.name,null,null,null,a))}}o.some(r=>r?.i18nMessage)!==null&&n.create.push(z6(n.job.allocateXrefId(),new pa,i.xref))}function W1(n,i,e,t,o,r,a,c,m,p,h){let g=typeof o=="string";if(m===ss.Structural){if(!c)switch(e){case Mi.Property:case Mi.Class:case Mi.Style:return ll(i,Ht.Property,null,t,null,null,p,a);case Mi.TwoWay:return ll(i,Ht.TwoWayProperty,null,t,null,null,p,a)}if(!g&&(e===Mi.Attribute||e===Mi.LegacyAnimation||e===Mi.Animation))return null}let S=uL.get(e);return m===ss.NgTemplate&&(e===Mi.Class||e===Mi.Style||e===Mi.Attribute&&!g)&&(S=Ht.Property),uu(i,S,t,TD(n.job,o,p),r,a,g,c,m,p,h)}function H0(n,i,e){i=U0(i);let t=new Array,o=i instanceof qh?i.expressions:[i];if(o.length===0)throw new Error("Expected listener to have non-empty expression list.");let r=o.map(c=>In(c,n.job,e)),a=r.pop();return t.push(...r.map(c=>Bs(new ma(c,c.sourceSpan)))),t.push(Bs(new wr(a,a.sourceSpan))),t}function hL(n,i,e){i=U0(i);let t=new Array;if(i instanceof qh)if(i.expressions.length===1)i=i.expressions[0];else throw new Error("Expected two-way listener to have a single expression.");let o=In(i,n.job,e),r=new Wr("$event"),a=new Bb(o,r);return t.push(Bs(new ma(a))),t.push(Bs(new wr(r))),t}function U0(n){return n instanceof as?n.ast:n}function fL(n,i){LJ(n.localRefs);for(let{name:e,value:t}of i.references)n.localRefs.push({name:e,target:t})}function LJ(n){if(!Array.isArray(n))throw new Error("AssertionError: expected an array")}function Or(n,i){if(i===null)return null;let e=i.start.moveBy(n.start),t=i.start.moveBy(n.end),o=i.fullStart.moveBy(n.start);return new _n(e,t,o)}function tx(n,i,e){let t=null;for(let o of e.children)if(!(o instanceof cx||o instanceof ZD)){if(t!==null)return null;if(o instanceof Dc||o instanceof Os&&o.tagName!==null)t=o;else return null}if(t!==null){for(let r of t.attributes)if(!r.name.startsWith(gJ)){let a=tu.securityContext(nu,r.name,!0);n.update.push(uu(i,Ht.Attribute,r.name,Me(r.value),null,a,!0,!1,null,xd(r.i18n),r.sourceSpan))}for(let r of t.inputs)if(r.type!==Mi.LegacyAnimation&&r.type!==Mi.Animation&&r.type!==Mi.Attribute){let a=tu.securityContext(nu,r.name,!0);n.create.push(ll(i,Ht.Property,null,r.name,null,null,null,a))}let o=t instanceof Dc?t.name:t.tagName;return o===nu?null:o}return null}function BJ(n){let i=new Set(n.params.map(e=>e.name));return Ft(n,e=>{if(e instanceof vu)for(let t of e.params)i.add(t.name);else if(e instanceof Wr&&i.has(e.name))return Zn(e.name);return e},Wn.None)}var VJ=!1;function zJ(){return VJ}function nx(n,i){return sx(Zn(cf).bitwiseAnd(Me(n),null),i)}function jJ(n){return(n.descendants?1:0)|(n.static?2:0)|(n.emitDistinctChangesOnly?4:0)}function $J(n,i){if(Array.isArray(n.predicate)){let e=[];return n.predicate.forEach(t=>{let o=t.split(",").map(r=>Me(r.trim()));e.push(...o)}),i.getConstLiteral(Qi(e),!0)}else switch(n.predicate.forwardRef){case 0:case 2:return n.predicate.expression;case 1:return Wt(he.resolveForwardRef).callFn([n.predicate.expression])}}function gL(n,i,e){let t=[];return e!==void 0&&t.push(...e),n.isSignal&&t.push(new Rs(Zn(Ls),n.propertyName)),t.push($J(n,i),Me(jJ(n))),n.read&&t.push(n.read),t}var pP=Symbol("queryAdvancePlaceholder");function _L(n){let i=[],e=0,t=()=>{e>0&&(i.unshift(Wt(he.queryAdvance).callFn(e===1?[]:[Me(e)]).toStmt()),e=0)};for(let o=n.length-1;o>=0;o--){let r=n[o];r===pP?e++:(t(),i.unshift(r))}return t(),i}function HJ(n,i,e){let t=[],o=[],r=u6(p=>o.push(p),eP),a=null,c=null;n.forEach(p=>{let h=gL(p,i);if(p.isSignal?(a??=Wt(he.viewQuerySignal),a=a.callFn(h)):(c??=Wt(he.viewQuery),c=c.callFn(h)),p.isSignal){o.push(pP);return}let g=r(),S=Wt(he.loadQuery).callFn([]),x=Wt(he.queryRefresh).callFn([g.set(S)]),v=Zn(Ls).prop(p.propertyName).set(p.first?g.prop("first"):g);o.push(x.and(v).toStmt())}),a!==null&&t.push(new ma(a)),c!==null&&t.push(new ma(c));let m=e?`${e}_Query`:null;return bm([new Sr(cf,iu),new Sr(Ls,ls)],[nx(1,t),nx(2,_L(o))],Ul,null,m)}function UJ(n,i,e){let t=[],o=[],r=u6(p=>o.push(p),eP),a=null,c=null;for(let p of n){let h=gL(p,i,[Zn("dirIndex")]);if(p.isSignal?(a??=Wt(he.contentQuerySignal),a=a.callFn(h)):(c??=Wt(he.contentQuery),c=c.callFn(h)),p.isSignal){o.push(pP);continue}let g=r(),S=Wt(he.loadQuery).callFn([]),x=Wt(he.queryRefresh).callFn([g.set(S)]),v=Zn(Ls).prop(p.propertyName).set(p.first?g.prop("first"):g);o.push(x.and(v).toStmt())}a!==null&&t.push(new ma(a)),c!==null&&t.push(new ma(c));let m=e?`${e}_ContentQueries`:null;return bm([new Sr(cf,iu),new Sr(Ls,ls),new Sr("dirIndex",iu)],[nx(1,t),nx(2,_L(o))],Ul,null,m)}var ED=class extends aX{constructor(){super(bD)}parse(i,e,t){return super.parse(i,e,t)}},q1=".",GJ="attr",sE="animate",WJ="class",qJ="style",QJ="*",lE="animate-",DD=class{_exprParser;_schemaRegistry;errors;constructor(i,e,t){this._exprParser=i,this._schemaRegistry=e,this.errors=t}createBoundHostProperties(i,e){let t=[];for(let o of Object.keys(i)){let r=i[o];typeof r=="string"?this.parsePropertyBinding(o,r,!0,!1,e,e.start.offset,void 0,[],t,e):this._reportError(`Value of the host property binding "${o}" needs to be a string representing an expression but got "${r}" (${typeof r})`,e)}return t}createDirectiveHostEventAsts(i,e){let t=[];for(let o of Object.keys(i)){let r=i[o];typeof r=="string"?this.parseEvent(o,r,!1,e,e,[],t,e):this._reportError(`Value of the host listener "${o}" needs to be a string representing an expression but got "${r}" (${typeof r})`,e)}return t}parseInterpolation(i,e,t){let o=e.fullStart.offset;try{let r=this._exprParser.parseInterpolation(i,e,o,t);return r&&this.errors.push(...r.errors),r}catch(r){return this._reportError(`${r}`,e),this._exprParser.wrapLiteralPrimitive("ERROR",e,o)}}parseInterpolationExpression(i,e){let t=e.start.offset;try{let o=this._exprParser.parseInterpolationExpression(i,e,t);return o&&this.errors.push(...o.errors),o}catch(o){return this._reportError(`${o}`,e),this._exprParser.wrapLiteralPrimitive("ERROR",e,t)}}parseInlineTemplateBinding(i,e,t,o,r,a,c,m){let p=t.start.offset+QJ.length,h=this._parseTemplateBindings(i,e,t,p,o);for(let g of h){let S=dm(t,g.sourceSpan),x=g.key.source,v=dm(t,g.key.span);if(g instanceof f0){let M=g.value?g.value.source:"$implicit",w=g.value?dm(t,g.value.span):void 0;c.push(new PE(x,M,S,v,w))}else if(g.value){let M=m?S:t,w=dm(t,g.value.ast.sourceSpan);this._parsePropertyAst(x,g.value,!1,M,v,w,r,a)}else r.push([x,""]),this.parseLiteralAttr(x,null,v,o,void 0,r,a,v)}}_parseTemplateBindings(i,e,t,o,r){try{let a=this._exprParser.parseTemplateBindings(i,e,t,o,r);return a.errors.forEach(c=>this.errors.push(c)),a.warnings.forEach(c=>{this._reportError(c,t,gm.WARNING)}),a.templateBindings}catch(a){return this._reportError(`${a}`,t),[]}}parseLiteralAttr(i,e,t,o,r,a,c,m){cE(i)?(i=i.substring(1),m!==void 0&&(m=dm(m,new As(m.start.offset+1,m.end.offset))),e&&this._reportError('Assigning animation triggers via @prop="exp" attributes with an expression is invalid. Use property bindings (e.g. [@prop]="exp") or use an attribute without a value (e.g. @prop) instead.',t,gm.ERROR),this._parseLegacyAnimation(i,e,t,o,m,r,a,c)):c.push(new Nh(i,this._exprParser.wrapLiteralPrimitive(e,"",o),vc.LITERAL_ATTR,t,m,r))}parsePropertyBinding(i,e,t,o,r,a,c,m,p,h){i.length===0&&this._reportError("Property name is missing in binding",r);let g=!1;i.startsWith(lE)?(g=!0,i=i.substring(lE.length),h!==void 0&&(h=dm(h,new As(h.start.offset+lE.length,h.end.offset)))):cE(i)&&(g=!0,i=i.substring(1),h!==void 0&&(h=dm(h,new As(h.start.offset+1,h.end.offset)))),g?this._parseLegacyAnimation(i,e,r,a,h,c,m,p):i.startsWith(`${sE}${q1}`)?this._parseAnimation(i,this.parseBinding(e,t,c||r,a),r,h,c,m,p):this._parsePropertyAst(i,this.parseBinding(e,t,c||r,a),o,r,h,c,m,p)}parsePropertyInterpolation(i,e,t,o,r,a,c,m){let p=this.parseInterpolation(e,o||t,m);return p?(this._parsePropertyAst(i,p,!1,t,c,o,r,a),!0):!1}_parsePropertyAst(i,e,t,o,r,a,c,m){c.push([i,e.source]),m.push(new Nh(i,e,t?vc.TWO_WAY:vc.DEFAULT,o,r,a))}_parseAnimation(i,e,t,o,r,a,c){a.push([i,e.source]),c.push(new Nh(i,e,vc.ANIMATION,t,o,r))}_parseLegacyAnimation(i,e,t,o,r,a,c,m){i.length===0&&this._reportError("Animation trigger is missing",t);let p=this.parseBinding(e||"undefined",!1,a||t,o);c.push([i,p.source]),m.push(new Nh(i,p,vc.LEGACY_ANIMATION,t,r,a))}parseBinding(i,e,t,o){try{let r=e?this._exprParser.parseSimpleBinding(i,t,o):this._exprParser.parseBinding(i,t,o);return r&&this.errors.push(...r.errors),r}catch(r){return this._reportError(`${r}`,t),this._exprParser.wrapLiteralPrimitive("ERROR",t,o)}}createBoundElementProperty(i,e,t=!1,o=!0){if(e.isLegacyAnimation)return new xb(e.name,Mi.LegacyAnimation,ro.NONE,e.expression,null,e.sourceSpan,e.keySpan,e.valueSpan);let r=null,a,c=null,m=e.name.split(q1),p;if(m.length>1)if(m[0]==GJ){c=m.slice(1).join(q1),t||this._validatePropertyOrAttributeName(c,e.sourceSpan,!0),p=dE(this._schemaRegistry,i,c,!0);let h=c.indexOf(":");if(h>-1){let g=c.substring(0,h),S=c.substring(h+1);c=Y1(g,S)}a=Mi.Attribute}else m[0]==WJ?(c=m[1],a=Mi.Class,p=[ro.NONE]):m[0]==qJ?(r=m.length>2?m[2]:null,c=m[1],a=Mi.Style,p=[ro.STYLE]):m[0]==sE&&(c=e.name,a=Mi.Animation,p=[ro.NONE]);if(c===null){let h=this._schemaRegistry.getMappedPropName(e.name);c=o?h:e.name,p=dE(this._schemaRegistry,i,h,!1),a=e.type===vc.TWO_WAY?Mi.TwoWay:Mi.Property,t||this._validatePropertyOrAttributeName(h,e.sourceSpan,!1)}return new xb(c,a,p[0],e.expression,r,e.sourceSpan,e.keySpan,e.valueSpan)}parseEvent(i,e,t,o,r,a,c,m){i.length===0&&this._reportError("Event name is missing in binding",o),cE(i)?(i=i.slice(1),m!==void 0&&(m=dm(m,new As(m.start.offset+1,m.end.offset))),this._parseLegacyAnimationEvent(i,e,o,r,c,m)):this._parseRegularEvent(i,e,t,o,r,a,c,m)}calcPossibleSecurityContexts(i,e,t){let o=this._schemaRegistry.getMappedPropName(e);return dE(this._schemaRegistry,i,o,t)}parseEventListenerName(i){let[e,t]=IG(i,[null,i]);return{eventName:t,target:e}}parseLegacyAnimationEventName(i){let e=AG(i,[i,null]);return{eventName:e[0],phase:e[1]===null?null:e[1].toLowerCase()}}_parseLegacyAnimationEvent(i,e,t,o,r,a){let{eventName:c,phase:m}=this.parseLegacyAnimationEventName(i),p=this._parseAction(e,o);r.push(new bb(c,m,Ha.LegacyAnimation,p,t,o,a)),c.length===0&&this._reportError("Animation event name is missing in binding",t),m?m!=="start"&&m!=="done"&&this._reportError(`The provided animation output phase value "${m}" for "@${c}" is not supported (use start or done)`,t):this._reportError(`The animation trigger output event (@${c}) is missing its phase value name (start or done are currently supported)`,t)}_parseRegularEvent(i,e,t,o,r,a,c,m){let{eventName:p,target:h}=this.parseEventListenerName(i),g=this.errors.length,S=this._parseAction(e,r),x=this.errors.length===g;a.push([i,S.source]),t&&x&&!this._isAllowedAssignmentEvent(S)&&this._reportError("Unsupported expression in a two-way binding",o);let v=Ha.Regular;t&&(v=Ha.TwoWay),i.startsWith(`${sE}${q1}`)&&(v=Ha.Animation),c.push(new bb(p,h,v,S,o,r,m))}_parseAction(i,e){let t=e&&e.start?e.start.offset:0;try{let o=this._exprParser.parseAction(i,e,t);return o&&this.errors.push(...o.errors),!o||o.ast instanceof xa?(this._reportError("Empty expressions are not allowed",e),this._exprParser.wrapLiteralPrimitive("ERROR",e,t)):o}catch(o){return this._reportError(`${o}`,e),this._exprParser.wrapLiteralPrimitive("ERROR",e,t)}}_reportError(i,e,t=gm.ERROR){this.errors.push(new rn(e,i,t))}_validatePropertyOrAttributeName(i,e,t){let o=t?this._schemaRegistry.validateAttribute(i):this._schemaRegistry.validateProperty(i);o.error&&this._reportError(o.msg,e,gm.ERROR)}_isAllowedAssignmentEvent(i){return i instanceof as?this._isAllowedAssignmentEvent(i.ast):i instanceof m0?this._isAllowedAssignmentEvent(i.expression):i instanceof Qh&&i.args.length===1&&i.receiver instanceof yc&&i.receiver.name==="$any"&&i.receiver.receiver instanceof Ec?this._isAllowedAssignmentEvent(i.args[0]):(i instanceof yc||i instanceof cu)&&!PD(i)}};function PD(n){return n instanceof r0||n instanceof a0?!0:n instanceof h0?PD(n.expression):n instanceof yc||n instanceof cu||n instanceof Qh?PD(n.receiver):!1}function cE(n){return n[0]=="@"}function dE(n,i,e,t){let o,r=a=>n.securityContext(a,e,t);return i===null?o=n.allKnownElementNames().map(r):(o=[],$h.parse(i).forEach(a=>{let c=a.element?[a.element]:n.allKnownElementNames(),m=new Set(a.notSelectors.filter(h=>h.isElementSelector()).map(h=>h.element)),p=c.filter(h=>!m.has(h));o.push(...p.map(r))})),o.length===0?[ro.NONE]:Array.from(new Set(o)).sort()}function dm(n,i){let e=i.start-n.start.offset,t=i.end-n.end.offset;return new _n(n.start.moveBy(e),n.end.moveBy(t),n.fullStart.moveBy(e),n.details)}function XJ(n){if(n==null||n.length===0||n[0]=="/")return!1;let i=n.match(YJ);return i===null||i[1]=="package"||i[1]=="asset"}var YJ=/^([^:/?#]+):/,KJ="select",ZJ="link",JJ="rel",eee="href",tee="stylesheet",nee="style",iee="script",oee="ngNonBindable",ree="ngProjectAs";function vL(n){let i=null,e=null,t=null,o=!1,r="";n.attrs.forEach(m=>{let p=m.name.toLowerCase();p==KJ?i=m.value:p==eee?e=m.value:p==JJ?t=m.value:m.name==oee?o=!0:m.name==ree&&m.value.length>0&&(r=m.value)}),i=aee(i);let a=n.name.toLowerCase(),c=Is.OTHER;return IE(a)?c=Is.NG_CONTENT:a==nee?c=Is.STYLE:a==iee?c=Is.SCRIPT:a==ZJ&&t==tee&&(c=Is.STYLESHEET),new ID(c,i,e,o,r)}var Is=(function(n){return n[n.NG_CONTENT=0]="NG_CONTENT",n[n.STYLE=1]="STYLE",n[n.STYLESHEET=2]="STYLESHEET",n[n.SCRIPT=3]="SCRIPT",n[n.OTHER=4]="OTHER",n})(Is||{}),ID=class{type;selectAttr;hrefAttr;nonBindable;projectAs;constructor(i,e,t,o,r){this.type=i,this.selectAttr=e,this.hrefAttr=t,this.nonBindable=o,this.projectAs=r}};function aee(n){return n===null||n.length===0?"*":n}var see=/^\s*([0-9A-Za-z_$]*)\s+of\s+([\S\s]*)/,lee=/^track\s+([\S\s]*)/,cee=/^(as\s+)(.*)/,hx=/^else[^\S\r\n]+if/,dee=/^let\s+([\S\s]*)/,mee=/^[$A-Z_][0-9A-Z_$]*$/i,RF=/(\s*)(\S+)(\s*)/,X_=new Set(["$index","$first","$last","$even","$odd","$count"]);function FF(n){return n==="empty"}function LF(n){return n==="else"||hx.test(n)}function pee(n,i,e,t){let o=vee(i),r=[],a=BF(n,o,t);a!==null&&r.push(new Yp(a.expression,So(e,n.children,n.children),a.expressionAlias,n.sourceSpan,n.startSourceSpan,n.endSourceSpan,n.nameSpan,n.i18n));for(let g of i)if(hx.test(g.name)){let S=BF(g,o,t);if(S!==null){let x=So(e,g.children,g.children);r.push(new Yp(S.expression,x,S.expressionAlias,g.sourceSpan,g.startSourceSpan,g.endSourceSpan,g.nameSpan,g.i18n))}}else if(g.name==="else"){let S=So(e,g.children,g.children);r.push(new Yp(null,S,null,g.sourceSpan,g.startSourceSpan,g.endSourceSpan,g.nameSpan,g.i18n))}let c=r.length>0?r[0].startSourceSpan:n.startSourceSpan,m=r.length>0?r[r.length-1].endSourceSpan:n.endSourceSpan,p=n.sourceSpan,h=r[r.length-1];return h!==void 0&&(p=new _n(c.start,h.sourceSpan.end)),{node:new kb(r,p,n.startSourceSpan,m,n.nameSpan),errors:o}}function uee(n,i,e,t){let o=[],r=fee(n,o,t),a=null,c=null;for(let m of i)m.name==="empty"?c!==null?o.push(new rn(m.sourceSpan,"@for loop can only have one @empty block")):m.parameters.length>0?o.push(new rn(m.sourceSpan,"@empty block cannot have parameters")):c=new x0(So(e,m.children,m.children),m.sourceSpan,m.startSourceSpan,m.endSourceSpan,m.nameSpan,m.i18n):o.push(new rn(m.sourceSpan,`Unrecognized @for loop block "${m.name}"`));if(r!==null)if(r.trackBy===null)o.push(new rn(n.startSourceSpan,'@for loop must have a "track" expression'));else{let m=c?.endSourceSpan??n.endSourceSpan,p=new _n(n.sourceSpan.start,m?.end??n.sourceSpan.end);gee(r.trackBy.expression,r.trackBy.keywordSpan,o),a=new Zh(r.itemName,r.expression,r.trackBy.expression,r.trackBy.keywordSpan,r.context,So(e,n.children,n.children),c,p,n.sourceSpan,n.startSourceSpan,m,n.nameSpan,n.i18n)}return{node:a,errors:o}}function hee(n,i,e){let t=Cee(n),o=n.parameters.length>0?G0(n.parameters[0],e):e.parseBinding("",!1,n.sourceSpan,0),r=[],a=[],c=[],m=null,p=null;for(let g of n.children){if(!(g instanceof rl))continue;if((g.name!=="case"||g.parameters.length===0)&&g.name!=="default"&&g.name!=="default never"){a.push(new Tb(g.name,g.sourceSpan,g.nameSpan));continue}p!==null&&t.push(new rn(g.sourceSpan,'@default block with "never" parameter must be the last case in a switch'));let S=g.name==="case",x=null;if(S)x=G0(g.parameters[0],e);else if(g.name==="default never"){(g.children.length>0||g.endSourceSpan!==null&&g.endSourceSpan.start.offset!==g.endSourceSpan.end.offset)&&t.push(new rn(g.sourceSpan,'@default block with "never" parameter cannot have a body')),c.length>0&&t.push(new rn(g.sourceSpan,'A @case block with no body cannot be followed by a @default block with "never" parameter')),p=new zE(g.sourceSpan,g.startSourceSpan,g.endSourceSpan,g.nameSpan);continue}let v=new VE(x,g.sourceSpan,g.startSourceSpan,g.endSourceSpan,g.nameSpan);if(c.push(v),g.children.length===0&&g.endSourceSpan!==null&&g.endSourceSpan.start.offset===g.endSourceSpan.end.offset){m===null&&(m=g.sourceSpan);continue}let w=g.sourceSpan,y=g.startSourceSpan;m!==null&&(w=new _n(m.start,g.sourceSpan.end),y=new _n(m.start,g.startSourceSpan.end),m=null);let k=new b0(c,So(i,g.children,g.children),w,y,g.endSourceSpan,g.nameSpan,g.i18n);r.push(k),c=[]}return{node:new Mb(o,r,a,p,n.sourceSpan,n.startSourceSpan,n.endSourceSpan,n.nameSpan),errors:t}}function fee(n,i,e){if(n.parameters.length===0)return i.push(new rn(n.startSourceSpan,"@for loop does not have an expression")),null;let[t,...o]=n.parameters,r=bee(t,i)?.match(see);if(!r||r[2].trim().length===0)return i.push(new rn(t.sourceSpan,'Cannot parse expression. @for loop expression must match the pattern " of "')),null;let[,a,c]=r;X_.has(a)&&i.push(new rn(t.sourceSpan,`@for loop item name cannot be one of ${Array.from(X_).join(", ")}.`));let m=t.expression.split(" ")[0],p=new _n(t.sourceSpan.start,t.sourceSpan.start.moveBy(m.length)),h={itemName:new xm(a,"$implicit",p,p),trackBy:null,expression:G0(t,e,c),context:Array.from(X_,g=>{let S=new _n(n.startSourceSpan.end,n.startSourceSpan.end);return new xm(g,g,S,S)})};for(let g of o){let S=g.expression.match(dee);if(S!==null){let v=new _n(g.sourceSpan.start.moveBy(S[0].length-S[1].length),g.sourceSpan.end);_ee(g.sourceSpan,S[1],v,a,h.context,i);continue}let x=g.expression.match(lee);if(x!==null){if(h.trackBy!==null)i.push(new rn(g.sourceSpan,'@for loop can only have one "track" expression'));else{let v=G0(g,e,x[1]);v.ast instanceof xa&&i.push(new rn(n.startSourceSpan,'@for loop must have a "track" expression'));let M=new _n(g.sourceSpan.start,g.sourceSpan.start.moveBy(5));h.trackBy={expression:v,keywordSpan:M}}continue}i.push(new rn(g.sourceSpan,`Unrecognized @for loop parameter "${g.expression}"`))}return h}function gee(n,i,e){let t=new AD;n.ast.visit(t),t.hasPipe&&e.push(new rn(i,"Cannot use pipes in track expressions"))}function _ee(n,i,e,t,o,r){let a=i.split(","),c=e.start;for(let m of a){let p=m.split("="),h=p.length===2?p[0].trim():"",g=p.length===2?p[1].trim():"";if(h.length===0||g.length===0)r.push(new rn(n,'Invalid @for loop "let" parameter. Parameter should match the pattern " = "'));else if(!X_.has(g))r.push(new rn(n,`Unknown "let" parameter variable "${g}". The allowed variables are: ${Array.from(X_).join(", ")}`));else if(h===t)r.push(new rn(n,`Invalid @for loop "let" parameter. Variable cannot be called "${t}"`));else if(o.some(S=>S.name===h))r.push(new rn(n,`Duplicate "let" parameter variable "${g}"`));else{let[,S,x]=p[0].match(RF)??[],v=S!==void 0&&p.length===2?new _n(c.moveBy(S.length),c.moveBy(S.length+x.length)):e,M;if(p.length===2){let[,y,k]=p[1].match(RF)??[];M=y!==void 0?new _n(c.moveBy(p[0].length+1+y.length),c.moveBy(p[0].length+1+y.length+k.length)):void 0}let w=new _n(v.start,M?.end??v.end);o.push(new xm(h,g,w,v,M))}c=c.moveBy(m.length+1)}}function vee(n){let i=[],e=!1;for(let t=0;t1&&t0&&i.push(new rn(o.startSourceSpan,"@else block cannot have parameters")),e=!0):hx.test(o.name)||i.push(new rn(o.startSourceSpan,`Unrecognized conditional block @${o.name}`))}return i}function Cee(n){let i=[],e=!1;if(n.parameters.length!==1)return i.push(new rn(n.startSourceSpan,"@switch block must have exactly one parameter")),i;for(let t of n.children)if(!(t instanceof V0||t instanceof _u&&t.value.trim().length===0)){if(!(t instanceof rl)||t.name!=="case"&&t.name!=="default"&&t.name!=="default never"){i.push(new rn(t.sourceSpan,"@switch block can only contain @case and @default blocks"));continue}t.name==="default never"?(e&&i.push(new rn(t.startSourceSpan,"@switch block can only have one @default block")),e=!0):t.name==="default"?(e?i.push(new rn(t.startSourceSpan,"@switch block can only have one @default block")):t.parameters.length>0&&i.push(new rn(t.startSourceSpan,"@default block cannot have parameters")),e=!0):t.name==="case"&&t.parameters.length!==1&&i.push(new rn(t.startSourceSpan,"@case block must have exactly one parameter"))}return i}function G0(n,i,e){let t,o;return typeof e=="string"?(t=Math.max(0,n.expression.lastIndexOf(e)),o=t+e.length):(t=0,o=n.expression.length),i.parseBinding(n.expression.slice(t,o),!1,n.sourceSpan,n.sourceSpan.start.offset+t)}function BF(n,i,e){if(n.parameters.length===0)return i.push(new rn(n.startSourceSpan,"Conditional block does not have an expression")),null;let t=G0(n.parameters[0],e),o=null;for(let r=1;r-1;c--){let m=e[c];if(m===")"){if(a=c,o--,o===0)break}else{if(t.test(m))continue;break}}return o!==0?(i.push(new rn(n.sourceSpan,"Unclosed parentheses in expression")),null):e.slice(r,a)}var AD=class extends Xh{hasPipe=!1;visitPipe(){this.hasPipe=!0}},xee=/^\d+\.?\d*(ms|s)?$/,yee=/^\s$/,VF=new Map([[al,za],[Sc,bd],[$a,yr]]),ja=(function(n){return n.IDLE="idle",n.TIMER="timer",n.INTERACTION="interaction",n.IMMEDIATE="immediate",n.HOVER="hover",n.VIEWPORT="viewport",n.NEVER="never",n})(ja||{});function See({expression:n,sourceSpan:i},e,t){let o=n.indexOf("never"),r=new _n(i.start.moveBy(o),i.start.moveBy(o+5)),a=uP(n,i),c=hP(n,i);o===-1?t.push(new rn(i,'Could not find "never" keyword in expression')):fP("never",e,t,new RE(r,i,a,null,c))}function mE({expression:n,sourceSpan:i},e,t,o){let r=n.indexOf("when"),a=new _n(i.start.moveBy(r),i.start.moveBy(r+4)),c=uP(n,i),m=hP(n,i);if(r===-1)o.push(new rn(i,'Could not find "when" keyword in expression'));else{let p=W0(n,r+1),h=e.parseBinding(n.slice(p),!1,i,i.start.offset+p);fP("when",t,o,new yb(h,i,c,a,m))}}function pE({expression:n,sourceSpan:i},e,t,o,r){let a=n.indexOf("on"),c=new _n(i.start.moveBy(a),i.start.moveBy(a+2)),m=uP(n,i),p=hP(n,i);if(a===-1)o.push(new rn(i,'Could not find "on" keyword in expression'));else{let h=W0(n,a+1),g=n.startsWith("hydrate");new OD(n,e,h,i,t,o,g?Iee:Pee,g,m,c,p).parse()}}function uP(n,i){return n.startsWith("prefetch")?new _n(i.start,i.start.moveBy(8)):null}function hP(n,i){return n.startsWith("hydrate")?new _n(i.start,i.start.moveBy(7)):null}var OD=class{expression;bindingParser;start;span;triggers;errors;validator;isHydrationTrigger;prefetchSpan;onSourceSpan;hydrateSpan;index=0;tokens;constructor(i,e,t,o,r,a,c,m,p,h,g){this.expression=i,this.bindingParser=e,this.start=t,this.span=o,this.triggers=r,this.errors=a,this.validator=c,this.isHydrationTrigger=m,this.prefetchSpan=p,this.onSourceSpan=h,this.hydrateSpan=g,this.tokens=new $0().tokenize(i.slice(t))}parse(){for(;this.tokens.length>0&&this.index0&&o.isCharacter(e[e.length-1])&&e.pop(),e.length===0&&o.isCharacter(ya)&&t.length>0){i.push({expression:this.tokenRangeText(t),start:t[0].index}),this.advance(),t=[];continue}t.push(o),this.advance()}return(!this.token().isCharacter(yr)||e.length>0)&&this.error(this.token(),"Unexpected end of expression"),this.index0)throw new Error(`"${ja.IDLE}" trigger cannot have parameters`);return new FE(i,e,t,o,r)}function Mee(n,i,e,t,o,r){if(n.length!==1)throw new Error(`"${ja.TIMER}" trigger must have exactly one parameter`);let a=ix(n[0].expression);if(a===null)throw new Error(`Could not parse time value of trigger "${ja.TIMER}"`);return new BE(a,i,e,t,o,r)}function kee(n,i,e,t,o,r){if(n.length>0)throw new Error(`"${ja.IMMEDIATE}" trigger cannot have parameters`);return new LE(i,e,t,o,r)}function Tee(n,i,e,t,o,r,a){return a(ja.HOVER,n),new Sb(n[0]?.expression??null,i,e,t,o,r)}function Eee(n,i,e,t,o,r,a){return a(ja.INTERACTION,n),new wb(n[0]?.expression??null,i,e,t,o,r)}function Dee(n,i,e,t,o,r,a,c,m,p){p(ja.VIEWPORT,t);let h,g;if(t.length===0)h=g=null;else if(!t[0].expression.startsWith("{"))h=t[0].expression,g=null;else{let S=e.parseBinding(t[0].expression,!1,r,r.start.offset+n+t[0].start);if(S.ast instanceof du){if(S.ast.keys.some(v=>v.kind==="spread"))throw new Error("Spread operator are not allowed in this context");if(S.ast.keys.some(v=>v.kind==="property"&&v.key==="root"))throw new Error('The "root" option is not supported in the options parameter of the "viewport" trigger')}else throw new Error('Options parameter of the "viewport" trigger must be an object literal');let x=S.ast.keys.findIndex(v=>v.kind==="property"&&v.key==="trigger");if(x===-1)h=null,g=S.ast;else{let v=S.ast.values[x],M=(w,y)=>y!==x;if(!(v instanceof yc)||!(v.receiver instanceof Ec))throw new Error('"trigger" option of the "viewport" trigger must be an identifier');h=v.name,g=new du(S.ast.span,S.ast.sourceSpan,S.ast.keys.filter(M),S.ast.values.filter(M))}}if(i&&h!==null)throw new Error('"viewport" hydration trigger cannot have a "trigger"');if(g){let S=ND.findDynamicNode(g);if(S!==null)throw new Error(`Options of the "viewport" trigger must be an object literal containing only literal values, but "${S.constructor.name}" was found`)}return new g0(h,g,o,r,a,c,m)}function Pee(n,i){if(i.length>1)throw new Error(`"${n}" trigger can only have zero or one parameters`)}function Iee(n,i){if(n===ja.VIEWPORT){if(i.length>1)throw new Error(`Hydration trigger "${n}" cannot have more than one parameter`);return}if(i.length>0)throw new Error(`Hydration trigger "${n}" cannot have parameters`)}function W0(n,i=0){let e=!1;for(let t=i;t0){let M=i[i.length-1];g=M.endSourceSpan,S=M.sourceSpan.end}let x=new _n(n.sourceSpan.start,S);return{node:new mu(So(e,n.children,n.children),m,p,h,r,a,c,n.nameSpan,x,n.sourceSpan,n.startSourceSpan,g,n.i18n),errors:o}}function jee(n,i,e){let t=null,o=null,r=null;for(let a of n)try{if(!RD(a.name)){i.push(new rn(a.startSourceSpan,`Unrecognized block "@${a.name}"`));break}switch(a.name){case"placeholder":t!==null?i.push(new rn(a.startSourceSpan,"@defer block can only have one @placeholder block")):t=$ee(a,e);break;case"loading":o!==null?i.push(new rn(a.startSourceSpan,"@defer block can only have one @loading block")):o=Hee(a,e);break;case"error":r!==null?i.push(new rn(a.startSourceSpan,"@defer block can only have one @error block")):r=Uee(a,e);break}}catch(c){i.push(new rn(a.startSourceSpan,c.message))}return{placeholder:t,loading:o,error:r}}function $ee(n,i){let e=null;for(let t of n.parameters)if(CL.test(t.expression)){if(e!=null)throw new Error('@placeholder block can only have one "minimum" parameter');let o=ix(t.expression.slice(W0(t.expression)));if(o===null)throw new Error('Could not parse time value of parameter "minimum"');e=o}else throw new Error(`Unrecognized parameter in @placeholder block: "${t.expression}"`);return new _0(So(i,n.children,n.children),e,n.nameSpan,n.sourceSpan,n.startSourceSpan,n.endSourceSpan,n.i18n)}function Hee(n,i){let e=null,t=null;for(let o of n.parameters)if(Lee.test(o.expression)){if(e!=null)throw new Error('@loading block can only have one "after" parameter');let r=ix(o.expression.slice(W0(o.expression)));if(r===null)throw new Error('Could not parse time value of parameter "after"');e=r}else if(CL.test(o.expression)){if(t!=null)throw new Error('@loading block can only have one "minimum" parameter');let r=ix(o.expression.slice(W0(o.expression)));if(r===null)throw new Error('Could not parse time value of parameter "minimum"');t=r}else throw new Error(`Unrecognized parameter in @loading block: "${o.expression}"`);return new v0(So(i,n.children,n.children),e,t,n.nameSpan,n.sourceSpan,n.startSourceSpan,n.endSourceSpan,n.i18n)}function Uee(n,i){if(n.parameters.length>0)throw new Error("@error block cannot have parameters");return new C0(So(i,n.children,n.children),n.nameSpan,n.sourceSpan,n.startSourceSpan,n.endSourceSpan,n.i18n)}function Gee(n,i,e,t){let o={},r={},a={};for(let c of n.parameters)Bee.test(c.expression)?mE(c,i,o,e):Vee.test(c.expression)?pE(c,i,o,e):Aee.test(c.expression)?mE(c,i,r,e):Oee.test(c.expression)?pE(c,i,r,e):Nee.test(c.expression)?mE(c,i,a,e):Ree.test(c.expression)?pE(c,i,a,e):Fee.test(c.expression)?See(c,a,e):e.push(new rn(c.sourceSpan,"Unrecognized trigger"));return a.never&&Object.keys(a).length>1&&e.push(new rn(n.startSourceSpan,"Cannot specify additional `hydrate` triggers if `hydrate never` is present")),{triggers:o,prefetchTriggers:r,hydrateTriggers:a}}var Wee=/^(?:(bind-)|(let-)|(ref-|#)|(on-)|(bindon-)|(@))(.*)$/,zF=1,jF=2,$F=3,HF=4,UF=5,qee=6,N_=7,mm={BANANA_BOX:{start:"[(",end:")]"},PROPERTY:{start:"[",end:"]"},EVENT:{start:"(",end:")"}},uE="*",Qee=new Set(["link","style","script","ng-template","ng-container","ng-content"]),Xee=new Set(["ngProjectAs","ngNonBindable"]);function Yee(n,i,e){let t=new FD(i,e),o=So(t,n,n),r=i.errors.concat(t.errors),a={nodes:o,errors:r,styleUrls:t.styleUrls,styles:t.styles,ngContentSelectors:t.ngContentSelectors};return e.collectCommentNodes&&(a.commentNodes=t.commentNodes),a}var FD=class{bindingParser;options;errors=[];styles=[];styleUrls=[];ngContentSelectors=[];commentNodes=[];inI18nBlock=!1;processedNodes=new Set;constructor(i,e){this.bindingParser=i,this.options=e}visitElement(i){let e=Z1(i.i18n);e&&(this.inI18nBlock&&this.reportError("Cannot mark an element as translatable inside of a translatable section. Please remove the nested i18n marker.",i.sourceSpan),this.inI18nBlock=!0);let t=vL(i);if(t.type===Is.SCRIPT)return null;if(t.type===Is.STYLE){let y=Kee(i);return y!==null&&this.styles.push(y),null}else if(t.type===Is.STYLESHEET&&XJ(t.hrefAttr))return this.styleUrls.push(t.hrefAttr),null;let o=JG(i.name),{attributes:r,boundEvents:a,references:c,variables:m,templateVariables:p,elementHasInlineTemplate:h,parsedProperties:g,templateParsedProperties:S,i18nAttrsMeta:x}=this.prepareAttributes(i.attrs,o),v=this.extractDirectives(i),M;t.nonBindable?M=So(GF,i.children).flat(1/0):M=So(this,i.children,i.children);let w;if(t.type===Is.NG_CONTENT){let y=t.selectAttr,k=i.attrs.map(I=>this.visitAttribute(I));w=new Jh(y,k,M,i.isSelfClosing,i.sourceSpan,i.startSourceSpan,i.endSourceSpan,i.i18n),this.ngContentSelectors.push(y)}else if(o){let y=this.categorizePropertyAttributes(i.name,g,x);w=new Os(i.name,r,y.bound,a,v,[],M,c,m,i.isSelfClosing,i.sourceSpan,i.startSourceSpan,i.endSourceSpan,i.i18n)}else{let y=this.categorizePropertyAttributes(i.name,g,x);if(i.name==="ng-container")for(let k of y.bound)k.type===Mi.Attribute&&this.reportError("Attribute bindings are not supported on ng-container. Use property bindings instead.",k.sourceSpan);w=new Dc(i.name,r,y.bound,a,v,M,c,i.isSelfClosing,i.sourceSpan,i.startSourceSpan,i.endSourceSpan,i.isVoid,i.i18n)}return h&&(w=this.wrapInTemplate(w,S,p,x,o,e)),e&&(this.inI18nBlock=!1),w}visitAttribute(i){return new Kh(i.name,i.value,i.sourceSpan,i.keySpan,i.valueSpan,i.i18n)}visitText(i){return this.processedNodes.has(i)?null:this._visitTextWithInterpolation(i.value,i.sourceSpan,i.tokens,i.i18n)}visitExpansion(i){if(!i.i18n)return null;if(!Z1(i.i18n))throw new Error(`Invalid type "${i.i18n.constructor}" for "i18n" property of ${i.sourceSpan.toString()}. Expected a "Message"`);let e=i.i18n,t={},o={};return Object.keys(e.placeholders).forEach(r=>{let a=e.placeholders[r];if(r.startsWith(iW)){let c=r.trim(),m=this.bindingParser.parseInterpolationExpression(a.text,a.sourceSpan);t[c]=new Yh(m,a.sourceSpan)}else o[r]=this._visitTextWithInterpolation(a.text,a.sourceSpan,null)}),new c6(t,o,i.sourceSpan,e)}visitExpansionCase(i){return null}visitComment(i){return this.options.collectCommentNodes&&this.commentNodes.push(new cx(i.value||"",i.sourceSpan)),null}visitLetDeclaration(i,e){let t=this.bindingParser.parseBinding(i.value,!1,i.valueSpan,i.valueSpan.start.offset);return t.errors.length===0&&t.ast instanceof xa&&this.reportError("@let declaration value cannot be empty",i.valueSpan),new ZD(i.name,t,i.sourceSpan,i.nameSpan,i.valueSpan)}visitComponent(i){let e=Z1(i.i18n);if(e&&(this.inI18nBlock&&this.reportError("Cannot mark a component as translatable inside of a translatable section. Please remove the nested i18n marker.",i.sourceSpan),this.inI18nBlock=!0),i.tagName!==null&&Qee.has(i.tagName))return this.reportError(`Tag name "${i.tagName}" cannot be used as a component tag`,i.startSourceSpan),null;let{attributes:t,boundEvents:o,references:r,templateVariables:a,elementHasInlineTemplate:c,parsedProperties:m,templateParsedProperties:p,i18nAttrsMeta:h}=this.prepareAttributes(i.attrs,!1);this.validateSelectorlessReferences(r);let g=this.extractDirectives(i),S;i.attrs.find(M=>M.name==="ngNonBindable")?S=So(GF,i.children).flat(1/0):S=So(this,i.children,i.children);let x=this.categorizePropertyAttributes(i.tagName,m,h),v=new $_(i.componentName,i.tagName,i.fullName,t,x.bound,o,g,S,r,i.isSelfClosing,i.sourceSpan,i.startSourceSpan,i.endSourceSpan,i.i18n);return c&&(v=this.wrapInTemplate(v,p,a,h,!1,e)),e&&(this.inI18nBlock=!1),v}visitDirective(){return null}visitBlockParameter(){return null}visitBlock(i,e){let t=Array.isArray(e)?e.indexOf(i):-1;if(t===-1)throw new Error("Visitor invoked incorrectly. Expecting visitBlock to be invoked siblings array as its context");if(this.processedNodes.has(i))return null;let o=null;switch(i.name){case"defer":o=zee(i,this.findConnectedBlocks(t,e,RD),this,this.bindingParser);break;case"switch":o=hee(i,this,this.bindingParser);break;case"for":o=uee(i,this.findConnectedBlocks(t,e,FF),this,this.bindingParser);break;case"if":o=pee(i,this.findConnectedBlocks(t,e,LF),this,this.bindingParser);break;default:let r;RD(i.name)?(r=`@${i.name} block can only be used after an @defer block.`,this.processedNodes.add(i)):FF(i.name)?(r=`@${i.name} block can only be used after an @for block.`,this.processedNodes.add(i)):LF(i.name)?(r=`@${i.name} block can only be used after an @if or @else if block.`,this.processedNodes.add(i)):r=`Unrecognized block @${i.name}.`,o={node:new Tb(i.name,i.sourceSpan,i.nameSpan),errors:[new rn(i.sourceSpan,r)]};break}return this.errors.push(...o.errors),o.node}findConnectedBlocks(i,e,t){let o=[];for(let r=i+1;r{let c=t[a.name];if(a.isLiteral)r.push(new Kh(a.name,a.expression.source||"",a.sourceSpan,a.keySpan,a.valueSpan,c));else{let m=this.bindingParser.createBoundElementProperty(i,a,!0,!1);o.push(OE.fromBoundElementProperty(m,c))}}),{bound:o,literal:r}}prepareAttributes(i,e){let t=[],o=[],r=[],a=[],c=[],m={},p=[],h=[],g=!1;for(let S of i){let x=!1,v=WF(S.name),M=!1;if(S.i18n&&(m[S.name]=S.i18n),v.startsWith(uE)){g&&this.reportError("Can't have multiple template bindings on one element. Use only one attribute prefixed with *",S.sourceSpan),M=!0,g=!0;let w=S.value,y=v.substring(uE.length),k=[],I=S.valueSpan?S.valueSpan.fullStart.offset:S.sourceSpan.fullStart.offset+S.name.length;this.bindingParser.parseInlineTemplateBinding(y,w,S.sourceSpan,I,[],p,k,!0),h.push(...k.map(P=>new xm(P.name,P.value,P.sourceSpan,P.keySpan,P.valueSpan)))}else x=this.parseAttribute(e,S,[],t,o,r,a);!x&&!M&&c.push(this.visitAttribute(S))}return{attributes:c,boundEvents:o,references:a,variables:r,templateVariables:h,elementHasInlineTemplate:g,parsedProperties:t,templateParsedProperties:p,i18nAttrsMeta:m}}parseAttribute(i,e,t,o,r,a,c){let m=WF(e.name),p=e.value,h=e.sourceSpan,g=e.valueSpan?e.valueSpan.fullStart.offset:h.fullStart.offset;function S(y,k,I){let P=e.name.length-m.length,R=y.start.moveBy(k.length+P),D=R.moveBy(I.length);return new _n(R,D,R,I)}let x=m.match(Wee);if(x){if(x[zF]!=null){let y=x[N_],k=S(h,x[zF],y);this.bindingParser.parsePropertyBinding(y,p,!1,!1,h,g,e.valueSpan,t,o,k)}else if(x[jF])if(i){let y=x[N_],k=S(h,x[jF],y);this.parseVariable(y,p,h,k,e.valueSpan,a)}else this.reportError('"let-" is only supported on ng-template elements.',h);else if(x[$F]){let y=x[N_],k=S(h,x[$F],y);this.parseReference(y,p,h,k,e.valueSpan,c)}else if(x[HF]){let y=[],k=x[N_],I=S(h,x[HF],k);this.bindingParser.parseEvent(k,p,!1,h,e.valueSpan||h,t,y,I),hE(y,r)}else if(x[UF]){let y=x[N_],k=S(h,x[UF],y);this.bindingParser.parsePropertyBinding(y,p,!1,!0,h,g,e.valueSpan,t,o,k),this.parseAssignmentEvent(y,p,h,e.valueSpan,t,r,k,g)}else if(x[qee]){let y=S(h,"",m);this.bindingParser.parseLiteralAttr(m,p,h,g,e.valueSpan,t,o,y)}return!0}let v=null;if(m.startsWith(mm.BANANA_BOX.start)?v=mm.BANANA_BOX:m.startsWith(mm.PROPERTY.start)?v=mm.PROPERTY:m.startsWith(mm.EVENT.start)&&(v=mm.EVENT),v!==null&&m.endsWith(v.end)&&m.length>v.start.length+v.end.length){let y=m.substring(v.start.length,m.length-v.end.length),k=S(h,v.start,y);if(v.start===mm.BANANA_BOX.start)this.bindingParser.parsePropertyBinding(y,p,!1,!0,h,g,e.valueSpan,t,o,k),this.parseAssignmentEvent(y,p,h,e.valueSpan,t,r,k,g);else if(v.start===mm.PROPERTY.start)this.bindingParser.parsePropertyBinding(y,p,!1,!1,h,g,e.valueSpan,t,o,k);else{let I=[];this.bindingParser.parseEvent(y,p,!1,h,e.valueSpan||h,t,I,k),hE(I,r)}return!0}let M=S(h,"",m);return this.bindingParser.parsePropertyInterpolation(m,p,h,e.valueSpan,t,o,M,e.valueTokens??null)}extractDirectives(i){let e=i instanceof Va?i.tagName:i.name,t=[],o=new Set;for(let r of i.directives){let a=!1;for(let x of r.attrs)x.name.startsWith(uE)?(a=!0,this.reportError(`Shorthand template syntax "${x.name}" is not supported inside a directive context`,x.sourceSpan)):Xee.has(x.name)&&(a=!0,this.reportError(`Attribute "${x.name}" is not supported in a directive context`,x.sourceSpan));if(!a&&o.has(r.name)&&(a=!0,this.reportError(`Cannot apply directive "${r.name}" multiple times on the same element`,r.sourceSpan)),a)continue;let{attributes:c,parsedProperties:m,boundEvents:p,references:h,i18nAttrsMeta:g}=this.prepareAttributes(r.attrs,!1);this.validateSelectorlessReferences(h);let{bound:S}=this.categorizePropertyAttributes(e,m,g);for(let x of S)x.type!==Mi.Property&&x.type!==Mi.TwoWay&&(a=!0,this.reportError("Binding is not supported in a directive context",x.sourceSpan));a||(o.add(r.name),t.push(new l6(r.name,c,S,p,h,r.sourceSpan,r.startSourceSpan,r.endSourceSpan,void 0)))}return t}filterAnimationAttributes(i){return i.filter(e=>!e.name.startsWith("animate."))}filterAnimationInputs(i){return i.filter(e=>e.type!==Mi.Animation)}wrapInTemplate(i,e,t,o,r,a){let c=this.categorizePropertyAttributes("ng-template",e,o),m=[];c.literal.forEach(S=>m.push(S)),c.bound.forEach(S=>m.push(S));let p={attributes:[],inputs:[],outputs:[]};(i instanceof Dc||i instanceof $_)&&(p.attributes.push(...this.filterAnimationAttributes(i.attributes)),p.inputs.push(...this.filterAnimationInputs(i.inputs)),p.outputs.push(...i.outputs));let h=r&&a?void 0:i.i18n,g;return i instanceof $_?g=i.tagName:i instanceof Os?g=null:g=i.name,new Os(g,p.attributes,p.inputs,p.outputs,[],m,[i],[],t,!1,i.sourceSpan,i.startSourceSpan,i.endSourceSpan,h)}_visitTextWithInterpolation(i,e,t,o){let r=Y6(i),a=this.bindingParser.parseInterpolation(r,e,t);return a?new Yh(a,e,o):new qp(r,e)}parseVariable(i,e,t,o,r,a){i.indexOf("-")>-1?this.reportError('"-" is not allowed in variable names',t):i.length===0&&this.reportError("Variable does not have a name",t),a.push(new xm(i,e,t,o,r))}parseReference(i,e,t,o,r,a){i.indexOf("-")>-1?this.reportError('"-" is not allowed in reference names',t):i.length===0?this.reportError("Reference does not have a name",t):a.some(c=>c.name===i)&&this.reportError(`Reference "#${i}" is defined more than once`,t),a.push(new y0(i,e,t,o,r))}parseAssignmentEvent(i,e,t,o,r,a,c,m){let p=[];this.bindingParser.parseEvent(`${i}Change`,e,!0,t,o||t,r,p,c),hE(p,a)}validateSelectorlessReferences(i){if(i.length===0)return;let e=new Set;for(let t of i)t.value.length>0?this.reportError("Cannot specify a value for a local reference in this context",t.valueSpan||t.sourceSpan):e.has(t.name)?this.reportError("Duplicate reference names are not allowed",t.sourceSpan):e.add(t.name)}reportError(i,e,t=gm.ERROR){this.errors.push(new rn(e,i,t))}},LD=class{visitElement(i){let e=vL(i);if(e.type===Is.SCRIPT||e.type===Is.STYLE||e.type===Is.STYLESHEET)return null;let t=So(this,i.children,null);return new Dc(i.name,So(this,i.attrs),[],[],[],t,[],i.isSelfClosing,i.sourceSpan,i.startSourceSpan,i.endSourceSpan,i.isVoid)}visitComment(i){return null}visitAttribute(i){return new Kh(i.name,i.value,i.sourceSpan,i.keySpan,i.valueSpan,i.i18n)}visitText(i){return new qp(i.value,i.sourceSpan)}visitExpansion(i){return null}visitExpansionCase(i){return null}visitBlock(i,e){let t=[new qp(i.startSourceSpan.toString(),i.startSourceSpan),...So(this,i.children)];return i.endSourceSpan!==null&&t.push(new qp(i.endSourceSpan.toString(),i.endSourceSpan)),t}visitBlockParameter(i,e){return null}visitLetDeclaration(i,e){return new qp(`@let ${i.name} = ${i.value};`,i.sourceSpan)}visitComponent(i,e){let t=So(this,i.children,null);return new Dc(i.fullName,So(this,i.attrs),[],[],[],t,[],i.isSelfClosing,i.sourceSpan,i.startSourceSpan,i.endSourceSpan,!1)}visitDirective(i,e){return null}},GF=new LD;function WF(n){return/^data-/i.test(n)?n.substring(5):n}function hE(n,i){i.push(...n.map(e=>NE.fromParsedEvent(e)))}function Kee(n){return n.children.length!==1||!(n.children[0]instanceof _u)?null:n.children[0].value}var Zee=[" ",` -`,"\r"," "];function Jee(n,i,e={}){let{preserveWhitespaces:t,enableI18nLegacyMessageIdFormat:o}=e,r=e.enableSelectorless??!1,a=ox(r),m=new ED().parse(n,i,Qe(W({leadingTriviaChars:Zee},e),{tokenizeExpansionForms:!0,tokenizeBlocks:e.enableBlockSyntax??!0,tokenizeLet:e.enableLetSyntax??!0,selectorlessEnabled:r}));if(!e.alwaysAttemptHtmlToR3AstConversion&&m.errors&&m.errors.length>0){let P={preserveWhitespaces:t,errors:m.errors,nodes:[],styleUrls:[],styles:[],ngContentSelectors:[]};return e.collectCommentNodes&&(P.commentNodes=[]),P}let p=m.rootNodes,h=!(e.preserveSignificantWhitespace??!0),g=new Zb(!t,o,e.preserveSignificantWhitespace,h),S=g.visitAllWithErrors(p);if(!e.alwaysAttemptHtmlToR3AstConversion&&S.errors&&S.errors.length>0){let P={preserveWhitespaces:t,errors:S.errors,nodes:[],styleUrls:[],styles:[],ngContentSelectors:[]};return e.collectCommentNodes&&(P.commentNodes=[]),P}p=S.rootNodes,t||(p=So(new Yb(!0,void 0,!1),p),g.hasI18nMeta&&(p=So(new Zb(!1,void 0,!0,h),p)));let{nodes:x,errors:v,styleUrls:M,styles:w,ngContentSelectors:y,commentNodes:k}=Yee(p,a,{collectCommentNodes:!!e.collectCommentNodes});v.push(...m.errors,...S.errors);let I={preserveWhitespaces:t,errors:v.length>0?v:null,nodes:x,styleUrls:M,styles:w,ngContentSelectors:y};return e.collectCommentNodes&&(I.commentNodes=k),I}var ete=new sf;function ox(n=!1){return new DD(new Kb(new $0,n),ete,[])}var bL="%COMP%",tte=`_nghost-${bL}`,nte=`_ngcontent-${bL}`;function xL(n,i,e){let t=new wm,o=QD(n.selector);return t.set("type",n.type.value),o.length>0&&t.set("selectors",Rh(o)),n.queries.length>0&&t.set("contentQueries",UJ(n.queries,i,n.name)),n.viewQueries.length&&t.set("viewQuery",HJ(n.viewQueries,i,n.name)),t.set("hostBindings",dte(n.host,n.typeSourceSpan,e,i,n.selector||"",n.name,t)),t.set("inputs",xR(n.inputs,!0)),t.set("outputs",xR(n.outputs)),n.exportAs!==null&&t.set("exportAs",Qi(n.exportAs.map(r=>Me(r)))),n.isStandalone===!1&&t.set("standalone",Me(!1)),n.isSignal&&t.set("signals",Me(!0)),t}function yL(n,i){let e=[],t=i.providers,o=i.viewProviders;if(t||o){let r=[t||new Tc([])];o&&r.push(o),e.push(Wt(he.ProvidersFeature).callFn(r))}if(i.hostDirectives?.length&&e.push(Wt(he.HostDirectivesFeature).callFn([fte(i.hostDirectives)])),i.usesInheritance&&e.push(Wt(he.InheritDefinitionFeature)),i.lifecycle.usesOnChanges&&e.push(Wt(he.NgOnChangesFeature)),i.controlCreate!==null&&e.push(Wt(he.ControlFeature).callFn([Me(i.controlCreate.passThroughInput)])),"externalStyles"in i&&i.externalStyles?.length){let r=i.externalStyles.map(a=>Me(a));e.push(Wt(he.ExternalStylesFeature).callFn([Qi(r)]))}e.length&&n.set("features",Qi(e))}function ite(n,i,e){let t=xL(n,i,e);yL(t,n);let o=Wt(he.defineDirective).callFn([t.toLiteralMap()],void 0,!0),r=cte(n);return{expression:o,type:r,statements:[]}}function ote(n,i,e){let t=xL(n,i,e);yL(t,n);let o=n.selector&&$h.parse(n.selector),r=o&&o[0];if(r){let v=r.getAttrs();v.length&&t.set("attrs",i.getConstLiteral(Qi(v.map(M=>M!=null?Me(M):Me(void 0))),!0))}let a=n.name,c=null;if(n.defer.mode===1&&n.defer.dependenciesFn!==null){let v=`${a}_DeferFn`;i.statements.push(new Fr(v,n.defer.dependenciesFn,void 0,la.Final)),c=Zn(v)}let m=n.isStandalone&&!n.hasDirectiveDependencies?is.DomOnly:is.Full,p=vJ(n.name,n.template.nodes,i,m,n.relativeContextFilePath,n.i18nUseExternalIds,n.defer,c,n.relativeTemplatePath,zJ());lL(p,Tt.Tmpl);let h=hJ(p,i);if(p.contentSelectors!==null&&t.set("ngContentSelectors",p.contentSelectors),t.set("decls",Me(p.root.decls)),t.set("vars",Me(p.root.vars)),p.consts.length>0&&(p.constsInitializers.length>0?t.set("consts",Fs([],[...p.constsInitializers,new wr(Qi(p.consts))])):t.set("consts",Qi(p.consts))),t.set("template",h),n.declarationListEmitMode!==3&&n.declarations.length>0)t.set("dependencies",ate(Qi(n.declarations.map(v=>v.type)),n.declarationListEmitMode));else if(n.declarationListEmitMode===3){let v=[n.type.value];n.rawImports&&v.push(n.rawImports),t.set("dependencies",Wt(he.getComponentDepsFactory).callFn(v))}n.encapsulation===null&&(n.encapsulation=jp.Emulated);let g=!!n.externalStyles?.length;if(n.styles&&n.styles.length){let M=(n.encapsulation==jp.Emulated?hte(n.styles,nte,tte):n.styles).reduce((w,y)=>(y.trim().length>0&&w.push(i.getConstLiteral(Me(y))),w),[]);M.length>0&&(g=!0,t.set("styles",Qi(M)))}!g&&n.encapsulation===jp.Emulated&&(n.encapsulation=jp.None),n.encapsulation!==jp.Emulated&&t.set("encapsulation",Me(n.encapsulation)),n.animations!==null&&t.set("data",ml([{key:"animation",value:n.animations,quoted:!1}])),n.changeDetection!==null&&(typeof n.changeDetection=="number"&&n.changeDetection!==qD.Default?t.set("changeDetection",Me(n.changeDetection)):typeof n.changeDetection=="object"&&t.set("changeDetection",n.changeDetection));let S=Wt(he.defineComponent).callFn([t.toLiteralMap()],void 0,!0),x=rte(n);return{expression:S,type:x,statements:[]}}function rte(n){let i=SL(n);return i.push(VD(n.template.ngContentSelectors)),i.push(ca(Me(n.isStandalone))),i.push(wL(n)),n.isSignal&&i.push(ca(Me(n.isSignal))),ca(Wt(he.ComponentDeclaration,i))}function ate(n,i){switch(i){case 0:return n;case 1:return Fs([],n);case 2:let e=n.prop("map").callFn([Wt(he.resolveForwardRef)]);return Fs([],e);case 3:throw new Error("Unsupported with an array of pre-resolved dependencies")}}function ste(n){return ca(Me(n))}function BD(n){let i=Object.keys(n).map(e=>{let t=Array.isArray(n[e])?n[e][0]:n[e];return{key:e,value:Me(t),quoted:!0}});return ml(i)}function VD(n){return n.length>0?ca(Qi(n.map(i=>Me(i)))):Mc}function SL(n){let i=n.selector!==null?n.selector.replace(/\n/g,""):null;return[lx(n.type.type,n.typeArgumentCount),i!==null?ste(i):Mc,n.exportAs!==null?VD(n.exportAs):Mc,ca(lte(n)),ca(BD(n.outputs)),VD(n.queries.map(e=>e.propertyName))]}function lte(n){return ml(Object.keys(n.inputs).map(i=>{let e=n.inputs[i],t=[{key:"alias",value:Me(e.bindingPropertyName),quoted:!0},{key:"required",value:Me(e.required),quoted:!0}];return e.isSignal&&t.push({key:"isSignal",value:Me(e.isSignal),quoted:!0}),{key:i,value:ml(t),quoted:!0}}))}function cte(n){let i=SL(n);return i.push(Mc),i.push(ca(Me(n.isStandalone))),i.push(wL(n)),n.isSignal&&i.push(ca(Me(n.isSignal))),ca(Wt(he.DirectiveDeclaration,i))}function dte(n,i,e,t,o,r,a){let c=e.createBoundHostProperties(n.properties,i),m=e.createDirectiveHostEventAsts(n.listeners,i);n.specialAttributes.styleAttr&&(n.attributes.style=Me(n.specialAttributes.styleAttr)),n.specialAttributes.classAttr&&(n.attributes.class=Me(n.specialAttributes.classAttr));let p=CJ({componentName:r,componentSelector:o,properties:c,events:m,attributes:n.attributes},e,t);lL(p,Tt.Host),a.set("hostAttrs",p.root.attributes);let h=p.root.vars;return h!==null&&h>0&&a.set("hostVars",Me(h)),fJ(p)}var mte=/^(?:\[([^\]]+)\])|(?:\(([^\)]+)\))$/;function pte(n){let i={},e={},t={},o={};for(let r of Object.keys(n)){let a=n[r],c=r.match(mte);if(c===null)switch(r){case"class":if(typeof a!="string")throw new Error("Class binding must be string");o.classAttr=a;break;case"style":if(typeof a!="string")throw new Error("Style binding must be string");o.styleAttr=a;break;default:typeof a=="string"?i[r]=Me(a):i[r]=a}else if(c[1]!=null){if(typeof a!="string")throw new Error("Property binding must be string");t[c[1]]=a}else if(c[2]!=null){if(typeof a!="string")throw new Error("Event binding must be string");e[c[2]]=a}}return{attributes:i,listeners:e,properties:t,specialAttributes:o}}function ute(n,i){let e=ox();return e.createDirectiveHostEventAsts(n.listeners,i),e.createBoundHostProperties(n.properties,i),e.errors}function hte(n,i,e){let t=new XE;return n.map(o=>t.shimCssText(o,i,e))}function wL(n){return n.hostDirectives?.length?ca(Qi(n.hostDirectives.map(i=>ml([{key:"directive",value:q0(i.directive.type),quoted:!1},{key:"inputs",value:BD(i.inputs||{}),quoted:!1},{key:"outputs",value:BD(i.outputs||{}),quoted:!1}])))):Mc}function fte(n){let i=[],e=!1;for(let t of n){if(!t.inputs&&!t.outputs)i.push(t.directive.type);else{let o=[{key:"directive",value:t.directive.type,quoted:!1}];if(t.inputs){let r=qF(t.inputs);r&&o.push({key:"inputs",value:r,quoted:!1})}if(t.outputs){let r=qF(t.outputs);r&&o.push({key:"outputs",value:r,quoted:!1})}i.push(ml(o))}t.isForwardReference&&(e=!0)}return e?new vm([],[new wr(Qi(i))]):Qi(i)}function qF(n){let i=[];for(let e in n)n.hasOwnProperty(e)&&i.push(Me(e),Me(n[e]));return i.length>0?Qi(i):null}var zD=class extends Xh{visit(i){i instanceof as?this.visit(i.ast):i.visit(this)}visitElement(i){this.visitAllTemplateNodes(i.attributes),this.visitAllTemplateNodes(i.inputs),this.visitAllTemplateNodes(i.outputs),this.visitAllTemplateNodes(i.directives),this.visitAllTemplateNodes(i.references),this.visitAllTemplateNodes(i.children)}visitTemplate(i){this.visitAllTemplateNodes(i.attributes),this.visitAllTemplateNodes(i.inputs),this.visitAllTemplateNodes(i.outputs),this.visitAllTemplateNodes(i.directives),this.visitAllTemplateNodes(i.templateAttrs),this.visitAllTemplateNodes(i.variables),this.visitAllTemplateNodes(i.references),this.visitAllTemplateNodes(i.children)}visitContent(i){this.visitAllTemplateNodes(i.children)}visitBoundAttribute(i){this.visit(i.value)}visitBoundEvent(i){this.visit(i.handler)}visitBoundText(i){this.visit(i.value)}visitIcu(i){Object.keys(i.vars).forEach(e=>this.visit(i.vars[e])),Object.keys(i.placeholders).forEach(e=>this.visit(i.placeholders[e]))}visitDeferredBlock(i){i.visitAll(this)}visitDeferredTrigger(i){i instanceof yb?this.visit(i.value):i instanceof g0&&i.options!==null&&this.visit(i.options)}visitDeferredBlockPlaceholder(i){this.visitAllTemplateNodes(i.children)}visitDeferredBlockError(i){this.visitAllTemplateNodes(i.children)}visitDeferredBlockLoading(i){this.visitAllTemplateNodes(i.children)}visitSwitchBlock(i){this.visit(i.expression),this.visitAllTemplateNodes(i.groups)}visitSwitchBlockCase(i){i.expression&&this.visit(i.expression)}visitSwitchBlockCaseGroup(i){this.visitAllTemplateNodes(i.cases),this.visitAllTemplateNodes(i.children)}visitSwitchExhaustiveCheck(i){}visitForLoopBlock(i){i.item.visit(this),this.visitAllTemplateNodes(i.contextVariables),this.visit(i.expression),this.visitAllTemplateNodes(i.children),i.empty?.visit(this)}visitForLoopBlockEmpty(i){this.visitAllTemplateNodes(i.children)}visitIfBlock(i){this.visitAllTemplateNodes(i.branches)}visitIfBlockBranch(i){i.expression&&this.visit(i.expression),i.expressionAlias?.visit(this),this.visitAllTemplateNodes(i.children)}visitLetDeclaration(i){this.visit(i.value)}visitComponent(i){this.visitAllTemplateNodes(i.attributes),this.visitAllTemplateNodes(i.inputs),this.visitAllTemplateNodes(i.outputs),this.visitAllTemplateNodes(i.directives),this.visitAllTemplateNodes(i.references),this.visitAllTemplateNodes(i.children)}visitDirective(i){this.visitAllTemplateNodes(i.attributes),this.visitAllTemplateNodes(i.inputs),this.visitAllTemplateNodes(i.outputs),this.visitAllTemplateNodes(i.references)}visitVariable(i){}visitReference(i){}visitTextAttribute(i){}visitText(i){}visitUnknownBlock(i){}visitAllTemplateNodes(i){for(let e of i)this.visit(e)}};var jD=class{directiveMatcher;constructor(i){this.directiveMatcher=i}bind(i){if(!i.template&&!i.host)throw new Error("Empty bound targets are not supported");let e=new Map,t=[],o=new Set,r=new Map,a=new Map,c=new Map,m=new Map,p=new Map,h=new Map,g=new Set,S=new Set,x=[];if(i.template){let v=rx.apply(i.template);gte(v,c),$D.apply(i.template,this.directiveMatcher,e,t,o,r,a),ax.applyWithScope(i.template,v,m,p,h,g,S,x)}return i.host&&(e.set(i.host.node,i.host.directives),ax.applyWithScope(i.host.node,rx.apply(i.host.node),m,p,h,g,S,x)),new HD(i,e,t,o,r,a,m,p,h,c,g,S,x)}},rx=class n{parentScope;rootNode;namedEntities=new Map;elementLikeInScope=new Set;childScopes=new Map;isDeferred;constructor(i,e){this.parentScope=i,this.rootNode=e,this.isDeferred=i!==null&&i.isDeferred?!0:e instanceof mu}static newRootScope(){return new n(null,null)}static apply(i){let e=n.newRootScope();return e.ingest(i),e}ingest(i){i instanceof Os?(i.variables.forEach(e=>this.visitVariable(e)),i.children.forEach(e=>e.visit(this))):i instanceof Yp?(i.expressionAlias!==null&&this.visitVariable(i.expressionAlias),i.children.forEach(e=>e.visit(this))):i instanceof Zh?(this.visitVariable(i.item),i.contextVariables.forEach(e=>this.visitVariable(e)),i.children.forEach(e=>e.visit(this))):i instanceof b0||i instanceof x0||i instanceof mu||i instanceof C0||i instanceof _0||i instanceof v0||i instanceof Jh?i.children.forEach(e=>e.visit(this)):i instanceof S0||i.forEach(e=>e.visit(this))}visitElement(i){this.visitElementLike(i)}visitTemplate(i){i.directives.forEach(e=>e.visit(this)),i.references.forEach(e=>this.visitReference(e)),this.ingestScopedNode(i)}visitVariable(i){this.maybeDeclare(i)}visitReference(i){this.maybeDeclare(i)}visitDeferredBlock(i){this.ingestScopedNode(i),i.placeholder?.visit(this),i.loading?.visit(this),i.error?.visit(this)}visitDeferredBlockPlaceholder(i){this.ingestScopedNode(i)}visitDeferredBlockError(i){this.ingestScopedNode(i)}visitDeferredBlockLoading(i){this.ingestScopedNode(i)}visitSwitchBlock(i){i.groups.forEach(e=>e.visit(this))}visitSwitchBlockCase(i){}visitSwitchBlockCaseGroup(i){this.ingestScopedNode(i)}visitSwitchExhaustiveCheck(i){}visitForLoopBlock(i){this.ingestScopedNode(i),i.empty?.visit(this)}visitForLoopBlockEmpty(i){this.ingestScopedNode(i)}visitIfBlock(i){i.branches.forEach(e=>e.visit(this))}visitIfBlockBranch(i){this.ingestScopedNode(i)}visitContent(i){this.ingestScopedNode(i)}visitLetDeclaration(i){this.maybeDeclare(i)}visitComponent(i){this.visitElementLike(i)}visitDirective(i){i.references.forEach(e=>this.visitReference(e))}visitBoundAttribute(i){}visitBoundEvent(i){}visitBoundText(i){}visitText(i){}visitTextAttribute(i){}visitIcu(i){}visitDeferredTrigger(i){}visitUnknownBlock(i){}visitElementLike(i){i.directives.forEach(e=>e.visit(this)),i.references.forEach(e=>this.visitReference(e)),i.children.forEach(e=>e.visit(this)),this.elementLikeInScope.add(i)}maybeDeclare(i){this.namedEntities.has(i.name)||this.namedEntities.set(i.name,i)}lookup(i){return this.namedEntities.has(i)?this.namedEntities.get(i):this.parentScope!==null?this.parentScope.lookup(i):null}getChildScope(i){let e=this.childScopes.get(i);if(e===void 0)throw new Error(`Assertion error: child scope for ${i} not found`);return e}ingestScopedNode(i){let e=new n(this,i);e.ingest(i),this.childScopes.set(i,e)}},$D=class n{directiveMatcher;directives;eagerDirectives;missingDirectives;bindings;references;isInDeferBlock=!1;constructor(i,e,t,o,r,a){this.directiveMatcher=i,this.directives=e,this.eagerDirectives=t,this.missingDirectives=o,this.bindings=r,this.references=a}static apply(i,e,t,o,r,a,c){new n(e,t,o,r,a,c).ingest(i)}ingest(i){i.forEach(e=>e.visit(this))}visitElement(i){this.visitElementOrTemplate(i)}visitTemplate(i){this.visitElementOrTemplate(i)}visitDeferredBlock(i){let e=this.isInDeferBlock;this.isInDeferBlock=!0,i.children.forEach(t=>t.visit(this)),this.isInDeferBlock=e,i.placeholder?.visit(this),i.loading?.visit(this),i.error?.visit(this)}visitDeferredBlockPlaceholder(i){i.children.forEach(e=>e.visit(this))}visitDeferredBlockError(i){i.children.forEach(e=>e.visit(this))}visitDeferredBlockLoading(i){i.children.forEach(e=>e.visit(this))}visitSwitchBlock(i){i.groups.forEach(e=>e.visit(this))}visitSwitchBlockCase(i){}visitSwitchBlockCaseGroup(i){i.children.forEach(e=>e.visit(this))}visitSwitchExhaustiveCheck(i){}visitForLoopBlock(i){i.item.visit(this),i.contextVariables.forEach(e=>e.visit(this)),i.children.forEach(e=>e.visit(this)),i.empty?.visit(this)}visitForLoopBlockEmpty(i){i.children.forEach(e=>e.visit(this))}visitIfBlock(i){i.branches.forEach(e=>e.visit(this))}visitIfBlockBranch(i){i.expressionAlias?.visit(this),i.children.forEach(e=>e.visit(this))}visitContent(i){i.children.forEach(e=>e.visit(this))}visitComponent(i){if(this.directiveMatcher instanceof eb){let e=this.directiveMatcher.match(i.componentName);e.length>0?this.trackSelectorlessMatchesAndDirectives(i,e):this.missingDirectives.add(i.componentName)}i.directives.forEach(e=>e.visit(this)),i.children.forEach(e=>e.visit(this))}visitDirective(i){if(this.directiveMatcher instanceof eb){let e=this.directiveMatcher.match(i.name);e.length>0?this.trackSelectorlessMatchesAndDirectives(i,e):this.missingDirectives.add(i.name)}}visitElementOrTemplate(i){if(this.directiveMatcher instanceof J1){let e=[],t=aW(i);this.directiveMatcher.match(t,(o,r)=>e.push(...r)),this.trackSelectorBasedBindingsAndDirectives(i,e)}else i.references.forEach(e=>{e.value.trim()===""&&this.references.set(e,i)});i.directives.forEach(e=>e.visit(this)),i.children.forEach(e=>e.visit(this))}trackMatchedDirectives(i,e){e.length>0&&(this.directives.set(i,e),this.isInDeferBlock||this.eagerDirectives.push(...e))}trackSelectorlessMatchesAndDirectives(i,e){if(e.length===0)return;this.trackMatchedDirectives(i,e);let t=(o,r,a)=>{o[a].hasBindingPropertyName(r.name)&&this.bindings.set(r,o)};for(let o of e)i.inputs.forEach(r=>t(o,r,"inputs")),i.attributes.forEach(r=>t(o,r,"inputs")),i.outputs.forEach(r=>t(o,r,"outputs"));i.references.forEach(o=>this.references.set(o,{directive:e[0],node:i}))}trackSelectorBasedBindingsAndDirectives(i,e){this.trackMatchedDirectives(i,e),i.references.forEach(o=>{let r=null;if(o.value.trim()==="")r=e.find(a=>a.isComponent)||null;else if(r=e.find(a=>a.exportAs!==null&&a.exportAs.some(c=>c===o.value))||null,r===null)return;r!==null?this.references.set(o,{directive:r,node:i}):this.references.set(o,i)});let t=(o,r)=>{let a=e.find(m=>m[r].hasBindingPropertyName(o.name)),c=a!==void 0?a:i;this.bindings.set(o,c)};i.inputs.forEach(o=>t(o,"inputs")),i.attributes.forEach(o=>t(o,"inputs")),i instanceof Os&&i.templateAttrs.forEach(o=>t(o,"inputs")),i.outputs.forEach(o=>t(o,"outputs"))}visitVariable(i){}visitReference(i){}visitTextAttribute(i){}visitBoundAttribute(i){}visitBoundEvent(i){}visitBoundAttributeOrEvent(i){}visitText(i){}visitBoundText(i){}visitIcu(i){}visitDeferredTrigger(i){}visitUnknownBlock(i){}visitLetDeclaration(i){}},ax=class n extends zD{bindings;symbols;usedPipes;eagerPipes;deferBlocks;nestingLevel;scope;rootNode;level;visitNode=i=>i.visit(this);constructor(i,e,t,o,r,a,c,m,p){super(),this.bindings=i,this.symbols=e,this.usedPipes=t,this.eagerPipes=o,this.deferBlocks=r,this.nestingLevel=a,this.scope=c,this.rootNode=m,this.level=p}static applyWithScope(i,e,t,o,r,a,c,m){let p=i instanceof Os?i:null;new n(t,o,a,c,m,r,e,p,0).ingest(i)}ingest(i){if(i instanceof Os)i.variables.forEach(this.visitNode),i.children.forEach(this.visitNode),this.nestingLevel.set(i,this.level);else if(i instanceof Yp)i.expressionAlias!==null&&this.visitNode(i.expressionAlias),i.children.forEach(this.visitNode),this.nestingLevel.set(i,this.level);else if(i instanceof Zh)this.visitNode(i.item),i.contextVariables.forEach(e=>this.visitNode(e)),i.trackBy.visit(this),i.children.forEach(this.visitNode),this.nestingLevel.set(i,this.level);else if(i instanceof mu){if(this.scope.rootNode!==i)throw new Error(`Assertion error: resolved incorrect scope for deferred block ${i}`);this.deferBlocks.push([i,this.scope]),i.children.forEach(e=>e.visit(this)),this.nestingLevel.set(i,this.level)}else i instanceof b0||i instanceof x0||i instanceof C0||i instanceof _0||i instanceof v0||i instanceof Jh?(i.children.forEach(e=>e.visit(this)),this.nestingLevel.set(i,this.level)):i instanceof S0?this.nestingLevel.set(i,0):i.forEach(this.visitNode)}visitTemplate(i){i.inputs.forEach(this.visitNode),i.outputs.forEach(this.visitNode),i.directives.forEach(this.visitNode),i.templateAttrs.forEach(this.visitNode),i.references.forEach(this.visitNode),this.ingestScopedNode(i)}visitVariable(i){this.rootNode!==null&&this.symbols.set(i,this.rootNode)}visitReference(i){this.rootNode!==null&&this.symbols.set(i,this.rootNode)}visitDeferredBlock(i){this.ingestScopedNode(i),i.triggers.when?.value.visit(this),i.prefetchTriggers.when?.value.visit(this),i.hydrateTriggers.when?.value.visit(this),i.hydrateTriggers.never?.visit(this),i.placeholder&&this.visitNode(i.placeholder),i.loading&&this.visitNode(i.loading),i.error&&this.visitNode(i.error)}visitDeferredBlockPlaceholder(i){this.ingestScopedNode(i)}visitDeferredBlockError(i){this.ingestScopedNode(i)}visitDeferredBlockLoading(i){this.ingestScopedNode(i)}visitSwitchBlockCase(i){i.expression?.visit(this)}visitSwitchBlockCaseGroup(i){i.cases.forEach(e=>e.visit(this)),this.ingestScopedNode(i)}visitSwitchExhaustiveCheck(i){}visitForLoopBlock(i){i.expression.visit(this),this.ingestScopedNode(i),i.empty?.visit(this)}visitForLoopBlockEmpty(i){this.ingestScopedNode(i)}visitIfBlockBranch(i){i.expression?.visit(this),this.ingestScopedNode(i)}visitContent(i){this.ingestScopedNode(i)}visitLetDeclaration(i){super.visitLetDeclaration(i),this.rootNode!==null&&this.symbols.set(i,this.rootNode)}visitPipe(i,e){return this.usedPipes.add(i.name),this.scope.isDeferred||this.eagerPipes.add(i.name),super.visitPipe(i,e)}visitPropertyRead(i,e){return this.maybeMap(i,i.name),super.visitPropertyRead(i,e)}visitSafePropertyRead(i,e){return this.maybeMap(i,i.name),super.visitSafePropertyRead(i,e)}ingestScopedNode(i){let e=this.scope.getChildScope(i);new n(this.bindings,this.symbols,this.usedPipes,this.eagerPipes,this.deferBlocks,this.nestingLevel,e,i,this.level+1).ingest(i)}maybeMap(i,e){if(!(i.receiver instanceof Ec))return;let t=this.scope.lookup(e);t!==null&&this.bindings.set(i,t)}},HD=class{target;directives;eagerDirectives;missingDirectives;bindings;references;exprTargets;symbols;nestingLevel;scopedNodeEntities;usedPipes;eagerPipes;deferredBlocks;deferredScopes;constructor(i,e,t,o,r,a,c,m,p,h,g,S,x){this.target=i,this.directives=e,this.eagerDirectives=t,this.missingDirectives=o,this.bindings=r,this.references=a,this.exprTargets=c,this.symbols=m,this.nestingLevel=p,this.scopedNodeEntities=h,this.usedPipes=g,this.eagerPipes=S,this.deferredBlocks=x.map(v=>v[0]),this.deferredScopes=new Map(x)}getEntitiesInScope(i){return this.scopedNodeEntities.get(i)??new Set}getDirectivesOfNode(i){return this.directives.get(i)||null}getReferenceTarget(i){return this.references.get(i)||null}getConsumerOfBinding(i){return this.bindings.get(i)||null}getExpressionTarget(i){return this.exprTargets.get(i)||null}getDefinitionNodeOfSymbol(i){return this.symbols.get(i)||null}getNestingLevel(i){return this.nestingLevel.get(i)||0}getUsedDirectives(){let i=new Set;return this.directives.forEach(e=>e.forEach(t=>i.add(t))),Array.from(i.values())}getEagerlyUsedDirectives(){let i=new Set(this.eagerDirectives);return Array.from(i.values())}getUsedPipes(){return Array.from(this.usedPipes)}getEagerlyUsedPipes(){return Array.from(this.eagerPipes)}getDeferBlocks(){return this.deferredBlocks}getDeferredTriggerTarget(i,e){if(!(e instanceof wb)&&!(e instanceof g0)&&!(e instanceof Sb))return null;let t=e.reference;if(t===null){let r=null;if(i.placeholder!==null){for(let a of i.placeholder.children)if(!(a instanceof cx)){if(r!==null)return null;a instanceof Dc&&(r=a)}}return r}let o=this.findEntityInScope(i,t);if(o instanceof y0&&this.getDefinitionNodeOfSymbol(o)!==i){let r=this.getReferenceTarget(o);if(r!==null)return this.referenceTargetToElement(r)}if(i.placeholder!==null){let r=this.findEntityInScope(i.placeholder,t),a=r instanceof y0?this.getReferenceTarget(r):null;if(a!==null)return this.referenceTargetToElement(a)}return null}isDeferred(i){for(let e of this.deferredBlocks){if(!this.deferredScopes.has(e))continue;let t=[this.deferredScopes.get(e)];for(;t.length>0;){let o=t.pop();if(o.elementLikeInScope.has(i))return!0;t.push(...o.childScopes.values())}}return!1}referencedDirectiveExists(i){return!this.missingDirectives.has(i)}findEntityInScope(i,e){let t=this.getEntitiesInScope(i);for(let o of t)if(o.name===e)return o;return null}referenceTargetToElement(i){return i instanceof Dc?i:i instanceof Os||i.node instanceof $_||i.node instanceof l6||i.node instanceof S0?null:this.referenceTargetToElement(i.node)}};function gte(n,i){let e=new Map;function t(r){if(e.has(r.rootNode))return e.get(r.rootNode);let a=r.namedEntities,c;return r.parentScope!==null?c=new Map([...t(r.parentScope),...a]):c=new Map(a),e.set(r.rootNode,c),c}let o=[n];for(;o.length>0;){let r=o.pop();for(let a of r.childScopes.values())o.push(a);t(r)}for(let[r,a]of e)i.set(r,new Set(a.values()))}var UD=class{},GD=class{jitEvaluator;FactoryTarget=Cd;ResourceLoader=UD;elementSchemaRegistry=new sf;constructor(i=new WE){this.jitEvaluator=i}compilePipe(i,e,t){let o={name:t.name,type:Nr(t.type),typeArgumentCount:0,pipeName:t.pipeName,pure:t.pure,isStandalone:t.isStandalone},r=AR(o);return this.jitExpression(r.expression,i,e,[])}compilePipeDeclaration(i,e,t){let o=Ote(t),r=AR(o);return this.jitExpression(r.expression,i,e,[])}compileInjectable(i,e,t){let{expression:o,statements:r}=yR({name:t.name,type:Nr(t.type),typeArgumentCount:t.typeArgumentCount,providedIn:JF(t.providedIn),useClass:Ih(t,"useClass"),useFactory:ZF(t,"useFactory"),useValue:Ih(t,"useValue"),useExisting:Ih(t,"useExisting"),deps:t.deps?.map(EL)},!0);return this.jitExpression(o,i,e,r)}compileInjectableDeclaration(i,e,t){let{expression:o,statements:r}=yR({name:t.type.name,type:Nr(t.type),typeArgumentCount:0,providedIn:JF(t.providedIn),useClass:Ih(t,"useClass"),useFactory:ZF(t,"useFactory"),useValue:Ih(t,"useValue"),useExisting:Ih(t,"useExisting"),deps:t.deps?.map(e6)},!0);return this.jitExpression(o,i,e,r)}compileInjector(i,e,t){let o={type:Nr(t.type),providers:t.providers&&t.providers.length>0?new ri(t.providers):null,imports:t.imports.map(a=>new ri(a))},r=IR(o);return this.jitExpression(r.expression,i,e,[])}compileInjectorDeclaration(i,e,t){let o=Nte(t),r=IR(o);return this.jitExpression(r.expression,i,e,[])}compileNgModule(i,e,t){let o={kind:_m.Global,type:Nr(t.type),bootstrap:t.bootstrap.map(Nr),declarations:t.declarations.map(Nr),publicDeclarationTypes:null,imports:t.imports.map(Nr),includeImportTypes:!0,exports:t.exports.map(Nr),selectorScopeMode:Ob.Inline,containsForwardDecls:!1,schemas:t.schemas?t.schemas.map(Nr):null,id:t.id?new ri(t.id):null},r=kW(o);return this.jitExpression(r.expression,i,e,[])}compileNgModuleDeclaration(i,e,t){let o=TW(t);return this.jitExpression(o,i,e,[])}compileDirective(i,e,t){let o=YF(t);return this.compileDirectiveFromMeta(i,e,o)}compileDirectiveDeclaration(i,e,t){let o=this.createParseSourceSpan("Directive",t.type.name,e),r=kL(t,o);return this.compileDirectiveFromMeta(i,e,r)}compileDirectiveFromMeta(i,e,t){let o=new db,r=ox(),a=ite(t,o,r);return this.jitExpression(a.expression,i,e,o.statements)}compileComponent(i,e,t){let{template:o,defer:r}=TL(t.template,t.name,e,t.preserveWhitespaces,void 0),a=Qe(W(W({},t),YF(t)),{selector:t.selector||this.elementSchemaRegistry.getDefaultComponentElementName(),template:o,declarations:t.declarations.map(bte),declarationListEmitMode:0,defer:r,styles:[...t.styles,...o.styles],encapsulation:t.encapsulation,changeDetection:t.changeDetection??null,animations:t.animations!=null?new ri(t.animations):null,viewProviders:t.viewProviders!=null?new ri(t.viewProviders):null,relativeContextFilePath:"",i18nUseExternalIds:!0,relativeTemplatePath:null}),c=`ng:///${t.name}.js`;return this.compileComponentFromMeta(i,c,a)}compileComponentDeclaration(i,e,t){let o=this.createParseSourceSpan("Component",t.type.name,e),r=Cte(t,o,e);return this.compileComponentFromMeta(i,e,r)}compileComponentFromMeta(i,e,t){let o=new db,r=ox(),a=ote(t,o,r);return this.jitExpression(a.expression,i,e,o.statements)}compileFactory(i,e,t){let o=$p({name:t.name,type:Nr(t.type),typeArgumentCount:t.typeArgumentCount,deps:Ste(t.deps),target:t.target});return this.jitExpression(o.expression,i,e,o.statements)}compileFactoryDeclaration(i,e,t){let o=$p({name:t.type.name,type:Nr(t.type),typeArgumentCount:0,deps:Array.isArray(t.deps)?t.deps.map(e6):t.deps,target:t.target});return this.jitExpression(o.expression,i,e,o.statements)}createParseSourceSpan(i,e,t){return CW(i,e,t)}jitExpression(i,e,t,o){let r=[...o,new Fr("$def",i,void 0,la.Exported)];return this.jitEvaluator.evaluateStatements(t,r,new QE(e),!0).$def}};function QF(n){return Qe(W({},n),{isSignal:n.isSignal,predicate:ML(n.predicate),read:n.read?new ri(n.read):null,static:n.static,emitDistinctChangesOnly:n.emitDistinctChangesOnly})}function XF(n){return{propertyName:n.propertyName,first:n.first??!1,predicate:ML(n.predicate),descendants:n.descendants??!1,read:n.read?new ri(n.read):null,static:n.static??!1,emitDistinctChangesOnly:n.emitDistinctChangesOnly??!0,isSignal:!!n.isSignal}}function ML(n){return Array.isArray(n)?n:KD(new ri(n),1)}function YF(n){let i=Ate(n.inputs||[]),e=gE(n.outputs||[]),t=n.propMetadata,o={},r={};for(let c in t)t.hasOwnProperty(c)&&t[c].forEach(m=>{Ete(m)?o[c]={bindingPropertyName:m.alias||c,classPropertyName:c,required:m.required||!1,isSignal:!!m.isSignal,transformFunction:m.transform!=null?new ri(m.transform):null}:Dte(m)&&(r[c]=m.alias||c)});let a=n.hostDirectives?.length?n.hostDirectives.map(c=>typeof c=="function"?{directive:Nr(c),inputs:null,outputs:null,isForwardReference:!1}:{directive:Nr(c.directive),isForwardReference:!1,inputs:c.inputs?gE(c.inputs):null,outputs:c.outputs?gE(c.outputs):null}):null;return Qe(W({},n),{typeArgumentCount:0,typeSourceSpan:n.typeSourceSpan,type:Nr(n.type),deps:null,host:W({},Mte(n.propMetadata,n.typeSourceSpan,n.host)),inputs:W(W({},i),o),outputs:W(W({},e),r),queries:n.queries.map(QF),providers:n.providers!=null?new ri(n.providers):null,viewQueries:n.viewQueries.map(QF),hostDirectives:a})}function kL(n,i){let e=n.hostDirectives?.length?n.hostDirectives.map(t=>({directive:Nr(t.directive),isForwardReference:!1,inputs:t.inputs?KF(t.inputs):null,outputs:t.outputs?KF(t.outputs):null})):null;return{name:n.type.name,type:Nr(n.type),typeSourceSpan:i,selector:n.selector??null,inputs:n.inputs?Pte(n.inputs):{},outputs:n.outputs??{},host:_te(n.host),queries:(n.queries??[]).map(XF),viewQueries:(n.viewQueries??[]).map(XF),providers:n.providers!==void 0?new ri(n.providers):null,exportAs:n.exportAs??null,usesInheritance:n.usesInheritance??!1,controlCreate:n.controlCreate??null,lifecycle:{usesOnChanges:n.usesOnChanges??!1},deps:null,typeArgumentCount:0,isStandalone:n.isStandalone??s6(n.version),isSignal:n.isSignal??!1,hostDirectives:e}}function _te(n={}){return{attributes:vte(n.attributes??{}),listeners:n.listeners??{},properties:n.properties??{},specialAttributes:{classAttr:n.classAttribute,styleAttr:n.styleAttribute}}}function KF(n){let i=null;for(let e=1;efE(c,!0))),n.directives&&r.push(...n.directives.map(c=>fE(c))),n.pipes&&r.push(...xte(n.pipes)));let a=r.some(({kind:c})=>c===tf.Directive||c===tf.NgModule);return Qe(W({},kL(n,i)),{template:t,styles:n.styles??[],declarations:r,viewProviders:n.viewProviders!==void 0?new ri(n.viewProviders):null,animations:n.animations!==void 0?new ri(n.animations):null,defer:o,changeDetection:n.changeDetection??qD.Default,encapsulation:n.encapsulation??jp.Emulated,declarationListEmitMode:2,relativeContextFilePath:"",i18nUseExternalIds:!0,relativeTemplatePath:null,hasDirectiveDependencies:a})}function bte(n){return Qe(W({},n),{type:new ri(n.type)})}function fE(n,i=null){return{kind:tf.Directive,isComponent:i||n.kind==="component",selector:n.selector,type:new ri(n.type),inputs:n.inputs??[],outputs:n.outputs??[],exportAs:n.exportAs??null}}function xte(n){return n?Object.keys(n).map(i=>({kind:tf.Pipe,name:i,type:new ri(n[i])})):[]}function yte(n){return{kind:tf.Pipe,name:n.name,type:new ri(n.type)}}function TL(n,i,e,t,o){let r=Jee(n,e,{preserveWhitespaces:t});if(r.errors!==null){let m=r.errors.map(p=>p.toString()).join(", ");throw new Error(`Errors during JIT compilation of template for ${i}: ${m}`)}let c=new jD(null).bind({template:r.nodes});return{template:r,defer:wte(c,o)}}function Ih(n,i){if(n.hasOwnProperty(i))return KD(new ri(n[i]),0)}function ZF(n,i){if(n.hasOwnProperty(i))return new ri(n[i])}function JF(n){let i=typeof n=="function"?new ri(n):new da(n??null);return KD(i,0)}function Ste(n){return n==null?null:n.map(EL)}function EL(n){let i=n.attribute!=null,e=n.token===null?null:new ri(n.token),t=i?new ri(n.attribute):e;return DL(t,i,n.host,n.optional,n.self,n.skipSelf)}function e6(n){let i=n.attribute??!1,e=n.token===null?null:new ri(n.token);return DL(e,i,n.host??!1,n.optional??!1,n.self??!1,n.skipSelf??!1)}function DL(n,i,e,t,o,r){let a=i?Me("unknown"):null;return{token:n,attributeNameType:a,host:e,optional:t,self:o,skipSelf:r}}function wte(n,i){let e=n.getDeferBlocks(),t=new Map;for(let o=0;or.msg).join(` -`));for(let r in n)n.hasOwnProperty(r)&&n[r].forEach(a=>{kte(a)?t.properties[a.hostPropertyName||r]=$G("this",r):Tte(a)&&(t.listeners[a.eventName||r]=`${r}(${(a.args||[]).join(",")})`)});return t}function kte(n){return n.ngMetadataName==="HostBinding"}function Tte(n){return n.ngMetadataName==="HostListener"}function Ete(n){return n.ngMetadataName==="Input"}function Dte(n){return n.ngMetadataName==="Output"}function Pte(n){return Object.keys(n).reduce((i,e)=>{let t=n[e];return typeof t=="string"||Array.isArray(t)?i[e]=Ite(t):i[e]={bindingPropertyName:t.publicName,classPropertyName:e,transformFunction:t.transformFunction!==null?new ri(t.transformFunction):null,required:t.isRequired,isSignal:t.isSignal},i},{})}function Ite(n){return typeof n=="string"?{bindingPropertyName:n,classPropertyName:n,transformFunction:null,required:!1,isSignal:!1}:{bindingPropertyName:n[0],classPropertyName:n[1],transformFunction:n[2]?new ri(n[2]):null,required:!1,isSignal:!1}}function Ate(n){return n.reduce((i,e)=>{if(typeof e=="string"){let[t,o]=PL(e);i[o]={bindingPropertyName:t,classPropertyName:o,required:!1,isSignal:!1,transformFunction:null}}else i[e.name]={bindingPropertyName:e.alias||e.name,classPropertyName:e.name,required:e.required||!1,isSignal:!1,transformFunction:e.transform!=null?new ri(e.transform):null};return i},{})}function gE(n){return n.reduce((i,e)=>{let[t,o]=PL(e);return i[o]=t,i},{})}function PL(n){let[i,e]=n.split(":",2).map(t=>t.trim());return[e??i,i]}function Ote(n){return{name:n.type.name,type:Nr(n.type),typeArgumentCount:0,pipeName:n.name,deps:null,pure:n.pure??!0,isStandalone:n.isStandalone??s6(n.version)}}function Nte(n){return{name:n.type.name,type:Nr(n.type),providers:n.providers!==void 0&&n.providers.length>0?new ri(n.providers):null,imports:n.imports!==void 0?n.imports.map(i=>new ri(i)):[]}}function Rte(n){let i=n.ng||(n.ng={});i.\u0275compilerFacade=new GD}var WD=class{closedByParent=!1;implicitNamespacePrefix=null;isVoid=!1;ignoreFirstLf=!1;canSelfClose=!0;preventNamespaceInheritance=!1;requireExtraParent(i){return!1}isClosedByChild(i){return!1}getContentType(){return Cc.PARSABLE_DATA}},gFe=new WD;var _Fe=new SE("21.2.6");Rte(j_);function vP(n){let i=n.cloneNode(!0),e=i.querySelectorAll("[id]"),t=n.nodeName.toLowerCase();i.removeAttribute("id");for(let o=0;o=t&&e<=o&&i>=r&&i<=a}function Bte(n,i){let e=i.leftn.right,o=i.topn.bottom;return e||t||o||r}function ev(n,i,e){n.top+=i,n.bottom=n.top+n.height,n.left+=e,n.right=n.left+n.width}function NL(n,i,e,t){let{top:o,right:r,bottom:a,left:c,width:m,height:p}=n,h=m*i,g=p*i;return t>o-g&&tc-h&&e{this.positions.set(e,{scrollPosition:{top:e.scrollTop,left:e.scrollLeft},clientRect:wP(e)})})}handleScroll(i){let e=Bp(i),t=this.positions.get(e);if(!t)return null;let o=t.scrollPosition,r,a;if(e===this._document){let p=this.getViewportScrollPosition();r=p.top,a=p.left}else r=e.scrollTop,a=e.scrollLeft;let c=o.top-r,m=o.left-a;return this.positions.forEach((p,h)=>{p.clientRect&&e!==h&&e.contains(h)&&ev(p.clientRect,c,m)}),o.top=r,o.left=a,{top:c,left:m}}getViewportScrollPosition(){return{top:window.scrollY,left:window.scrollX}}};function WL(n,i){let e=n.rootNodes;if(e.length===1&&e[0].nodeType===i.ELEMENT_NODE)return e[0];let t=i.createElement("div");return e.forEach(o=>t.appendChild(o)),t}function MP(n,i,e){for(let t in i)if(i.hasOwnProperty(t)){let o=i[t];o?n.setProperty(t,o,e?.has(t)?"important":""):n.removeProperty(t)}return n}function hf(n,i){let e=i?"":"none";MP(n.style,{"touch-action":i?"":"none","-webkit-user-drag":i?"":"none","-webkit-tap-highlight-color":i?"":"transparent","user-select":e,"-ms-user-select":e,"-webkit-user-select":e,"-moz-user-select":e})}function RL(n,i,e){MP(n.style,{position:i?"":"fixed",top:i?"":"0",opacity:i?"":"0",left:i?"":"-999em"},e)}function gx(n,i){return i&&i!="none"?n+" "+i:n}function FL(n,i){n.style.width=`${i.width}px`,n.style.height=`${i.height}px`,n.style.transform=tv(i.left,i.top)}function tv(n,i){return`translate3d(${Math.round(n)}px, ${Math.round(i)}px, 0)`}var Z0={capture:!0},gP={passive:!1,capture:!0},Vte=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275cmp=F({type:n,selectors:[["ng-component"]],hostAttrs:["cdk-drag-resets-container",""],decls:0,vars:0,template:function(t,o){},styles:[`@layer cdk-resets{.cdk-drag-preview{background:none;border:none;padding:0;color:inherit;inset:auto}}.cdk-drag-placeholder *,.cdk-drag-preview *{pointer-events:none !important} -`],encapsulation:2,changeDetection:0})}return n})(),qL=(()=>{class n{_ngZone=f(Pi);_document=f(co);_styleLoader=f(pr);_renderer=f(pd).createRenderer(null,null);_cleanupDocumentTouchmove;_scroll=new je;_dropInstances=new Set;_dragInstances=new Set;_activeDragInstances=se([]);_globalListeners;_draggingPredicate=e=>e.isDragging();_domNodesToDirectives=null;pointerMove=new je;pointerUp=new je;constructor(){}registerDropContainer(e){this._dropInstances.has(e)||this._dropInstances.add(e)}registerDragItem(e){this._dragInstances.add(e),this._dragInstances.size===1&&this._ngZone.runOutsideAngular(()=>{this._cleanupDocumentTouchmove?.(),this._cleanupDocumentTouchmove=this._renderer.listen(this._document,"touchmove",this._persistentTouchmoveListener,gP)})}removeDropContainer(e){this._dropInstances.delete(e)}removeDragItem(e){this._dragInstances.delete(e),this.stopDragging(e),this._dragInstances.size===0&&this._cleanupDocumentTouchmove?.()}startDragging(e,t){if(!(this._activeDragInstances().indexOf(e)>-1)&&(this._styleLoader.load(Vte),this._activeDragInstances.update(o=>[...o,e]),this._activeDragInstances().length===1)){let o=t.type.startsWith("touch"),r=c=>this.pointerUp.next(c),a=[["scroll",c=>this._scroll.next(c),Z0],["selectstart",this._preventDefaultWhileDragging,gP]];o?a.push(["touchend",r,Z0],["touchcancel",r,Z0]):a.push(["mouseup",r,Z0]),o||a.push(["mousemove",c=>this.pointerMove.next(c),gP]),this._ngZone.runOutsideAngular(()=>{this._globalListeners=a.map(([c,m,p])=>this._renderer.listen(this._document,c,m,p))})}}stopDragging(e){this._activeDragInstances.update(t=>{let o=t.indexOf(e);return o>-1?(t.splice(o,1),[...t]):t}),this._activeDragInstances().length===0&&this._clearGlobalListeners()}isDragging(e){return this._activeDragInstances().indexOf(e)>-1}scrolled(e){let t=[this._scroll];return e&&e!==this._document&&t.push(new Pr(o=>this._ngZone.runOutsideAngular(()=>{let r=this._renderer.listen(e,"scroll",a=>{this._activeDragInstances().length&&o.next(a)},Z0);return()=>{r()}}))),En(...t)}registerDirectiveNode(e,t){this._domNodesToDirectives??=new WeakMap,this._domNodesToDirectives.set(e,t)}removeDirectiveNode(e){this._domNodesToDirectives?.delete(e)}getDragDirectiveForNode(e){return this._domNodesToDirectives?.get(e)||null}ngOnDestroy(){this._dragInstances.forEach(e=>this.removeDragItem(e)),this._dropInstances.forEach(e=>this.removeDropContainer(e)),this._domNodesToDirectives=null,this._clearGlobalListeners(),this.pointerMove.complete(),this.pointerUp.complete()}_preventDefaultWhileDragging=e=>{this._activeDragInstances().length>0&&e.preventDefault()};_persistentTouchmoveListener=e=>{this._activeDragInstances().length>0&&(this._activeDragInstances().some(this._draggingPredicate)&&e.preventDefault(),this.pointerMove.next(e))};_clearGlobalListeners(){this._globalListeners?.forEach(e=>e()),this._globalListeners=void 0}static \u0275fac=function(t){return new(t||n)};static \u0275prov=K({token:n,factory:n.\u0275fac,providedIn:"root"})}return n})();function LL(n){let i=n.toLowerCase().indexOf("ms")>-1?1:1e3;return parseFloat(n)*i}function zte(n){let i=getComputedStyle(n),e=_P(i,"transition-property"),t=e.find(c=>c==="transform"||c==="all");if(!t)return 0;let o=e.indexOf(t),r=_P(i,"transition-duration"),a=_P(i,"transition-delay");return LL(r[o])+LL(a[o])}function _P(n,i){return n.getPropertyValue(i).split(",").map(t=>t.trim())}var jte=new Set(["position"]),bP=class{_document;_rootElement;_direction;_initialDomRect;_previewTemplate;_previewClass;_pickupPositionOnPage;_initialTransform;_zIndex;_renderer;_previewEmbeddedView=null;_preview;get element(){return this._preview}constructor(i,e,t,o,r,a,c,m,p,h){this._document=i,this._rootElement=e,this._direction=t,this._initialDomRect=o,this._previewTemplate=r,this._previewClass=a,this._pickupPositionOnPage=c,this._initialTransform=m,this._zIndex=p,this._renderer=h}attach(i){this._preview=this._createPreview(),i.appendChild(this._preview),BL(this._preview)&&this._preview.showPopover()}destroy(){this._preview.remove(),this._previewEmbeddedView?.destroy(),this._preview=this._previewEmbeddedView=null}setTransform(i){this._preview.style.transform=i}getBoundingClientRect(){return this._preview.getBoundingClientRect()}addClass(i){this._preview.classList.add(i)}getTransitionDuration(){return zte(this._preview)}addEventListener(i,e){return this._renderer.listen(this._preview,i,e)}_createPreview(){let i=this._previewTemplate,e=this._previewClass,t=i?i.template:null,o;if(t&&i){let r=i.matchSize?this._initialDomRect:null,a=i.viewContainer.createEmbeddedView(t,i.context);a.detectChanges(),o=WL(a,this._document),this._previewEmbeddedView=a,i.matchSize?FL(o,r):o.style.transform=tv(this._pickupPositionOnPage.x,this._pickupPositionOnPage.y)}else o=vP(this._rootElement),FL(o,this._initialDomRect),this._initialTransform&&(o.style.transform=this._initialTransform);return MP(o.style,{"pointer-events":"none",margin:BL(o)?"0 auto 0 0":"0",position:"fixed",top:"0",left:"0","z-index":this._zIndex+""},jte),hf(o,!1),o.classList.add("cdk-drag-preview"),o.setAttribute("popover","manual"),o.setAttribute("dir",this._direction),e&&(Array.isArray(e)?e.forEach(r=>o.classList.add(r)):o.classList.add(e)),o}};function BL(n){return"showPopover"in n}var $te={passive:!0},VL={passive:!1},Hte={passive:!1,capture:!0},Ute=800,zL="cdk-drag-placeholder",jL=new Set(["position"]);function Gte(n,i,e={dragStartThreshold:5,pointerDirectionChangeThreshold:5}){let t=n.get(pi,null,{optional:!0})||n.get(pd).createRenderer(null,null);return new xP(i,e,n.get(co),n.get(Pi),n.get(hd),n.get(qL),t)}var xP=class{_config;_document;_ngZone;_viewportRuler;_dragDropRegistry;_renderer;_rootElementCleanups;_cleanupShadowRootSelectStart;_preview=null;_previewContainer;_placeholderRef=null;_placeholder;_pickupPositionInElement;_pickupPositionOnPage;_marker;_anchor=null;_passiveTransform={x:0,y:0};_activeTransform={x:0,y:0};_initialTransform;_hasStartedDragging=se(!1);_hasMoved=!1;_initialContainer;_initialIndex;_parentPositions;_moveEvents=new je;_pointerDirectionDelta;_pointerPositionAtLastDirectionChange;_lastKnownPointerPosition;_rootElement;_ownerSVGElement=null;_rootElementTapHighlight;_pointerMoveSubscription=go.EMPTY;_pointerUpSubscription=go.EMPTY;_scrollSubscription=go.EMPTY;_resizeSubscription=go.EMPTY;_lastTouchEventTime;_dragStartTime;_boundaryElement=null;_nativeInteractionsEnabled=!0;_initialDomRect;_previewRect;_boundaryRect;_previewTemplate;_placeholderTemplate;_handles=[];_disabledHandles=new Set;_dropContainer;_direction="ltr";_parentDragRef=null;_cachedShadowRoot;lockAxis=null;dragStartDelay=0;previewClass;scale=1;get disabled(){return this._disabled||!!(this._dropContainer&&this._dropContainer.disabled)}set disabled(i){i!==this._disabled&&(this._disabled=i,this._toggleNativeDragInteractions(),this._handles.forEach(e=>hf(e,i)))}_disabled=!1;beforeStarted=new je;started=new je;released=new je;ended=new je;entered=new je;exited=new je;dropped=new je;moved=this._moveEvents;data;constrainPosition;constructor(i,e,t,o,r,a,c){this._config=e,this._document=t,this._ngZone=o,this._viewportRuler=r,this._dragDropRegistry=a,this._renderer=c,this.withRootElement(i).withParent(e.parentDragRef||null),this._parentPositions=new fx(t),a.registerDragItem(this)}getPlaceholderElement(){return this._placeholder}getRootElement(){return this._rootElement}getVisibleElement(){return this.isDragging()?this.getPlaceholderElement():this.getRootElement()}withHandles(i){this._handles=i.map(t=>$l(t)),this._handles.forEach(t=>hf(t,this.disabled)),this._toggleNativeDragInteractions();let e=new Set;return this._disabledHandles.forEach(t=>{this._handles.indexOf(t)>-1&&e.add(t)}),this._disabledHandles=e,this}withPreviewTemplate(i){return this._previewTemplate=i,this}withPlaceholderTemplate(i){return this._placeholderTemplate=i,this}withRootElement(i){let e=$l(i);if(e!==this._rootElement){this._removeRootElementListeners();let t=this._renderer;this._rootElementCleanups=this._ngZone.runOutsideAngular(()=>[t.listen(e,"mousedown",this._pointerDown,VL),t.listen(e,"touchstart",this._pointerDown,$te),t.listen(e,"dragstart",this._nativeDragStart,VL)]),this._initialTransform=void 0,this._rootElement=e}return typeof SVGElement<"u"&&this._rootElement instanceof SVGElement&&(this._ownerSVGElement=this._rootElement.ownerSVGElement),this}withBoundaryElement(i){return this._boundaryElement=i?$l(i):null,this._resizeSubscription.unsubscribe(),i&&(this._resizeSubscription=this._viewportRuler.change(10).subscribe(()=>this._containInsideBoundaryOnResize())),this}withParent(i){return this._parentDragRef=i,this}dispose(){this._removeRootElementListeners(),this.isDragging()&&this._rootElement?.remove(),this._marker?.remove(),this._destroyPreview(),this._destroyPlaceholder(),this._dragDropRegistry.removeDragItem(this),this._removeListeners(),this.beforeStarted.complete(),this.started.complete(),this.released.complete(),this.ended.complete(),this.entered.complete(),this.exited.complete(),this.dropped.complete(),this._moveEvents.complete(),this._handles=[],this._disabledHandles.clear(),this._dropContainer=void 0,this._resizeSubscription.unsubscribe(),this._parentPositions.clear(),this._boundaryElement=this._rootElement=this._ownerSVGElement=this._placeholderTemplate=this._previewTemplate=this._marker=this._parentDragRef=null}isDragging(){return this._hasStartedDragging()&&this._dragDropRegistry.isDragging(this)}reset(){this._rootElement.style.transform=this._initialTransform||"",this._activeTransform={x:0,y:0},this._passiveTransform={x:0,y:0}}resetToBoundary(){if(this._boundaryElement&&this._rootElement&&Bte(this._boundaryElement.getBoundingClientRect(),this._rootElement.getBoundingClientRect())){let i=this._boundaryElement.getBoundingClientRect(),e=this._rootElement.getBoundingClientRect(),t=0,o=0;e.lefti.right&&(t=i.right-e.right),e.topi.bottom&&(o=i.bottom-e.bottom);let r=this._activeTransform.x,a=this._activeTransform.y,c=r+t,m=a+o;this._rootElement.style.transform=tv(c,m),this._activeTransform={x:c,y:m},this._passiveTransform={x:c,y:m}}}disableHandle(i){!this._disabledHandles.has(i)&&this._handles.indexOf(i)>-1&&(this._disabledHandles.add(i),hf(i,!0))}enableHandle(i){this._disabledHandles.has(i)&&(this._disabledHandles.delete(i),hf(i,this.disabled))}withDirection(i){return this._direction=i,this}_withDropContainer(i){this._dropContainer=i}getFreeDragPosition(){let i=this.isDragging()?this._activeTransform:this._passiveTransform;return{x:i.x,y:i.y}}setFreeDragPosition(i){return this._activeTransform={x:0,y:0},this._passiveTransform.x=i.x,this._passiveTransform.y=i.y,this._dropContainer||this._applyRootElementTransform(i.x,i.y),this}withPreviewContainer(i){return this._previewContainer=i,this}_sortFromLastPointerPosition(){let i=this._lastKnownPointerPosition;i&&this._dropContainer&&this._updateActiveDropContainer(this._getConstrainedPointerPosition(i),i)}_removeListeners(){this._pointerMoveSubscription.unsubscribe(),this._pointerUpSubscription.unsubscribe(),this._scrollSubscription.unsubscribe(),this._cleanupShadowRootSelectStart?.(),this._cleanupShadowRootSelectStart=void 0}_destroyPreview(){this._preview?.destroy(),this._preview=null}_destroyPlaceholder(){this._anchor?.remove(),this._placeholder?.remove(),this._placeholderRef?.destroy(),this._placeholder=this._anchor=this._placeholderRef=null}_pointerDown=i=>{if(this.beforeStarted.next(),this._handles.length){let e=this._getTargetHandle(i);e&&!this._disabledHandles.has(e)&&!this.disabled&&this._initializeDragSequence(e,i)}else this.disabled||this._initializeDragSequence(this._rootElement,i)};_pointerMove=i=>{let e=this._getPointerPositionOnPage(i);if(!this._hasStartedDragging()){let o=Math.abs(e.x-this._pickupPositionOnPage.x),r=Math.abs(e.y-this._pickupPositionOnPage.y);if(o+r>=this._config.dragStartThreshold){let c=Date.now()>=this._dragStartTime+this._getDragStartDelay(i),m=this._dropContainer;if(!c){this._endDragSequence(i);return}(!m||!m.isDragging()&&!m.isReceiving())&&(i.cancelable&&i.preventDefault(),this._hasStartedDragging.set(!0),this._ngZone.run(()=>this._startDragSequence(i)))}return}i.cancelable&&i.preventDefault();let t=this._getConstrainedPointerPosition(e);if(this._hasMoved=!0,this._lastKnownPointerPosition=e,this._updatePointerDirectionDelta(t),this._dropContainer)this._updateActiveDropContainer(t,e);else{let o=this.constrainPosition?this._initialDomRect:this._pickupPositionOnPage,r=this._activeTransform;r.x=t.x-o.x+this._passiveTransform.x,r.y=t.y-o.y+this._passiveTransform.y,this._applyRootElementTransform(r.x,r.y)}this._moveEvents.observers.length&&this._ngZone.run(()=>{this._moveEvents.next({source:this,pointerPosition:t,event:i,distance:this._getDragDistance(t),delta:this._pointerDirectionDelta})})};_pointerUp=i=>{this._endDragSequence(i)};_endDragSequence(i){if(this._dragDropRegistry.isDragging(this)&&(this._removeListeners(),this._dragDropRegistry.stopDragging(this),this._toggleNativeDragInteractions(),this._handles&&(this._rootElement.style.webkitTapHighlightColor=this._rootElementTapHighlight),!!this._hasStartedDragging()))if(this.released.next({source:this,event:i}),this._dropContainer)this._dropContainer._stopScrolling(),this._animatePreviewToPlaceholder().then(()=>{this._cleanupDragArtifacts(i),this._cleanupCachedDimensions(),this._dragDropRegistry.stopDragging(this)});else{this._passiveTransform.x=this._activeTransform.x;let e=this._getPointerPositionOnPage(i);this._passiveTransform.y=this._activeTransform.y,this._ngZone.run(()=>{this.ended.next({source:this,distance:this._getDragDistance(e),dropPoint:e,event:i})}),this._cleanupCachedDimensions(),this._dragDropRegistry.stopDragging(this)}}_startDragSequence(i){J0(i)&&(this._lastTouchEventTime=Date.now()),this._toggleNativeDragInteractions();let e=this._getShadowRoot(),t=this._dropContainer;if(e&&this._ngZone.runOutsideAngular(()=>{this._cleanupShadowRootSelectStart=this._renderer.listen(e,"selectstart",Wte,Hte)}),t){let o=this._rootElement,r=o.parentNode,a=this._placeholder=this._createPlaceholderElement(),c=this._marker=this._marker||this._document.createComment("");r.insertBefore(c,o),this._initialTransform=o.style.transform||"",this._preview=new bP(this._document,this._rootElement,this._direction,this._initialDomRect,this._previewTemplate||null,this.previewClass||null,this._pickupPositionOnPage,this._initialTransform,this._config.zIndex||1e3,this._renderer),this._preview.attach(this._getPreviewInsertionPoint(r,e)),RL(o,!1,jL),this._document.body.appendChild(r.replaceChild(a,o)),this.started.next({source:this,event:i}),t.start(),this._initialContainer=t,this._initialIndex=t.getItemIndex(this)}else this.started.next({source:this,event:i}),this._initialContainer=this._initialIndex=void 0;this._parentPositions.cache(t?t.getScrollableParents():[])}_initializeDragSequence(i,e){this._parentDragRef&&e.stopPropagation();let t=this.isDragging(),o=J0(e),r=!o&&e.button!==0,a=this._rootElement,c=Bp(e),m=!o&&this._lastTouchEventTime&&this._lastTouchEventTime+Ute>Date.now(),p=o?_1(e):g1(e);if(c&&c.draggable&&e.type==="mousedown"&&e.preventDefault(),t||r||m||p)return;if(this._handles.length){let S=a.style;this._rootElementTapHighlight=S.webkitTapHighlightColor||"",S.webkitTapHighlightColor="transparent"}this._hasMoved=!1,this._hasStartedDragging.set(this._hasMoved),this._removeListeners(),this._initialDomRect=this._rootElement.getBoundingClientRect(),this._pointerMoveSubscription=this._dragDropRegistry.pointerMove.subscribe(this._pointerMove),this._pointerUpSubscription=this._dragDropRegistry.pointerUp.subscribe(this._pointerUp),this._scrollSubscription=this._dragDropRegistry.scrolled(this._getShadowRoot()).subscribe(S=>this._updateOnScroll(S)),this._boundaryElement&&(this._boundaryRect=wP(this._boundaryElement));let h=this._previewTemplate;this._pickupPositionInElement=h&&h.template&&!h.matchSize?{x:0,y:0}:this._getPointerPositionInElement(this._initialDomRect,i,e);let g=this._pickupPositionOnPage=this._lastKnownPointerPosition=this._getPointerPositionOnPage(e);this._pointerDirectionDelta={x:0,y:0},this._pointerPositionAtLastDirectionChange={x:g.x,y:g.y},this._dragStartTime=Date.now(),this._dragDropRegistry.startDragging(this,e)}_cleanupDragArtifacts(i){RL(this._rootElement,!0,jL),this._marker.parentNode.replaceChild(this._rootElement,this._marker),this._destroyPreview(),this._destroyPlaceholder(),this._initialDomRect=this._boundaryRect=this._previewRect=this._initialTransform=void 0,this._ngZone.run(()=>{let e=this._dropContainer,t=e.getItemIndex(this),o=this._getPointerPositionOnPage(i),r=this._getDragDistance(o),a=e._isOverContainer(o.x,o.y);this.ended.next({source:this,distance:r,dropPoint:o,event:i}),this.dropped.next({item:this,currentIndex:t,previousIndex:this._initialIndex,container:e,previousContainer:this._initialContainer,isPointerOverContainer:a,distance:r,dropPoint:o,event:i}),e.drop(this,t,this._initialIndex,this._initialContainer,a,r,o,i),this._dropContainer=this._initialContainer})}_updateActiveDropContainer({x:i,y:e},{x:t,y:o}){let r=this._initialContainer._getSiblingContainerFromPosition(this,i,e);!r&&this._dropContainer!==this._initialContainer&&this._initialContainer._isOverContainer(i,e)&&(r=this._initialContainer),r&&r!==this._dropContainer&&this._ngZone.run(()=>{let a=this._dropContainer.getItemIndex(this),c=this._dropContainer.getItemAtIndex(a+1)?.getVisibleElement()||null;this.exited.next({item:this,container:this._dropContainer}),this._dropContainer.exit(this),this._conditionallyInsertAnchor(r,this._dropContainer,c),this._dropContainer=r,this._dropContainer.enter(this,i,e,r===this._initialContainer&&r.sortingDisabled?this._initialIndex:void 0),this.entered.next({item:this,container:r,currentIndex:r.getItemIndex(this)})}),this.isDragging()&&(this._dropContainer._startScrollingIfNecessary(t,o),this._dropContainer._sortItem(this,i,e,this._pointerDirectionDelta),this.constrainPosition?this._applyPreviewTransform(i,e):this._applyPreviewTransform(i-this._pickupPositionInElement.x,e-this._pickupPositionInElement.y))}_animatePreviewToPlaceholder(){if(!this._hasMoved)return Promise.resolve();let i=this._placeholder.getBoundingClientRect();this._preview.addClass("cdk-drag-animating"),this._applyPreviewTransform(i.left,i.top);let e=this._preview.getTransitionDuration();return e===0?Promise.resolve():this._ngZone.runOutsideAngular(()=>new Promise(t=>{let o=c=>{(!c||this._preview&&Bp(c)===this._preview.element&&c.propertyName==="transform")&&(a(),t(),clearTimeout(r))},r=setTimeout(o,e*1.5),a=this._preview.addEventListener("transitionend",o)}))}_createPlaceholderElement(){let i=this._placeholderTemplate,e=i?i.template:null,t;return e?(this._placeholderRef=i.viewContainer.createEmbeddedView(e,i.context),this._placeholderRef.detectChanges(),t=WL(this._placeholderRef,this._document)):t=vP(this._rootElement),t.style.pointerEvents="none",t.classList.add(zL),t}_getPointerPositionInElement(i,e,t){let o=e===this._rootElement?null:e,r=o?o.getBoundingClientRect():i,a=J0(t)?t.targetTouches[0]:t,c=this._getViewportScrollPosition(),m=a.pageX-r.left-c.left,p=a.pageY-r.top-c.top;return{x:r.left-i.left+m,y:r.top-i.top+p}}_getPointerPositionOnPage(i){let e=this._getViewportScrollPosition(),t=J0(i)?i.touches[0]||i.changedTouches[0]||{pageX:0,pageY:0}:i,o=t.pageX-e.left,r=t.pageY-e.top;if(this._ownerSVGElement){let a=this._ownerSVGElement.getScreenCTM();if(a){let c=this._ownerSVGElement.createSVGPoint();return c.x=o,c.y=r,c.matrixTransform(a.inverse())}}return{x:o,y:r}}_getConstrainedPointerPosition(i){let e=this._dropContainer?this._dropContainer.lockAxis:null,{x:t,y:o}=this.constrainPosition?this.constrainPosition(i,this,this._initialDomRect,this._pickupPositionInElement):i;if(this.lockAxis==="x"||e==="x"?o=this._pickupPositionOnPage.y-(this.constrainPosition?this._pickupPositionInElement.y:0):(this.lockAxis==="y"||e==="y")&&(t=this._pickupPositionOnPage.x-(this.constrainPosition?this._pickupPositionInElement.x:0)),this._boundaryRect){let{x:r,y:a}=this.constrainPosition?{x:0,y:0}:this._pickupPositionInElement,c=this._boundaryRect,{width:m,height:p}=this._getPreviewRect(),h=c.top+a,g=c.bottom-(p-a),S=c.left+r,x=c.right-(m-r);t=$L(t,S,x),o=$L(o,h,g)}return{x:t,y:o}}_updatePointerDirectionDelta(i){let{x:e,y:t}=i,o=this._pointerDirectionDelta,r=this._pointerPositionAtLastDirectionChange,a=Math.abs(e-r.x),c=Math.abs(t-r.y);return a>this._config.pointerDirectionChangeThreshold&&(o.x=e>r.x?1:-1,r.x=e),c>this._config.pointerDirectionChangeThreshold&&(o.y=t>r.y?1:-1,r.y=t),o}_toggleNativeDragInteractions(){if(!this._rootElement||!this._handles)return;let i=this._handles.length>0||!this.isDragging();i!==this._nativeInteractionsEnabled&&(this._nativeInteractionsEnabled=i,hf(this._rootElement,i))}_removeRootElementListeners(){this._rootElementCleanups?.forEach(i=>i()),this._rootElementCleanups=void 0}_applyRootElementTransform(i,e){let t=1/this.scale,o=tv(i*t,e*t),r=this._rootElement.style;this._initialTransform==null&&(this._initialTransform=r.transform&&r.transform!="none"?r.transform:""),r.transform=gx(o,this._initialTransform)}_applyPreviewTransform(i,e){let t=this._previewTemplate?.template?void 0:this._initialTransform,o=tv(i,e);this._preview.setTransform(gx(o,t))}_getDragDistance(i){let e=this._pickupPositionOnPage;return e?{x:i.x-e.x,y:i.y-e.y}:{x:0,y:0}}_cleanupCachedDimensions(){this._boundaryRect=this._previewRect=void 0,this._parentPositions.clear()}_containInsideBoundaryOnResize(){let{x:i,y:e}=this._passiveTransform;if(i===0&&e===0||this.isDragging()||!this._boundaryElement)return;let t=this._rootElement.getBoundingClientRect(),o=this._boundaryElement.getBoundingClientRect();if(o.width===0&&o.height===0||t.width===0&&t.height===0)return;let r=o.left-t.left,a=t.right-o.right,c=o.top-t.top,m=t.bottom-o.bottom;o.width>t.width?(r>0&&(i+=r),a>0&&(i-=a)):i=0,o.height>t.height?(c>0&&(e+=c),m>0&&(e-=m)):e=0,(i!==this._passiveTransform.x||e!==this._passiveTransform.y)&&this.setFreeDragPosition({y:e,x:i})}_getDragStartDelay(i){let e=this.dragStartDelay;return typeof e=="number"?e:J0(i)?e.touch:e?e.mouse:0}_updateOnScroll(i){let e=this._parentPositions.handleScroll(i);if(e){let t=Bp(i);this._boundaryRect&&t!==this._boundaryElement&&t.contains(this._boundaryElement)&&ev(this._boundaryRect,e.top,e.left),this._pickupPositionOnPage.x+=e.left,this._pickupPositionOnPage.y+=e.top,this._dropContainer||(this._activeTransform.x-=e.left,this._activeTransform.y-=e.top,this._applyRootElementTransform(this._activeTransform.x,this._activeTransform.y))}}_getViewportScrollPosition(){return this._parentPositions.positions.get(this._document)?.scrollPosition||this._parentPositions.getViewportScrollPosition()}_getShadowRoot(){return this._cachedShadowRoot===void 0&&(this._cachedShadowRoot=h1(this._rootElement)),this._cachedShadowRoot}_getPreviewInsertionPoint(i,e){let t=this._previewContainer||"global";if(t==="parent")return i;if(t==="global"){let o=this._document;return e||o.fullscreenElement||o.webkitFullscreenElement||o.mozFullScreenElement||o.msFullscreenElement||o.body}return $l(t)}_getPreviewRect(){return(!this._previewRect||!this._previewRect.width&&!this._previewRect.height)&&(this._previewRect=this._preview?this._preview.getBoundingClientRect():this._initialDomRect),this._previewRect}_nativeDragStart=i=>{if(this._handles.length){let e=this._getTargetHandle(i);e&&!this._disabledHandles.has(e)&&!this.disabled&&i.preventDefault()}else this.disabled||i.preventDefault()};_getTargetHandle(i){return this._handles.find(e=>i.target&&(i.target===e||e.contains(i.target)))}_conditionallyInsertAnchor(i,e,t){if(i===this._initialContainer)this._anchor?.remove(),this._anchor=null;else if(e===this._initialContainer&&e.hasAnchor){let o=this._anchor??=vP(this._placeholder);o.classList.remove(zL),o.classList.add("cdk-drag-anchor"),o.style.transform="",t?t.before(o):$l(e.element).appendChild(o)}}};function $L(n,i,e){return Math.max(i,Math.min(e,n))}function J0(n){return n.type[0]==="t"}function Wte(n){n.preventDefault()}function QL(n,i,e){let t=HL(i,n.length-1),o=HL(e,n.length-1);if(t===o)return;let r=n[t],a=o0)return null;let c=this.orientation==="horizontal",m=r.findIndex(w=>w.drag===i),p=r[a],h=r[m].clientRect,g=p.clientRect,S=m>a?1:-1,x=this._getItemOffsetPx(h,g,S),v=this._getSiblingOffsetPx(m,r,S),M=r.slice();return QL(r,m,a),r.forEach((w,y)=>{if(M[y]===w)return;let k=w.drag===i,I=k?x:v,P=k?i.getPlaceholderElement():w.drag.getRootElement();w.offset+=I;let R=Math.round(w.offset*(1/w.drag.scale));c?(P.style.transform=gx(`translate3d(${R}px, 0, 0)`,w.initialTransform),ev(w.clientRect,0,I)):(P.style.transform=gx(`translate3d(0, ${R}px, 0)`,w.initialTransform),ev(w.clientRect,I,0))}),this._previousSwap.overlaps=CP(g,e,t),this._previousSwap.drag=p.drag,this._previousSwap.delta=c?o.x:o.y,{previousIndex:m,currentIndex:a}}enter(i,e,t,o){let r=this._activeDraggables,a=r.indexOf(i),c=i.getPlaceholderElement();a>-1&&r.splice(a,1);let m=o==null||o<0?this._getItemIndexFromPointerPosition(i,e,t):o,p=r[m];if(p===i&&(p=r[m+1]),!p&&(m==null||m===-1||m{let e=i.getRootElement();if(e){let t=this._itemPositions.find(o=>o.drag===i)?.initialTransform;e.style.transform=t||""}}),this._itemPositions=[],this._activeDraggables=[],this._previousSwap.drag=null,this._previousSwap.delta=0,this._previousSwap.overlaps=!1}getActiveItemsSnapshot(){return this._activeDraggables}getItemIndex(i){return this._getVisualItemPositions().findIndex(e=>e.drag===i)}getItemAtIndex(i){return this._getVisualItemPositions()[i]?.drag||null}updateOnScroll(i,e){this._itemPositions.forEach(({clientRect:t})=>{ev(t,i,e)}),this._itemPositions.forEach(({drag:t})=>{this._dragDropRegistry.isDragging(t)&&t._sortFromLastPointerPosition()})}withElementContainer(i){this._element=i}_cacheItemPositions(){let i=this.orientation==="horizontal";this._itemPositions=this._activeDraggables.map(e=>{let t=e.getVisibleElement();return{drag:e,offset:0,initialTransform:t.style.transform||"",clientRect:wP(t)}}).sort((e,t)=>i?e.clientRect.left-t.clientRect.left:e.clientRect.top-t.clientRect.top)}_getVisualItemPositions(){return this.orientation==="horizontal"&&this.direction==="rtl"?this._itemPositions.slice().reverse():this._itemPositions}_getItemOffsetPx(i,e,t){let o=this.orientation==="horizontal",r=o?e.left-i.left:e.top-i.top;return t===-1&&(r+=o?e.width-i.width:e.height-i.height),r}_getSiblingOffsetPx(i,e,t){let o=this.orientation==="horizontal",r=e[i].clientRect,a=e[i+t*-1],c=r[o?"width":"height"]*t;if(a){let m=o?"left":"top",p=o?"right":"bottom";t===-1?c-=a.clientRect[m]-r[p]:c+=r[m]-a.clientRect[p]}return c}_shouldEnterAsFirstChild(i,e){if(!this._activeDraggables.length)return!1;let t=this._itemPositions,o=this.orientation==="horizontal";if(t[0].drag!==this._activeDraggables[0]){let a=t[t.length-1].clientRect;return o?i>=a.right:e>=a.bottom}else{let a=t[0].clientRect;return o?i<=a.left:e<=a.top}}_getItemIndexFromPointerPosition(i,e,t,o){let r=this.orientation==="horizontal",a=this._itemPositions.findIndex(({drag:c,clientRect:m})=>{if(c===i)return!1;if(o){let p=r?o.x:o.y;if(c===this._previousSwap.drag&&this._previousSwap.overlaps&&p===this._previousSwap.delta)return!1}return r?e>=Math.floor(m.left)&&e=Math.floor(m.top)&&tm?h.after(p):h.before(p),QL(this._activeItems,m,r);let g=this._getRootNode().elementFromPoint(e,t);return a.deltaX=o.x,a.deltaY=o.y,a.drag=c,a.overlaps=h===g||h.contains(g),{previousIndex:m,currentIndex:r}}enter(i,e,t,o){let r=this._activeItems.indexOf(i);r>-1&&this._activeItems.splice(r,1);let a=o==null||o<0?this._getItemIndexFromPointerPosition(i,e,t):o;a===-1&&(a=this._getClosestItemIndexToPointer(i,e,t));let c=this._activeItems[a];c&&!this._dragDropRegistry.isDragging(c)?(this._activeItems.splice(a,0,i),c.getRootElement().before(i.getPlaceholderElement())):(this._activeItems.push(i),this._element.appendChild(i.getPlaceholderElement()))}withItems(i){this._activeItems=i.slice()}withSortPredicate(i){this._sortPredicate=i}reset(){let i=this._element,e=this._previousSwap;for(let t=this._relatedNodes.length-1;t>-1;t--){let[o,r]=this._relatedNodes[t];o.parentNode===i&&o.nextSibling!==r&&(r===null?i.appendChild(o):r.parentNode===i&&i.insertBefore(o,r))}this._relatedNodes=[],this._activeItems=[],e.drag=null,e.deltaX=e.deltaY=0,e.overlaps=!1}getActiveItemsSnapshot(){return this._activeItems}getItemIndex(i){return this._activeItems.indexOf(i)}getItemAtIndex(i){return this._activeItems[i]||null}updateOnScroll(){this._activeItems.forEach(i=>{this._dragDropRegistry.isDragging(i)&&i._sortFromLastPointerPosition()})}withElementContainer(i){i!==this._element&&(this._element=i,this._rootNode=void 0)}_getItemIndexFromPointerPosition(i,e,t){let o=this._getRootNode().elementFromPoint(Math.floor(e),Math.floor(t)),r=o?this._activeItems.findIndex(a=>{let c=a.getRootElement();return o===c||c.contains(o)}):-1;return r===-1||!this._sortPredicate(r,i)?-1:r}_getRootNode(){return this._rootNode||(this._rootNode=h1(this._element)||this._document),this._rootNode}_getClosestItemIndexToPointer(i,e,t){if(this._activeItems.length===0)return-1;if(this._activeItems.length===1)return 0;let o=1/0,r=-1;for(let a=0;a!0;sortPredicate=()=>!0;beforeStarted=new je;entered=new je;exited=new je;dropped=new je;sorted=new je;receivingStarted=new je;receivingStopped=new je;data;_container;_isDragging=!1;_parentPositions;_sortStrategy;_domRect;_draggables=[];_siblings=[];_activeSiblings=new Set;_viewportScrollSubscription=go.EMPTY;_verticalScrollDirection=ul.NONE;_horizontalScrollDirection=Ga.NONE;_scrollNode;_stopScrollTimers=new je;_cachedShadowRoot=null;_document;_scrollableElements=[];_initialScrollSnap;_direction="ltr";constructor(i,e,t,o,r){this._dragDropRegistry=e,this._ngZone=o,this._viewportRuler=r;let a=this.element=$l(i);this._document=t,this.withOrientation("vertical").withElementContainer(a),e.registerDropContainer(this),this._parentPositions=new fx(t)}dispose(){this._stopScrolling(),this._stopScrollTimers.complete(),this._viewportScrollSubscription.unsubscribe(),this.beforeStarted.complete(),this.entered.complete(),this.exited.complete(),this.dropped.complete(),this.sorted.complete(),this.receivingStarted.complete(),this.receivingStopped.complete(),this._activeSiblings.clear(),this._scrollNode=null,this._parentPositions.clear(),this._dragDropRegistry.removeDropContainer(this)}isDragging(){return this._isDragging}start(){this._draggingStarted(),this._notifyReceivingSiblings()}enter(i,e,t,o){this._draggingStarted(),o==null&&this.sortingDisabled&&(o=this._draggables.indexOf(i)),this._sortStrategy.enter(i,e,t,o),this._cacheParentPositions(),this._notifyReceivingSiblings(),this.entered.next({item:i,container:this,currentIndex:this.getItemIndex(i)})}exit(i){this._reset(),this.exited.next({item:i,container:this})}drop(i,e,t,o,r,a,c,m={}){this._reset(),this.dropped.next({item:i,currentIndex:e,previousIndex:t,container:this,previousContainer:o,isPointerOverContainer:r,distance:a,dropPoint:c,event:m})}withItems(i){let e=this._draggables;return this._draggables=i,i.forEach(t=>t._withDropContainer(this)),this.isDragging()&&(e.filter(o=>o.isDragging()).every(o=>i.indexOf(o)===-1)?this._reset():this._sortStrategy.withItems(this._draggables)),this}withDirection(i){return this._direction=i,this._sortStrategy instanceof _x&&(this._sortStrategy.direction=i),this}connectedTo(i){return this._siblings=i.slice(),this}withOrientation(i){if(i==="mixed")this._sortStrategy=new yP(this._document,this._dragDropRegistry);else{let e=new _x(this._dragDropRegistry);e.direction=this._direction,e.orientation=i,this._sortStrategy=e}return this._sortStrategy.withElementContainer(this._container),this._sortStrategy.withSortPredicate((e,t)=>this.sortPredicate(e,t,this)),this}withScrollableParents(i){let e=this._container;return this._scrollableElements=i.indexOf(e)===-1?[e,...i]:i.slice(),this}withElementContainer(i){if(i===this._container)return this;let e=$l(this.element),t=this._scrollableElements.indexOf(this._container),o=this._scrollableElements.indexOf(i);return t>-1&&this._scrollableElements.splice(t,1),o>-1&&this._scrollableElements.splice(o,1),this._sortStrategy&&this._sortStrategy.withElementContainer(i),this._cachedShadowRoot=null,this._scrollableElements.unshift(i),this._container=i,this}getScrollableParents(){return this._scrollableElements}getItemIndex(i){return this._isDragging?this._sortStrategy.getItemIndex(i):this._draggables.indexOf(i)}getItemAtIndex(i){return this._isDragging?this._sortStrategy.getItemAtIndex(i):this._draggables[i]||null}isReceiving(){return this._activeSiblings.size>0}_sortItem(i,e,t,o){if(this.sortingDisabled||!this._domRect||!NL(this._domRect,UL,e,t))return;let r=this._sortStrategy.sort(i,e,t,o);r&&this.sorted.next({previousIndex:r.previousIndex,currentIndex:r.currentIndex,container:this,item:i})}_startScrollingIfNecessary(i,e){if(this.autoScrollDisabled)return;let t,o=ul.NONE,r=Ga.NONE;if(this._parentPositions.positions.forEach((a,c)=>{c===this._document||!a.clientRect||t||NL(a.clientRect,UL,i,e)&&([o,r]=Qte(c,a.clientRect,this._direction,i,e),(o||r)&&(t=c))}),!o&&!r){let{width:a,height:c}=this._viewportRuler.getViewportSize(),m={width:a,height:c,top:0,right:a,bottom:c,left:0};o=YL(m,e),r=KL(m,i),t=window}t&&(o!==this._verticalScrollDirection||r!==this._horizontalScrollDirection||t!==this._scrollNode)&&(this._verticalScrollDirection=o,this._horizontalScrollDirection=r,this._scrollNode=t,(o||r)&&t?this._ngZone.runOutsideAngular(this._startScrollInterval):this._stopScrolling())}_stopScrolling(){this._stopScrollTimers.next()}_draggingStarted(){let i=this._container.style;this.beforeStarted.next(),this._isDragging=!0,this._initialScrollSnap=i.msScrollSnapType||i.scrollSnapType||"",i.scrollSnapType=i.msScrollSnapType="none",this._sortStrategy.start(this._draggables),this._cacheParentPositions(),this._viewportScrollSubscription.unsubscribe(),this._listenToScrollEvents()}_cacheParentPositions(){this._parentPositions.cache(this._scrollableElements),this._domRect=this._parentPositions.positions.get(this._container).clientRect}_reset(){this._isDragging=!1;let i=this._container.style;i.scrollSnapType=i.msScrollSnapType=this._initialScrollSnap,this._siblings.forEach(e=>e._stopReceiving(this)),this._sortStrategy.reset(),this._stopScrolling(),this._viewportScrollSubscription.unsubscribe(),this._parentPositions.clear()}_startScrollInterval=()=>{this._stopScrolling(),e1(0,ah).pipe(tt(this._stopScrollTimers)).subscribe(()=>{let i=this._scrollNode,e=this.autoScrollStep;this._verticalScrollDirection===ul.UP?i.scrollBy(0,-e):this._verticalScrollDirection===ul.DOWN&&i.scrollBy(0,e),this._horizontalScrollDirection===Ga.LEFT?i.scrollBy(-e,0):this._horizontalScrollDirection===Ga.RIGHT&&i.scrollBy(e,0)})};_isOverContainer(i,e){return this._domRect!=null&&CP(this._domRect,i,e)}_getSiblingContainerFromPosition(i,e,t){return this._siblings.find(o=>o._canReceive(i,e,t))}_canReceive(i,e,t){if(!this._domRect||!CP(this._domRect,e,t)||!this.enterPredicate(i,this))return!1;let o=this._getShadowRoot().elementFromPoint(e,t);return o?o===this._container||this._container.contains(o):!1}_startReceiving(i,e){let t=this._activeSiblings;!t.has(i)&&e.every(o=>this.enterPredicate(o,this)||this._draggables.indexOf(o)>-1)&&(t.add(i),this._cacheParentPositions(),this._listenToScrollEvents(),this.receivingStarted.next({initiator:i,receiver:this,items:e}))}_stopReceiving(i){this._activeSiblings.delete(i),this._viewportScrollSubscription.unsubscribe(),this.receivingStopped.next({initiator:i,receiver:this})}_listenToScrollEvents(){this._viewportScrollSubscription=this._dragDropRegistry.scrolled(this._getShadowRoot()).subscribe(i=>{if(this.isDragging()){let e=this._parentPositions.handleScroll(i);e&&this._sortStrategy.updateOnScroll(e.top,e.left)}else this.isReceiving()&&this._cacheParentPositions()})}_getShadowRoot(){if(!this._cachedShadowRoot){let i=h1(this._container);this._cachedShadowRoot=i||this._document}return this._cachedShadowRoot}_notifyReceivingSiblings(){let i=this._sortStrategy.getActiveItemsSnapshot().filter(e=>e.isDragging());this._siblings.forEach(e=>e._startReceiving(this,i))}};function YL(n,i){let{top:e,bottom:t,height:o}=n,r=o*XL;return i>=e-r&&i<=e+r?ul.UP:i>=t-r&&i<=t+r?ul.DOWN:ul.NONE}function KL(n,i){let{left:e,right:t,width:o}=n,r=o*XL;return i>=e-r&&i<=e+r?Ga.LEFT:i>=t-r&&i<=t+r?Ga.RIGHT:Ga.NONE}function Qte(n,i,e,t,o){let r=YL(i,o),a=KL(i,t),c=ul.NONE,m=Ga.NONE;if(r){let p=n.scrollTop;r===ul.UP?p>0&&(c=ul.UP):n.scrollHeight-p>n.clientHeight&&(c=ul.DOWN)}if(a){let p=n.scrollLeft;e==="rtl"?a===Ga.RIGHT?p<0&&(m=Ga.RIGHT):n.scrollWidth+p>n.clientWidth&&(m=Ga.LEFT):a===Ga.LEFT?p>0&&(m=Ga.LEFT):n.scrollWidth-p>n.clientWidth&&(m=Ga.RIGHT)}return[c,m]}var Xte=(()=>{class n{_injector=f(Wo);constructor(){}createDrag(e,t){return Gte(this._injector,e,t)}createDropList(e){return qte(this._injector,e)}static \u0275fac=function(t){return new(t||n)};static \u0275prov=K({token:n,factory:n.\u0275fac,providedIn:"root"})}return n})();var ZL=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275mod=Gt({type:n});static \u0275inj=Ut({providers:[Xte],imports:[fd]})}return n})();var Yte=[[["caption"]],[["colgroup"],["col"]],"*"],Kte=["caption","colgroup, col","*"];function Zte(n,i){n&1&&nn(0,2)}function Jte(n,i){n&1&&(s(0,"thead",0),mo(1,1),l(),s(2,"tbody",0),mo(3,2)(4,3),l(),s(5,"tfoot",0),mo(6,4),l())}function ene(n,i){n&1&&mo(0,1)(1,2)(2,3)(3,4)}var Xl=new $t("CDK_TABLE");var bx=(()=>{class n{template=f(jo);constructor(){}static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["","cdkCellDef",""]]})}return n})(),xx=(()=>{class n{template=f(jo);constructor(){}static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["","cdkHeaderCellDef",""]]})}return n})(),t8=(()=>{class n{template=f(jo);constructor(){}static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["","cdkFooterCellDef",""]]})}return n})(),Om=(()=>{class n{_table=f(Xl,{optional:!0});_hasStickyChanged=!1;get name(){return this._name}set name(e){this._setNameInput(e)}_name;get sticky(){return this._sticky}set sticky(e){e!==this._sticky&&(this._sticky=e,this._hasStickyChanged=!0)}_sticky=!1;get stickyEnd(){return this._stickyEnd}set stickyEnd(e){e!==this._stickyEnd&&(this._stickyEnd=e,this._hasStickyChanged=!0)}_stickyEnd=!1;cell;headerCell;footerCell;cssClassFriendlyName;_columnCssClassName;constructor(){}hasStickyChanged(){let e=this._hasStickyChanged;return this.resetStickyChanged(),e}resetStickyChanged(){this._hasStickyChanged=!1}_updateColumnCssClassName(){this._columnCssClassName=[`cdk-column-${this.cssClassFriendlyName}`]}_setNameInput(e){e&&(this._name=e,this.cssClassFriendlyName=e.replace(/[^a-z0-9_-]/gi,"-"),this._updateColumnCssClassName())}static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["","cdkColumnDef",""]],contentQueries:function(t,o,r){if(t&1&&Vi(r,bx,5)(r,xx,5)(r,t8,5),t&2){let a;pt(a=ut())&&(o.cell=a.first),pt(a=ut())&&(o.headerCell=a.first),pt(a=ut())&&(o.footerCell=a.first)}},inputs:{name:[0,"cdkColumnDef","name"],sticky:[2,"sticky","sticky",gt],stickyEnd:[2,"stickyEnd","stickyEnd",gt]}})}return n})(),Cx=class{constructor(i,e){e.nativeElement.classList.add(...i._columnCssClassName)}},n8=(()=>{class n extends Cx{constructor(){super(f(Om),f(Qt))}static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["cdk-header-cell"],["th","cdk-header-cell",""]],hostAttrs:["role","columnheader",1,"cdk-header-cell"],features:[ci]})}return n})();var i8=(()=>{class n extends Cx{constructor(){let e=f(Om),t=f(Qt);super(e,t);let o=e._table?._getCellRole();o&&t.nativeElement.setAttribute("role",o)}static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["cdk-cell"],["td","cdk-cell",""]],hostAttrs:[1,"cdk-cell"],features:[ci]})}return n})();var TP=(()=>{class n{template=f(jo);_differs=f(ud);columns;_columnsDiffer;constructor(){}ngOnChanges(e){if(!this._columnsDiffer){let t=e.columns&&e.columns.currentValue||[];this._columnsDiffer=this._differs.find(t).create(),this._columnsDiffer.diff(t)}}getColumnsDiff(){return this._columnsDiffer.diff(this.columns)}extractCellTemplate(e){return this instanceof iv?e.headerCell.template:this instanceof EP?e.footerCell.template:e.cell.template}static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,features:[dn]})}return n})(),iv=(()=>{class n extends TP{_table=f(Xl,{optional:!0});_hasStickyChanged=!1;get sticky(){return this._sticky}set sticky(e){e!==this._sticky&&(this._sticky=e,this._hasStickyChanged=!0)}_sticky=!1;constructor(){super(f(jo),f(ud))}ngOnChanges(e){super.ngOnChanges(e)}hasStickyChanged(){let e=this._hasStickyChanged;return this.resetStickyChanged(),e}resetStickyChanged(){this._hasStickyChanged=!1}static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["","cdkHeaderRowDef",""]],inputs:{columns:[0,"cdkHeaderRowDef","columns"],sticky:[2,"cdkHeaderRowDefSticky","sticky",gt]},features:[ci,dn]})}return n})(),EP=(()=>{class n extends TP{_table=f(Xl,{optional:!0});_hasStickyChanged=!1;get sticky(){return this._sticky}set sticky(e){e!==this._sticky&&(this._sticky=e,this._hasStickyChanged=!0)}_sticky=!1;constructor(){super(f(jo),f(ud))}ngOnChanges(e){super.ngOnChanges(e)}hasStickyChanged(){let e=this._hasStickyChanged;return this.resetStickyChanged(),e}resetStickyChanged(){this._hasStickyChanged=!1}static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["","cdkFooterRowDef",""]],inputs:{columns:[0,"cdkFooterRowDef","columns"],sticky:[2,"cdkFooterRowDefSticky","sticky",gt]},features:[ci,dn]})}return n})(),yx=(()=>{class n extends TP{_table=f(Xl,{optional:!0});when;constructor(){super(f(jo),f(ud))}static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["","cdkRowDef",""]],inputs:{columns:[0,"cdkRowDefColumns","columns"],when:[0,"cdkRowDefWhen","when"]},features:[ci]})}return n})(),bu=(()=>{class n{_viewContainer=f(Ji);cells;context;static mostRecentCellOutlet=null;constructor(){n.mostRecentCellOutlet=this}ngOnDestroy(){n.mostRecentCellOutlet===this&&(n.mostRecentCellOutlet=null)}static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["","cdkCellOutlet",""]]})}return n})(),DP=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275cmp=F({type:n,selectors:[["cdk-header-row"],["tr","cdk-header-row",""]],hostAttrs:["role","row",1,"cdk-header-row"],decls:1,vars:0,consts:[["cdkCellOutlet",""]],template:function(t,o){t&1&&mo(0,0)},dependencies:[bu],encapsulation:2})}return n})();var PP=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275cmp=F({type:n,selectors:[["cdk-row"],["tr","cdk-row",""]],hostAttrs:["role","row",1,"cdk-row"],decls:1,vars:0,consts:[["cdkCellOutlet",""]],template:function(t,o){t&1&&mo(0,0)},dependencies:[bu],encapsulation:2})}return n})(),o8=(()=>{class n{templateRef=f(jo);_contentClassNames=["cdk-no-data-row","cdk-row"];_cellClassNames=["cdk-cell","cdk-no-data-cell"];_cellSelector="td, cdk-cell, [cdk-cell], .cdk-cell";constructor(){}static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["ng-template","cdkNoDataRow",""]]})}return n})(),JL=["top","bottom","left","right"],kP=class{_isNativeHtmlTable;_stickCellCss;_isBrowser;_needsPositionStickyOnElement;direction;_positionListener;_tableInjector;_elemSizeCache=new WeakMap;_resizeObserver=globalThis?.ResizeObserver?new globalThis.ResizeObserver(i=>this._updateCachedSizes(i)):null;_updatedStickyColumnsParamsToReplay=[];_stickyColumnsReplayTimeout=null;_cachedCellWidths=[];_borderCellCss;_destroyed=!1;constructor(i,e,t=!0,o=!0,r,a,c){this._isNativeHtmlTable=i,this._stickCellCss=e,this._isBrowser=t,this._needsPositionStickyOnElement=o,this.direction=r,this._positionListener=a,this._tableInjector=c,this._borderCellCss={top:`${e}-border-elem-top`,bottom:`${e}-border-elem-bottom`,left:`${e}-border-elem-left`,right:`${e}-border-elem-right`}}clearStickyPositioning(i,e){(e.includes("left")||e.includes("right"))&&this._removeFromStickyColumnReplayQueue(i);let t=[];for(let o of i)o.nodeType===o.ELEMENT_NODE&&t.push(o,...Array.from(o.children));aa({write:()=>{for(let o of t)this._removeStickyStyle(o,e)}},{injector:this._tableInjector})}updateStickyColumns(i,e,t,o=!0,r=!0){if(!i.length||!this._isBrowser||!(e.some(w=>w)||t.some(w=>w))){this._positionListener?.stickyColumnsUpdated({sizes:[]}),this._positionListener?.stickyEndColumnsUpdated({sizes:[]});return}let a=i[0],c=a.children.length,m=this.direction==="rtl",p=m?"right":"left",h=m?"left":"right",g=e.lastIndexOf(!0),S=t.indexOf(!0),x,v,M;r&&this._updateStickyColumnReplayQueue({rows:[...i],stickyStartStates:[...e],stickyEndStates:[...t]}),aa({earlyRead:()=>{x=this._getCellWidths(a,o),v=this._getStickyStartColumnPositions(x,e),M=this._getStickyEndColumnPositions(x,t)},write:()=>{for(let w of i)for(let y=0;y!!w)&&(this._positionListener.stickyColumnsUpdated({sizes:g===-1?[]:x.slice(0,g+1).map((w,y)=>e[y]?w:null)}),this._positionListener.stickyEndColumnsUpdated({sizes:S===-1?[]:x.slice(S).map((w,y)=>t[y+S]?w:null).reverse()}))}},{injector:this._tableInjector})}stickRows(i,e,t){if(!this._isBrowser)return;let o=t==="bottom"?i.slice().reverse():i,r=t==="bottom"?e.slice().reverse():e,a=[],c=[],m=[];aa({earlyRead:()=>{for(let p=0,h=0;p{let p=r.lastIndexOf(!0);for(let h=0;h{let t=i.querySelector("tfoot");t&&(e.some(o=>!o)?this._removeStickyStyle(t,["bottom"]):this._addStickyStyle(t,"bottom",0,!1))}},{injector:this._tableInjector})}destroy(){this._stickyColumnsReplayTimeout&&clearTimeout(this._stickyColumnsReplayTimeout),this._resizeObserver?.disconnect(),this._destroyed=!0}_removeStickyStyle(i,e){if(!i.classList.contains(this._stickCellCss))return;for(let o of e)i.style[o]="",i.classList.remove(this._borderCellCss[o]);JL.some(o=>e.indexOf(o)===-1&&i.style[o])?i.style.zIndex=this._getCalculatedZIndex(i):(i.style.zIndex="",this._needsPositionStickyOnElement&&(i.style.position=""),i.classList.remove(this._stickCellCss))}_addStickyStyle(i,e,t,o){i.classList.add(this._stickCellCss),o&&i.classList.add(this._borderCellCss[e]),i.style[e]=`${t}px`,i.style.zIndex=this._getCalculatedZIndex(i),this._needsPositionStickyOnElement&&(i.style.cssText+="position: -webkit-sticky; position: sticky; ")}_getCalculatedZIndex(i){let e={top:100,bottom:10,left:1,right:1},t=0;for(let o of JL)i.style[o]&&(t+=e[o]);return t?`${t}`:""}_getCellWidths(i,e=!0){if(!e&&this._cachedCellWidths.length)return this._cachedCellWidths;let t=[],o=i.children;for(let r=0;r0;r--)e[r]&&(t[r]=o,o+=i[r]);return t}_retrieveElementSize(i){let e=this._elemSizeCache.get(i);if(e)return e;let t=i.getBoundingClientRect(),o={width:t.width,height:t.height};return this._resizeObserver&&(this._elemSizeCache.set(i,o),this._resizeObserver.observe(i,{box:"border-box"})),o}_updateStickyColumnReplayQueue(i){this._removeFromStickyColumnReplayQueue(i.rows),this._stickyColumnsReplayTimeout||this._updatedStickyColumnsParamsToReplay.push(i)}_removeFromStickyColumnReplayQueue(i){let e=new Set(i);for(let t of this._updatedStickyColumnsParamsToReplay)t.rows=t.rows.filter(o=>!e.has(o));this._updatedStickyColumnsParamsToReplay=this._updatedStickyColumnsParamsToReplay.filter(t=>!!t.rows.length)}_updateCachedSizes(i){let e=!1;for(let t of i){let o=t.borderBoxSize?.length?{width:t.borderBoxSize[0].inlineSize,height:t.borderBoxSize[0].blockSize}:{width:t.contentRect.width,height:t.contentRect.height};o.width!==this._elemSizeCache.get(t.target)?.width&&tne(t.target)&&(e=!0),this._elemSizeCache.set(t.target,o)}e&&this._updatedStickyColumnsParamsToReplay.length&&(this._stickyColumnsReplayTimeout&&clearTimeout(this._stickyColumnsReplayTimeout),this._stickyColumnsReplayTimeout=setTimeout(()=>{if(!this._destroyed){for(let t of this._updatedStickyColumnsParamsToReplay)this.updateStickyColumns(t.rows,t.stickyStartStates,t.stickyEndStates,!0,!1);this._updatedStickyColumnsParamsToReplay=[],this._stickyColumnsReplayTimeout=null}},0))}};function tne(n){return["cdk-cell","cdk-header-cell","cdk-footer-cell"].some(i=>n.classList.contains(i))}var nv=new $t("STICKY_POSITIONING_LISTENER");var IP=(()=>{class n{viewContainer=f(Ji);elementRef=f(Qt);constructor(){let e=f(Xl);e._rowOutlet=this,e._outletAssigned()}static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["","rowOutlet",""]]})}return n})(),AP=(()=>{class n{viewContainer=f(Ji);elementRef=f(Qt);constructor(){let e=f(Xl);e._headerRowOutlet=this,e._outletAssigned()}static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["","headerRowOutlet",""]]})}return n})(),OP=(()=>{class n{viewContainer=f(Ji);elementRef=f(Qt);constructor(){let e=f(Xl);e._footerRowOutlet=this,e._outletAssigned()}static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["","footerRowOutlet",""]]})}return n})(),NP=(()=>{class n{viewContainer=f(Ji);elementRef=f(Qt);constructor(){let e=f(Xl);e._noDataRowOutlet=this,e._outletAssigned()}static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["","noDataRowOutlet",""]]})}return n})(),RP=(()=>{class n{_differs=f(ud);_changeDetectorRef=f(X);_elementRef=f(Qt);_dir=f(ts,{optional:!0});_platform=f(Zs);_viewRepeater;_viewportRuler=f(hd);_injector=f(Wo);_virtualScrollViewport=f(v5,{optional:!0,host:!0});_positionListener=f(nv,{optional:!0})||f(nv,{optional:!0,skipSelf:!0});_document=f(co);_data;_renderedRange;_onDestroy=new je;_renderRows;_renderChangeSubscription=null;_columnDefsByName=new Map;_rowDefs;_headerRowDefs;_footerRowDefs;_dataDiffer;_defaultRowDef=null;_customColumnDefs=new Set;_customRowDefs=new Set;_customHeaderRowDefs=new Set;_customFooterRowDefs=new Set;_customNoDataRow=null;_headerRowDefChanged=!0;_footerRowDefChanged=!0;_stickyColumnStylesNeedReset=!0;_forceRecalculateCellWidths=!0;_cachedRenderRowsMap=new Map;_isNativeHtmlTable;_stickyStyler;stickyCssClass="cdk-table-sticky";needsPositionStickyOnElement=!0;_isServer;_isShowingNoDataRow=!1;_hasAllOutlets=!1;_hasInitialized=!1;_headerRowStickyUpdates=new je;_footerRowStickyUpdates=new je;_disableVirtualScrolling=!1;_getCellRole(){if(this._cellRoleInternal===void 0){let e=this._elementRef.nativeElement.getAttribute("role");return e==="grid"||e==="treegrid"?"gridcell":"cell"}return this._cellRoleInternal}_cellRoleInternal=void 0;get trackBy(){return this._trackByFn}set trackBy(e){this._trackByFn=e}_trackByFn;get dataSource(){return this._dataSource}set dataSource(e){this._dataSource!==e&&(this._switchDataSource(e),this._changeDetectorRef.markForCheck())}_dataSource;_dataSourceChanges=new je;_dataStream=new je;get multiTemplateDataRows(){return this._multiTemplateDataRows}set multiTemplateDataRows(e){this._multiTemplateDataRows=e,this._rowOutlet&&this._rowOutlet.viewContainer.length&&(this._forceRenderDataRows(),this.updateStickyColumnStyles())}_multiTemplateDataRows=!1;get fixedLayout(){return this._virtualScrollEnabled()?!0:this._fixedLayout}set fixedLayout(e){this._fixedLayout=e,this._forceRecalculateCellWidths=!0,this._stickyColumnStylesNeedReset=!0}_fixedLayout=!1;recycleRows=!1;contentChanged=new _e;viewChange=new zt({start:0,end:Number.MAX_VALUE});_rowOutlet;_headerRowOutlet;_footerRowOutlet;_noDataRowOutlet;_contentColumnDefs;_contentRowDefs;_contentHeaderRowDefs;_contentFooterRowDefs;_noDataRow;constructor(){f(new Ks("role"),{optional:!0})||this._elementRef.nativeElement.setAttribute("role","table"),this._isServer=!this._platform.isBrowser,this._isNativeHtmlTable=this._elementRef.nativeElement.nodeName==="TABLE",this._dataDiffer=this._differs.find([]).create((t,o)=>this.trackBy?this.trackBy(o.dataIndex,o.data):o)}ngOnInit(){this._setupStickyStyler(),this._viewportRuler.change().pipe(tt(this._onDestroy)).subscribe(()=>{this._forceRecalculateCellWidths=!0})}ngAfterContentInit(){this._viewRepeater=this.recycleRows||this._virtualScrollEnabled()?new _5:new S5,this._virtualScrollEnabled()&&this._setupVirtualScrolling(this._virtualScrollViewport),this._hasInitialized=!0}ngAfterContentChecked(){this._canRender()&&this._render()}ngOnDestroy(){this._stickyStyler?.destroy(),[this._rowOutlet?.viewContainer,this._headerRowOutlet?.viewContainer,this._footerRowOutlet?.viewContainer,this._cachedRenderRowsMap,this._customColumnDefs,this._customRowDefs,this._customHeaderRowDefs,this._customFooterRowDefs,this._columnDefsByName].forEach(e=>{e?.clear()}),this._headerRowDefs=[],this._footerRowDefs=[],this._defaultRowDef=null,this._headerRowStickyUpdates.complete(),this._footerRowStickyUpdates.complete(),this._onDestroy.next(),this._onDestroy.complete(),hh(this.dataSource)&&this.dataSource.disconnect(this)}renderRows(){this._renderRows=this._getAllRenderRows();let e=this._dataDiffer.diff(this._renderRows);if(!e){this._updateNoDataRow(),this.contentChanged.next();return}let t=this._rowOutlet.viewContainer;this._viewRepeater.applyChanges(e,t,(o,r,a)=>this._getEmbeddedViewArgs(o.item,a),o=>o.item.data,o=>{o.operation===g5.INSERTED&&o.context&&this._renderCellTemplateForItem(o.record.item.rowDef,o.context)}),this._updateRowIndexContext(),e.forEachIdentityChange(o=>{let r=t.get(o.currentIndex);r.context.$implicit=o.item.data}),this._updateNoDataRow(),this.contentChanged.next(),this.updateStickyColumnStyles()}addColumnDef(e){this._customColumnDefs.add(e)}removeColumnDef(e){this._customColumnDefs.delete(e)}addRowDef(e){this._customRowDefs.add(e)}removeRowDef(e){this._customRowDefs.delete(e)}addHeaderRowDef(e){this._customHeaderRowDefs.add(e),this._headerRowDefChanged=!0}removeHeaderRowDef(e){this._customHeaderRowDefs.delete(e),this._headerRowDefChanged=!0}addFooterRowDef(e){this._customFooterRowDefs.add(e),this._footerRowDefChanged=!0}removeFooterRowDef(e){this._customFooterRowDefs.delete(e),this._footerRowDefChanged=!0}setNoDataRow(e){this._customNoDataRow=e}updateStickyHeaderRowStyles(){let e=this._getRenderedRows(this._headerRowOutlet);if(this._isNativeHtmlTable){let o=e8(this._headerRowOutlet,"thead");o&&(o.style.display=e.length?"":"none")}let t=this._headerRowDefs.map(o=>o.sticky);this._stickyStyler.clearStickyPositioning(e,["top"]),this._stickyStyler.stickRows(e,t,"top"),this._headerRowDefs.forEach(o=>o.resetStickyChanged())}updateStickyFooterRowStyles(){let e=this._getRenderedRows(this._footerRowOutlet);if(this._isNativeHtmlTable){let o=e8(this._footerRowOutlet,"tfoot");o&&(o.style.display=e.length?"":"none")}let t=this._footerRowDefs.map(o=>o.sticky);this._stickyStyler.clearStickyPositioning(e,["bottom"]),this._stickyStyler.stickRows(e,t,"bottom"),this._stickyStyler.updateStickyFooterContainer(this._elementRef.nativeElement,t),this._footerRowDefs.forEach(o=>o.resetStickyChanged())}updateStickyColumnStyles(){let e=this._getRenderedRows(this._headerRowOutlet),t=this._getRenderedRows(this._rowOutlet),o=this._getRenderedRows(this._footerRowOutlet);(this._isNativeHtmlTable&&!this.fixedLayout||this._stickyColumnStylesNeedReset)&&(this._stickyStyler.clearStickyPositioning([...e,...t,...o],["left","right"]),this._stickyColumnStylesNeedReset=!1),e.forEach((r,a)=>{this._addStickyColumnStyles([r],this._headerRowDefs[a])}),this._rowDefs.forEach(r=>{let a=[];for(let c=0;c{this._addStickyColumnStyles([r],this._footerRowDefs[a])}),Array.from(this._columnDefsByName.values()).forEach(r=>r.resetStickyChanged())}stickyColumnsUpdated(e){this._positionListener?.stickyColumnsUpdated(e)}stickyEndColumnsUpdated(e){this._positionListener?.stickyEndColumnsUpdated(e)}stickyHeaderRowsUpdated(e){this._headerRowStickyUpdates.next(e),this._positionListener?.stickyHeaderRowsUpdated(e)}stickyFooterRowsUpdated(e){this._footerRowStickyUpdates.next(e),this._positionListener?.stickyFooterRowsUpdated(e)}_outletAssigned(){!this._hasAllOutlets&&this._rowOutlet&&this._headerRowOutlet&&this._footerRowOutlet&&this._noDataRowOutlet&&(this._hasAllOutlets=!0,this._canRender()&&this._render())}_canRender(){return this._hasAllOutlets&&this._hasInitialized}_render(){this._cacheRowDefs(),this._cacheColumnDefs(),!this._headerRowDefs.length&&!this._footerRowDefs.length&&this._rowDefs.length;let t=this._renderUpdatedColumns()||this._headerRowDefChanged||this._footerRowDefChanged;this._stickyColumnStylesNeedReset=this._stickyColumnStylesNeedReset||t,this._forceRecalculateCellWidths=t,this._headerRowDefChanged&&(this._forceRenderHeaderRows(),this._headerRowDefChanged=!1),this._footerRowDefChanged&&(this._forceRenderFooterRows(),this._footerRowDefChanged=!1),this.dataSource&&this._rowDefs.length>0&&!this._renderChangeSubscription?this._observeRenderChanges():this._stickyColumnStylesNeedReset&&this.updateStickyColumnStyles(),this._checkStickyStates()}_getAllRenderRows(){if(!Array.isArray(this._data)||!this._renderedRange)return[];let e=[],t=Math.min(this._data.length,this._renderedRange.end),o=this._cachedRenderRowsMap;this._cachedRenderRowsMap=new Map;for(let r=this._renderedRange.start;r{let c=o&&o.has(a)?o.get(a):[];if(c.length){let m=c.shift();return m.dataIndex=t,m}else return{data:e,rowDef:a,dataIndex:t}})}_cacheColumnDefs(){this._columnDefsByName.clear(),vx(this._getOwnDefs(this._contentColumnDefs),this._customColumnDefs).forEach(t=>{this._columnDefsByName.has(t.name),this._columnDefsByName.set(t.name,t)})}_cacheRowDefs(){this._headerRowDefs=vx(this._getOwnDefs(this._contentHeaderRowDefs),this._customHeaderRowDefs),this._footerRowDefs=vx(this._getOwnDefs(this._contentFooterRowDefs),this._customFooterRowDefs),this._rowDefs=vx(this._getOwnDefs(this._contentRowDefs),this._customRowDefs);let e=this._rowDefs.filter(t=>!t.when);this._defaultRowDef=e[0]}_renderUpdatedColumns(){let e=(a,c)=>{let m=!!c.getColumnsDiff();return a||m},t=this._rowDefs.reduce(e,!1);t&&this._forceRenderDataRows();let o=this._headerRowDefs.reduce(e,!1);o&&this._forceRenderHeaderRows();let r=this._footerRowDefs.reduce(e,!1);return r&&this._forceRenderFooterRows(),t||o||r}_switchDataSource(e){this._data=[],hh(this.dataSource)&&this.dataSource.disconnect(this),this._renderChangeSubscription&&(this._renderChangeSubscription.unsubscribe(),this._renderChangeSubscription=null),e||(this._dataDiffer&&this._dataDiffer.diff([]),this._rowOutlet&&this._rowOutlet.viewContainer.clear()),this._dataSource=e}_observeRenderChanges(){if(!this.dataSource)return;let e;hh(this.dataSource)?e=this.dataSource.connect(this):Zd(this.dataSource)?e=this.dataSource:Array.isArray(this.dataSource)&&(e=_t(this.dataSource)),this._renderChangeSubscription=ir([e,this.viewChange]).pipe(tt(this._onDestroy)).subscribe(([t,o])=>{this._data=t||[],this._renderedRange=o,this._dataStream.next(t),this.renderRows()})}_forceRenderHeaderRows(){this._headerRowOutlet.viewContainer.length>0&&this._headerRowOutlet.viewContainer.clear(),this._headerRowDefs.forEach((e,t)=>this._renderRow(this._headerRowOutlet,e,t)),this.updateStickyHeaderRowStyles()}_forceRenderFooterRows(){this._footerRowOutlet.viewContainer.length>0&&this._footerRowOutlet.viewContainer.clear(),this._footerRowDefs.forEach((e,t)=>this._renderRow(this._footerRowOutlet,e,t)),this.updateStickyFooterRowStyles()}_addStickyColumnStyles(e,t){let o=Array.from(t?.columns||[]).map(c=>{let m=this._columnDefsByName.get(c);return m}),r=o.map(c=>c.sticky),a=o.map(c=>c.stickyEnd);this._stickyStyler.updateStickyColumns(e,r,a,!this.fixedLayout||this._forceRecalculateCellWidths)}_getRenderedRows(e){let t=[];for(let o=0;o!r.when||r.when(t,e));else{let r=this._rowDefs.find(a=>a.when&&a.when(t,e))||this._defaultRowDef;r&&o.push(r)}return o.length,o}_getEmbeddedViewArgs(e,t){let o=e.rowDef,r={$implicit:e.data};return{templateRef:o.template,context:r,index:t}}_renderRow(e,t,o,r={}){let a=e.viewContainer.createEmbeddedView(t.template,r,o);return this._renderCellTemplateForItem(t,r),a}_renderCellTemplateForItem(e,t){for(let o of this._getCellTemplates(e))bu.mostRecentCellOutlet&&bu.mostRecentCellOutlet._viewContainer.createEmbeddedView(o,t);this._changeDetectorRef.markForCheck()}_updateRowIndexContext(){let e=this._rowOutlet.viewContainer;for(let t=0,o=e.length;t{let o=this._columnDefsByName.get(t);return e.extractCellTemplate(o)})}_forceRenderDataRows(){this._dataDiffer.diff([]),this._rowOutlet.viewContainer.clear(),this.renderRows()}_checkStickyStates(){let e=(t,o)=>t||o.hasStickyChanged();this._headerRowDefs.reduce(e,!1)&&this.updateStickyHeaderRowStyles(),this._footerRowDefs.reduce(e,!1)&&this.updateStickyFooterRowStyles(),Array.from(this._columnDefsByName.values()).reduce(e,!1)&&(this._stickyColumnStylesNeedReset=!0,this.updateStickyColumnStyles())}_setupStickyStyler(){let e=this._dir?this._dir.value:"ltr",t=this._injector;this._stickyStyler=new kP(this._isNativeHtmlTable,this.stickyCssClass,this._platform.isBrowser,this.needsPositionStickyOnElement,e,this,t),(this._dir?this._dir.change:_t()).pipe(tt(this._onDestroy)).subscribe(o=>{this._stickyStyler.direction=o,this.updateStickyColumnStyles()})}_setupVirtualScrolling(e){let t=typeof requestAnimationFrame<"u"?ah:kN;this.viewChange.next({start:0,end:0}),e.renderedRangeStream.pipe(t1(0,t),tt(this._onDestroy)).subscribe(this.viewChange),e.attach({dataStream:this._dataStream,measureRangeSize:(o,r)=>this._measureRangeSize(o,r)}),ir([e.renderedContentOffset,this._headerRowStickyUpdates]).pipe(tt(this._onDestroy)).subscribe(([o,r])=>{if(!(!r.sizes||!r.offsets||!r.elements))for(let a=0;a{if(!(!r.sizes||!r.offsets||!r.elements))for(let a=0;a!t._table||t._table===this)}_updateNoDataRow(){let e=this._customNoDataRow||this._noDataRow;if(!e)return;let t=this._rowOutlet.viewContainer.length===0;if(t===this._isShowingNoDataRow)return;let o=this._noDataRowOutlet.viewContainer;if(t){let r=o.createEmbeddedView(e.templateRef),a=r.rootNodes[0];if(r.rootNodes.length===1&&a?.nodeType===this._document.ELEMENT_NODE){a.setAttribute("role","row"),a.classList.add(...e._contentClassNames);let c=a.querySelectorAll(e._cellSelector);for(let m=0;m=e.end||t!=="vertical")return 0;let o=this.viewChange.value,r=this._rowOutlet.viewContainer;e.starto.end;let a=e.start-o.start,c=e.end-e.start,m,p;for(let S=0;S-1;S--){let x=r.get(S+a);if(x&&x.rootNodes.length){p=x.rootNodes[x.rootNodes.length-1];break}}let h=m?.getBoundingClientRect?.(),g=p?.getBoundingClientRect?.();return h&&g?g.bottom-h.top:0}_virtualScrollEnabled(){return!this._disableVirtualScrolling&&this._virtualScrollViewport!=null}static \u0275fac=function(t){return new(t||n)};static \u0275cmp=F({type:n,selectors:[["cdk-table"],["table","cdk-table",""]],contentQueries:function(t,o,r){if(t&1&&Vi(r,o8,5)(r,Om,5)(r,yx,5)(r,iv,5)(r,EP,5),t&2){let a;pt(a=ut())&&(o._noDataRow=a.first),pt(a=ut())&&(o._contentColumnDefs=a),pt(a=ut())&&(o._contentRowDefs=a),pt(a=ut())&&(o._contentHeaderRowDefs=a),pt(a=ut())&&(o._contentFooterRowDefs=a)}},hostAttrs:[1,"cdk-table"],hostVars:2,hostBindings:function(t,o){t&2&&Ue("cdk-table-fixed-layout",o.fixedLayout)},inputs:{trackBy:"trackBy",dataSource:"dataSource",multiTemplateDataRows:[2,"multiTemplateDataRows","multiTemplateDataRows",gt],fixedLayout:[2,"fixedLayout","fixedLayout",gt],recycleRows:[2,"recycleRows","recycleRows",gt]},outputs:{contentChanged:"contentChanged"},exportAs:["cdkTable"],features:[Cn([{provide:Xl,useExisting:n},{provide:nv,useValue:null}])],ngContentSelectors:Kte,decls:5,vars:2,consts:[["role","rowgroup"],["headerRowOutlet",""],["rowOutlet",""],["noDataRowOutlet",""],["footerRowOutlet",""]],template:function(t,o){t&1&&(ii(Yte),nn(0),nn(1,1),A(2,Zte,1,0),A(3,Jte,7,0)(4,ene,4,0)),t&2&&(u(2),O(o._isServer?2:-1),u(),O(o._isNativeHtmlTable?3:4))},dependencies:[AP,IP,NP,OP],styles:[`.cdk-table-fixed-layout{table-layout:fixed} -`],encapsulation:2})}return n})();function vx(n,i){return n.concat(Array.from(i))}function e8(n,i){let e=i.toUpperCase(),t=n.viewContainer.element.nativeElement;for(;t;){let o=t.nodeType===1?t.nodeName:null;if(o===e)return t;if(o==="TABLE")break;t=t.parentNode}return null}var Sx=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275mod=Gt({type:n});static \u0275inj=Ut({imports:[C5]})}return n})();var r8=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275mod=Gt({type:n});static \u0275inj=Ut({imports:[fd,ui,fd]})}return n})();var qn=(function(n){return n[n.State=0]="State",n[n.Transition=1]="Transition",n[n.Sequence=2]="Sequence",n[n.Group=3]="Group",n[n.Animate=4]="Animate",n[n.Keyframes=5]="Keyframes",n[n.Style=6]="Style",n[n.Trigger=7]="Trigger",n[n.Reference=8]="Reference",n[n.AnimateChild=9]="AnimateChild",n[n.AnimateRef=10]="AnimateRef",n[n.Query=11]="Query",n[n.Stagger=12]="Stagger",n})(qn||{}),hl="*";function FP(n,i){return{type:qn.Trigger,name:n,definitions:i,options:{}}}function LP(n,i=null){return{type:qn.Animate,styles:i,timings:n}}function a8(n,i=null){return{type:qn.Sequence,steps:n,options:i}}function yu(n){return{type:qn.Style,styles:n,offset:null}}function wx(n,i,e){return{type:qn.State,name:n,styles:i,options:e}}function BP(n,i,e=null){return{type:qn.Transition,expr:n,animation:i,options:e}}var Oc=class{_onDoneFns=[];_onStartFns=[];_onDestroyFns=[];_originalOnDoneFns=[];_originalOnStartFns=[];_started=!1;_destroyed=!1;_finished=!1;_position=0;parentPlayer=null;totalTime;constructor(i=0,e=0){this.totalTime=i+e}_onFinish(){this._finished||(this._finished=!0,this._onDoneFns.forEach(i=>i()),this._onDoneFns=[])}onStart(i){this._originalOnStartFns.push(i),this._onStartFns.push(i)}onDone(i){this._originalOnDoneFns.push(i),this._onDoneFns.push(i)}onDestroy(i){this._onDestroyFns.push(i)}hasStarted(){return this._started}init(){}play(){this.hasStarted()||(this._onStart(),this.triggerMicrotask()),this._started=!0}triggerMicrotask(){queueMicrotask(()=>this._onFinish())}_onStart(){this._onStartFns.forEach(i=>i()),this._onStartFns=[]}pause(){}restart(){}finish(){this._onFinish()}destroy(){this._destroyed||(this._destroyed=!0,this.hasStarted()||this._onStart(),this.finish(),this._onDestroyFns.forEach(i=>i()),this._onDestroyFns=[])}reset(){this._started=!1,this._finished=!1,this._onStartFns=this._originalOnStartFns,this._onDoneFns=this._originalOnDoneFns}setPosition(i){this._position=this.totalTime?i*this.totalTime:1}getPosition(){return this.totalTime?this._position/this.totalTime:1}triggerCallback(i){let e=i=="start"?this._onStartFns:this._onDoneFns;e.forEach(t=>t()),e.length=0}},xu=class{_onDoneFns=[];_onStartFns=[];_finished=!1;_started=!1;_destroyed=!1;_onDestroyFns=[];parentPlayer=null;totalTime=0;players;constructor(i){this.players=i;let e=0,t=0,o=0,r=this.players.length;r==0?queueMicrotask(()=>this._onFinish()):this.players.forEach(a=>{a.onDone(()=>{++e==r&&this._onFinish()}),a.onDestroy(()=>{++t==r&&this._onDestroy()}),a.onStart(()=>{++o==r&&this._onStart()})}),this.totalTime=this.players.reduce((a,c)=>Math.max(a,c.totalTime),0)}_onFinish(){this._finished||(this._finished=!0,this._onDoneFns.forEach(i=>i()),this._onDoneFns=[])}init(){this.players.forEach(i=>i.init())}onStart(i){this._onStartFns.push(i)}_onStart(){this.hasStarted()||(this._started=!0,this._onStartFns.forEach(i=>i()),this._onStartFns=[])}onDone(i){this._onDoneFns.push(i)}onDestroy(i){this._onDestroyFns.push(i)}hasStarted(){return this._started}play(){this.parentPlayer||this.init(),this._onStart(),this.players.forEach(i=>i.play())}pause(){this.players.forEach(i=>i.pause())}restart(){this.players.forEach(i=>i.restart())}finish(){this._onFinish(),this.players.forEach(i=>i.finish())}destroy(){this._onDestroy()}_onDestroy(){this._destroyed||(this._destroyed=!0,this._onFinish(),this.players.forEach(i=>i.destroy()),this._onDestroyFns.forEach(i=>i()),this._onDestroyFns=[])}reset(){this.players.forEach(i=>i.reset()),this._destroyed=!1,this._finished=!1,this._started=!1}setPosition(i){let e=i*this.totalTime;this.players.forEach(t=>{let o=t.totalTime?Math.min(1,e/t.totalTime):1;t.setPosition(o)})}getPosition(){let i=this.players.reduce((e,t)=>e===null||t.totalTime>e.totalTime?t:e,null);return i!=null?i.getPosition():0}beforeDestroy(){this.players.forEach(i=>{i.beforeDestroy&&i.beforeDestroy()})}triggerCallback(i){let e=i=="start"?this._onStartFns:this._onDoneFns;e.forEach(t=>t()),e.length=0}},ff="!";function s8(n){return new fn(3e3,!1)}function nne(){return new fn(3100,!1)}function ine(){return new fn(3101,!1)}function one(n){return new fn(3001,!1)}function rne(n){return new fn(3003,!1)}function ane(n){return new fn(3004,!1)}function c8(n,i){return new fn(3005,!1)}function d8(){return new fn(3006,!1)}function m8(){return new fn(3007,!1)}function p8(n,i){return new fn(3008,!1)}function u8(n){return new fn(3002,!1)}function h8(n,i,e,t,o){return new fn(3010,!1)}function f8(){return new fn(3011,!1)}function g8(){return new fn(3012,!1)}function _8(){return new fn(3200,!1)}function v8(){return new fn(3202,!1)}function C8(){return new fn(3013,!1)}function b8(n){return new fn(3014,!1)}function x8(n){return new fn(3015,!1)}function y8(n){return new fn(3016,!1)}function S8(n,i){return new fn(3404,!1)}function sne(n){return new fn(3502,!1)}function w8(n){return new fn(3503,!1)}function M8(){return new fn(3300,!1)}function k8(n){return new fn(3504,!1)}function T8(n){return new fn(3301,!1)}function E8(n,i){return new fn(3302,!1)}function D8(n){return new fn(3303,!1)}function P8(n,i){return new fn(3400,!1)}function I8(n){return new fn(3401,!1)}function A8(n){return new fn(3402,!1)}function O8(n,i){return new fn(3505,!1)}function Ed(n){switch(n.length){case 0:return new Oc;case 1:return n[0];default:return new xu(n)}}function $P(n,i,e=new Map,t=new Map){let o=[],r=[],a=-1,c=null;if(i.forEach(m=>{let p=m.get("offset"),h=p==a,g=h&&c||new Map;m.forEach((S,x)=>{let v=x,M=S;if(x!=="offset")switch(v=n.normalizePropertyName(v,o),M){case ff:M=e.get(x);break;case hl:M=t.get(x);break;default:M=n.normalizeStyleValue(x,v,M,o);break}g.set(v,M)}),h||r.push(g),c=g,a=p}),o.length)throw sne(o);return r}function Mx(n,i,e,t){switch(i){case"start":n.onStart(()=>t(e&&VP(e,"start",n)));break;case"done":n.onDone(()=>t(e&&VP(e,"done",n)));break;case"destroy":n.onDestroy(()=>t(e&&VP(e,"destroy",n)));break}}function VP(n,i,e){let t=e.totalTime,o=!!e.disabled,r=kx(n.element,n.triggerName,n.fromState,n.toState,i||n.phaseName,t??n.totalTime,o),a=n._data;return a!=null&&(r._data=a),r}function kx(n,i,e,t,o="",r=0,a){return{element:n,triggerName:i,fromState:e,toState:t,phaseName:o,totalTime:r,disabled:!!a}}function us(n,i,e){let t=n.get(i);return t||n.set(i,t=e),t}function HP(n){let i=n.indexOf(":"),e=n.substring(1,i),t=n.slice(i+1);return[e,t]}var lne=typeof document>"u"?null:document.documentElement;function Tx(n){let i=n.parentNode||n.host||null;return i===lne?null:i}function cne(n){return n.substring(1,6)=="ebkit"}var Su=null,l8=!1;function N8(n){Su||(Su=dne()||{},l8=Su.style?"WebkitAppearance"in Su.style:!1);let i=!0;return Su.style&&!cne(n)&&(i=n in Su.style,!i&&l8&&(i="Webkit"+n.charAt(0).toUpperCase()+n.slice(1)in Su.style)),i}function dne(){return typeof document<"u"?document.body:null}function UP(n,i){for(;i;){if(i===n)return!0;i=Tx(i)}return!1}function GP(n,i,e){if(e)return Array.from(n.querySelectorAll(i));let t=n.querySelector(i);return t?[t]:[]}var mne=1e3,WP="{{",pne="}}",qP="ng-enter",Ex="ng-leave",ov="ng-trigger",rv=".ng-trigger",QP="ng-animating",Dx=".ng-animating";function Nc(n){if(typeof n=="number")return n;let i=n.match(/^(-?[\.\d]+)(m?s)/);return!i||i.length<2?0:zP(parseFloat(i[1]),i[2])}function zP(n,i){return i==="s"?n*mne:n}function av(n,i,e){return n.hasOwnProperty("duration")?n:hne(n,i,e)}var une=/^(-?[\.\d]+)(m?s)(?:\s+(-?[\.\d]+)(m?s))?(?:\s+([-a-z]+(?:\(.+?\))?))?$/i;function hne(n,i,e){let t,o=0,r="";if(typeof n=="string"){let a=n.match(une);if(a===null)return i.push(s8(n)),{duration:0,delay:0,easing:""};t=zP(parseFloat(a[1]),a[2]);let c=a[3];c!=null&&(o=zP(parseFloat(c),a[4]));let m=a[5];m&&(r=m)}else t=n;if(!e){let a=!1,c=i.length;t<0&&(i.push(nne()),a=!0),o<0&&(i.push(ine()),a=!0),a&&i.splice(c,0,s8(n))}return{duration:t,delay:o,easing:r}}function R8(n){return n.length?n[0]instanceof Map?n:n.map(i=>new Map(Object.entries(i))):[]}function Yl(n,i,e){i.forEach((t,o)=>{let r=Px(o);e&&!e.has(o)&&e.set(o,n.style[r]),n.style[r]=t})}function Nm(n,i){i.forEach((e,t)=>{let o=Px(t);n.style[o]=""})}function gf(n){return Array.isArray(n)?n.length==1?n[0]:a8(n):n}function F8(n,i,e){let t=i.params||{},o=XP(n);o.length&&o.forEach(r=>{t.hasOwnProperty(r)||e.push(one(r))})}var jP=new RegExp(`${WP}\\s*(.+?)\\s*${pne}`,"g");function XP(n){let i=[];if(typeof n=="string"){let e;for(;e=jP.exec(n);)i.push(e[1]);jP.lastIndex=0}return i}function _f(n,i,e){let t=`${n}`,o=t.replace(jP,(r,a)=>{let c=i[a];return c==null&&(e.push(rne(a)),c=""),c.toString()});return o==t?n:o}var fne=/-+([a-z0-9])/g;function Px(n){return n.replace(fne,(...i)=>i[1].toUpperCase())}function L8(n,i){return n===0||i===0}function B8(n,i,e){if(e.size&&i.length){let t=i[0],o=[];if(e.forEach((r,a)=>{t.has(a)||o.push(a),t.set(a,r)}),o.length)for(let r=1;ra.set(c,Ix(n,c)))}}return i}function hs(n,i,e){switch(i.type){case qn.Trigger:return n.visitTrigger(i,e);case qn.State:return n.visitState(i,e);case qn.Transition:return n.visitTransition(i,e);case qn.Sequence:return n.visitSequence(i,e);case qn.Group:return n.visitGroup(i,e);case qn.Animate:return n.visitAnimate(i,e);case qn.Keyframes:return n.visitKeyframes(i,e);case qn.Style:return n.visitStyle(i,e);case qn.Reference:return n.visitReference(i,e);case qn.AnimateChild:return n.visitAnimateChild(i,e);case qn.AnimateRef:return n.visitAnimateRef(i,e);case qn.Query:return n.visitQuery(i,e);case qn.Stagger:return n.visitStagger(i,e);default:throw ane(i.type)}}function Ix(n,i){return window.getComputedStyle(n)[i]}var pI=(()=>{class n{validateStyleProperty(e){return N8(e)}containsElement(e,t){return UP(e,t)}getParentElement(e){return Tx(e)}query(e,t,o){return GP(e,t,o)}computeStyle(e,t,o){return o||""}animate(e,t,o,r,a,c=[],m){return new Oc(o,r)}static \u0275fac=function(t){return new(t||n)};static \u0275prov=K({token:n,factory:n.\u0275fac})}return n})(),Mu=class{static NOOP=new pI},ku=class{};var gne=new Set(["width","height","minWidth","minHeight","maxWidth","maxHeight","left","top","bottom","right","fontSize","outlineWidth","outlineOffset","paddingTop","paddingLeft","paddingBottom","paddingRight","marginTop","marginLeft","marginBottom","marginRight","borderRadius","borderWidth","borderTopWidth","borderLeftWidth","borderRightWidth","borderBottomWidth","textIndent","perspective"]),Fx=class extends ku{normalizePropertyName(i,e){return Px(i)}normalizeStyleValue(i,e,t,o){let r="",a=t.toString().trim();if(gne.has(e)&&t!==0&&t!=="0")if(typeof t=="number")r="px";else{let c=t.match(/^[+-]?[\d\.]+([a-z]*)$/);c&&c[1].length==0&&o.push(c8(i,t))}return a+r}};var Lx="*";function _ne(n,i){let e=[];return typeof n=="string"?n.split(/\s*,\s*/).forEach(t=>vne(t,e,i)):e.push(n),e}function vne(n,i,e){if(n[0]==":"){let m=Cne(n,e);if(typeof m=="function"){i.push(m);return}n=m}let t=n.match(/^(\*|[-\w]+)\s*()\s*(\*|[-\w]+)$/);if(t==null||t.length<4)return e.push(x8(n)),i;let o=t[1],r=t[2],a=t[3];i.push(V8(o,a));let c=o==Lx&&a==Lx;r[0]=="<"&&!c&&i.push(V8(a,o))}function Cne(n,i){switch(n){case":enter":return"void => *";case":leave":return"* => void";case":increment":return(e,t)=>parseFloat(t)>parseFloat(e);case":decrement":return(e,t)=>parseFloat(t) *"}}var Ax=new Set(["true","1"]),Ox=new Set(["false","0"]);function V8(n,i){let e=Ax.has(n)||Ox.has(n),t=Ax.has(i)||Ox.has(i);return(o,r)=>{let a=n==Lx||n==o,c=i==Lx||i==r;return!a&&e&&typeof o=="boolean"&&(a=o?Ax.has(n):Ox.has(n)),!c&&t&&typeof r=="boolean"&&(c=r?Ax.has(i):Ox.has(i)),a&&c}}var X8=":self",bne=new RegExp(`s*${X8}s*,?`,"g");function Y8(n,i,e,t){return new tI(n).build(i,e,t)}var z8="",tI=class{_driver;constructor(i){this._driver=i}build(i,e,t){let o=new nI(e);return this._resetContextStyleTimingState(o),hs(this,gf(i),o)}_resetContextStyleTimingState(i){i.currentQuerySelector=z8,i.collectedStyles=new Map,i.collectedStyles.set(z8,new Map),i.currentTime=0}visitTrigger(i,e){let t=e.queryCount=0,o=e.depCount=0,r=[],a=[];return i.name.charAt(0)=="@"&&e.errors.push(d8()),i.definitions.forEach(c=>{if(this._resetContextStyleTimingState(e),c.type==qn.State){let m=c,p=m.name;p.toString().split(/\s*,\s*/).forEach(h=>{m.name=h,r.push(this.visitState(m,e))}),m.name=p}else if(c.type==qn.Transition){let m=this.visitTransition(c,e);t+=m.queryCount,o+=m.depCount,a.push(m)}else e.errors.push(m8())}),{type:qn.Trigger,name:i.name,states:r,transitions:a,queryCount:t,depCount:o,options:null}}visitState(i,e){let t=this.visitStyle(i.styles,e),o=i.options&&i.options.params||null;if(t.containsDynamicStyles){let r=new Set,a=o||{};t.styles.forEach(c=>{c instanceof Map&&c.forEach(m=>{XP(m).forEach(p=>{a.hasOwnProperty(p)||r.add(p)})})}),r.size&&e.errors.push(p8(i.name,[...r.values()]))}return{type:qn.State,name:i.name,style:t,options:o?{params:o}:null}}visitTransition(i,e){e.queryCount=0,e.depCount=0;let t=hs(this,gf(i.animation),e),o=_ne(i.expr,e.errors);return{type:qn.Transition,matchers:o,animation:t,queryCount:e.queryCount,depCount:e.depCount,options:wu(i.options)}}visitSequence(i,e){return{type:qn.Sequence,steps:i.steps.map(t=>hs(this,t,e)),options:wu(i.options)}}visitGroup(i,e){let t=e.currentTime,o=0,r=i.steps.map(a=>{e.currentTime=t;let c=hs(this,a,e);return o=Math.max(o,e.currentTime),c});return e.currentTime=o,{type:qn.Group,steps:r,options:wu(i.options)}}visitAnimate(i,e){let t=wne(i.timings,e.errors);e.currentAnimateTimings=t;let o,r=i.styles?i.styles:yu({});if(r.type==qn.Keyframes)o=this.visitKeyframes(r,e);else{let a=i.styles,c=!1;if(!a){c=!0;let p={};t.easing&&(p.easing=t.easing),a=yu(p)}e.currentTime+=t.duration+t.delay;let m=this.visitStyle(a,e);m.isEmptyStep=c,o=m}return e.currentAnimateTimings=null,{type:qn.Animate,timings:t,style:o,options:null}}visitStyle(i,e){let t=this._makeStyleAst(i,e);return this._validateStyleAst(t,e),t}_makeStyleAst(i,e){let t=[],o=Array.isArray(i.styles)?i.styles:[i.styles];for(let c of o)typeof c=="string"?c===hl?t.push(c):e.errors.push(u8(c)):t.push(new Map(Object.entries(c)));let r=!1,a=null;return t.forEach(c=>{if(c instanceof Map&&(c.has("easing")&&(a=c.get("easing"),c.delete("easing")),!r)){for(let m of c.values())if(m.toString().indexOf(WP)>=0){r=!0;break}}}),{type:qn.Style,styles:t,easing:a,offset:i.offset,containsDynamicStyles:r,options:null}}_validateStyleAst(i,e){let t=e.currentAnimateTimings,o=e.currentTime,r=e.currentTime;t&&r>0&&(r-=t.duration+t.delay),i.styles.forEach(a=>{typeof a!="string"&&a.forEach((c,m)=>{let p=e.collectedStyles.get(e.currentQuerySelector),h=p.get(m),g=!0;h&&(r!=o&&r>=h.startTime&&o<=h.endTime&&(e.errors.push(h8(m,h.startTime,h.endTime,r,o)),g=!1),r=h.startTime),g&&p.set(m,{startTime:r,endTime:o}),e.options&&F8(c,e.options,e.errors)})})}visitKeyframes(i,e){let t={type:qn.Keyframes,styles:[],options:null};if(!e.currentAnimateTimings)return e.errors.push(f8()),t;let o=1,r=0,a=[],c=!1,m=!1,p=0,h=i.steps.map(y=>{let k=this._makeStyleAst(y,e),I=k.offset!=null?k.offset:Sne(k.styles),P=0;return I!=null&&(r++,P=k.offset=I),m=m||P<0||P>1,c=c||P0&&r{let I=S>0?k==x?1:S*k:a[k],P=I*w;e.currentTime=v+M.delay+P,M.duration=P,this._validateStyleAst(y,e),y.offset=I,t.styles.push(y)}),t}visitReference(i,e){return{type:qn.Reference,animation:hs(this,gf(i.animation),e),options:wu(i.options)}}visitAnimateChild(i,e){return e.depCount++,{type:qn.AnimateChild,options:wu(i.options)}}visitAnimateRef(i,e){return{type:qn.AnimateRef,animation:this.visitReference(i.animation,e),options:wu(i.options)}}visitQuery(i,e){let t=e.currentQuerySelector,o=i.options||{};e.queryCount++,e.currentQuery=i;let[r,a]=xne(i.selector);e.currentQuerySelector=t.length?t+" "+r:r,us(e.collectedStyles,e.currentQuerySelector,new Map);let c=hs(this,gf(i.animation),e);return e.currentQuery=null,e.currentQuerySelector=t,{type:qn.Query,selector:r,limit:o.limit||0,optional:!!o.optional,includeSelf:a,animation:c,originalSelector:i.selector,options:wu(i.options)}}visitStagger(i,e){e.currentQuery||e.errors.push(C8());let t=i.timings==="full"?{duration:0,delay:0,easing:"full"}:av(i.timings,e.errors,!0);return{type:qn.Stagger,animation:hs(this,gf(i.animation),e),timings:t,options:null}}};function xne(n){let i=!!n.split(/\s*,\s*/).find(e=>e==X8);return i&&(n=n.replace(bne,"")),n=n.replace(/@\*/g,rv).replace(/@\w+/g,e=>rv+"-"+e.slice(1)).replace(/:animating/g,Dx),[n,i]}function yne(n){return n?W({},n):null}var nI=class{errors;queryCount=0;depCount=0;currentTransition=null;currentQuery=null;currentQuerySelector=null;currentAnimateTimings=null;currentTime=0;collectedStyles=new Map;options=null;unsupportedCSSPropertiesFound=new Set;constructor(i){this.errors=i}};function Sne(n){if(typeof n=="string")return null;let i=null;if(Array.isArray(n))n.forEach(e=>{if(e instanceof Map&&e.has("offset")){let t=e;i=parseFloat(t.get("offset")),t.delete("offset")}});else if(n instanceof Map&&n.has("offset")){let e=n;i=parseFloat(e.get("offset")),e.delete("offset")}return i}function wne(n,i){if(n.hasOwnProperty("duration"))return n;if(typeof n=="number"){let r=av(n,i).duration;return YP(r,0,"")}let e=n;if(e.split(/\s+/).some(r=>r.charAt(0)=="{"&&r.charAt(1)=="{")){let r=YP(0,0,"");return r.dynamic=!0,r.strValue=e,r}let o=av(e,i);return YP(o.duration,o.delay,o.easing)}function wu(n){return n?(n=W({},n),n.params&&(n.params=yne(n.params))):n={},n}function YP(n,i,e){return{duration:n,delay:i,easing:e}}function uI(n,i,e,t,o,r,a=null,c=!1){return{type:1,element:n,keyframes:i,preStyleProps:e,postStyleProps:t,duration:o,delay:r,totalTime:o+r,easing:a,subTimeline:c}}var lv=class{_map=new Map;get(i){return this._map.get(i)||[]}append(i,e){let t=this._map.get(i);t||this._map.set(i,t=[]),t.push(...e)}has(i){return this._map.has(i)}clear(){this._map.clear()}},Mne=1,kne=":enter",Tne=new RegExp(kne,"g"),Ene=":leave",Dne=new RegExp(Ene,"g");function K8(n,i,e,t,o,r=new Map,a=new Map,c,m,p=[]){return new iI().buildKeyframes(n,i,e,t,o,r,a,c,m,p)}var iI=class{buildKeyframes(i,e,t,o,r,a,c,m,p,h=[]){p=p||new lv;let g=new oI(i,e,p,o,r,h,[]);g.options=m;let S=m.delay?Nc(m.delay):0;g.currentTimeline.delayNextStep(S),g.currentTimeline.setStyles([a],null,g.errors,m),hs(this,t,g);let x=g.timelines.filter(v=>v.containsAnimation());if(x.length&&c.size){let v;for(let M=x.length-1;M>=0;M--){let w=x[M];if(w.element===e){v=w;break}}v&&!v.allowOnlyTimelineStyles()&&v.setStyles([c],null,g.errors,m)}return x.length?x.map(v=>v.buildKeyframes()):[uI(e,[],[],[],0,S,"",!1)]}visitTrigger(i,e){}visitState(i,e){}visitTransition(i,e){}visitAnimateChild(i,e){let t=e.subInstructions.get(e.element);if(t){let o=e.createSubContext(i.options),r=e.currentTimeline.currentTime,a=this._visitSubInstructions(t,o,o.options);r!=a&&e.transformIntoNewTimeline(a)}e.previousNode=i}visitAnimateRef(i,e){let t=e.createSubContext(i.options);t.transformIntoNewTimeline(),this._applyAnimationRefDelays([i.options,i.animation.options],e,t),this.visitReference(i.animation,t),e.transformIntoNewTimeline(t.currentTimeline.currentTime),e.previousNode=i}_applyAnimationRefDelays(i,e,t){for(let o of i){let r=o?.delay;if(r){let a=typeof r=="number"?r:Nc(_f(r,o?.params??{},e.errors));t.delayNextStep(a)}}}_visitSubInstructions(i,e,t){let r=e.currentTimeline.currentTime,a=t.duration!=null?Nc(t.duration):null,c=t.delay!=null?Nc(t.delay):null;return a!==0&&i.forEach(m=>{let p=e.appendInstructionToTimeline(m,a,c);r=Math.max(r,p.duration+p.delay)}),r}visitReference(i,e){e.updateOptions(i.options,!0),hs(this,i.animation,e),e.previousNode=i}visitSequence(i,e){let t=e.subContextCount,o=e,r=i.options;if(r&&(r.params||r.delay)&&(o=e.createSubContext(r),o.transformIntoNewTimeline(),r.delay!=null)){o.previousNode.type==qn.Style&&(o.currentTimeline.snapshotCurrentStyles(),o.previousNode=Bx);let a=Nc(r.delay);o.delayNextStep(a)}i.steps.length&&(i.steps.forEach(a=>hs(this,a,o)),o.currentTimeline.applyStylesToKeyframe(),o.subContextCount>t&&o.transformIntoNewTimeline()),e.previousNode=i}visitGroup(i,e){let t=[],o=e.currentTimeline.currentTime,r=i.options&&i.options.delay?Nc(i.options.delay):0;i.steps.forEach(a=>{let c=e.createSubContext(i.options);r&&c.delayNextStep(r),hs(this,a,c),o=Math.max(o,c.currentTimeline.currentTime),t.push(c.currentTimeline)}),t.forEach(a=>e.currentTimeline.mergeTimelineCollectedStyles(a)),e.transformIntoNewTimeline(o),e.previousNode=i}_visitTiming(i,e){if(i.dynamic){let t=i.strValue,o=e.params?_f(t,e.params,e.errors):t;return av(o,e.errors)}else return{duration:i.duration,delay:i.delay,easing:i.easing}}visitAnimate(i,e){let t=e.currentAnimateTimings=this._visitTiming(i.timings,e),o=e.currentTimeline;t.delay&&(e.incrementTime(t.delay),o.snapshotCurrentStyles());let r=i.style;r.type==qn.Keyframes?this.visitKeyframes(r,e):(e.incrementTime(t.duration),this.visitStyle(r,e),o.applyStylesToKeyframe()),e.currentAnimateTimings=null,e.previousNode=i}visitStyle(i,e){let t=e.currentTimeline,o=e.currentAnimateTimings;!o&&t.hasCurrentStyleProperties()&&t.forwardFrame();let r=o&&o.easing||i.easing;i.isEmptyStep?t.applyEmptyStep(r):t.setStyles(i.styles,r,e.errors,e.options),e.previousNode=i}visitKeyframes(i,e){let t=e.currentAnimateTimings,o=e.currentTimeline.duration,r=t.duration,c=e.createSubContext().currentTimeline;c.easing=t.easing,i.styles.forEach(m=>{let p=m.offset||0;c.forwardTime(p*r),c.setStyles(m.styles,m.easing,e.errors,e.options),c.applyStylesToKeyframe()}),e.currentTimeline.mergeTimelineCollectedStyles(c),e.transformIntoNewTimeline(o+r),e.previousNode=i}visitQuery(i,e){let t=e.currentTimeline.currentTime,o=i.options||{},r=o.delay?Nc(o.delay):0;r&&(e.previousNode.type===qn.Style||t==0&&e.currentTimeline.hasCurrentStyleProperties())&&(e.currentTimeline.snapshotCurrentStyles(),e.previousNode=Bx);let a=t,c=e.invokeQuery(i.selector,i.originalSelector,i.limit,i.includeSelf,!!o.optional,e.errors);e.currentQueryTotal=c.length;let m=null;c.forEach((p,h)=>{e.currentQueryIndex=h;let g=e.createSubContext(i.options,p);r&&g.delayNextStep(r),p===e.element&&(m=g.currentTimeline),hs(this,i.animation,g),g.currentTimeline.applyStylesToKeyframe();let S=g.currentTimeline.currentTime;a=Math.max(a,S)}),e.currentQueryIndex=0,e.currentQueryTotal=0,e.transformIntoNewTimeline(a),m&&(e.currentTimeline.mergeTimelineCollectedStyles(m),e.currentTimeline.snapshotCurrentStyles()),e.previousNode=i}visitStagger(i,e){let t=e.parentContext,o=e.currentTimeline,r=i.timings,a=Math.abs(r.duration),c=a*(e.currentQueryTotal-1),m=a*e.currentQueryIndex;switch(r.duration<0?"reverse":r.easing){case"reverse":m=c-m;break;case"full":m=t.currentStaggerTime;break}let h=e.currentTimeline;m&&h.delayNextStep(m);let g=h.currentTime;hs(this,i.animation,e),e.previousNode=i,t.currentStaggerTime=o.currentTime-g+(o.startTime-t.currentTimeline.startTime)}},Bx={},oI=class n{_driver;element;subInstructions;_enterClassName;_leaveClassName;errors;timelines;parentContext=null;currentTimeline;currentAnimateTimings=null;previousNode=Bx;subContextCount=0;options={};currentQueryIndex=0;currentQueryTotal=0;currentStaggerTime=0;constructor(i,e,t,o,r,a,c,m){this._driver=i,this.element=e,this.subInstructions=t,this._enterClassName=o,this._leaveClassName=r,this.errors=a,this.timelines=c,this.currentTimeline=m||new Vx(this._driver,e,0),c.push(this.currentTimeline)}get params(){return this.options.params}updateOptions(i,e){if(!i)return;let t=i,o=this.options;t.duration!=null&&(o.duration=Nc(t.duration)),t.delay!=null&&(o.delay=Nc(t.delay));let r=t.params;if(r){let a=o.params;a||(a=this.options.params={}),Object.keys(r).forEach(c=>{(!e||!a.hasOwnProperty(c))&&(a[c]=_f(r[c],a,this.errors))})}}_copyOptions(){let i={};if(this.options){let e=this.options.params;if(e){let t=i.params={};Object.keys(e).forEach(o=>{t[o]=e[o]})}}return i}createSubContext(i=null,e,t){let o=e||this.element,r=new n(this._driver,o,this.subInstructions,this._enterClassName,this._leaveClassName,this.errors,this.timelines,this.currentTimeline.fork(o,t||0));return r.previousNode=this.previousNode,r.currentAnimateTimings=this.currentAnimateTimings,r.options=this._copyOptions(),r.updateOptions(i),r.currentQueryIndex=this.currentQueryIndex,r.currentQueryTotal=this.currentQueryTotal,r.parentContext=this,this.subContextCount++,r}transformIntoNewTimeline(i){return this.previousNode=Bx,this.currentTimeline=this.currentTimeline.fork(this.element,i),this.timelines.push(this.currentTimeline),this.currentTimeline}appendInstructionToTimeline(i,e,t){let o={duration:e??i.duration,delay:this.currentTimeline.currentTime+(t??0)+i.delay,easing:""},r=new rI(this._driver,i.element,i.keyframes,i.preStyleProps,i.postStyleProps,o,i.stretchStartingKeyframe);return this.timelines.push(r),o}incrementTime(i){this.currentTimeline.forwardTime(this.currentTimeline.duration+i)}delayNextStep(i){i>0&&this.currentTimeline.delayNextStep(i)}invokeQuery(i,e,t,o,r,a){let c=[];if(o&&c.push(this.element),i.length>0){i=i.replace(Tne,"."+this._enterClassName),i=i.replace(Dne,"."+this._leaveClassName);let m=t!=1,p=this._driver.query(this.element,i,m);t!==0&&(p=t<0?p.slice(p.length+t,p.length):p.slice(0,t)),c.push(...p)}return!r&&c.length==0&&a.push(b8(e)),c}},Vx=class n{_driver;element;startTime;_elementTimelineStylesLookup;duration=0;easing=null;_previousKeyframe=new Map;_currentKeyframe=new Map;_keyframes=new Map;_styleSummary=new Map;_localTimelineStyles=new Map;_globalTimelineStyles;_pendingStyles=new Map;_backFill=new Map;_currentEmptyStepKeyframe=null;constructor(i,e,t,o){this._driver=i,this.element=e,this.startTime=t,this._elementTimelineStylesLookup=o,this._elementTimelineStylesLookup||(this._elementTimelineStylesLookup=new Map),this._globalTimelineStyles=this._elementTimelineStylesLookup.get(e),this._globalTimelineStyles||(this._globalTimelineStyles=this._localTimelineStyles,this._elementTimelineStylesLookup.set(e,this._localTimelineStyles)),this._loadKeyframe()}containsAnimation(){switch(this._keyframes.size){case 0:return!1;case 1:return this.hasCurrentStyleProperties();default:return!0}}hasCurrentStyleProperties(){return this._currentKeyframe.size>0}get currentTime(){return this.startTime+this.duration}delayNextStep(i){let e=this._keyframes.size===1&&this._pendingStyles.size;this.duration||e?(this.forwardTime(this.currentTime+i),e&&this.snapshotCurrentStyles()):this.startTime+=i}fork(i,e){return this.applyStylesToKeyframe(),new n(this._driver,i,e||this.currentTime,this._elementTimelineStylesLookup)}_loadKeyframe(){this._currentKeyframe&&(this._previousKeyframe=this._currentKeyframe),this._currentKeyframe=this._keyframes.get(this.duration),this._currentKeyframe||(this._currentKeyframe=new Map,this._keyframes.set(this.duration,this._currentKeyframe))}forwardFrame(){this.duration+=Mne,this._loadKeyframe()}forwardTime(i){this.applyStylesToKeyframe(),this.duration=i,this._loadKeyframe()}_updateStyle(i,e){this._localTimelineStyles.set(i,e),this._globalTimelineStyles.set(i,e),this._styleSummary.set(i,{time:this.currentTime,value:e})}allowOnlyTimelineStyles(){return this._currentEmptyStepKeyframe!==this._currentKeyframe}applyEmptyStep(i){i&&this._previousKeyframe.set("easing",i);for(let[e,t]of this._globalTimelineStyles)this._backFill.set(e,t||hl),this._currentKeyframe.set(e,hl);this._currentEmptyStepKeyframe=this._currentKeyframe}setStyles(i,e,t,o){e&&this._previousKeyframe.set("easing",e);let r=o&&o.params||{},a=Pne(i,this._globalTimelineStyles);for(let[c,m]of a){let p=_f(m,r,t);this._pendingStyles.set(c,p),this._localTimelineStyles.has(c)||this._backFill.set(c,this._globalTimelineStyles.get(c)??hl),this._updateStyle(c,p)}}applyStylesToKeyframe(){this._pendingStyles.size!=0&&(this._pendingStyles.forEach((i,e)=>{this._currentKeyframe.set(e,i)}),this._pendingStyles.clear(),this._localTimelineStyles.forEach((i,e)=>{this._currentKeyframe.has(e)||this._currentKeyframe.set(e,i)}))}snapshotCurrentStyles(){for(let[i,e]of this._localTimelineStyles)this._pendingStyles.set(i,e),this._updateStyle(i,e)}getFinalKeyframe(){return this._keyframes.get(this.duration)}get properties(){let i=[];for(let e in this._currentKeyframe)i.push(e);return i}mergeTimelineCollectedStyles(i){i._styleSummary.forEach((e,t)=>{let o=this._styleSummary.get(t);(!o||e.time>o.time)&&this._updateStyle(t,e.value)})}buildKeyframes(){this.applyStylesToKeyframe();let i=new Set,e=new Set,t=this._keyframes.size===1&&this.duration===0,o=[];this._keyframes.forEach((c,m)=>{let p=new Map([...this._backFill,...c]);p.forEach((h,g)=>{h===ff?i.add(g):h===hl&&e.add(g)}),t||p.set("offset",m/this.duration),o.push(p)});let r=[...i.values()],a=[...e.values()];if(t){let c=o[0],m=new Map(c);c.set("offset",0),m.set("offset",1),o=[c,m]}return uI(this.element,o,r,a,this.duration,this.startTime,this.easing,!1)}},rI=class extends Vx{keyframes;preStyleProps;postStyleProps;_stretchStartingKeyframe;timings;constructor(i,e,t,o,r,a,c=!1){super(i,e,a.delay),this.keyframes=t,this.preStyleProps=o,this.postStyleProps=r,this._stretchStartingKeyframe=c,this.timings={duration:a.duration,delay:a.delay,easing:a.easing}}containsAnimation(){return this.keyframes.length>1}buildKeyframes(){let i=this.keyframes,{delay:e,duration:t,easing:o}=this.timings;if(this._stretchStartingKeyframe&&e){let r=[],a=t+e,c=e/a,m=new Map(i[0]);m.set("offset",0),r.push(m);let p=new Map(i[0]);p.set("offset",j8(c)),r.push(p);let h=i.length-1;for(let g=1;g<=h;g++){let S=new Map(i[g]),x=S.get("offset"),v=e+x*t;S.set("offset",j8(v/a)),r.push(S)}t=a,e=0,o="",i=r}return uI(this.element,i,this.preStyleProps,this.postStyleProps,t,e,o,!0)}};function j8(n,i=3){let e=Math.pow(10,i-1);return Math.round(n*e)/e}function Pne(n,i){let e=new Map,t;return n.forEach(o=>{if(o==="*"){t??=i.keys();for(let r of t)e.set(r,hl)}else for(let[r,a]of o)e.set(r,a)}),e}function $8(n,i,e,t,o,r,a,c,m,p,h,g,S){return{type:0,element:n,triggerName:i,isRemovalTransition:o,fromState:e,fromStyles:r,toState:t,toStyles:a,timelines:c,queriedElements:m,preStyleProps:p,postStyleProps:h,totalTime:g,errors:S}}var KP={},zx=class{_triggerName;ast;_stateStyles;constructor(i,e,t){this._triggerName=i,this.ast=e,this._stateStyles=t}match(i,e,t,o){return Ine(this.ast.matchers,i,e,t,o)}buildStyles(i,e,t){let o=this._stateStyles.get("*");return i!==void 0&&(o=this._stateStyles.get(i?.toString())||o),o?o.buildStyles(e,t):new Map}build(i,e,t,o,r,a,c,m,p,h){let g=[],S=this.ast.options&&this.ast.options.params||KP,x=c&&c.params||KP,v=this.buildStyles(t,x,g),M=m&&m.params||KP,w=this.buildStyles(o,M,g),y=new Set,k=new Map,I=new Map,P=o==="void",R={params:Z8(M,S),delay:this.ast.options?.delay},D=h?[]:K8(i,e,this.ast.animation,r,a,v,w,R,p,g),N=0;return D.forEach(q=>{N=Math.max(q.duration+q.delay,N)}),g.length?$8(e,this._triggerName,t,o,P,v,w,[],[],k,I,N,g):(D.forEach(q=>{let de=q.element,fe=us(k,de,new Set);q.preStyleProps.forEach(ue=>fe.add(ue));let G=us(I,de,new Set);q.postStyleProps.forEach(ue=>G.add(ue)),de!==e&&y.add(de)}),$8(e,this._triggerName,t,o,P,v,w,D,[...y.values()],k,I,N))}};function Ine(n,i,e,t,o){return n.some(r=>r(i,e,t,o))}function Z8(n,i){let e=W({},i);return Object.entries(n).forEach(([t,o])=>{o!=null&&(e[t]=o)}),e}var aI=class{styles;defaultParams;normalizer;constructor(i,e,t){this.styles=i,this.defaultParams=e,this.normalizer=t}buildStyles(i,e){let t=new Map,o=Z8(i,this.defaultParams);return this.styles.styles.forEach(r=>{typeof r!="string"&&r.forEach((a,c)=>{a&&(a=_f(a,o,e));let m=this.normalizer.normalizePropertyName(c,e);a=this.normalizer.normalizeStyleValue(c,m,a,e),t.set(c,a)})}),t}};function Ane(n,i,e){return new sI(n,i,e)}var sI=class{name;ast;_normalizer;transitionFactories=[];fallbackTransition;states=new Map;constructor(i,e,t){this.name=i,this.ast=e,this._normalizer=t,e.states.forEach(o=>{let r=o.options&&o.options.params||{};this.states.set(o.name,new aI(o.style,r,t))}),H8(this.states,"true","1"),H8(this.states,"false","0"),e.transitions.forEach(o=>{this.transitionFactories.push(new zx(i,o,this.states))}),this.fallbackTransition=One(i,this.states)}get containsQueries(){return this.ast.queryCount>0}matchTransition(i,e,t,o){return this.transitionFactories.find(a=>a.match(i,e,t,o))||null}matchStyles(i,e,t){return this.fallbackTransition.buildStyles(i,e,t)}};function One(n,i,e){let t=[(a,c)=>!0],o={type:qn.Sequence,steps:[],options:null},r={type:qn.Transition,animation:o,matchers:t,options:null,queryCount:0,depCount:0};return new zx(n,r,i)}function H8(n,i,e){n.has(i)?n.has(e)||n.set(e,n.get(i)):n.has(e)&&n.set(i,n.get(e))}var Nne=new lv,lI=class{bodyNode;_driver;_normalizer;_animations=new Map;_playersById=new Map;players=[];constructor(i,e,t){this.bodyNode=i,this._driver=e,this._normalizer=t}register(i,e){let t=[],o=[],r=Y8(this._driver,e,t,o);if(t.length)throw w8(t);this._animations.set(i,r)}_buildPlayer(i,e,t){let o=i.element,r=$P(this._normalizer,i.keyframes,e,t);return this._driver.animate(o,r,i.duration,i.delay,i.easing,[],!0)}create(i,e,t={}){let o=[],r=this._animations.get(i),a,c=new Map;if(r?(a=K8(this._driver,e,r,qP,Ex,new Map,new Map,t,Nne,o),a.forEach(h=>{let g=us(c,h.element,new Map);h.postStyleProps.forEach(S=>g.set(S,null))})):(o.push(M8()),a=[]),o.length)throw k8(o);c.forEach((h,g)=>{h.forEach((S,x)=>{h.set(x,this._driver.computeStyle(g,x,hl))})});let m=a.map(h=>{let g=c.get(h.element);return this._buildPlayer(h,new Map,g)}),p=Ed(m);return this._playersById.set(i,p),p.onDestroy(()=>this.destroy(i)),this.players.push(p),p}destroy(i){let e=this._getPlayer(i);e.destroy(),this._playersById.delete(i);let t=this.players.indexOf(e);t>=0&&this.players.splice(t,1)}_getPlayer(i){let e=this._playersById.get(i);if(!e)throw T8(i);return e}listen(i,e,t,o){let r=kx(e,"","","");return Mx(this._getPlayer(i),t,r,o),()=>{}}command(i,e,t,o){if(t=="register"){this.register(i,o[0]);return}if(t=="create"){let a=o[0]||{};this.create(i,e,a);return}let r=this._getPlayer(i);switch(t){case"play":r.play();break;case"pause":r.pause();break;case"reset":r.reset();break;case"restart":r.restart();break;case"finish":r.finish();break;case"init":r.init();break;case"setPosition":r.setPosition(parseFloat(o[0]));break;case"destroy":this.destroy(i);break}}},U8="ng-animate-queued",Rne=".ng-animate-queued",ZP="ng-animate-disabled",Fne=".ng-animate-disabled",Lne="ng-star-inserted",Bne=".ng-star-inserted",Vne=[],J8={namespaceId:"",setForRemoval:!1,setForMove:!1,hasAnimation:!1,removedBeforeQueried:!1},zne={namespaceId:"",setForMove:!1,setForRemoval:!1,hasAnimation:!1,removedBeforeQueried:!0},Kl="__ng_removed",cv=class{namespaceId;value;options;get params(){return this.options.params}constructor(i,e=""){this.namespaceId=e;let t=i&&i.hasOwnProperty("value"),o=t?i.value:i;if(this.value=$ne(o),t){let r=i,{value:a}=r,c=wN(r,["value"]);this.options=c}else this.options={};this.options.params||(this.options.params={})}absorbOptions(i){let e=i.params;if(e){let t=this.options.params;Object.keys(e).forEach(o=>{t[o]==null&&(t[o]=e[o])})}}},sv="void",JP=new cv(sv),cI=class{id;hostElement;_engine;players=[];_triggers=new Map;_queue=[];_elementListeners=new Map;_hostClassName;constructor(i,e,t){this.id=i,this.hostElement=e,this._engine=t,this._hostClassName="ng-tns-"+i,fl(e,this._hostClassName)}listen(i,e,t,o){if(!this._triggers.has(e))throw E8(t,e);if(t==null||t.length==0)throw D8(e);if(!Hne(t))throw P8(t,e);let r=us(this._elementListeners,i,[]),a={name:e,phase:t,callback:o};r.push(a);let c=us(this._engine.statesByElement,i,new Map);return c.has(e)||(fl(i,ov),fl(i,ov+"-"+e),c.set(e,JP)),()=>{this._engine.afterFlush(()=>{let m=r.indexOf(a);m>=0&&r.splice(m,1),this._triggers.has(e)||c.delete(e)})}}register(i,e){return this._triggers.has(i)?!1:(this._triggers.set(i,e),!0)}_getTrigger(i){let e=this._triggers.get(i);if(!e)throw I8(i);return e}trigger(i,e,t,o=!0){let r=this._getTrigger(e),a=new dv(this.id,e,i),c=this._engine.statesByElement.get(i);c||(fl(i,ov),fl(i,ov+"-"+e),this._engine.statesByElement.set(i,c=new Map));let m=c.get(e),p=new cv(t,this.id);if(!(t&&t.hasOwnProperty("value"))&&m&&p.absorbOptions(m.options),c.set(e,p),m||(m=JP),!(p.value===sv)&&m.value===p.value){if(!Wne(m.params,p.params)){let M=[],w=r.matchStyles(m.value,m.params,M),y=r.matchStyles(p.value,p.params,M);M.length?this._engine.reportError(M):this._engine.afterFlush(()=>{Nm(i,w),Yl(i,y)})}return}let S=us(this._engine.playersByElement,i,[]);S.forEach(M=>{M.namespaceId==this.id&&M.triggerName==e&&M.queued&&M.destroy()});let x=r.matchTransition(m.value,p.value,i,p.params),v=!1;if(!x){if(!o)return;x=r.fallbackTransition,v=!0}return this._engine.totalQueuedPlayers++,this._queue.push({element:i,triggerName:e,transition:x,fromState:m,toState:p,player:a,isFallbackTransition:v}),v||(fl(i,U8),a.onStart(()=>{vf(i,U8)})),a.onDone(()=>{let M=this.players.indexOf(a);M>=0&&this.players.splice(M,1);let w=this._engine.playersByElement.get(i);if(w){let y=w.indexOf(a);y>=0&&w.splice(y,1)}}),this.players.push(a),S.push(a),a}deregister(i){this._triggers.delete(i),this._engine.statesByElement.forEach(e=>e.delete(i)),this._elementListeners.forEach((e,t)=>{this._elementListeners.set(t,e.filter(o=>o.name!=i))})}clearElementCache(i){this._engine.statesByElement.delete(i),this._elementListeners.delete(i);let e=this._engine.playersByElement.get(i);e&&(e.forEach(t=>t.destroy()),this._engine.playersByElement.delete(i))}_signalRemovalForInnerTriggers(i,e){let t=this._engine.driver.query(i,rv,!0);t.forEach(o=>{if(o[Kl])return;let r=this._engine.fetchNamespacesByElement(o);r.size?r.forEach(a=>a.triggerLeaveAnimation(o,e,!1,!0)):this.clearElementCache(o)}),this._engine.afterFlushAnimationsDone(()=>t.forEach(o=>this.clearElementCache(o)))}triggerLeaveAnimation(i,e,t,o){let r=this._engine.statesByElement.get(i),a=new Map;if(r){let c=[];if(r.forEach((m,p)=>{if(a.set(p,m.value),this._triggers.has(p)){let h=this.trigger(i,p,sv,o);h&&c.push(h)}}),c.length)return this._engine.markElementAsRemoved(this.id,i,!0,e,a),t&&Ed(c).onDone(()=>this._engine.processLeaveNode(i)),!0}return!1}prepareLeaveAnimationListeners(i){let e=this._elementListeners.get(i),t=this._engine.statesByElement.get(i);if(e&&t){let o=new Set;e.forEach(r=>{let a=r.name;if(o.has(a))return;o.add(a);let m=this._triggers.get(a).fallbackTransition,p=t.get(a)||JP,h=new cv(sv),g=new dv(this.id,a,i);this._engine.totalQueuedPlayers++,this._queue.push({element:i,triggerName:a,transition:m,fromState:p,toState:h,player:g,isFallbackTransition:!0})})}}removeNode(i,e){let t=this._engine;if(i.childElementCount&&this._signalRemovalForInnerTriggers(i,e),this.triggerLeaveAnimation(i,e,!0))return;let o=!1;if(t.totalAnimations){let r=t.players.length?t.playersByQueriedElement.get(i):[];if(r&&r.length)o=!0;else{let a=i;for(;a=a.parentNode;)if(t.statesByElement.get(a)){o=!0;break}}}if(this.prepareLeaveAnimationListeners(i),o)t.markElementAsRemoved(this.id,i,!1,e);else{let r=i[Kl];(!r||r===J8)&&(t.afterFlush(()=>this.clearElementCache(i)),t.destroyInnerAnimations(i),t._onRemovalComplete(i,e))}}insertNode(i,e){fl(i,this._hostClassName)}drainQueuedTransitions(i){let e=[];return this._queue.forEach(t=>{let o=t.player;if(o.destroyed)return;let r=t.element,a=this._elementListeners.get(r);a&&a.forEach(c=>{if(c.name==t.triggerName){let m=kx(r,t.triggerName,t.fromState.value,t.toState.value);m._data=i,Mx(t.player,c.phase,m,c.callback)}}),o.markedForDestroy?this._engine.afterFlush(()=>{o.destroy()}):e.push(t)}),this._queue=[],e.sort((t,o)=>{let r=t.transition.ast.depCount,a=o.transition.ast.depCount;return r==0||a==0?r-a:this._engine.driver.containsElement(t.element,o.element)?1:-1})}destroy(i){this.players.forEach(e=>e.destroy()),this._signalRemovalForInnerTriggers(this.hostElement,i)}},dI=class{bodyNode;driver;_normalizer;players=[];newHostElements=new Map;playersByElement=new Map;playersByQueriedElement=new Map;statesByElement=new Map;disabledNodes=new Set;totalAnimations=0;totalQueuedPlayers=0;_namespaceLookup={};_namespaceList=[];_flushFns=[];_whenQuietFns=[];namespacesByHostElement=new Map;collectedEnterElements=[];collectedLeaveElements=[];onRemovalComplete=(i,e)=>{};_onRemovalComplete(i,e){this.onRemovalComplete(i,e)}constructor(i,e,t){this.bodyNode=i,this.driver=e,this._normalizer=t}get queuedPlayers(){let i=[];return this._namespaceList.forEach(e=>{e.players.forEach(t=>{t.queued&&i.push(t)})}),i}createNamespace(i,e){let t=new cI(i,e,this);return this.bodyNode&&this.driver.containsElement(this.bodyNode,e)?this._balanceNamespaceList(t,e):(this.newHostElements.set(e,t),this.collectEnterElement(e)),this._namespaceLookup[i]=t}_balanceNamespaceList(i,e){let t=this._namespaceList,o=this.namespacesByHostElement;if(t.length-1>=0){let a=!1,c=this.driver.getParentElement(e);for(;c;){let m=o.get(c);if(m){let p=t.indexOf(m);t.splice(p+1,0,i),a=!0;break}c=this.driver.getParentElement(c)}a||t.unshift(i)}else t.push(i);return o.set(e,i),i}register(i,e){let t=this._namespaceLookup[i];return t||(t=this.createNamespace(i,e)),t}registerTrigger(i,e,t){let o=this._namespaceLookup[i];o&&o.register(e,t)&&this.totalAnimations++}destroy(i,e){i&&(this.afterFlush(()=>{}),this.afterFlushAnimationsDone(()=>{let t=this._fetchNamespace(i);this.namespacesByHostElement.delete(t.hostElement);let o=this._namespaceList.indexOf(t);o>=0&&this._namespaceList.splice(o,1),t.destroy(e),delete this._namespaceLookup[i]}))}_fetchNamespace(i){return this._namespaceLookup[i]}fetchNamespacesByElement(i){let e=new Set,t=this.statesByElement.get(i);if(t){for(let o of t.values())if(o.namespaceId){let r=this._fetchNamespace(o.namespaceId);r&&e.add(r)}}return e}trigger(i,e,t,o){if(Nx(e)){let r=this._fetchNamespace(i);if(r)return r.trigger(e,t,o),!0}return!1}insertNode(i,e,t,o){if(!Nx(e))return;let r=e[Kl];if(r&&r.setForRemoval){r.setForRemoval=!1,r.setForMove=!0;let a=this.collectedLeaveElements.indexOf(e);a>=0&&this.collectedLeaveElements.splice(a,1)}if(i){let a=this._fetchNamespace(i);a&&a.insertNode(e,t)}o&&this.collectEnterElement(e)}collectEnterElement(i){this.collectedEnterElements.push(i)}markElementAsDisabled(i,e){e?this.disabledNodes.has(i)||(this.disabledNodes.add(i),fl(i,ZP)):this.disabledNodes.has(i)&&(this.disabledNodes.delete(i),vf(i,ZP))}removeNode(i,e,t){if(Nx(e)){let o=i?this._fetchNamespace(i):null;o?o.removeNode(e,t):this.markElementAsRemoved(i,e,!1,t);let r=this.namespacesByHostElement.get(e);r&&r.id!==i&&r.removeNode(e,t)}else this._onRemovalComplete(e,t)}markElementAsRemoved(i,e,t,o,r){this.collectedLeaveElements.push(e),e[Kl]={namespaceId:i,setForRemoval:o,hasAnimation:t,removedBeforeQueried:!1,previousTriggersValues:r}}listen(i,e,t,o,r){return Nx(e)?this._fetchNamespace(i).listen(e,t,o,r):()=>{}}_buildInstruction(i,e,t,o,r){return i.transition.build(this.driver,i.element,i.fromState.value,i.toState.value,t,o,i.fromState.options,i.toState.options,e,r)}destroyInnerAnimations(i){let e=this.driver.query(i,rv,!0);e.forEach(t=>this.destroyActiveAnimationsForElement(t)),this.playersByQueriedElement.size!=0&&(e=this.driver.query(i,Dx,!0),e.forEach(t=>this.finishActiveQueriedAnimationOnElement(t)))}destroyActiveAnimationsForElement(i){let e=this.playersByElement.get(i);e&&e.forEach(t=>{t.queued?t.markedForDestroy=!0:t.destroy()})}finishActiveQueriedAnimationOnElement(i){let e=this.playersByQueriedElement.get(i);e&&e.forEach(t=>t.finish())}whenRenderingDone(){return new Promise(i=>{if(this.players.length)return Ed(this.players).onDone(()=>i());i()})}processLeaveNode(i){let e=i[Kl];if(e&&e.setForRemoval){if(i[Kl]=J8,e.namespaceId){this.destroyInnerAnimations(i);let t=this._fetchNamespace(e.namespaceId);t&&t.clearElementCache(i)}this._onRemovalComplete(i,e.setForRemoval)}i.classList?.contains(ZP)&&this.markElementAsDisabled(i,!1),this.driver.query(i,Fne,!0).forEach(t=>{this.markElementAsDisabled(t,!1)})}flush(i=-1){let e=[];if(this.newHostElements.size&&(this.newHostElements.forEach((t,o)=>this._balanceNamespaceList(t,o)),this.newHostElements.clear()),this.totalAnimations&&this.collectedEnterElements.length)for(let t=0;tt()),this._flushFns=[],this._whenQuietFns.length){let t=this._whenQuietFns;this._whenQuietFns=[],e.length?Ed(e).onDone(()=>{t.forEach(o=>o())}):t.forEach(o=>o())}}reportError(i){throw A8(i)}_flushAnimations(i,e){let t=new lv,o=[],r=new Map,a=[],c=new Map,m=new Map,p=new Map,h=new Set;this.disabledNodes.forEach(me=>{h.add(me);let V=this.driver.query(me,Rne,!0);for(let Y=0;Y{let Y=qP+M++;v.set(V,Y),me.forEach(ie=>fl(ie,Y))});let w=[],y=new Set,k=new Set;for(let me=0;mey.add(ie)):k.add(V))}let I=new Map,P=q8(S,Array.from(y));P.forEach((me,V)=>{let Y=Ex+M++;I.set(V,Y),me.forEach(ie=>fl(ie,Y))}),i.push(()=>{x.forEach((me,V)=>{let Y=v.get(V);me.forEach(ie=>vf(ie,Y))}),P.forEach((me,V)=>{let Y=I.get(V);me.forEach(ie=>vf(ie,Y))}),w.forEach(me=>{this.processLeaveNode(me)})});let R=[],D=[];for(let me=this._namespaceList.length-1;me>=0;me--)this._namespaceList[me].drainQueuedTransitions(e).forEach(Y=>{let ie=Y.player,oe=Y.element;if(R.push(ie),this.collectedEnterElements.length){let Oe=oe[Kl];if(Oe&&Oe.setForMove){if(Oe.previousTriggersValues&&Oe.previousTriggersValues.has(Y.triggerName)){let Ge=Oe.previousTriggersValues.get(Y.triggerName),ct=this.statesByElement.get(Y.element);if(ct&&ct.has(Y.triggerName)){let kt=ct.get(Y.triggerName);kt.value=Ge,ct.set(Y.triggerName,kt)}}ie.destroy();return}}let Te=!g||!this.driver.containsElement(g,oe),Le=I.get(oe),Ye=v.get(oe),Xe=this._buildInstruction(Y,t,Ye,Le,Te);if(Xe.errors&&Xe.errors.length){D.push(Xe);return}if(Te){ie.onStart(()=>Nm(oe,Xe.fromStyles)),ie.onDestroy(()=>Yl(oe,Xe.toStyles)),o.push(ie);return}if(Y.isFallbackTransition){ie.onStart(()=>Nm(oe,Xe.fromStyles)),ie.onDestroy(()=>Yl(oe,Xe.toStyles)),o.push(ie);return}let xe=[];Xe.timelines.forEach(Oe=>{Oe.stretchStartingKeyframe=!0,this.disabledNodes.has(Oe.element)||xe.push(Oe)}),Xe.timelines=xe,t.append(oe,Xe.timelines);let Q={instruction:Xe,player:ie,element:oe};a.push(Q),Xe.queriedElements.forEach(Oe=>us(c,Oe,[]).push(ie)),Xe.preStyleProps.forEach((Oe,Ge)=>{if(Oe.size){let ct=m.get(Ge);ct||m.set(Ge,ct=new Set),Oe.forEach((kt,Xn)=>ct.add(Xn))}}),Xe.postStyleProps.forEach((Oe,Ge)=>{let ct=p.get(Ge);ct||p.set(Ge,ct=new Set),Oe.forEach((kt,Xn)=>ct.add(Xn))})});if(D.length){let me=[];D.forEach(V=>{me.push(O8(V.triggerName,V.errors))}),R.forEach(V=>V.destroy()),this.reportError(me)}let N=new Map,q=new Map;a.forEach(me=>{let V=me.element;t.has(V)&&(q.set(V,V),this._beforeAnimationBuild(me.player.namespaceId,me.instruction,N))}),o.forEach(me=>{let V=me.element;this._getPreviousPlayers(V,!1,me.namespaceId,me.triggerName,null).forEach(ie=>{us(N,V,[]).push(ie),ie.destroy()})});let de=w.filter(me=>Q8(me,m,p)),fe=new Map;W8(fe,this.driver,k,p,hl).forEach(me=>{Q8(me,m,p)&&de.push(me)});let ue=new Map;x.forEach((me,V)=>{W8(ue,this.driver,new Set(me),m,ff)}),de.forEach(me=>{let V=fe.get(me),Y=ue.get(me);fe.set(me,new Map([...V?.entries()??[],...Y?.entries()??[]]))});let be=[],le=[],De={};a.forEach(me=>{let{element:V,player:Y,instruction:ie}=me;if(t.has(V)){if(h.has(V)){Y.onDestroy(()=>Yl(V,ie.toStyles)),Y.disabled=!0,Y.overrideTotalTime(ie.totalTime),o.push(Y);return}let oe=De;if(q.size>1){let Le=V,Ye=[];for(;Le=Le.parentNode;){let Xe=q.get(Le);if(Xe){oe=Xe;break}Ye.push(Le)}Ye.forEach(Xe=>q.set(Xe,oe))}let Te=this._buildAnimation(Y.namespaceId,ie,N,r,ue,fe);if(Y.setRealPlayer(Te),oe===De)be.push(Y);else{let Le=this.playersByElement.get(oe);Le&&Le.length&&(Y.parentPlayer=Ed(Le)),o.push(Y)}}else Nm(V,ie.fromStyles),Y.onDestroy(()=>Yl(V,ie.toStyles)),le.push(Y),h.has(V)&&o.push(Y)}),le.forEach(me=>{let V=r.get(me.element);if(V&&V.length){let Y=Ed(V);me.setRealPlayer(Y)}}),o.forEach(me=>{me.parentPlayer?me.syncPlayerEvents(me.parentPlayer):me.destroy()});for(let me=0;me!Te.destroyed);oe.length?Une(this,V,oe):this.processLeaveNode(V)}return w.length=0,be.forEach(me=>{this.players.push(me),me.onDone(()=>{me.destroy();let V=this.players.indexOf(me);this.players.splice(V,1)}),me.play()}),be}afterFlush(i){this._flushFns.push(i)}afterFlushAnimationsDone(i){this._whenQuietFns.push(i)}_getPreviousPlayers(i,e,t,o,r){let a=[];if(e){let c=this.playersByQueriedElement.get(i);c&&(a=c)}else{let c=this.playersByElement.get(i);if(c){let m=!r||r==sv;c.forEach(p=>{p.queued||!m&&p.triggerName!=o||a.push(p)})}}return(t||o)&&(a=a.filter(c=>!(t&&t!=c.namespaceId||o&&o!=c.triggerName))),a}_beforeAnimationBuild(i,e,t){let o=e.triggerName,r=e.element,a=e.isRemovalTransition?void 0:i,c=e.isRemovalTransition?void 0:o;for(let m of e.timelines){let p=m.element,h=p!==r,g=us(t,p,[]);this._getPreviousPlayers(p,h,a,c,e.toState).forEach(x=>{let v=x.getRealPlayer();v.beforeDestroy&&v.beforeDestroy(),x.destroy(),g.push(x)})}Nm(r,e.fromStyles)}_buildAnimation(i,e,t,o,r,a){let c=e.triggerName,m=e.element,p=[],h=new Set,g=new Set,S=e.timelines.map(v=>{let M=v.element;h.add(M);let w=M[Kl];if(w&&w.removedBeforeQueried)return new Oc(v.duration,v.delay);let y=M!==m,k=Gne((t.get(M)||Vne).map(N=>N.getRealPlayer())).filter(N=>{let q=N;return q.element?q.element===M:!1}),I=r.get(M),P=a.get(M),R=$P(this._normalizer,v.keyframes,I,P),D=this._buildPlayer(v,R,k);if(v.subTimeline&&o&&g.add(M),y){let N=new dv(i,c,M);N.setRealPlayer(D),p.push(N)}return D});p.forEach(v=>{us(this.playersByQueriedElement,v.element,[]).push(v),v.onDone(()=>jne(this.playersByQueriedElement,v.element,v))}),h.forEach(v=>fl(v,QP));let x=Ed(S);return x.onDestroy(()=>{h.forEach(v=>vf(v,QP)),Yl(m,e.toStyles)}),g.forEach(v=>{us(o,v,[]).push(x)}),x}_buildPlayer(i,e,t){return e.length>0?this.driver.animate(i.element,e,i.duration,i.delay,i.easing,t):new Oc(i.duration,i.delay)}},dv=class{namespaceId;triggerName;element;_player=new Oc;_containsRealPlayer=!1;_queuedCallbacks=new Map;destroyed=!1;parentPlayer=null;markedForDestroy=!1;disabled=!1;queued=!0;totalTime=0;constructor(i,e,t){this.namespaceId=i,this.triggerName=e,this.element=t}setRealPlayer(i){this._containsRealPlayer||(this._player=i,this._queuedCallbacks.forEach((e,t)=>{e.forEach(o=>Mx(i,t,void 0,o))}),this._queuedCallbacks.clear(),this._containsRealPlayer=!0,this.overrideTotalTime(i.totalTime),this.queued=!1)}getRealPlayer(){return this._player}overrideTotalTime(i){this.totalTime=i}syncPlayerEvents(i){let e=this._player;e.triggerCallback&&i.onStart(()=>e.triggerCallback("start")),i.onDone(()=>this.finish()),i.onDestroy(()=>this.destroy())}_queueEvent(i,e){us(this._queuedCallbacks,i,[]).push(e)}onDone(i){this.queued&&this._queueEvent("done",i),this._player.onDone(i)}onStart(i){this.queued&&this._queueEvent("start",i),this._player.onStart(i)}onDestroy(i){this.queued&&this._queueEvent("destroy",i),this._player.onDestroy(i)}init(){this._player.init()}hasStarted(){return this.queued?!1:this._player.hasStarted()}play(){!this.queued&&this._player.play()}pause(){!this.queued&&this._player.pause()}restart(){!this.queued&&this._player.restart()}finish(){this._player.finish()}destroy(){this.destroyed=!0,this._player.destroy()}reset(){!this.queued&&this._player.reset()}setPosition(i){this.queued||this._player.setPosition(i)}getPosition(){return this.queued?0:this._player.getPosition()}triggerCallback(i){let e=this._player;e.triggerCallback&&e.triggerCallback(i)}};function jne(n,i,e){let t=n.get(i);if(t){if(t.length){let o=t.indexOf(e);t.splice(o,1)}t.length==0&&n.delete(i)}return t}function $ne(n){return n??null}function Nx(n){return n&&n.nodeType===1}function Hne(n){return n=="start"||n=="done"}function G8(n,i){let e=n.style.display;return n.style.display=i??"none",e}function W8(n,i,e,t,o){let r=[];e.forEach(m=>r.push(G8(m)));let a=[];t.forEach((m,p)=>{let h=new Map;m.forEach(g=>{let S=i.computeStyle(p,g,o);h.set(g,S),(!S||S.length==0)&&(p[Kl]=zne,a.push(p))}),n.set(p,h)});let c=0;return e.forEach(m=>G8(m,r[c++])),a}function q8(n,i){let e=new Map;if(n.forEach(c=>e.set(c,[])),i.length==0)return e;let t=1,o=new Set(i),r=new Map;function a(c){if(!c)return t;let m=r.get(c);if(m)return m;let p=c.parentNode;return e.has(p)?m=p:o.has(p)?m=t:m=a(p),r.set(c,m),m}return i.forEach(c=>{let m=a(c);m!==t&&e.get(m).push(c)}),e}function fl(n,i){n.classList?.add(i)}function vf(n,i){n.classList?.remove(i)}function Une(n,i,e){Ed(e).onDone(()=>n.processLeaveNode(i))}function Gne(n){let i=[];return e7(n,i),i}function e7(n,i){for(let e=0;eo.add(r)):i.set(n,t),e.delete(n),!0}var Cf=class{_driver;_normalizer;_transitionEngine;_timelineEngine;_triggerCache={};onRemovalComplete=(i,e)=>{};constructor(i,e,t){this._driver=e,this._normalizer=t,this._transitionEngine=new dI(i.body,e,t),this._timelineEngine=new lI(i.body,e,t),this._transitionEngine.onRemovalComplete=(o,r)=>this.onRemovalComplete(o,r)}registerTrigger(i,e,t,o,r){let a=i+"-"+o,c=this._triggerCache[a];if(!c){let m=[],p=[],h=Y8(this._driver,r,m,p);if(m.length)throw S8(o,m);c=Ane(o,h,this._normalizer),this._triggerCache[a]=c}this._transitionEngine.registerTrigger(e,o,c)}register(i,e){this._transitionEngine.register(i,e)}destroy(i,e){this._transitionEngine.destroy(i,e)}onInsert(i,e,t,o){this._transitionEngine.insertNode(i,e,t,o)}onRemove(i,e,t){this._transitionEngine.removeNode(i,e,t)}disableAnimations(i,e){this._transitionEngine.markElementAsDisabled(i,e)}process(i,e,t,o){if(t.charAt(0)=="@"){let[r,a]=HP(t),c=o;this._timelineEngine.command(r,e,a,c)}else this._transitionEngine.trigger(i,e,t,o)}listen(i,e,t,o,r){if(t.charAt(0)=="@"){let[a,c]=HP(t);return this._timelineEngine.listen(a,e,c,r)}return this._transitionEngine.listen(i,e,t,o,r)}flush(i=-1){this._transitionEngine.flush(i)}get players(){return[...this._transitionEngine.players,...this._timelineEngine.players]}whenRenderingDone(){return this._transitionEngine.whenRenderingDone()}afterFlushAnimationsDone(i){this._transitionEngine.afterFlushAnimationsDone(i)}};function qne(n,i){let e=null,t=null;return Array.isArray(i)&&i.length?(e=eI(i[0]),i.length>1&&(t=eI(i[i.length-1]))):i instanceof Map&&(e=eI(i)),e||t?new Qne(n,e,t):null}var Qne=(()=>{class n{_element;_startStyles;_endStyles;static initialStylesByElement=new WeakMap;_state=0;_initialStyles;constructor(e,t,o){this._element=e,this._startStyles=t,this._endStyles=o;let r=n.initialStylesByElement.get(e);r||n.initialStylesByElement.set(e,r=new Map),this._initialStyles=r}start(){this._state<1&&(this._startStyles&&Yl(this._element,this._startStyles,this._initialStyles),this._state=1)}finish(){this.start(),this._state<2&&(Yl(this._element,this._initialStyles),this._endStyles&&(Yl(this._element,this._endStyles),this._endStyles=null),this._state=1)}destroy(){this.finish(),this._state<3&&(n.initialStylesByElement.delete(this._element),this._startStyles&&(Nm(this._element,this._startStyles),this._endStyles=null),this._endStyles&&(Nm(this._element,this._endStyles),this._endStyles=null),Yl(this._element,this._initialStyles),this._state=3)}}return n})();function eI(n){let i=null;return n.forEach((e,t)=>{Xne(t)&&(i=i||new Map,i.set(t,e))}),i}function Xne(n){return n==="display"||n==="position"}var jx=class{element;keyframes;options;_specialStyles;_onDoneFns=[];_onStartFns=[];_onDestroyFns=[];_duration;_delay;_initialized=!1;_finished=!1;_started=!1;_destroyed=!1;_finalKeyframe;_originalOnDoneFns=[];_originalOnStartFns=[];domPlayer=null;time=0;parentPlayer=null;currentSnapshot=new Map;constructor(i,e,t,o){this.element=i,this.keyframes=e,this.options=t,this._specialStyles=o,this._duration=t.duration,this._delay=t.delay||0,this.time=this._duration+this._delay}_onFinish(){this._finished||(this._finished=!0,this._onDoneFns.forEach(i=>i()),this._onDoneFns=[])}init(){this._buildPlayer()&&this._preparePlayerBeforeStart()}_buildPlayer(){if(this._initialized)return this.domPlayer;this._initialized=!0;let i=this.keyframes,e=this._triggerWebAnimation(this.element,i,this.options);if(!e)return this._onFinish(),null;this.domPlayer=e,this._finalKeyframe=i.length?i[i.length-1]:new Map;let t=()=>this._onFinish();return e.addEventListener("finish",t),this.onDestroy(()=>{e.removeEventListener("finish",t)}),e}_preparePlayerBeforeStart(){this._delay?this._resetDomPlayerState():this.domPlayer?.pause()}_convertKeyframesToObject(i){let e=[];return i.forEach(t=>{e.push(Object.fromEntries(t))}),e}_triggerWebAnimation(i,e,t){let o=this._convertKeyframesToObject(e);try{return i.animate(o,t)}catch{return null}}onStart(i){this._originalOnStartFns.push(i),this._onStartFns.push(i)}onDone(i){this._originalOnDoneFns.push(i),this._onDoneFns.push(i)}onDestroy(i){this._onDestroyFns.push(i)}play(){let i=this._buildPlayer();i&&(this.hasStarted()||(this._onStartFns.forEach(e=>e()),this._onStartFns=[],this._started=!0,this._specialStyles&&this._specialStyles.start()),i.play())}pause(){this.init(),this.domPlayer?.pause()}finish(){this.init(),this.domPlayer&&(this._specialStyles&&this._specialStyles.finish(),this._onFinish(),this.domPlayer.finish())}reset(){this._resetDomPlayerState(),this._destroyed=!1,this._finished=!1,this._started=!1,this._onStartFns=this._originalOnStartFns,this._onDoneFns=this._originalOnDoneFns}_resetDomPlayerState(){this.domPlayer?.cancel()}restart(){this.reset(),this.play()}hasStarted(){return this._started}destroy(){this._destroyed||(this._destroyed=!0,this._resetDomPlayerState(),this._onFinish(),this._specialStyles&&this._specialStyles.destroy(),this._onDestroyFns.forEach(i=>i()),this._onDestroyFns=[])}setPosition(i){this.domPlayer||this.init(),this.domPlayer&&(this.domPlayer.currentTime=i*this.time)}getPosition(){return this.domPlayer?+(this.domPlayer.currentTime??0)/this.time:this._initialized?1:0}get totalTime(){return this._delay+this._duration}beforeDestroy(){let i=new Map;this.hasStarted()&&this._finalKeyframe.forEach((t,o)=>{o!=="offset"&&i.set(o,this._finished?t:Ix(this.element,o))}),this.currentSnapshot=i}triggerCallback(i){let e=i==="start"?this._onStartFns:this._onDoneFns;e.forEach(t=>t()),e.length=0}},$x=class{validateStyleProperty(i){return!0}validateAnimatableStyleProperty(i){return!0}containsElement(i,e){return UP(i,e)}getParentElement(i){return Tx(i)}query(i,e,t){return GP(i,e,t)}computeStyle(i,e,t){return Ix(i,e)}animate(i,e,t,o,r,a=[]){let c=o==0?"both":"forwards",m={duration:t,delay:o,fill:c};r&&(m.easing=r);let p=new Map,h=a.filter(x=>x instanceof jx);L8(t,o)&&h.forEach(x=>{x.currentSnapshot.forEach((v,M)=>p.set(M,v))});let g=R8(e).map(x=>new Map(x));g=B8(i,g,p);let S=qne(i,g);return new jx(i,g,m,S)}};var Rx="@",t7="@.disabled",Hx=class{namespaceId;delegate;engine;_onDestroy;\u0275type=0;constructor(i,e,t,o){this.namespaceId=i,this.delegate=e,this.engine=t,this._onDestroy=o}get data(){return this.delegate.data}destroyNode(i){this.delegate.destroyNode?.(i)}destroy(){this.engine.destroy(this.namespaceId,this.delegate),this.engine.afterFlushAnimationsDone(()=>{queueMicrotask(()=>{this.delegate.destroy()})}),this._onDestroy?.()}createElement(i,e){return this.delegate.createElement(i,e)}createComment(i){return this.delegate.createComment(i)}createText(i){return this.delegate.createText(i)}appendChild(i,e){this.delegate.appendChild(i,e),this.engine.onInsert(this.namespaceId,e,i,!1)}insertBefore(i,e,t,o=!0){this.delegate.insertBefore(i,e,t),this.engine.onInsert(this.namespaceId,e,i,o)}removeChild(i,e,t,o){if(o){this.delegate.removeChild(i,e,t,o);return}this.parentNode(e)&&this.engine.onRemove(this.namespaceId,e,this.delegate)}selectRootElement(i,e){return this.delegate.selectRootElement(i,e)}parentNode(i){return this.delegate.parentNode(i)}nextSibling(i){return this.delegate.nextSibling(i)}setAttribute(i,e,t,o){this.delegate.setAttribute(i,e,t,o)}removeAttribute(i,e,t){this.delegate.removeAttribute(i,e,t)}addClass(i,e){this.delegate.addClass(i,e)}removeClass(i,e){this.delegate.removeClass(i,e)}setStyle(i,e,t,o){this.delegate.setStyle(i,e,t,o)}removeStyle(i,e,t){this.delegate.removeStyle(i,e,t)}setProperty(i,e,t){e.charAt(0)==Rx&&e==t7?this.disableAnimations(i,!!t):this.delegate.setProperty(i,e,t)}setValue(i,e){this.delegate.setValue(i,e)}listen(i,e,t,o){return this.delegate.listen(i,e,t,o)}disableAnimations(i,e){this.engine.disableAnimations(i,e)}},mI=class extends Hx{factory;constructor(i,e,t,o,r){super(e,t,o,r),this.factory=i,this.namespaceId=e}setProperty(i,e,t){e.charAt(0)==Rx?e.charAt(1)=="."&&e==t7?(t=t===void 0?!0:!!t,this.disableAnimations(i,t)):this.engine.process(this.namespaceId,i,e.slice(1),t):this.delegate.setProperty(i,e,t)}listen(i,e,t,o){if(e.charAt(0)==Rx){let r=Yne(i),a=e.slice(1),c="";return a.charAt(0)!=Rx&&([a,c]=Kne(a)),this.engine.listen(this.namespaceId,r,a,c,m=>{let p=m._data||-1;this.factory.scheduleListenerCallback(p,t,m)})}return this.delegate.listen(i,e,t,o)}};function Yne(n){switch(n){case"body":return document.body;case"document":return document;case"window":return window;default:return n}}function Kne(n){let i=n.indexOf("."),e=n.substring(0,i),t=n.slice(i+1);return[e,t]}var Ux=class{delegate;engine;_zone;_currentId=0;_microtaskId=1;_animationCallbacksBuffer=[];_rendererCache=new Map;_cdRecurDepth=0;constructor(i,e,t){this.delegate=i,this.engine=e,this._zone=t,e.onRemovalComplete=(o,r)=>{r?.removeChild(null,o)}}createRenderer(i,e){let o=this.delegate.createRenderer(i,e);if(!i||!e?.data?.animation){let p=this._rendererCache,h=p.get(o);if(!h){let g=()=>p.delete(o);h=new Hx("",o,this.engine,g),p.set(o,h)}return h}let r=e.id,a=e.id+"-"+this._currentId;this._currentId++,this.engine.register(a,i);let c=p=>{Array.isArray(p)?p.forEach(c):this.engine.registerTrigger(r,a,i,p.name,p)};return e.data.animation.forEach(c),new mI(this,a,o,this.engine)}begin(){this._cdRecurDepth++,this.delegate.begin&&this.delegate.begin()}_scheduleCountTask(){queueMicrotask(()=>{this._microtaskId++})}scheduleListenerCallback(i,e,t){if(i>=0&&ie(t));return}let o=this._animationCallbacksBuffer;o.length==0&&queueMicrotask(()=>{this._zone.run(()=>{o.forEach(r=>{let[a,c]=r;a(c)}),this._animationCallbacksBuffer=[]})}),o.push([e,t])}end(){this._cdRecurDepth--,this._cdRecurDepth==0&&this._zone.runOutsideAngular(()=>{this._scheduleCountTask(),this.engine.flush(this._microtaskId)}),this.delegate.end&&this.delegate.end()}whenRenderingDone(){return this.engine.whenRenderingDone()}componentReplaced(i){this.engine.flush(),this.delegate.componentReplaced?.(i)}};var Jne=(()=>{class n extends Cf{constructor(e,t,o){super(e,t,o)}ngOnDestroy(){this.flush()}static \u0275fac=function(t){return new(t||n)(ge(co),ge(Mu),ge(ku))};static \u0275prov=K({token:n,factory:n.\u0275fac})}return n})();function eie(){return new Fx}function tie(){return new Ux(f(a5),f(Cf),f(Pi))}var i7=[{provide:ku,useFactory:eie},{provide:Cf,useClass:Jne},{provide:pd,useFactory:tie}],nie=[{provide:Mu,useClass:pI},{provide:BT,useValue:"NoopAnimations"},...i7],n7=[{provide:Mu,useFactory:()=>new $x},{provide:BT,useFactory:()=>"BrowserAnimations"},...i7],o7=(()=>{class n{static withConfig(e){return{ngModule:n,providers:e.disableAnimations?nie:n7}}static \u0275fac=function(t){return new(t||n)};static \u0275mod=Gt({type:n});static \u0275inj=Ut({providers:n7,imports:[p1]})}return n})();function iie(n,i){return typeof n>"u"?typeof i>"u"?n:i:n}function _I(n,i){return n=iie(n,i),typeof n=="function"?function(){for(var t=arguments,o=arguments.length,r=Array(o),a=0;a"u"?"undefined":hI(n))==="object"&&n.nodeType===1&&hI(n.style)==="object"&&hI(n.ownerDocument)==="object"};function s7(n,i){if(i=bI(i,!0),!a7(i))return-1;for(var e=0;e0;)e[t]=i[t+1];return e=e.map(bI),oie(n,e)}function aie(n){for(var i=arguments,e=[],t=arguments.length-1;t-- >0;)e[t]=i[t+1];return e.map(bI).reduce(function(o,r){var a=s7(n,r);return a!==-1?o.concat(n.splice(a,1)):o},[])}function bI(n,i){if(typeof n=="string")try{return document.querySelector(n)}catch(e){throw e}if(!a7(n)&&!i)throw new TypeError(n+" is not a DOM element.");return n}function sie(n,i){i=i||{};var e=_I(i.allowUpdate,!0);return function(o){if(o=o||window.event,n.target=o.target||o.srcElement||o.originalTarget,n.element=this,n.type=o.type,!!e(o)){if(o.targetTouches)n.x=o.targetTouches[0].clientX,n.y=o.targetTouches[0].clientY,n.pageX=o.targetTouches[0].pageX,n.pageY=o.targetTouches[0].pageY,n.screenX=o.targetTouches[0].screenX,n.screenY=o.targetTouches[0].screenY;else{if(o.pageX===null&&o.clientX!==null){var r=o.target&&o.target.ownerDocument||document,a=r.documentElement,c=r.body;n.pageX=o.clientX+(a&&a.scrollLeft||c&&c.scrollLeft||0)-(a&&a.clientLeft||c&&c.clientLeft||0),n.pageY=o.clientY+(a&&a.scrollTop||c&&c.scrollTop||0)-(a&&a.clientTop||c&&c.clientTop||0)}else n.pageX=o.pageX,n.pageY=o.pageY;n.x=o.clientX,n.y=o.clientY,n.screenX=o.screenX,n.screenY=o.screenY}n.clientX=n.x,n.clientY=n.y}}}function lie(){var n={top:{value:0,enumerable:!0},left:{value:0,enumerable:!0},right:{value:window.innerWidth,enumerable:!0},bottom:{value:window.innerHeight,enumerable:!0},width:{value:window.innerWidth,enumerable:!0},height:{value:window.innerHeight,enumerable:!0},x:{value:0,enumerable:!0},y:{value:0,enumerable:!0}};if(Object.create)return Object.create({},n);var i={};return Object.defineProperties(i,n),i}function l7(n){if(n===window)return lie();try{var i=n.getBoundingClientRect();return i.x===void 0&&(i.x=i.left,i.y=i.top),i}catch{throw new TypeError("Can't call getBoundingClientRect on "+n)}}function cie(n,i){var e=l7(i);return n.y>e.top&&n.ye.left&&n.x"u")return function(){};for(var n=0,i=pv.length;n"u")return function(){};for(var n=0,i=pv.length;nue.right-e.margin.right?be=Math.ceil(Math.min(1,(a.x-ue.right)/e.margin.right+1)*e.maxSpeed.right):be=0,a.yue.bottom-e.margin.bottom?le=Math.ceil(Math.min(1,(a.y-ue.bottom)/e.margin.bottom+1)*e.maxSpeed.bottom):le=0,e.syncMove()&&m.dispatch(G,{pageX:a.pageX+be,pageY:a.pageY+le,clientX:a.x+be,clientY:a.y+le}),setTimeout(function(){le&&de(G,le),be&&fe(G,be)})}function de(G,ue){G===window?window.scrollTo(G.pageXOffset,G.pageYOffset+ue):G.scrollTop+=ue}function fe(G,ue){G===window?window.scrollTo(G.pageXOffset+ue,G.pageYOffset):G.scrollLeft+=ue}}function uie(n,i){return new pie(n,i)}function r7(n,i,e){return e?n.y>e.top&&n.ye.left&&n.x{class n{constructor(){this.currentDrag=new je}static{this.\u0275fac=function(t){return new(t||n)}}static{this.\u0275prov=K({token:n,factory:n.\u0275fac,providedIn:"root"})}}return n})(),fie=(()=>{class n{constructor(){this.elementRef=f(Qt)}static{this.\u0275fac=function(t){return new(t||n)}}static{this.\u0275dir=ft({type:n,selectors:[["","mwlDraggableScrollContainer",""]]})}}return n})();function gie(n,i,e){e&&e.split(" ").forEach(t=>n.addClass(i.nativeElement,t))}function _ie(n,i,e){e&&e.split(" ").forEach(t=>n.removeClass(i.nativeElement,t))}var d7=(()=>{class n{constructor(){this.dragAxis={x:!0,y:!0},this.dragSnapGrid={},this.ghostDragEnabled=!0,this.showOriginalElementWhileDragging=!1,this.dragCursor="",this.autoScroll={margin:20},this.dragPointerDown=new _e,this.dragStart=new _e,this.ghostElementCreated=new _e,this.dragging=new _e,this.dragEnd=new _e,this.pointerDown$=new je,this.pointerMove$=new je,this.pointerUp$=new je,this.eventListenerSubscriptions={},this.destroy$=new je,this.timeLongPress={timerBegin:0,timerEnd:0},this.element=f(Qt),this.renderer=f(pi),this.draggableHelper=f(hie),this.zone=f(Pi),this.vcr=f(Ji),this.scrollContainer=f(fie,{optional:!0}),this.document=f(co)}ngOnInit(){this.checkEventListeners();let e=this.pointerDown$.pipe(Yn(()=>this.canDrag()),vr(t=>{t.event.stopPropagation&&!this.scrollContainer&&t.event.stopPropagation();let o=this.renderer.createElement("style");this.renderer.setAttribute(o,"type","text/css"),this.renderer.appendChild(o,this.renderer.createText(` - body * { - -moz-user-select: none; - -ms-user-select: none; - -webkit-user-select: none; - user-select: none; - } - `)),requestAnimationFrame(()=>{this.document.head.appendChild(o)});let r=this.getScrollPosition(),a=new Pr(x=>{let v=this.scrollContainer?this.scrollContainer.elementRef.nativeElement:"window";return this.renderer.listen(v,"scroll",M=>x.next(M))}).pipe(xi(r),xt(()=>this.getScrollPosition())),c=new je,m=new rh;this.dragPointerDown.observers.length>0&&this.zone.run(()=>{this.dragPointerDown.next({x:0,y:0})});let p=En(this.pointerUp$,this.pointerDown$,m,this.destroy$).pipe(Vl()),h=ir([this.pointerMove$,a]).pipe(xt(([x,v])=>({currentDrag$:c,transformX:x.clientX-t.clientX,transformY:x.clientY-t.clientY,clientX:x.clientX,clientY:x.clientY,scrollLeft:v.left,scrollTop:v.top,target:x.event.target})),xt(x=>(this.dragSnapGrid.x&&(x.transformX=Math.round(x.transformX/this.dragSnapGrid.x)*this.dragSnapGrid.x),this.dragSnapGrid.y&&(x.transformY=Math.round(x.transformY/this.dragSnapGrid.y)*this.dragSnapGrid.y),x)),xt(x=>(this.dragAxis.x||(x.transformX=0),this.dragAxis.y||(x.transformY=0),x)),xt(x=>{let v=x.scrollLeft-r.left,M=x.scrollTop-r.top;return Qe(W({},x),{x:x.transformX+v,y:x.transformY+M})}),Yn(({x,y:v,transformX:M,transformY:w})=>!this.validateDrag||this.validateDrag({x,y:v,transform:{x:M,y:w}})),tt(p),Vl()),g=h.pipe(Gi(1),Vl()),S=h.pipe(x_(1),Vl());return g.subscribe(({clientX:x,clientY:v,x:M,y:w})=>{if(this.dragStart.observers.length>0&&this.zone.run(()=>{this.dragStart.next({cancelDrag$:m})}),this.scroller=c7([this.scrollContainer?this.scrollContainer.elementRef.nativeElement:this.document.defaultView],Qe(W({},this.autoScroll),{autoScroll(){return!0}})),gie(this.renderer,this.element,this.dragActiveClass),this.ghostDragEnabled){let y=this.element.nativeElement.getBoundingClientRect(),k=this.element.nativeElement.cloneNode(!0);if(this.showOriginalElementWhileDragging||this.renderer.setStyle(this.element.nativeElement,"visibility","hidden"),this.ghostElementAppendTo?this.ghostElementAppendTo.appendChild(k):this.element.nativeElement.parentNode.insertBefore(k,this.element.nativeElement.nextSibling),this.ghostElement=k,this.document.body.style.cursor=this.dragCursor,this.setElementStyles(k,{position:"fixed",top:`${y.top}px`,left:`${y.left}px`,width:`${y.width}px`,height:`${y.height}px`,cursor:this.dragCursor,margin:"0",willChange:"transform",pointerEvents:"none"}),this.ghostElementTemplate){let I=this.vcr.createEmbeddedView(this.ghostElementTemplate);k.innerHTML="",I.rootNodes.filter(P=>P instanceof Node).forEach(P=>{k.appendChild(P)}),S.subscribe(()=>{this.vcr.remove(this.vcr.indexOf(I))})}this.ghostElementCreated.observers.length>0&&this.zone.run(()=>{this.ghostElementCreated.emit({clientX:x-M,clientY:v-w,element:k})}),S.subscribe(()=>{k.parentElement.removeChild(k),this.ghostElement=null,this.renderer.setStyle(this.element.nativeElement,"visibility","")})}this.draggableHelper.currentDrag.next(c)}),S.pipe(vr(x=>{let v=m.pipe(EN(),Gi(1),xt(M=>Qe(W({},x),{dragCancelled:M>0})));return m.complete(),v})).subscribe(({x,y:v,dragCancelled:M})=>{this.scroller.destroy(),this.dragEnd.observers.length>0&&this.zone.run(()=>{this.dragEnd.next({x,y:v,dragCancelled:M})}),_ie(this.renderer,this.element,this.dragActiveClass),c.complete()}),En(p,S).pipe(Gi(1)).subscribe(()=>{requestAnimationFrame(()=>{this.document.head.removeChild(o)})}),h}),Vl());En(e.pipe(Gi(1),xt(t=>[,t])),e.pipe(n1())).pipe(Yn(([t,o])=>t?t.x!==o.x||t.y!==o.y:!0),xt(([t,o])=>o)).subscribe(({x:t,y:o,currentDrag$:r,clientX:a,clientY:c,transformX:m,transformY:p,target:h})=>{this.dragging.observers.length>0&&this.zone.run(()=>{this.dragging.next({x:t,y:o})}),requestAnimationFrame(()=>{if(this.ghostElement){let g=`translate3d(${m}px, ${p}px, 0px)`;this.setElementStyles(this.ghostElement,{transform:g,"-webkit-transform":g,"-ms-transform":g,"-moz-transform":g,"-o-transform":g})}}),r.next({clientX:a,clientY:c,dropData:this.dropData,target:h})})}ngOnChanges(e){e.dragAxis&&this.checkEventListeners()}ngOnDestroy(){this.unsubscribeEventListeners(),this.pointerDown$.complete(),this.pointerMove$.complete(),this.pointerUp$.complete(),this.destroy$.next()}checkEventListeners(){let e=this.canDrag(),t=Object.keys(this.eventListenerSubscriptions).length>0;e&&!t?this.zone.runOutsideAngular(()=>{this.eventListenerSubscriptions.mousedown=this.renderer.listen(this.element.nativeElement,"mousedown",o=>{this.onMouseDown(o)}),this.eventListenerSubscriptions.mouseup=this.renderer.listen("document","mouseup",o=>{this.onMouseUp(o)}),this.eventListenerSubscriptions.touchstart=this.renderer.listen(this.element.nativeElement,"touchstart",o=>{this.onTouchStart(o)}),this.eventListenerSubscriptions.touchend=this.renderer.listen("document","touchend",o=>{this.onTouchEnd(o)}),this.eventListenerSubscriptions.touchcancel=this.renderer.listen("document","touchcancel",o=>{this.onTouchEnd(o)}),this.eventListenerSubscriptions.mouseenter=this.renderer.listen(this.element.nativeElement,"mouseenter",()=>{this.onMouseEnter()}),this.eventListenerSubscriptions.mouseleave=this.renderer.listen(this.element.nativeElement,"mouseleave",()=>{this.onMouseLeave()})}):!e&&t&&this.unsubscribeEventListeners()}onMouseDown(e){e.button===0&&(this.eventListenerSubscriptions.mousemove||(this.eventListenerSubscriptions.mousemove=this.renderer.listen("document","mousemove",t=>{this.pointerMove$.next({event:t,clientX:t.clientX,clientY:t.clientY})})),this.pointerDown$.next({event:e,clientX:e.clientX,clientY:e.clientY}))}onMouseUp(e){e.button===0&&(this.eventListenerSubscriptions.mousemove&&(this.eventListenerSubscriptions.mousemove(),delete this.eventListenerSubscriptions.mousemove),this.pointerUp$.next({event:e,clientX:e.clientX,clientY:e.clientY}))}onTouchStart(e){let t,o,r;if(this.touchStartLongPress&&(this.timeLongPress.timerBegin=Date.now(),o=!1,r=this.hasScrollbar(),t=this.getScrollPosition()),!this.eventListenerSubscriptions.touchmove){let a=ra(this.document,"contextmenu").subscribe(m=>{m.preventDefault()}),c=ra(this.document,"touchmove",{passive:!1}).subscribe(m=>{this.touchStartLongPress&&!o&&r&&(o=this.shouldBeginDrag(e,m,t)),(!this.touchStartLongPress||!r||o)&&(m.preventDefault(),this.pointerMove$.next({event:m,clientX:m.targetTouches[0].clientX,clientY:m.targetTouches[0].clientY}))});this.eventListenerSubscriptions.touchmove=()=>{a.unsubscribe(),c.unsubscribe()}}this.pointerDown$.next({event:e,clientX:e.touches[0].clientX,clientY:e.touches[0].clientY})}onTouchEnd(e){this.eventListenerSubscriptions.touchmove&&(this.eventListenerSubscriptions.touchmove(),delete this.eventListenerSubscriptions.touchmove,this.touchStartLongPress&&this.enableScroll()),this.pointerUp$.next({event:e,clientX:e.changedTouches[0].clientX,clientY:e.changedTouches[0].clientY})}onMouseEnter(){this.setCursor(this.dragCursor)}onMouseLeave(){this.setCursor("")}canDrag(){return this.dragAxis.x||this.dragAxis.y}setCursor(e){this.eventListenerSubscriptions.mousemove||this.renderer.setStyle(this.element.nativeElement,"cursor",e)}unsubscribeEventListeners(){Object.keys(this.eventListenerSubscriptions).forEach(e=>{this.eventListenerSubscriptions[e](),delete this.eventListenerSubscriptions[e]})}setElementStyles(e,t){Object.keys(t).forEach(o=>{this.renderer.setStyle(e,o,t[o])})}getScrollElement(){return this.scrollContainer?this.scrollContainer.elementRef.nativeElement:this.document.body}getScrollPosition(){return this.scrollContainer?{top:this.scrollContainer.elementRef.nativeElement.scrollTop,left:this.scrollContainer.elementRef.nativeElement.scrollLeft}:{top:window.pageYOffset||this.document.documentElement.scrollTop,left:window.pageXOffset||this.document.documentElement.scrollLeft}}shouldBeginDrag(e,t,o){let r=this.getScrollPosition(),a={top:Math.abs(r.top-o.top),left:Math.abs(r.left-o.left)},c=Math.abs(t.targetTouches[0].clientX-e.touches[0].clientX)-a.left,m=Math.abs(t.targetTouches[0].clientY-e.touches[0].clientY)-a.top,p=c+m,h=this.touchStartLongPress;return(p>h.delta||a.top>0||a.left>0)&&(this.timeLongPress.timerBegin=Date.now()),this.timeLongPress.timerEnd=Date.now(),this.timeLongPress.timerEnd-this.timeLongPress.timerBegin>=h.delay?(this.disableScroll(),!0):!1}enableScroll(){this.scrollContainer&&this.renderer.setStyle(this.scrollContainer.elementRef.nativeElement,"overflow",""),this.renderer.setStyle(this.document.body,"overflow","")}disableScroll(){this.scrollContainer&&this.renderer.setStyle(this.scrollContainer.elementRef.nativeElement,"overflow","hidden"),this.renderer.setStyle(this.document.body,"overflow","hidden")}hasScrollbar(){let e=this.getScrollElement(),t=e.scrollWidth>e.clientWidth,o=e.scrollHeight>e.clientHeight;return t||o}static{this.\u0275fac=function(t){return new(t||n)}}static{this.\u0275dir=ft({type:n,selectors:[["","mwlDraggable",""]],inputs:{dropData:"dropData",dragAxis:"dragAxis",dragSnapGrid:"dragSnapGrid",ghostDragEnabled:"ghostDragEnabled",showOriginalElementWhileDragging:"showOriginalElementWhileDragging",validateDrag:"validateDrag",dragCursor:"dragCursor",dragActiveClass:"dragActiveClass",ghostElementAppendTo:"ghostElementAppendTo",ghostElementTemplate:"ghostElementTemplate",touchStartLongPress:"touchStartLongPress",autoScroll:"autoScroll"},outputs:{dragPointerDown:"dragPointerDown",dragStart:"dragStart",ghostElementCreated:"ghostElementCreated",dragging:"dragging",dragEnd:"dragEnd"},features:[dn]})}}return n})();var Gx=(()=>{class n{static{this.\u0275fac=function(t){return new(t||n)}}static{this.\u0275mod=Gt({type:n})}static{this.\u0275inj=Ut({})}}return n})();var uv=class{constructor(i){this.rawFile=i;let e=i instanceof HTMLInputElement?i.value:i;this[`_createFrom${typeof e=="string"?"FakePath":"Object"}`](e)}_createFromFakePath(i){this.lastModifiedDate=void 0,this.size=void 0,this.type=`like/${i.slice(i.lastIndexOf(".")+1).toLowerCase()}`,this.name=i.slice(i.lastIndexOf("/")+i.lastIndexOf("\\")+2)}_createFromObject(i){this.size=i.size,this.type=i.type,this.name=i.name}},xI=class{constructor(i,e,t){this.url="/",this.headers=[],this.withCredentials=!0,this.formData=[],this.isReady=!1,this.isUploading=!1,this.isUploaded=!1,this.isSuccess=!1,this.isCancel=!1,this.isError=!1,this.progress=0,this.uploader=i,this.some=e,this.options=t,this.file=new uv(e),this._file=e,i.options&&(this.method=i.options.method||"POST",this.alias=i.options.itemAlias||"file"),this.url=i.options.url}upload(){try{this.uploader.uploadItem(this)}catch{this.uploader._onCompleteItem(this,"",0,{}),this.uploader._onErrorItem(this,"",0,{})}}cancel(){this.uploader.cancelItem(this)}remove(){this.uploader.removeFromQueue(this)}onBeforeUpload(){}onBuildForm(i){return{form:i}}onProgress(i){return{progress:i}}onSuccess(i,e,t){return{response:i,status:e,headers:t}}onError(i,e,t){return{response:i,status:e,headers:t}}onCancel(i,e,t){return{response:i,status:e,headers:t}}onComplete(i,e,t){return{response:i,status:e,headers:t}}_onBeforeUpload(){this.isReady=!0,this.isUploading=!0,this.isUploaded=!1,this.isSuccess=!1,this.isCancel=!1,this.isError=!1,this.progress=0,this.onBeforeUpload()}_onBuildForm(i){this.onBuildForm(i)}_onProgress(i){this.progress=i,this.onProgress(i)}_onSuccess(i,e,t){this.isReady=!1,this.isUploading=!1,this.isUploaded=!0,this.isSuccess=!0,this.isCancel=!1,this.isError=!1,this.progress=100,this.index=void 0,this.onSuccess(i,e,t)}_onError(i,e,t){this.isReady=!1,this.isUploading=!1,this.isUploaded=!0,this.isSuccess=!1,this.isCancel=!1,this.isError=!0,this.progress=0,this.index=void 0,this.onError(i,e,t)}_onCancel(i,e,t){this.isReady=!1,this.isUploading=!1,this.isUploaded=!1,this.isSuccess=!1,this.isCancel=!0,this.isError=!1,this.progress=0,this.index=void 0,this.onCancel(i,e,t)}_onComplete(i,e,t){this.onComplete(i,e,t),this.uploader.options.removeAfterUpload&&this.remove()}_prepareToUploading(){this.index=this.index||++this.uploader._nextIndex,this.isReady=!0}},Cie=(()=>{class n{static getMimeClass(e){let t="application";return e?.type&&this.mime_psd.indexOf(e.type)!==-1||e?.type?.match("image.*")?t="image":e?.type?.match("video.*")?t="video":e?.type?.match("audio.*")?t="audio":e?.type==="application/pdf"?t="pdf":e?.type&&this.mime_compress.indexOf(e.type)!==-1?t="compress":e?.type&&this.mime_doc.indexOf(e.type)!==-1?t="doc":e?.type&&this.mime_xsl.indexOf(e.type)!==-1?t="xls":e?.type&&this.mime_ppt.indexOf(e.type)!==-1&&(t="ppt"),t==="application"&&e?.name&&(t=this.fileTypeDetection(e.name)),t}static fileTypeDetection(e){let t={jpg:"image",jpeg:"image",tif:"image",psd:"image",bmp:"image",png:"image",nef:"image",tiff:"image",cr2:"image",dwg:"image",cdr:"image",ai:"image",indd:"image",pin:"image",cdp:"image",skp:"image",stp:"image","3dm":"image",mp3:"audio",wav:"audio",wma:"audio",mod:"audio",m4a:"audio",compress:"compress",zip:"compress",rar:"compress","7z":"compress",lz:"compress",z01:"compress",bz2:"compress",gz:"compress",pdf:"pdf",xls:"xls",xlsx:"xls",ods:"xls",mp4:"video",avi:"video",wmv:"video",mpg:"video",mts:"video",flv:"video","3gp":"video",vob:"video",m4v:"video",mpeg:"video",m2ts:"video",mov:"video",doc:"doc",docx:"doc",eps:"doc",txt:"doc",odt:"doc",rtf:"doc",ppt:"ppt",pptx:"ppt",pps:"ppt",ppsx:"ppt",odp:"ppt"},o=e.split(".");if(o.length<2)return"application";let r=o[o.length-1].toLowerCase();return t[r]===void 0?"application":t[r]}}return n.mime_doc=["application/msword","application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/vnd.openxmlformats-officedocument.wordprocessingml.template","application/vnd.ms-word.document.macroEnabled.12","application/vnd.ms-word.template.macroEnabled.12"],n.mime_xsl=["application/vnd.ms-excel","application/vnd.ms-excel","application/vnd.ms-excel","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","application/vnd.openxmlformats-officedocument.spreadsheetml.template","application/vnd.ms-excel.sheet.macroEnabled.12","application/vnd.ms-excel.template.macroEnabled.12","application/vnd.ms-excel.addin.macroEnabled.12","application/vnd.ms-excel.sheet.binary.macroEnabled.12"],n.mime_ppt=["application/vnd.ms-powerpoint","application/vnd.ms-powerpoint","application/vnd.ms-powerpoint","application/vnd.ms-powerpoint","application/vnd.openxmlformats-officedocument.presentationml.presentation","application/vnd.openxmlformats-officedocument.presentationml.template","application/vnd.openxmlformats-officedocument.presentationml.slideshow","application/vnd.ms-powerpoint.addin.macroEnabled.12","application/vnd.ms-powerpoint.presentation.macroEnabled.12","application/vnd.ms-powerpoint.presentation.macroEnabled.12","application/vnd.ms-powerpoint.slideshow.macroEnabled.12"],n.mime_psd=["image/photoshop","image/x-photoshop","image/psd","application/photoshop","application/psd","zz-application/zz-winassoc-psd"],n.mime_compress=["application/x-gtar","application/x-gcompress","application/compress","application/x-tar","application/x-rar-compressed","application/octet-stream","application/x-zip-compressed","application/zip-compressed","application/x-7z-compressed","application/gzip","application/x-bzip2"],n})();function bie(n){return File&&n instanceof File}var Wa=class{constructor(i){this.isUploading=!1,this.queue=[],this.progress=0,this._nextIndex=0,this.options={autoUpload:!1,isHTML5:!0,filters:[],removeAfterUpload:!1,disableMultipart:!1,formatDataFunction:e=>e._file,formatDataFunctionIsAsync:!1,url:""},this.setOptions(i),this.response=new _e}setOptions(i){this.options=Object.assign(this.options,i),this.authToken=this.options.authToken,this.authTokenHeader=this.options.authTokenHeader||"Authorization",this.autoUpload=this.options.autoUpload,this.options.filters?.unshift({name:"queueLimit",fn:this._queueLimitFilter}),this.options.maxFileSize&&this.options.filters?.unshift({name:"fileSize",fn:this._fileSizeFilter}),this.options.allowedFileType&&this.options.filters?.unshift({name:"fileType",fn:this._fileTypeFilter}),this.options.allowedMimeType&&this.options.filters?.unshift({name:"mimeType",fn:this._mimeTypeFilter});for(let e=0;e{o||(o=this.options);let h=new uv(p);if(this._isValidFile(h,a,o)){let g=new xI(this,p,o);m.push(g),this.queue.push(g),this._onAfterAddingFile(g)}else if(this._failFilterIndex){let g=a[this._failFilterIndex];this._onWhenAddingFileFailed(h,g,o)}}),this.queue.length!==c&&(this._onAfterAddingAll(m),this.progress=this._getTotalProgress()),this._render(),this.options.autoUpload&&this.uploadAll()}removeFromQueue(i){let e=this.getIndexOfItem(i),t=this.queue[e];t.isUploading&&t.cancel(),this.queue.splice(e,1),this.progress=this._getTotalProgress()}clearQueue(){for(;this.queue.length;)this.queue[0].remove();this.progress=0}uploadItem(i){let e=this.getIndexOfItem(i),t=this.queue[e],o=this.options.isHTML5?"_xhrTransport":"_iframeTransport";t._prepareToUploading(),!this.isUploading&&(this.isUploading=!0,this[o](t))}cancelItem(i){let e=this.getIndexOfItem(i),t=this.queue[e],o=this.options.isHTML5?t._xhr:t._form;t&&t.isUploading&&o.abort()}uploadAll(){let i=this.getNotUploadedItems().filter(e=>!e.isUploading);i.length&&(i.map(e=>e._prepareToUploading()),i[0].upload())}cancelAll(){this.getNotUploadedItems().map(e=>e.cancel())}isFile(i){return bie(i)}isFileLikeObject(i){return i instanceof uv}getIndexOfItem(i){return typeof i=="number"?i:this.queue.indexOf(i)}getNotUploadedItems(){return this.queue.filter(i=>!i.isUploaded)}getReadyItems(){return this.queue.filter(i=>i.isReady&&!i.isUploading).sort((i,e)=>i.index-e.index)}onAfterAddingAll(i){return{fileItems:i}}onBuildItemForm(i,e){return{fileItem:i,form:e}}onAfterAddingFile(i){return{fileItem:i}}onWhenAddingFileFailed(i,e,t){return{item:i,filter:e,options:t}}onBeforeUploadItem(i){return{fileItem:i}}onProgressItem(i,e){return{fileItem:i,progress:e}}onProgressAll(i){return{progress:i}}onSuccessItem(i,e,t,o){return{item:i,response:e,status:t,headers:o}}onErrorItem(i,e,t,o){return{item:i,response:e,status:t,headers:o}}onCancelItem(i,e,t,o){return{item:i,response:e,status:t,headers:o}}onCompleteItem(i,e,t,o){return{item:i,response:e,status:t,headers:o}}onCompleteAll(){}_mimeTypeFilter(i){return!(i?.type&&this.options.allowedMimeType&&this.options.allowedMimeType?.indexOf(i.type)===-1)}_fileSizeFilter(i){return!(this.options.maxFileSize&&i.size>this.options.maxFileSize)}_fileTypeFilter(i){return!(this.options.allowedFileType&&this.options.allowedFileType.indexOf(Cie.getMimeClass(i))===-1)}_onErrorItem(i,e,t,o){i._onError(e,t,o),this.onErrorItem(i,e,t,o)}_onCompleteItem(i,e,t,o){i._onComplete(e,t,o),this.onCompleteItem(i,e,t,o);let r=this.getReadyItems()[0];if(this.isUploading=!1,r){r.upload();return}this.onCompleteAll(),this.progress=this._getTotalProgress(),this._render()}_headersGetter(i){return e=>e?i[e.toLowerCase()]||void 0:i}_xhrTransport(i){let e=this,t=i._xhr=new XMLHttpRequest,o;if(this._onBeforeUploadItem(i),typeof i._file.size!="number")throw new TypeError("The file specified is no longer valid");if(this.options.disableMultipart)this.options.formatDataFunction&&(o=this.options.formatDataFunction(i));else{o=new FormData,this._onBuildItemForm(i,o);let r=()=>o.append(i.alias,i._file,i.file.name);this.options.parametersBeforeFiles||r(),this.options.additionalParameter!==void 0&&Object.keys(this.options.additionalParameter).forEach(a=>{let c=this.options.additionalParameter?.[a];typeof c=="string"&&c.indexOf("{{file_name}}")>=0&&i.file?.name&&(c=c.replace("{{file_name}}",i.file.name)),o.append(a,c)}),r&&this.options.parametersBeforeFiles&&r()}if(t.upload.onprogress=r=>{let a=Math.round(r.lengthComputable?r.loaded*100/r.total:0);this._onProgressItem(i,a)},t.onload=()=>{let r=this._parseHeaders(t.getAllResponseHeaders()),a=this._transformResponse(t.response,r),m=`_on${this._isSuccessCode(t.status)?"Success":"Error"}Item`;this[m](i,a,t.status,r),this._onCompleteItem(i,a,t.status,r)},t.onerror=()=>{let r=this._parseHeaders(t.getAllResponseHeaders()),a=this._transformResponse(t.response,r);this._onErrorItem(i,a,t.status,r),this._onCompleteItem(i,a,t.status,r)},t.onabort=()=>{let r=this._parseHeaders(t.getAllResponseHeaders()),a=this._transformResponse(t.response,r);this._onCancelItem(i,a,t.status,r),this._onCompleteItem(i,a,t.status,r)},i.method&&i.url&&t.open(i.method,i.url,!0),t.withCredentials=i.withCredentials,this.options.headers)for(let r of this.options.headers)t.setRequestHeader(r.name,r.value);if(i.headers.length)for(let r of i.headers)t.setRequestHeader(r.name,r.value);this.authToken&&this.authTokenHeader&&t.setRequestHeader(this.authTokenHeader,this.authToken),t.onreadystatechange=function(){t.readyState==XMLHttpRequest.DONE&&e.response.emit(t.responseText)},this.options.formatDataFunctionIsAsync?o.then(r=>t.send(JSON.stringify(r))):t.send(o),this._render()}_getTotalProgress(i=0){if(this.options.removeAfterUpload)return i;let e=this.getNotUploadedItems().length,t=e?this.queue.length-e:this.queue.length,o=100/this.queue.length,r=i*o/100;return Math.round(t*o+r)}_getFilters(i){if(!i)return this.options?.filters||[];if(Array.isArray(i))return i;if(typeof i=="string"){let e=i.match(/[^\s,]+/g);return this.options?.filters||[].filter(t=>e?.indexOf(t.name)!==-1)}return this.options?.filters||[]}_render(){}_queueLimitFilter(){return this.options.queueLimit===void 0||this.queue.length(this._failFilterIndex&&this._failFilterIndex++,o.fn.call(this,i,t))):!0}_isSuccessCode(i){return i>=200&&i<300||i===304}_transformResponse(i,e){return i}_parseHeaders(i){let e={},t,o,r;return i&&i.split(` -`).map(a=>{r=a.indexOf(":"),t=a.slice(0,r).trim().toLowerCase(),o=a.slice(r+1).trim(),t&&(e[t]=e[t]?e[t]+", "+o:o)}),e}_onWhenAddingFileFailed(i,e,t){this.onWhenAddingFileFailed(i,e,t)}_onAfterAddingFile(i){this.onAfterAddingFile(i)}_onAfterAddingAll(i){this.onAfterAddingAll(i)}_onBeforeUploadItem(i){i._onBeforeUpload(),this.onBeforeUploadItem(i)}_onBuildItemForm(i,e){i._onBuildForm(e),this.onBuildItemForm(i,e)}_onProgressItem(i,e){let t=this._getTotalProgress(e);this.progress=t,i._onProgress(e),this.onProgressItem(i,e),this.onProgressAll(t),this._render()}_onSuccessItem(i,e,t,o){i._onSuccess(e,t,o),this.onSuccessItem(i,e,t,o)}_onCancelItem(i,e,t,o){i._onCancel(e,t,o),this.onCancelItem(i,e,t,o)}};var Rc=(()=>{class n{constructor(e){this.onFileSelected=new _e,this.element=e}getOptions(){return this.uploader?.options}getFilters(){return""}isEmptyAfterSelection(){return!!this.element.nativeElement.attributes.multiple}onChange(){let e=this.element.nativeElement.files,t=this.getOptions(),o=this.getFilters();this.uploader?.addToQueue(e,t,o),this.onFileSelected.emit(e),this.isEmptyAfterSelection()&&(this.element.nativeElement.value="")}}return n.\u0275fac=function(e){return new(e||n)(rt(Qt))},n.\u0275dir=ft({type:n,selectors:[["","ng2FileSelect",""]],hostBindings:function(e,t){e&1&&_("change",function(){return t.onChange()})},inputs:{uploader:"uploader"},outputs:{onFileSelected:"onFileSelected"},standalone:!1}),n})(),gl=(()=>{class n{}return n.\u0275fac=function(e){return new(e||n)},n.\u0275mod=Gt({type:n}),n.\u0275inj=Ut({imports:[ne]}),n})();var Qn="primary",Tv=Symbol("RouteTitle"),kI=class{params;constructor(i){this.params=i||{}}has(i){return Object.prototype.hasOwnProperty.call(this.params,i)}get(i){if(this.has(i)){let e=this.params[i];return Array.isArray(e)?e[0]:e}return null}getAll(i){if(this.has(i)){let e=this.params[i];return Array.isArray(e)?e:[e]}return[]}get keys(){return Object.keys(this.params)}};function Du(n){return new kI(n)}function yI(n,i,e){for(let t=0;tn.length||e.pathMatch==="full"&&(i.hasChildren()||t.lengthn.length||e.pathMatch==="full"&&i.hasChildren()&&e.path!=="**")return null;let c={};return!yI(r,n.slice(0,r.length),c)||!yI(a,n.slice(n.length-a.length),c)?null:{consumed:n,posParams:c}}function Kx(n){return new Promise((i,e)=>{n.pipe(dd()).subscribe({next:t=>i(t),error:t=>e(t)})})}function xie(n,i){if(n.length!==i.length)return!1;for(let e=0;et[r]===o)}else return n===i}function yie(n){return n.length>0?n[n.length-1]:null}function Pu(n){return Zd(n)?n:HN(n)?nr(Promise.resolve(n)):_t(n)}function C7(n){return Zd(n)?Kx(n):Promise.resolve(n)}var Sie={exact:x7,subset:y7},b7={exact:wie,subset:Mie,ignored:()=>!0},jI={paths:"exact",fragment:"ignored",matrixParams:"ignored",queryParams:"exact"},Cv={paths:"subset",fragment:"ignored",matrixParams:"ignored",queryParams:"subset"};function $I(n,i,e){let t=n instanceof qa?n:i.parseUrl(n);return mn(()=>EI(i.lastSuccessfulNavigation()?.finalUrl??new qa,t,W(W({},Cv),e)))}function EI(n,i,e){return Sie[e.paths](n.root,i.root,e.matrixParams)&&b7[e.queryParams](n.queryParams,i.queryParams)&&!(e.fragment==="exact"&&n.fragment!==i.fragment)}function wie(n,i){return Fc(n,i)}function x7(n,i,e){if(!Eu(n.segments,i.segments)||!Qx(n.segments,i.segments,e)||n.numberOfChildren!==i.numberOfChildren)return!1;for(let t in i.children)if(!n.children[t]||!x7(n.children[t],i.children[t],e))return!1;return!0}function Mie(n,i){return Object.keys(i).length<=Object.keys(n).length&&Object.keys(i).every(e=>v7(n[e],i[e]))}function y7(n,i,e){return S7(n,i,i.segments,e)}function S7(n,i,e,t){if(n.segments.length>e.length){let o=n.segments.slice(0,e.length);return!(!Eu(o,e)||i.hasChildren()||!Qx(o,e,t))}else if(n.segments.length===e.length){if(!Eu(n.segments,e)||!Qx(n.segments,e,t))return!1;for(let o in i.children)if(!n.children[o]||!y7(n.children[o],i.children[o],t))return!1;return!0}else{let o=e.slice(0,n.segments.length),r=e.slice(n.segments.length);return!Eu(n.segments,o)||!Qx(n.segments,o,t)||!n.children[Qn]?!1:S7(n.children[Qn],i,r,t)}}function Qx(n,i,e){return i.every((t,o)=>b7[e](n[o].parameters,t.parameters))}var qa=class{root;queryParams;fragment;_queryParamMap;constructor(i=new Yi([],{}),e={},t=null){this.root=i,this.queryParams=e,this.fragment=t}get queryParamMap(){return this._queryParamMap??=Du(this.queryParams),this._queryParamMap}toString(){return Eie.serialize(this)}},Yi=class{segments;children;parent=null;constructor(i,e){this.segments=i,this.children=e,Object.values(e).forEach(t=>t.parent=this)}hasChildren(){return this.numberOfChildren>0}get numberOfChildren(){return Object.keys(this.children).length}toString(){return Xx(this)}},Rm=class{path;parameters;_parameterMap;constructor(i,e){this.path=i,this.parameters=e}get parameterMap(){return this._parameterMap??=Du(this.parameters),this._parameterMap}toString(){return M7(this)}};function kie(n,i){return Eu(n,i)&&n.every((e,t)=>Fc(e.parameters,i[t].parameters))}function Eu(n,i){return n.length!==i.length?!1:n.every((e,t)=>e.path===i[t].path)}function Tie(n,i){let e=[];return Object.entries(n.children).forEach(([t,o])=>{t===Qn&&(e=e.concat(i(o,t)))}),Object.entries(n.children).forEach(([t,o])=>{t!==Qn&&(e=e.concat(i(o,t)))}),e}var Lm=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275prov=K({token:n,factory:()=>new Pd,providedIn:"root"})}return n})(),Pd=class{parse(i){let e=new PI(i);return new qa(e.parseRootSegment(),e.parseQueryParams(),e.parseFragment())}serialize(i){let e=`/${fv(i.root,!0)}`,t=Iie(i.queryParams),o=typeof i.fragment=="string"?`#${Die(i.fragment)}`:"";return`${e}${t}${o}`}},Eie=new Pd;function Xx(n){return n.segments.map(i=>M7(i)).join("/")}function fv(n,i){if(!n.hasChildren())return Xx(n);if(i){let e=n.children[Qn]?fv(n.children[Qn],!1):"",t=[];return Object.entries(n.children).forEach(([o,r])=>{o!==Qn&&t.push(`${o}:${fv(r,!1)}`)}),t.length>0?`${e}(${t.join("//")})`:e}else{let e=Tie(n,(t,o)=>o===Qn?[fv(n.children[Qn],!1)]:[`${o}:${fv(t,!1)}`]);return Object.keys(n.children).length===1&&n.children[Qn]!=null?`${Xx(n)}/${e[0]}`:`${Xx(n)}/(${e.join("//")})`}}function w7(n){return encodeURIComponent(n).replace(/%40/g,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",")}function Wx(n){return w7(n).replace(/%3B/gi,";")}function Die(n){return encodeURI(n)}function DI(n){return w7(n).replace(/\(/g,"%28").replace(/\)/g,"%29").replace(/%26/gi,"&")}function Yx(n){return decodeURIComponent(n)}function m7(n){return Yx(n.replace(/\+/g,"%20"))}function M7(n){return`${DI(n.path)}${Pie(n.parameters)}`}function Pie(n){return Object.entries(n).map(([i,e])=>`;${DI(i)}=${DI(e)}`).join("")}function Iie(n){let i=Object.entries(n).map(([e,t])=>Array.isArray(t)?t.map(o=>`${Wx(e)}=${Wx(o)}`).join("&"):`${Wx(e)}=${Wx(t)}`).filter(e=>e);return i.length?`?${i.join("&")}`:""}var Aie=/^[^\/()?;#]+/;function SI(n){let i=n.match(Aie);return i?i[0]:""}var Oie=/^[^\/()?;=#]+/;function Nie(n){let i=n.match(Oie);return i?i[0]:""}var Rie=/^[^=?&#]+/;function Fie(n){let i=n.match(Rie);return i?i[0]:""}var Lie=/^[^&#]+/;function Bie(n){let i=n.match(Lie);return i?i[0]:""}var PI=class{url;remaining;constructor(i){this.url=i,this.remaining=i}parseRootSegment(){return this.consumeOptional("/"),this.remaining===""||this.peekStartsWith("?")||this.peekStartsWith("#")?new Yi([],{}):new Yi([],this.parseChildren())}parseQueryParams(){let i={};if(this.consumeOptional("?"))do this.parseQueryParam(i);while(this.consumeOptional("&"));return i}parseFragment(){return this.consumeOptional("#")?decodeURIComponent(this.remaining):null}parseChildren(i=0){if(i>50)throw new fn(4010,!1);if(this.remaining==="")return{};this.consumeOptional("/");let e=[];for(this.peekStartsWith("(")||e.push(this.parseSegment());this.peekStartsWith("/")&&!this.peekStartsWith("//")&&!this.peekStartsWith("/(");)this.capture("/"),e.push(this.parseSegment());let t={};this.peekStartsWith("/(")&&(this.capture("/"),t=this.parseParens(!0,i));let o={};return this.peekStartsWith("(")&&(o=this.parseParens(!1,i)),(e.length>0||Object.keys(t).length>0)&&(o[Qn]=new Yi(e,t)),o}parseSegment(){let i=SI(this.remaining);if(i===""&&this.peekStartsWith(";"))throw new fn(4009,!1);return this.capture(i),new Rm(Yx(i),this.parseMatrixParams())}parseMatrixParams(){let i={};for(;this.consumeOptional(";");)this.parseParam(i);return i}parseParam(i){let e=Nie(this.remaining);if(!e)return;this.capture(e);let t="";if(this.consumeOptional("=")){let o=SI(this.remaining);o&&(t=o,this.capture(t))}i[Yx(e)]=Yx(t)}parseQueryParam(i){let e=Fie(this.remaining);if(!e)return;this.capture(e);let t="";if(this.consumeOptional("=")){let a=Bie(this.remaining);a&&(t=a,this.capture(t))}let o=m7(e),r=m7(t);if(i.hasOwnProperty(o)){let a=i[o];Array.isArray(a)||(a=[a],i[o]=a),a.push(r)}else i[o]=r}parseParens(i,e){let t={};for(this.capture("(");!this.consumeOptional(")")&&this.remaining.length>0;){let o=SI(this.remaining),r=this.remaining[o.length];if(r!=="/"&&r!==")"&&r!==";")throw new fn(4010,!1);let a;o.indexOf(":")>-1?(a=o.slice(0,o.indexOf(":")),this.capture(a),this.capture(":")):i&&(a=Qn);let c=this.parseChildren(e+1);t[a??Qn]=Object.keys(c).length===1&&c[Qn]?c[Qn]:new Yi([],c),this.consumeOptional("//")}return t}peekStartsWith(i){return this.remaining.startsWith(i)}consumeOptional(i){return this.peekStartsWith(i)?(this.remaining=this.remaining.substring(i.length),!0):!1}capture(i){if(!this.consumeOptional(i))throw new fn(4011,!1)}};function k7(n){return n.segments.length>0?new Yi([],{[Qn]:n}):n}function T7(n){let i={};for(let[t,o]of Object.entries(n.children)){let r=T7(o);if(t===Qn&&r.segments.length===0&&r.hasChildren())for(let[a,c]of Object.entries(r.children))i[a]=c;else(r.segments.length>0||r.hasChildren())&&(i[t]=r)}let e=new Yi(n.segments,i);return Vie(e)}function Vie(n){if(n.numberOfChildren===1&&n.children[Qn]){let i=n.children[Qn];return new Yi(n.segments.concat(i.segments),i.children)}return n}function Fm(n){return n instanceof qa}function E7(n,i,e=null,t=null,o=new Pd){let r=D7(n);return P7(r,i,e,t,o)}function D7(n){let i;function e(r){let a={};for(let m of r.children){let p=e(m);a[m.outlet]=p}let c=new Yi(r.url,a);return r===n&&(i=c),c}let t=e(n.root),o=k7(t);return i??o}function P7(n,i,e,t,o){let r=n;for(;r.parent;)r=r.parent;if(i.length===0)return wI(r,r,r,e,t,o);let a=zie(i);if(a.toRoot())return wI(r,r,new Yi([],{}),e,t,o);let c=jie(a,r,n),m=c.processChildren?_v(c.segmentGroup,c.index,a.commands):A7(c.segmentGroup,c.index,a.commands);return wI(r,c.segmentGroup,m,e,t,o)}function Zx(n){return typeof n=="object"&&n!=null&&!n.outlets&&!n.segmentPath}function bv(n){return typeof n=="object"&&n!=null&&n.outlets}function p7(n,i,e){n||="\u0275";let t=new qa;return t.queryParams={[n]:i},e.parse(e.serialize(t)).queryParams[n]}function wI(n,i,e,t,o,r){let a={};for(let[p,h]of Object.entries(t??{}))a[p]=Array.isArray(h)?h.map(g=>p7(p,g,r)):p7(p,h,r);let c;n===i?c=e:c=I7(n,i,e);let m=k7(T7(c));return new qa(m,a,o)}function I7(n,i,e){let t={};return Object.entries(n.children).forEach(([o,r])=>{r===i?t[o]=e:t[o]=I7(r,i,e)}),new Yi(n.segments,t)}var Jx=class{isAbsolute;numberOfDoubleDots;commands;constructor(i,e,t){if(this.isAbsolute=i,this.numberOfDoubleDots=e,this.commands=t,i&&t.length>0&&Zx(t[0]))throw new fn(4003,!1);let o=t.find(bv);if(o&&o!==yie(t))throw new fn(4004,!1)}toRoot(){return this.isAbsolute&&this.commands.length===1&&this.commands[0]=="/"}};function zie(n){if(typeof n[0]=="string"&&n.length===1&&n[0]==="/")return new Jx(!0,0,n);let i=0,e=!1,t=n.reduce((o,r,a)=>{if(typeof r=="object"&&r!=null){if(r.outlets){let c={};return Object.entries(r.outlets).forEach(([m,p])=>{c[m]=typeof p=="string"?p.split("/"):p}),[...o,{outlets:c}]}if(r.segmentPath)return[...o,r.segmentPath]}return typeof r!="string"?[...o,r]:a===0?(r.split("/").forEach((c,m)=>{m==0&&c==="."||(m==0&&c===""?e=!0:c===".."?i++:c!=""&&o.push(c))}),o):[...o,r]},[]);return new Jx(e,i,t)}var yf=class{segmentGroup;processChildren;index;constructor(i,e,t){this.segmentGroup=i,this.processChildren=e,this.index=t}};function jie(n,i,e){if(n.isAbsolute)return new yf(i,!0,0);if(!e)return new yf(i,!1,NaN);if(e.parent===null)return new yf(e,!0,0);let t=Zx(n.commands[0])?0:1,o=e.segments.length-1+t;return $ie(e,o,n.numberOfDoubleDots)}function $ie(n,i,e){let t=n,o=i,r=e;for(;r>o;){if(r-=o,t=t.parent,!t)throw new fn(4005,!1);o=t.segments.length}return new yf(t,!1,o-r)}function Hie(n){return bv(n[0])?n[0].outlets:{[Qn]:n}}function A7(n,i,e){if(n??=new Yi([],{}),n.segments.length===0&&n.hasChildren())return _v(n,i,e);let t=Uie(n,i,e),o=e.slice(t.commandIndex);if(t.match&&t.pathIndexr!==Qn)&&n.children[Qn]&&n.numberOfChildren===1&&n.children[Qn].segments.length===0){let r=_v(n.children[Qn],i,e);return new Yi(n.segments,r.children)}return Object.entries(t).forEach(([r,a])=>{typeof a=="string"&&(a=[a]),a!==null&&(o[r]=A7(n.children[r],i,a))}),Object.entries(n.children).forEach(([r,a])=>{t[r]===void 0&&(o[r]=a)}),new Yi(n.segments,o)}}function Uie(n,i,e){let t=0,o=i,r={match:!1,pathIndex:0,commandIndex:0};for(;o=e.length)return r;let a=n.segments[o],c=e[t];if(bv(c))break;let m=`${c}`,p=t0&&m===void 0)break;if(m&&p&&typeof p=="object"&&p.outlets===void 0){if(!h7(m,p,a))return r;t+=2}else{if(!h7(m,{},a))return r;t++}o++}return{match:!0,pathIndex:o,commandIndex:t}}function II(n,i,e){let t=n.segments.slice(0,i),o=0;for(;o{typeof t=="string"&&(t=[t]),t!==null&&(i[e]=II(new Yi([],{}),0,t))}),i}function u7(n){let i={};return Object.entries(n).forEach(([e,t])=>i[e]=`${t}`),i}function h7(n,i,e){return n==e.path&&Fc(i,e.parameters)}var Sf="imperative",Lr=(function(n){return n[n.NavigationStart=0]="NavigationStart",n[n.NavigationEnd=1]="NavigationEnd",n[n.NavigationCancel=2]="NavigationCancel",n[n.NavigationError=3]="NavigationError",n[n.RoutesRecognized=4]="RoutesRecognized",n[n.ResolveStart=5]="ResolveStart",n[n.ResolveEnd=6]="ResolveEnd",n[n.GuardsCheckStart=7]="GuardsCheckStart",n[n.GuardsCheckEnd=8]="GuardsCheckEnd",n[n.RouteConfigLoadStart=9]="RouteConfigLoadStart",n[n.RouteConfigLoadEnd=10]="RouteConfigLoadEnd",n[n.ChildActivationStart=11]="ChildActivationStart",n[n.ChildActivationEnd=12]="ChildActivationEnd",n[n.ActivationStart=13]="ActivationStart",n[n.ActivationEnd=14]="ActivationEnd",n[n.Scroll=15]="Scroll",n[n.NavigationSkipped=16]="NavigationSkipped",n})(Lr||{}),js=class{id;url;constructor(i,e){this.id=i,this.url=e}},Bc=class extends js{type=Lr.NavigationStart;navigationTrigger;restoredState;constructor(i,e,t="imperative",o=null){super(i,e),this.navigationTrigger=t,this.restoredState=o}toString(){return`NavigationStart(id: ${this.id}, url: '${this.url}')`}},Mr=class extends js{urlAfterRedirects;type=Lr.NavigationEnd;constructor(i,e,t){super(i,e),this.urlAfterRedirects=t}toString(){return`NavigationEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}')`}},wa=(function(n){return n[n.Redirect=0]="Redirect",n[n.SupersededByNewNavigation=1]="SupersededByNewNavigation",n[n.NoDataFromResolver=2]="NoDataFromResolver",n[n.GuardRejected=3]="GuardRejected",n[n.Aborted=4]="Aborted",n})(wa||{}),Mf=(function(n){return n[n.IgnoredSameUrlNavigation=0]="IgnoredSameUrlNavigation",n[n.IgnoredByUrlHandlingStrategy=1]="IgnoredByUrlHandlingStrategy",n})(Mf||{}),fs=class extends js{reason;code;type=Lr.NavigationCancel;constructor(i,e,t,o){super(i,e),this.reason=t,this.code=o}toString(){return`NavigationCancel(id: ${this.id}, url: '${this.url}')`}};function O7(n){return n instanceof fs&&(n.code===wa.Redirect||n.code===wa.SupersededByNewNavigation)}var Vc=class extends js{reason;code;type=Lr.NavigationSkipped;constructor(i,e,t,o){super(i,e),this.reason=t,this.code=o}},Id=class extends js{error;target;type=Lr.NavigationError;constructor(i,e,t,o){super(i,e),this.error=t,this.target=o}toString(){return`NavigationError(id: ${this.id}, url: '${this.url}', error: ${this.error})`}},xv=class extends js{urlAfterRedirects;state;type=Lr.RoutesRecognized;constructor(i,e,t,o){super(i,e),this.urlAfterRedirects=t,this.state=o}toString(){return`RoutesRecognized(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`}},ey=class extends js{urlAfterRedirects;state;type=Lr.GuardsCheckStart;constructor(i,e,t,o){super(i,e),this.urlAfterRedirects=t,this.state=o}toString(){return`GuardsCheckStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`}},ty=class extends js{urlAfterRedirects;state;shouldActivate;type=Lr.GuardsCheckEnd;constructor(i,e,t,o,r){super(i,e),this.urlAfterRedirects=t,this.state=o,this.shouldActivate=r}toString(){return`GuardsCheckEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state}, shouldActivate: ${this.shouldActivate})`}},ny=class extends js{urlAfterRedirects;state;type=Lr.ResolveStart;constructor(i,e,t,o){super(i,e),this.urlAfterRedirects=t,this.state=o}toString(){return`ResolveStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`}},iy=class extends js{urlAfterRedirects;state;type=Lr.ResolveEnd;constructor(i,e,t,o){super(i,e),this.urlAfterRedirects=t,this.state=o}toString(){return`ResolveEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`}},oy=class{route;type=Lr.RouteConfigLoadStart;constructor(i){this.route=i}toString(){return`RouteConfigLoadStart(path: ${this.route.path})`}},ry=class{route;type=Lr.RouteConfigLoadEnd;constructor(i){this.route=i}toString(){return`RouteConfigLoadEnd(path: ${this.route.path})`}},ay=class{snapshot;type=Lr.ChildActivationStart;constructor(i){this.snapshot=i}toString(){return`ChildActivationStart(path: '${this.snapshot.routeConfig&&this.snapshot.routeConfig.path||""}')`}},sy=class{snapshot;type=Lr.ChildActivationEnd;constructor(i){this.snapshot=i}toString(){return`ChildActivationEnd(path: '${this.snapshot.routeConfig&&this.snapshot.routeConfig.path||""}')`}},ly=class{snapshot;type=Lr.ActivationStart;constructor(i){this.snapshot=i}toString(){return`ActivationStart(path: '${this.snapshot.routeConfig&&this.snapshot.routeConfig.path||""}')`}},cy=class{snapshot;type=Lr.ActivationEnd;constructor(i){this.snapshot=i}toString(){return`ActivationEnd(path: '${this.snapshot.routeConfig&&this.snapshot.routeConfig.path||""}')`}},kf=class{routerEvent;position;anchor;scrollBehavior;type=Lr.Scroll;constructor(i,e,t,o){this.routerEvent=i,this.position=e,this.anchor=t,this.scrollBehavior=o}toString(){let i=this.position?`${this.position[0]}, ${this.position[1]}`:null;return`Scroll(anchor: '${this.anchor}', position: '${i}')`}},Tf=class{},yv=class{},Ef=class{url;navigationBehaviorOptions;constructor(i,e){this.url=i,this.navigationBehaviorOptions=e}};function Wie(n){return!(n instanceof Tf)&&!(n instanceof Ef)&&!(n instanceof yv)}var dy=class{rootInjector;outlet=null;route=null;children;attachRef=null;get injector(){return this.route?.snapshot._environmentInjector??this.rootInjector}constructor(i){this.rootInjector=i,this.children=new Iu(this.rootInjector)}},Iu=(()=>{class n{rootInjector;contexts=new Map;constructor(e){this.rootInjector=e}onChildOutletCreated(e,t){let o=this.getOrCreateContext(e);o.outlet=t,this.contexts.set(e,o)}onChildOutletDestroyed(e){let t=this.getContext(e);t&&(t.outlet=null,t.attachRef=null)}onOutletDeactivated(){let e=this.contexts;return this.contexts=new Map,e}onOutletReAttached(e){this.contexts=e}getOrCreateContext(e){let t=this.getContext(e);return t||(t=new dy(this.rootInjector),this.contexts.set(e,t)),t}getContext(e){return this.contexts.get(e)||null}static \u0275fac=function(t){return new(t||n)(ge(zl))};static \u0275prov=K({token:n,factory:n.\u0275fac,providedIn:"root"})}return n})(),my=class{_root;constructor(i){this._root=i}get root(){return this._root.value}parent(i){let e=this.pathFromRoot(i);return e.length>1?e[e.length-2]:null}children(i){let e=AI(i,this._root);return e?e.children.map(t=>t.value):[]}firstChild(i){let e=AI(i,this._root);return e&&e.children.length>0?e.children[0].value:null}siblings(i){let e=OI(i,this._root);return e.length<2?[]:e[e.length-2].children.map(o=>o.value).filter(o=>o!==i)}pathFromRoot(i){return OI(i,this._root).map(e=>e.value)}};function AI(n,i){if(n===i.value)return i;for(let e of i.children){let t=AI(n,e);if(t)return t}return null}function OI(n,i){if(n===i.value)return[i];for(let e of i.children){let t=OI(n,e);if(t.length)return t.unshift(i),t}return[]}var zs=class{value;children;constructor(i,e){this.value=i,this.children=e}toString(){return`TreeNode(${this.value})`}};function xf(n){let i={};return n&&n.children.forEach(e=>i[e.value.outlet]=e),i}var Sv=class extends my{snapshot;constructor(i,e){super(i),this.snapshot=e,UI(this,i)}toString(){return this.snapshot.toString()}};function N7(n,i){let e=qie(n,i),t=new zt([new Rm("",{})]),o=new zt({}),r=new zt({}),a=new zt({}),c=new zt(""),m=new it(t,o,a,c,r,Qn,n,e.root);return m.snapshot=e.root,new Sv(new zs(m,[]),e)}function qie(n,i){let e={},t={},o={},a=new Df([],e,o,"",t,Qn,n,null,{},i);return new wv("",new zs(a,[]))}var it=class{urlSubject;paramsSubject;queryParamsSubject;fragmentSubject;dataSubject;outlet;component;snapshot;_futureSnapshot;_routerState;_paramMap;_queryParamMap;title;url;params;queryParams;fragment;data;constructor(i,e,t,o,r,a,c,m){this.urlSubject=i,this.paramsSubject=e,this.queryParamsSubject=t,this.fragmentSubject=o,this.dataSubject=r,this.outlet=a,this.component=c,this._futureSnapshot=m,this.title=this.dataSubject?.pipe(xt(p=>p[Tv]))??_t(void 0),this.url=i,this.params=e,this.queryParams=t,this.fragment=o,this.data=r}get routeConfig(){return this._futureSnapshot.routeConfig}get root(){return this._routerState.root}get parent(){return this._routerState.parent(this)}get firstChild(){return this._routerState.firstChild(this)}get children(){return this._routerState.children(this)}get pathFromRoot(){return this._routerState.pathFromRoot(this)}get paramMap(){return this._paramMap??=this.params.pipe(xt(i=>Du(i))),this._paramMap}get queryParamMap(){return this._queryParamMap??=this.queryParams.pipe(xt(i=>Du(i))),this._queryParamMap}toString(){return this.snapshot?this.snapshot.toString():`Future(${this._futureSnapshot})`}};function HI(n,i,e="emptyOnly"){let t,{routeConfig:o}=n;return i!==null&&(e==="always"||o?.path===""||!i.component&&!i.routeConfig?.loadComponent)?t={params:W(W({},i.params),n.params),data:W(W({},i.data),n.data),resolve:W(W(W(W({},n.data),i.data),o?.data),n._resolvedData)}:t={params:W({},n.params),data:W({},n.data),resolve:W(W({},n.data),n._resolvedData??{})},o&&F7(o)&&(t.resolve[Tv]=o.title),t}var Df=class{url;params;queryParams;fragment;data;outlet;component;routeConfig;_resolve;_resolvedData;_routerState;_paramMap;_queryParamMap;_environmentInjector;get title(){return this.data?.[Tv]}constructor(i,e,t,o,r,a,c,m,p,h){this.url=i,this.params=e,this.queryParams=t,this.fragment=o,this.data=r,this.outlet=a,this.component=c,this.routeConfig=m,this._resolve=p,this._environmentInjector=h}get root(){return this._routerState.root}get parent(){return this._routerState.parent(this)}get firstChild(){return this._routerState.firstChild(this)}get children(){return this._routerState.children(this)}get pathFromRoot(){return this._routerState.pathFromRoot(this)}get paramMap(){return this._paramMap??=Du(this.params),this._paramMap}get queryParamMap(){return this._queryParamMap??=Du(this.queryParams),this._queryParamMap}toString(){let i=this.url.map(t=>t.toString()).join("/"),e=this.routeConfig?this.routeConfig.path:"";return`Route(url:'${i}', path:'${e}')`}},wv=class extends my{url;constructor(i,e){super(e),this.url=i,UI(this,e)}toString(){return R7(this._root)}};function UI(n,i){i.value._routerState=n,i.children.forEach(e=>UI(n,e))}function R7(n){let i=n.children.length>0?` { ${n.children.map(R7).join(", ")} } `:"";return`${n.value}${i}`}function MI(n){if(n.snapshot){let i=n.snapshot,e=n._futureSnapshot;n.snapshot=e,Fc(i.queryParams,e.queryParams)||n.queryParamsSubject.next(e.queryParams),i.fragment!==e.fragment&&n.fragmentSubject.next(e.fragment),Fc(i.params,e.params)||n.paramsSubject.next(e.params),xie(i.url,e.url)||n.urlSubject.next(e.url),Fc(i.data,e.data)||n.dataSubject.next(e.data)}else n.snapshot=n._futureSnapshot,n.dataSubject.next(n._futureSnapshot.data)}function NI(n,i){let e=Fc(n.params,i.params)&&kie(n.url,i.url),t=!n.parent!=!i.parent;return e&&!t&&(!n.parent||NI(n.parent,i.parent))}function F7(n){return typeof n.title=="string"||n.title===null}var L7=new $t(""),Ad=(()=>{class n{activated=null;get activatedComponentRef(){return this.activated}_activatedRoute=null;name=Qn;activateEvents=new _e;deactivateEvents=new _e;attachEvents=new _e;detachEvents=new _e;routerOutletData=ae();parentContexts=f(Iu);location=f(Ji);changeDetector=f(X);inputBinder=f(Ev,{optional:!0});supportsBindingToComponentInputs=!0;ngOnChanges(e){if(e.name){let{firstChange:t,previousValue:o}=e.name;if(t)return;this.isTrackedInParentContexts(o)&&(this.deactivate(),this.parentContexts.onChildOutletDestroyed(o)),this.initializeOutletWithName()}}ngOnDestroy(){this.isTrackedInParentContexts(this.name)&&this.parentContexts.onChildOutletDestroyed(this.name),this.inputBinder?.unsubscribeFromRouteData(this)}isTrackedInParentContexts(e){return this.parentContexts.getContext(e)?.outlet===this}ngOnInit(){this.initializeOutletWithName()}initializeOutletWithName(){if(this.parentContexts.onChildOutletCreated(this.name,this),this.activated)return;let e=this.parentContexts.getContext(this.name);e?.route&&(e.attachRef?this.attach(e.attachRef,e.route):this.activateWith(e.route,e.injector))}get isActivated(){return!!this.activated}get component(){if(!this.activated)throw new fn(4012,!1);return this.activated.instance}get activatedRoute(){if(!this.activated)throw new fn(4012,!1);return this._activatedRoute}get activatedRouteData(){return this._activatedRoute?this._activatedRoute.snapshot.data:{}}detach(){if(!this.activated)throw new fn(4012,!1);this.location.detach();let e=this.activated;return this.activated=null,this._activatedRoute=null,this.detachEvents.emit(e.instance),e}attach(e,t){this.activated=e,this._activatedRoute=t,this.location.insert(e.hostView),this.inputBinder?.bindActivatedRouteToOutletComponent(this),this.attachEvents.emit(e.instance)}deactivate(){if(this.activated){let e=this.component;this.activated.destroy(),this.activated=null,this._activatedRoute=null,this.deactivateEvents.emit(e)}}activateWith(e,t){if(this.isActivated)throw new fn(4013,!1);this._activatedRoute=e;let o=this.location,a=e.snapshot.component,c=this.parentContexts.getOrCreateContext(this.name).children,m=new RI(e,c,o.injector,this.routerOutletData);this.activated=o.createComponent(a,{index:o.length,injector:m,environmentInjector:t}),this.changeDetector.markForCheck(),this.inputBinder?.bindActivatedRouteToOutletComponent(this),this.activateEvents.emit(this.activated.instance)}static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["router-outlet"]],inputs:{name:"name",routerOutletData:[1,"routerOutletData"]},outputs:{activateEvents:"activate",deactivateEvents:"deactivate",attachEvents:"attach",detachEvents:"detach"},exportAs:["outlet"],features:[dn]})}return n})(),RI=class{route;childContexts;parent;outletData;constructor(i,e,t,o){this.route=i,this.childContexts=e,this.parent=t,this.outletData=o}get(i,e){return i===it?this.route:i===Iu?this.childContexts:i===L7?this.outletData:this.parent.get(i,e)}},Ev=new $t(""),GI=(()=>{class n{outletDataSubscriptions=new Map;bindActivatedRouteToOutletComponent(e){this.unsubscribeFromRouteData(e),this.subscribeToRouteData(e)}unsubscribeFromRouteData(e){this.outletDataSubscriptions.get(e)?.unsubscribe(),this.outletDataSubscriptions.delete(e)}subscribeToRouteData(e){let{activatedRoute:t}=e,o=ir([t.queryParams,t.params,t.data]).pipe(hn(([r,a,c],m)=>(c=W(W(W({},r),a),c),m===0?_t(c):Promise.resolve(c)))).subscribe(r=>{if(!e.isActivated||!e.activatedComponentRef||e.activatedRoute!==t||t.component===null){this.unsubscribeFromRouteData(e);return}let a=JN(t.component);if(!a){this.unsubscribeFromRouteData(e);return}for(let{templateName:c}of a.inputs)e.activatedComponentRef.setInput(c,r[c])});this.outletDataSubscriptions.set(e,o)}static \u0275fac=function(t){return new(t||n)};static \u0275prov=K({token:n,factory:n.\u0275fac})}return n})(),WI=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275cmp=F({type:n,selectors:[["ng-component"]],exportAs:["emptyRouterOutlet"],decls:1,vars:0,template:function(t,o){t&1&&B(0,"router-outlet")},dependencies:[Ad],encapsulation:2})}return n})();function qI(n){let i=n.children&&n.children.map(qI),e=i?Qe(W({},n),{children:i}):W({},n);return!e.component&&!e.loadComponent&&(i||e.loadChildren)&&e.outlet&&e.outlet!==Qn&&(e.component=WI),e}function Qie(n,i,e){let t=Mv(n,i._root,e?e._root:void 0);return new Sv(t,i)}function Mv(n,i,e){if(e&&n.shouldReuseRoute(i.value,e.value.snapshot)){let t=e.value;t._futureSnapshot=i.value;let o=Xie(n,i,e);return new zs(t,o)}else{if(n.shouldAttach(i.value)){let r=n.retrieve(i.value);if(r!==null){let a=r.route;return a.value._futureSnapshot=i.value,a.children=i.children.map(c=>Mv(n,c)),a}}let t=Yie(i.value),o=i.children.map(r=>Mv(n,r));return new zs(t,o)}}function Xie(n,i,e){return i.children.map(t=>{for(let o of e.children)if(n.shouldReuseRoute(t.value,o.value.snapshot))return Mv(n,t,o);return Mv(n,t)})}function Yie(n){return new it(new zt(n.url),new zt(n.params),new zt(n.queryParams),new zt(n.fragment),new zt(n.data),n.outlet,n.component,n)}var Pf=class{redirectTo;navigationBehaviorOptions;constructor(i,e){this.redirectTo=i,this.navigationBehaviorOptions=e}},B7="ngNavigationCancelingError";function py(n,i){let{redirectTo:e,navigationBehaviorOptions:t}=Fm(i)?{redirectTo:i,navigationBehaviorOptions:void 0}:i,o=V7(!1,wa.Redirect);return o.url=e,o.navigationBehaviorOptions=t,o}function V7(n,i){let e=new Error(`NavigationCancelingError: ${n||""}`);return e[B7]=!0,e.cancellationCode=i,e}function Kie(n){return z7(n)&&Fm(n.url)}function z7(n){return!!n&&n[B7]}var FI=class{routeReuseStrategy;futureState;currState;forwardEvent;inputBindingEnabled;constructor(i,e,t,o,r){this.routeReuseStrategy=i,this.futureState=e,this.currState=t,this.forwardEvent=o,this.inputBindingEnabled=r}activate(i){let e=this.futureState._root,t=this.currState?this.currState._root:null;this.deactivateChildRoutes(e,t,i),MI(this.futureState.root),this.activateChildRoutes(e,t,i)}deactivateChildRoutes(i,e,t){let o=xf(e);i.children.forEach(r=>{let a=r.value.outlet;this.deactivateRoutes(r,o[a],t),delete o[a]}),Object.values(o).forEach(r=>{this.deactivateRouteAndItsChildren(r,t)})}deactivateRoutes(i,e,t){let o=i.value,r=e?e.value:null;if(o===r)if(o.component){let a=t.getContext(o.outlet);a&&this.deactivateChildRoutes(i,e,a.children)}else this.deactivateChildRoutes(i,e,t);else r&&this.deactivateRouteAndItsChildren(e,t)}deactivateRouteAndItsChildren(i,e){i.value.component&&this.routeReuseStrategy.shouldDetach(i.value.snapshot)?this.detachAndStoreRouteSubtree(i,e):this.deactivateRouteAndOutlet(i,e)}detachAndStoreRouteSubtree(i,e){let t=e.getContext(i.value.outlet),o=t&&i.value.component?t.children:e,r=xf(i);for(let a of Object.values(r))this.deactivateRouteAndItsChildren(a,o);if(t&&t.outlet){let a=t.outlet.detach(),c=t.children.onOutletDeactivated();this.routeReuseStrategy.store(i.value.snapshot,{componentRef:a,route:i,contexts:c})}}deactivateRouteAndOutlet(i,e){let t=e.getContext(i.value.outlet),o=t&&i.value.component?t.children:e,r=xf(i);for(let a of Object.values(r))this.deactivateRouteAndItsChildren(a,o);t&&(t.outlet&&(t.outlet.deactivate(),t.children.onOutletDeactivated()),t.attachRef=null,t.route=null)}activateChildRoutes(i,e,t){let o=xf(e);i.children.forEach(r=>{this.activateRoutes(r,o[r.value.outlet],t),this.forwardEvent(new cy(r.value.snapshot))}),i.children.length&&this.forwardEvent(new sy(i.value.snapshot))}activateRoutes(i,e,t){let o=i.value,r=e?e.value:null;if(MI(o),o===r)if(o.component){let a=t.getOrCreateContext(o.outlet);this.activateChildRoutes(i,e,a.children)}else this.activateChildRoutes(i,e,t);else if(o.component){let a=t.getOrCreateContext(o.outlet);if(this.routeReuseStrategy.shouldAttach(o.snapshot)){let c=this.routeReuseStrategy.retrieve(o.snapshot);this.routeReuseStrategy.store(o.snapshot,null),a.children.onOutletReAttached(c.contexts),a.attachRef=c.componentRef,a.route=c.route.value,a.outlet&&a.outlet.attach(c.componentRef,c.route.value),MI(c.route.value),this.activateChildRoutes(i,null,a.children)}else a.attachRef=null,a.route=o,a.outlet&&a.outlet.activateWith(o,a.injector),this.activateChildRoutes(i,null,a.children)}else this.activateChildRoutes(i,null,t)}},uy=class{path;route;constructor(i){this.path=i,this.route=this.path[this.path.length-1]}},wf=class{component;route;constructor(i,e){this.component=i,this.route=e}};function Zie(n,i,e){let t=n._root,o=i?i._root:null;return gv(t,o,e,[t.value])}function Jie(n){let i=n.routeConfig?n.routeConfig.canActivateChild:null;return!i||i.length===0?null:{node:n,guards:i}}function Af(n,i){let e=Symbol(),t=i.get(n,e);return t===e?typeof n=="function"&&!ON(n)?n:i.get(n):t}function gv(n,i,e,t,o={canDeactivateChecks:[],canActivateChecks:[]}){let r=xf(i);return n.children.forEach(a=>{eoe(a,r[a.value.outlet],e,t.concat([a.value]),o),delete r[a.value.outlet]}),Object.entries(r).forEach(([a,c])=>vv(c,e.getContext(a),o)),o}function eoe(n,i,e,t,o={canDeactivateChecks:[],canActivateChecks:[]}){let r=n.value,a=i?i.value:null,c=e?e.getContext(n.value.outlet):null;if(a&&r.routeConfig===a.routeConfig){let m=toe(a,r,r.routeConfig.runGuardsAndResolvers);m?o.canActivateChecks.push(new uy(t)):(r.data=a.data,r._resolvedData=a._resolvedData),r.component?gv(n,i,c?c.children:null,t,o):gv(n,i,e,t,o),m&&c&&c.outlet&&c.outlet.isActivated&&o.canDeactivateChecks.push(new wf(c.outlet.component,a))}else a&&vv(i,c,o),o.canActivateChecks.push(new uy(t)),r.component?gv(n,null,c?c.children:null,t,o):gv(n,null,e,t,o);return o}function toe(n,i,e){if(typeof e=="function")return Ms(i._environmentInjector,()=>e(n,i));switch(e){case"pathParamsChange":return!Eu(n.url,i.url);case"pathParamsOrQueryParamsChange":return!Eu(n.url,i.url)||!Fc(n.queryParams,i.queryParams);case"always":return!0;case"paramsOrQueryParamsChange":return!NI(n,i)||!Fc(n.queryParams,i.queryParams);default:return!NI(n,i)}}function vv(n,i,e){let t=xf(n),o=n.value;Object.entries(t).forEach(([r,a])=>{o.component?i?vv(a,i.children.getContext(r),e):vv(a,null,e):vv(a,i,e)}),o.component?i&&i.outlet&&i.outlet.isActivated?e.canDeactivateChecks.push(new wf(i.outlet.component,o)):e.canDeactivateChecks.push(new wf(null,o)):e.canDeactivateChecks.push(new wf(null,o))}function Dv(n){return typeof n=="function"}function noe(n){return typeof n=="boolean"}function ioe(n){return n&&Dv(n.canLoad)}function ooe(n){return n&&Dv(n.canActivate)}function roe(n){return n&&Dv(n.canActivateChild)}function aoe(n){return n&&Dv(n.canDeactivate)}function soe(n){return n&&Dv(n.canMatch)}function j7(n){return n instanceof TN||n?.name==="EmptyError"}var qx=Symbol("INITIAL_VALUE");function If(){return hn(n=>ir(n.map(i=>i.pipe(Gi(1),xi(qx)))).pipe(xt(i=>{for(let e of i)if(e!==!0){if(e===qx)return qx;if(e===!1||loe(e))return e}return!0}),Yn(i=>i!==qx),Gi(1)))}function loe(n){return Fm(n)||n instanceof Pf}function $7(n){return n.aborted?_t(void 0).pipe(Gi(1)):new Pr(i=>{let e=()=>{i.next(),i.complete()};return n.addEventListener("abort",e),()=>n.removeEventListener("abort",e)})}function H7(n){return tt($7(n))}function coe(n){return vr(i=>{let{targetSnapshot:e,currentSnapshot:t,guards:{canActivateChecks:o,canDeactivateChecks:r}}=i;return r.length===0&&o.length===0?_t(Qe(W({},i),{guardsResult:!0})):doe(r,e,t).pipe(vr(a=>a&&noe(a)?moe(e,o,n):_t(a)),xt(a=>Qe(W({},i),{guardsResult:a})))})}function doe(n,i,e){return nr(n).pipe(vr(t=>goe(t.component,t.route,e,i)),dd(t=>t!==!0,!0))}function moe(n,i,e){return nr(i).pipe(Jd(t=>C_(uoe(t.route.parent,e),poe(t.route,e),foe(n,t.path),hoe(n,t.route))),dd(t=>t!==!0,!0))}function poe(n,i){return n!==null&&i&&i(new ly(n)),_t(!0)}function uoe(n,i){return n!==null&&i&&i(new ay(n)),_t(!0)}function hoe(n,i){let e=i.routeConfig?i.routeConfig.canActivate:null;if(!e||e.length===0)return _t(!0);let t=e.map(o=>sh(()=>{let r=i._environmentInjector,a=Af(o,r),c=ooe(a)?a.canActivate(i,n):Ms(r,()=>a(i,n));return Pu(c).pipe(dd())}));return _t(t).pipe(If())}function foe(n,i){let e=i[i.length-1],o=i.slice(0,i.length-1).reverse().map(r=>Jie(r)).filter(r=>r!==null).map(r=>sh(()=>{let a=r.guards.map(c=>{let m=r.node._environmentInjector,p=Af(c,m),h=roe(p)?p.canActivateChild(e,n):Ms(m,()=>p(e,n));return Pu(h).pipe(dd())});return _t(a).pipe(If())}));return _t(o).pipe(If())}function goe(n,i,e,t){let o=i&&i.routeConfig?i.routeConfig.canDeactivate:null;if(!o||o.length===0)return _t(!0);let r=o.map(a=>{let c=i._environmentInjector,m=Af(a,c),p=aoe(m)?m.canDeactivate(n,i,e,t):Ms(c,()=>m(n,i,e,t));return Pu(p).pipe(dd())});return _t(r).pipe(If())}function _oe(n,i,e,t,o){let r=i.canLoad;if(r===void 0||r.length===0)return _t(!0);let a=r.map(c=>{let m=Af(c,n),p=ioe(m)?m.canLoad(i,e):Ms(n,()=>m(i,e)),h=Pu(p);return o?h.pipe(H7(o)):h});return _t(a).pipe(If(),U7(t))}function U7(n){return MN(yi(i=>{if(typeof i!="boolean")throw py(n,i)}),xt(i=>i===!0))}function voe(n,i,e,t,o,r){let a=i.canMatch;if(!a||a.length===0)return _t(!0);let c=a.map(m=>{let p=Af(m,n),h=soe(p)?p.canMatch(i,e,o):Ms(n,()=>p(i,e,o));return Pu(h).pipe(H7(r))});return _t(c).pipe(If(),U7(t))}var Dd=class n extends Error{segmentGroup;constructor(i){super(),this.segmentGroup=i||null,Object.setPrototypeOf(this,n.prototype)}},kv=class n extends Error{urlTree;constructor(i){super(),this.urlTree=i,Object.setPrototypeOf(this,n.prototype)}};function Coe(n){throw new fn(4e3,!1)}function boe(n){throw V7(!1,wa.GuardRejected)}var LI=class{urlSerializer;urlTree;constructor(i,e){this.urlSerializer=i,this.urlTree=e}async lineralizeSegments(i,e){let t=[],o=e.root;for(;;){if(t=t.concat(o.segments),o.numberOfChildren===0)return t;if(o.numberOfChildren>1||!o.children[Qn])throw Coe(`${i.redirectTo}`);o=o.children[Qn]}}async applyRedirectCommands(i,e,t,o,r){let a=await xoe(e,o,r);if(a instanceof qa)throw new kv(a);let c=this.applyRedirectCreateUrlTree(a,this.urlSerializer.parse(a),i,t);if(a[0]==="/")throw new kv(c);return c}applyRedirectCreateUrlTree(i,e,t,o){let r=this.createSegmentGroup(i,e.root,t,o);return new qa(r,this.createQueryParams(e.queryParams,this.urlTree.queryParams),e.fragment)}createQueryParams(i,e){let t={};return Object.entries(i).forEach(([o,r])=>{if(typeof r=="string"&&r[0]===":"){let c=r.substring(1);t[o]=e[c]}else t[o]=r}),t}createSegmentGroup(i,e,t,o){let r=this.createSegments(i,e.segments,t,o),a={};return Object.entries(e.children).forEach(([c,m])=>{a[c]=this.createSegmentGroup(i,m,t,o)}),new Yi(r,a)}createSegments(i,e,t,o){return e.map(r=>r.path[0]===":"?this.findPosParam(i,r,o):this.findOrReturn(r,t))}findPosParam(i,e,t){let o=t[e.path.substring(1)];if(!o)throw new fn(4001,!1);return o}findOrReturn(i,e){let t=0;for(let o of e){if(o.path===i.path)return e.splice(t),o;t++}return i}};function xoe(n,i,e){if(typeof n=="string")return Promise.resolve(n);let t=n;return Kx(Pu(Ms(e,()=>t(i))))}function yoe(n,i){return n.providers&&!n._injector&&(n._injector=a1(n.providers,i,`Route: ${n.path}`)),n._injector??i}function Lc(n){return n.outlet||Qn}function Soe(n,i){let e=n.filter(t=>Lc(t)===i);return e.push(...n.filter(t=>Lc(t)!==i)),e}var BI={matched:!1,consumedSegments:[],remainingSegments:[],parameters:{},positionalParamSegments:{}};function G7(n){return{routeConfig:n.routeConfig,url:n.url,params:n.params,queryParams:n.queryParams,fragment:n.fragment,data:n.data,outlet:n.outlet,title:n.title,paramMap:n.paramMap,queryParamMap:n.queryParamMap}}function woe(n,i,e,t,o,r,a){let c=W7(n,i,e);if(!c.matched)return _t(c);let m=G7(r(c));return t=yoe(i,t),voe(t,i,e,o,m,a).pipe(xt(p=>p===!0?c:W({},BI)))}function W7(n,i,e){if(i.path==="")return i.pathMatch==="full"&&(n.hasChildren()||e.length>0)?W({},BI):{matched:!0,consumedSegments:[],remainingSegments:e,parameters:{},positionalParamSegments:{}};let o=(i.matcher||_7)(e,n,i);if(!o)return W({},BI);let r={};Object.entries(o.posParams??{}).forEach(([c,m])=>{r[c]=m.path});let a=o.consumed.length>0?W(W({},r),o.consumed[o.consumed.length-1].parameters):r;return{matched:!0,consumedSegments:o.consumed,remainingSegments:e.slice(o.consumed.length),parameters:a,positionalParamSegments:o.posParams??{}}}function f7(n,i,e,t){return e.length>0&&Toe(n,e,t)?{segmentGroup:new Yi(i,koe(t,new Yi(e,n.children))),slicedSegments:[]}:e.length===0&&Eoe(n,e,t)?{segmentGroup:new Yi(n.segments,Moe(n,e,t,n.children)),slicedSegments:e}:{segmentGroup:new Yi(n.segments,n.children),slicedSegments:e}}function Moe(n,i,e,t){let o={};for(let r of e)if(fy(n,i,r)&&!t[Lc(r)]){let a=new Yi([],{});o[Lc(r)]=a}return W(W({},t),o)}function koe(n,i){let e={};e[Qn]=i;for(let t of n)if(t.path===""&&Lc(t)!==Qn){let o=new Yi([],{});e[Lc(t)]=o}return e}function Toe(n,i,e){return e.some(t=>fy(n,i,t)&&Lc(t)!==Qn)}function Eoe(n,i,e){return e.some(t=>fy(n,i,t))}function fy(n,i,e){return(n.hasChildren()||i.length>0)&&e.pathMatch==="full"?!1:e.path===""}function Doe(n,i,e){return i.length===0&&!n.children[e]}var VI=class{};async function Poe(n,i,e,t,o,r,a="emptyOnly",c){return new zI(n,i,e,t,o,a,r,c).recognize()}var Ioe=31,zI=class{injector;configLoader;rootComponentType;config;urlTree;paramsInheritanceStrategy;urlSerializer;abortSignal;applyRedirects;absoluteRedirectCount=0;allowRedirects=!0;constructor(i,e,t,o,r,a,c,m){this.injector=i,this.configLoader=e,this.rootComponentType=t,this.config=o,this.urlTree=r,this.paramsInheritanceStrategy=a,this.urlSerializer=c,this.abortSignal=m,this.applyRedirects=new LI(this.urlSerializer,this.urlTree)}noMatchError(i){return new fn(4002,`'${i.segmentGroup}'`)}async recognize(){let i=f7(this.urlTree.root,[],[],this.config).segmentGroup,{children:e,rootSnapshot:t}=await this.match(i),o=new zs(t,e),r=new wv("",o),a=E7(t,[],this.urlTree.queryParams,this.urlTree.fragment);return a.queryParams=this.urlTree.queryParams,r.url=this.urlSerializer.serialize(a),{state:r,tree:a}}async match(i){let e=new Df([],Object.freeze({}),Object.freeze(W({},this.urlTree.queryParams)),this.urlTree.fragment,Object.freeze({}),Qn,this.rootComponentType,null,{},this.injector);try{return{children:await this.processSegmentGroup(this.injector,this.config,i,Qn,e),rootSnapshot:e}}catch(t){if(t instanceof kv)return this.urlTree=t.urlTree,this.match(t.urlTree.root);throw t instanceof Dd?this.noMatchError(t):t}}async processSegmentGroup(i,e,t,o,r){if(t.segments.length===0&&t.hasChildren())return this.processChildren(i,e,t,r);let a=await this.processSegment(i,e,t,t.segments,o,!0,r);return a instanceof zs?[a]:[]}async processChildren(i,e,t,o){let r=[];for(let m of Object.keys(t.children))m==="primary"?r.unshift(m):r.push(m);let a=[];for(let m of r){let p=t.children[m],h=Soe(e,m),g=await this.processSegmentGroup(i,h,p,m,o);a.push(...g)}let c=q7(a);return Aoe(c),c}async processSegment(i,e,t,o,r,a,c){for(let m of e)try{return await this.processSegmentAgainstRoute(m._injector??i,e,m,t,o,r,a,c)}catch(p){if(p instanceof Dd||j7(p))continue;throw p}if(Doe(t,o,r))return new VI;throw new Dd(t)}async processSegmentAgainstRoute(i,e,t,o,r,a,c,m){if(Lc(t)!==a&&(a===Qn||!fy(o,r,t)))throw new Dd(o);if(t.redirectTo===void 0)return this.matchSegmentAgainstRoute(i,o,t,r,a,m);if(this.allowRedirects&&c)return this.expandSegmentAgainstRouteUsingRedirect(i,o,e,t,r,a,m);throw new Dd(o)}async expandSegmentAgainstRouteUsingRedirect(i,e,t,o,r,a,c){let{matched:m,parameters:p,consumedSegments:h,positionalParamSegments:g,remainingSegments:S}=W7(e,o,r);if(!m)throw new Dd(e);typeof o.redirectTo=="string"&&o.redirectTo[0]==="/"&&(this.absoluteRedirectCount++,this.absoluteRedirectCount>Ioe&&(this.allowRedirects=!1));let x=this.createSnapshot(i,o,r,p,c);if(this.abortSignal.aborted)throw new Error(this.abortSignal.reason);let v=await this.applyRedirects.applyRedirectCommands(h,o.redirectTo,g,G7(x),i),M=await this.applyRedirects.lineralizeSegments(o,v);return this.processSegment(i,t,e,M.concat(S),a,!1,c)}createSnapshot(i,e,t,o,r){let a=new Df(t,o,Object.freeze(W({},this.urlTree.queryParams)),this.urlTree.fragment,Noe(e),Lc(e),e.component??e._loadedComponent??null,e,Roe(e),i),c=HI(a,r,this.paramsInheritanceStrategy);return a.params=Object.freeze(c.params),a.data=Object.freeze(c.data),a}async matchSegmentAgainstRoute(i,e,t,o,r,a){if(this.abortSignal.aborted)throw new Error(this.abortSignal.reason);let c=I=>this.createSnapshot(i,t,I.consumedSegments,I.parameters,a),m=await Kx(woe(e,t,o,i,this.urlSerializer,c,this.abortSignal));if(t.path==="**"&&(e.children={}),!m?.matched)throw new Dd(e);i=t._injector??i;let{routes:p}=await this.getChildConfig(i,t,o),h=t._loadedInjector??i,{parameters:g,consumedSegments:S,remainingSegments:x}=m,v=this.createSnapshot(i,t,S,g,a),{segmentGroup:M,slicedSegments:w}=f7(e,S,x,p);if(w.length===0&&M.hasChildren()){let I=await this.processChildren(h,p,M,v);return new zs(v,I)}if(p.length===0&&w.length===0)return new zs(v,[]);let y=Lc(t)===r,k=await this.processSegment(h,p,M,w,y?Qn:r,!0,v);return new zs(v,k instanceof zs?[k]:[])}async getChildConfig(i,e,t){if(e.children)return{routes:e.children,injector:i};if(e.loadChildren){if(e._loadedRoutes!==void 0){let r=e._loadedNgModuleFactory;return r&&!e._loadedInjector&&(e._loadedInjector=r.create(i).injector),{routes:e._loadedRoutes,injector:e._loadedInjector}}if(this.abortSignal.aborted)throw new Error(this.abortSignal.reason);if(await Kx(_oe(i,e,t,this.urlSerializer,this.abortSignal))){let r=await this.configLoader.loadChildren(i,e);return e._loadedRoutes=r.routes,e._loadedInjector=r.injector,e._loadedNgModuleFactory=r.factory,r}throw boe(e)}return{routes:[],injector:i}}};function Aoe(n){n.sort((i,e)=>i.value.outlet===Qn?-1:e.value.outlet===Qn?1:i.value.outlet.localeCompare(e.value.outlet))}function Ooe(n){let i=n.value.routeConfig;return i&&i.path===""}function q7(n){let i=[],e=new Set;for(let t of n){if(!Ooe(t)){i.push(t);continue}let o=i.find(r=>t.value.routeConfig===r.value.routeConfig);o!==void 0?(o.children.push(...t.children),e.add(o)):i.push(t)}for(let t of e){let o=q7(t.children);i.push(new zs(t.value,o))}return i.filter(t=>!e.has(t))}function Noe(n){return n.data||{}}function Roe(n){return n.resolve||{}}function Foe(n,i,e,t,o,r,a){return vr(async c=>{let{state:m,tree:p}=await Poe(n,i,e,t,c.extractedUrl,o,r,a);return Qe(W({},c),{targetSnapshot:m,urlAfterRedirects:p})})}function Loe(n){return vr(i=>{let{targetSnapshot:e,guards:{canActivateChecks:t}}=i;if(!t.length)return _t(i);let o=new Set(t.map(c=>c.route)),r=new Set;for(let c of o)if(!r.has(c))for(let m of Q7(c))r.add(m);let a=0;return nr(r).pipe(Jd(c=>o.has(c)?Boe(c,e,n):(c.data=HI(c,c.parent,n).resolve,_t(void 0))),yi(()=>a++),x_(1),vr(c=>a===r.size?_t(i):$r))})}function Q7(n){let i=n.children.map(e=>Q7(e)).flat();return[n,...i]}function Boe(n,i,e){let t=n.routeConfig,o=n._resolve;return t?.title!==void 0&&!F7(t)&&(o[Tv]=t.title),sh(()=>(n.data=HI(n,n.parent,e).resolve,Voe(o,n,i).pipe(xt(r=>(n._resolvedData=r,n.data=W(W({},n.data),r),null)))))}function Voe(n,i,e){let t=TI(n);if(t.length===0)return _t({});let o={};return nr(t).pipe(vr(r=>zoe(n[r],i,e).pipe(dd(),yi(a=>{if(a instanceof Pf)throw py(new Pd,a);o[r]=a}))),x_(1),xt(()=>o),Zi(r=>j7(r)?$r:zo(r)))}function zoe(n,i,e){let t=i._environmentInjector,o=Af(n,t),r=o.resolve?o.resolve(i,e):Ms(t,()=>o(i,e));return Pu(r)}function g7(n){return hn(i=>{let e=n(i);return e?nr(e).pipe(xt(()=>i)):_t(i)})}var QI=(()=>{class n{buildTitle(e){let t,o=e.root;for(;o!==void 0;)t=this.getResolvedTitleForRoute(o)??t,o=o.children.find(r=>r.outlet===Qn);return t}getResolvedTitleForRoute(e){return e.data[Tv]}static \u0275fac=function(t){return new(t||n)};static \u0275prov=K({token:n,factory:()=>f(X7),providedIn:"root"})}return n})(),X7=(()=>{class n extends QI{title;constructor(e){super(),this.title=e}updateTitle(e){let t=this.buildTitle(e);t!==void 0&&this.title.setTitle(t)}static \u0275fac=function(t){return new(t||n)(ge(nm))};static \u0275prov=K({token:n,factory:n.\u0275fac,providedIn:"root"})}return n})(),Bm=new $t("",{factory:()=>({})}),Of=new $t(""),gy=(()=>{class n{componentLoaders=new WeakMap;childrenLoaders=new WeakMap;onLoadStartListener;onLoadEndListener;compiler=f(QN);async loadComponent(e,t){if(this.componentLoaders.get(t))return this.componentLoaders.get(t);if(t._loadedComponent)return Promise.resolve(t._loadedComponent);this.onLoadStartListener&&this.onLoadStartListener(t);let o=(async()=>{try{let r=await C7(Ms(e,()=>t.loadComponent())),a=await Z7(K7(r));return this.onLoadEndListener&&this.onLoadEndListener(t),t._loadedComponent=a,a}finally{this.componentLoaders.delete(t)}})();return this.componentLoaders.set(t,o),o}loadChildren(e,t){if(this.childrenLoaders.get(t))return this.childrenLoaders.get(t);if(t._loadedRoutes)return Promise.resolve({routes:t._loadedRoutes,injector:t._loadedInjector});this.onLoadStartListener&&this.onLoadStartListener(t);let o=(async()=>{try{let r=await Y7(t,this.compiler,e,this.onLoadEndListener);return t._loadedRoutes=r.routes,t._loadedInjector=r.injector,t._loadedNgModuleFactory=r.factory,r}finally{this.childrenLoaders.delete(t)}})();return this.childrenLoaders.set(t,o),o}static \u0275fac=function(t){return new(t||n)};static \u0275prov=K({token:n,factory:n.\u0275fac,providedIn:"root"})}return n})();async function Y7(n,i,e,t){let o=await C7(Ms(e,()=>n.loadChildren())),r=await Z7(K7(o)),a;r instanceof zN||Array.isArray(r)?a=r:a=await i.compileModuleAsync(r),t&&t(n);let c,m,p=!1,h;return Array.isArray(a)?(m=a,p=!0):(c=a.create(e).injector,h=a,m=c.get(Of,[],{optional:!0,self:!0}).flat()),{routes:m.map(qI),injector:c,factory:h}}function joe(n){return n&&typeof n=="object"&&"default"in n}function K7(n){return joe(n)?n.default:n}async function Z7(n){return n}var _y=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275prov=K({token:n,factory:()=>f($oe),providedIn:"root"})}return n})(),$oe=(()=>{class n{shouldProcessUrl(e){return!0}extract(e){return e}merge(e,t){return e}static \u0275fac=function(t){return new(t||n)};static \u0275prov=K({token:n,factory:n.\u0275fac,providedIn:"root"})}return n})(),XI=new $t(""),YI=new $t("");function J7(n,i,e){let t=n.get(YI),o=n.get(co);if(!o.startViewTransition||t.skipNextTransition)return t.skipNextTransition=!1,new Promise(p=>setTimeout(p));let r,a=new Promise(p=>{r=p}),c=o.startViewTransition(()=>(r(),Hoe(n)));c.updateCallbackDone.catch(p=>{}),c.ready.catch(p=>{}),c.finished.catch(p=>{});let{onViewTransitionCreated:m}=t;return m&&Ms(n,()=>m({transition:c,from:i,to:e})),a}function Hoe(n){return new Promise(i=>{aa({read:()=>setTimeout(i)},{injector:n})})}var Uoe=()=>{},KI=new $t(""),vy=(()=>{class n{currentNavigation=se(null,{equal:()=>!1});currentTransition=null;lastSuccessfulNavigation=se(null);events=new je;transitionAbortWithErrorSubject=new je;configLoader=f(gy);environmentInjector=f(zl);destroyRef=f(em);urlSerializer=f(Lm);rootContexts=f(Iu);location=f(dc);inputBindingEnabled=f(Ev,{optional:!0})!==null;titleStrategy=f(QI);options=f(Bm,{optional:!0})||{};paramsInheritanceStrategy=this.options.paramsInheritanceStrategy||"emptyOnly";urlHandlingStrategy=f(_y);createViewTransition=f(XI,{optional:!0});navigationErrorHandler=f(KI,{optional:!0});navigationId=0;get hasRequestedNavigation(){return this.navigationId!==0}transitions;afterPreactivation=()=>_t(void 0);rootComponentType=null;destroyed=!1;constructor(){let e=o=>this.events.next(new oy(o)),t=o=>this.events.next(new ry(o));this.configLoader.onLoadEndListener=t,this.configLoader.onLoadStartListener=e,this.destroyRef.onDestroy(()=>{this.destroyed=!0})}complete(){this.transitions?.complete()}handleNavigationRequest(e){let t=++this.navigationId;rr(()=>{this.transitions?.next(Qe(W({},e),{extractedUrl:this.urlHandlingStrategy.extract(e.rawUrl),targetSnapshot:null,targetRouterState:null,guards:{canActivateChecks:[],canDeactivateChecks:[]},guardsResult:null,id:t,routesRecognizeHandler:{},beforeActivateHandler:{}}))})}setupNavigations(e){return this.transitions=new zt(null),this.transitions.pipe(Yn(t=>t!==null),hn(t=>{let o=!1,r=new AbortController,a=()=>!o&&this.currentTransition?.id===t.id;return _t(t).pipe(hn(c=>{if(this.navigationId>t.id)return this.cancelNavigationTransition(t,"",wa.SupersededByNewNavigation),$r;this.currentTransition=t;let m=this.lastSuccessfulNavigation();this.currentNavigation.set({id:c.id,initialUrl:c.rawUrl,extractedUrl:c.extractedUrl,targetBrowserUrl:typeof c.extras.browserUrl=="string"?this.urlSerializer.parse(c.extras.browserUrl):c.extras.browserUrl,trigger:c.source,extras:c.extras,previousNavigation:m?Qe(W({},m),{previousNavigation:null}):null,abort:()=>r.abort(),routesRecognizeHandler:c.routesRecognizeHandler,beforeActivateHandler:c.beforeActivateHandler});let p=!e.navigated||this.isUpdatingInternalState()||this.isUpdatedBrowserUrl(),h=c.extras.onSameUrlNavigation??e.onSameUrlNavigation;if(!p&&h!=="reload")return this.events.next(new Vc(c.id,this.urlSerializer.serialize(c.rawUrl),"",Mf.IgnoredSameUrlNavigation)),c.resolve(!1),$r;if(this.urlHandlingStrategy.shouldProcessUrl(c.rawUrl))return _t(c).pipe(hn(g=>(this.events.next(new Bc(g.id,this.urlSerializer.serialize(g.extractedUrl),g.source,g.restoredState)),g.id!==this.navigationId?$r:Promise.resolve(g))),Foe(this.environmentInjector,this.configLoader,this.rootComponentType,e.config,this.urlSerializer,this.paramsInheritanceStrategy,r.signal),yi(g=>{t.targetSnapshot=g.targetSnapshot,t.urlAfterRedirects=g.urlAfterRedirects,this.currentNavigation.update(S=>(S.finalUrl=g.urlAfterRedirects,S)),this.events.next(new yv)}),hn(g=>nr(t.routesRecognizeHandler.deferredHandle??_t(void 0)).pipe(xt(()=>g))),yi(()=>{let g=new xv(c.id,this.urlSerializer.serialize(c.extractedUrl),this.urlSerializer.serialize(c.urlAfterRedirects),c.targetSnapshot);this.events.next(g)}));if(p&&this.urlHandlingStrategy.shouldProcessUrl(c.currentRawUrl)){let{id:g,extractedUrl:S,source:x,restoredState:v,extras:M}=c,w=new Bc(g,this.urlSerializer.serialize(S),x,v);this.events.next(w);let y=N7(this.rootComponentType,this.environmentInjector).snapshot;return this.currentTransition=t=Qe(W({},c),{targetSnapshot:y,urlAfterRedirects:S,extras:Qe(W({},M),{skipLocationChange:!1,replaceUrl:!1})}),this.currentNavigation.update(k=>(k.finalUrl=S,k)),_t(t)}else return this.events.next(new Vc(c.id,this.urlSerializer.serialize(c.extractedUrl),"",Mf.IgnoredByUrlHandlingStrategy)),c.resolve(!1),$r}),xt(c=>{let m=new ey(c.id,this.urlSerializer.serialize(c.extractedUrl),this.urlSerializer.serialize(c.urlAfterRedirects),c.targetSnapshot);return this.events.next(m),this.currentTransition=t=Qe(W({},c),{guards:Zie(c.targetSnapshot,c.currentSnapshot,this.rootContexts)}),t}),coe(c=>this.events.next(c)),hn(c=>{if(t.guardsResult=c.guardsResult,c.guardsResult&&typeof c.guardsResult!="boolean")throw py(this.urlSerializer,c.guardsResult);let m=new ty(c.id,this.urlSerializer.serialize(c.extractedUrl),this.urlSerializer.serialize(c.urlAfterRedirects),c.targetSnapshot,!!c.guardsResult);if(this.events.next(m),!a())return $r;if(!c.guardsResult)return this.cancelNavigationTransition(c,"",wa.GuardRejected),$r;if(c.guards.canActivateChecks.length===0)return _t(c);let p=new ny(c.id,this.urlSerializer.serialize(c.extractedUrl),this.urlSerializer.serialize(c.urlAfterRedirects),c.targetSnapshot);if(this.events.next(p),!a())return $r;let h=!1;return _t(c).pipe(Loe(this.paramsInheritanceStrategy),yi({next:()=>{h=!0;let g=new iy(c.id,this.urlSerializer.serialize(c.extractedUrl),this.urlSerializer.serialize(c.urlAfterRedirects),c.targetSnapshot);this.events.next(g)},complete:()=>{h||this.cancelNavigationTransition(c,"",wa.NoDataFromResolver)}}))}),g7(c=>{let m=h=>{let g=[];if(h.routeConfig?._loadedComponent)h.component=h.routeConfig?._loadedComponent;else if(h.routeConfig?.loadComponent){let S=h._environmentInjector;g.push(this.configLoader.loadComponent(S,h.routeConfig).then(x=>{h.component=x}))}for(let S of h.children)g.push(...m(S));return g},p=m(c.targetSnapshot.root);return p.length===0?_t(c):nr(Promise.all(p).then(()=>c))}),g7(()=>this.afterPreactivation()),hn(()=>{let{currentSnapshot:c,targetSnapshot:m}=t,p=this.createViewTransition?.(this.environmentInjector,c.root,m.root);return p?nr(p).pipe(xt(()=>t)):_t(t)}),Gi(1),hn(c=>{let m=Qie(e.routeReuseStrategy,c.targetSnapshot,c.currentRouterState);this.currentTransition=t=c=Qe(W({},c),{targetRouterState:m}),this.currentNavigation.update(h=>(h.targetRouterState=m,h)),this.events.next(new Tf);let p=t.beforeActivateHandler.deferredHandle;return p?nr(p.then(()=>c)):_t(c)}),yi(c=>{new FI(e.routeReuseStrategy,t.targetRouterState,t.currentRouterState,m=>this.events.next(m),this.inputBindingEnabled).activate(this.rootContexts),a()&&(o=!0,this.currentNavigation.update(m=>(m.abort=Uoe,m)),this.lastSuccessfulNavigation.set(rr(this.currentNavigation)),this.events.next(new Mr(c.id,this.urlSerializer.serialize(c.extractedUrl),this.urlSerializer.serialize(c.urlAfterRedirects))),this.titleStrategy?.updateTitle(c.targetRouterState.snapshot),c.resolve(!0))}),tt($7(r.signal).pipe(Yn(()=>!o&&!t.targetRouterState),yi(()=>{this.cancelNavigationTransition(t,r.signal.reason+"",wa.Aborted)}))),yi({complete:()=>{o=!0}}),tt(this.transitionAbortWithErrorSubject.pipe(yi(c=>{throw c}))),IN(()=>{r.abort(),o||this.cancelNavigationTransition(t,"",wa.SupersededByNewNavigation),this.currentTransition?.id===t.id&&(this.currentNavigation.set(null),this.currentTransition=null)}),Zi(c=>{if(o=!0,this.destroyed)return t.resolve(!1),$r;if(z7(c))this.events.next(new fs(t.id,this.urlSerializer.serialize(t.extractedUrl),c.message,c.cancellationCode)),Kie(c)?this.events.next(new Ef(c.url,c.navigationBehaviorOptions)):t.resolve(!1);else{let m=new Id(t.id,this.urlSerializer.serialize(t.extractedUrl),c,t.targetSnapshot??void 0);try{let p=Ms(this.environmentInjector,()=>this.navigationErrorHandler?.(m));if(p instanceof Pf){let{message:h,cancellationCode:g}=py(this.urlSerializer,p);this.events.next(new fs(t.id,this.urlSerializer.serialize(t.extractedUrl),h,g)),this.events.next(new Ef(p.redirectTo,p.navigationBehaviorOptions))}else throw this.events.next(m),c}catch(p){this.options.resolveNavigationPromiseOnError?t.resolve(!1):t.reject(p)}}return $r}))}))}cancelNavigationTransition(e,t,o){let r=new fs(e.id,this.urlSerializer.serialize(e.extractedUrl),t,o);this.events.next(r),e.resolve(!1)}isUpdatingInternalState(){return this.currentTransition?.extractedUrl.toString()!==this.currentTransition?.currentUrlTree.toString()}isUpdatedBrowserUrl(){let e=this.urlHandlingStrategy.extract(this.urlSerializer.parse(this.location.path(!0))),t=rr(this.currentNavigation),o=t?.targetBrowserUrl??t?.extractedUrl;return e.toString()!==o?.toString()&&!t?.extras.skipLocationChange}static \u0275fac=function(t){return new(t||n)};static \u0275prov=K({token:n,factory:n.\u0275fac,providedIn:"root"})}return n})();function Goe(n){return n!==Sf}var eB=new $t("");var tB=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275prov=K({token:n,factory:()=>f(Woe),providedIn:"root"})}return n})(),hy=class{shouldDetach(i){return!1}store(i,e){}shouldAttach(i){return!1}retrieve(i){return null}shouldReuseRoute(i,e){return i.routeConfig===e.routeConfig}shouldDestroyInjector(i){return!0}},Woe=(()=>{class n extends hy{static \u0275fac=(()=>{let e;return function(o){return(e||(e=bi(n)))(o||n)}})();static \u0275prov=K({token:n,factory:n.\u0275fac,providedIn:"root"})}return n})(),Cy=(()=>{class n{urlSerializer=f(Lm);options=f(Bm,{optional:!0})||{};canceledNavigationResolution=this.options.canceledNavigationResolution||"replace";location=f(dc);urlHandlingStrategy=f(_y);urlUpdateStrategy=this.options.urlUpdateStrategy||"deferred";currentUrlTree=new qa;getCurrentUrlTree(){return this.currentUrlTree}rawUrlTree=this.currentUrlTree;getRawUrlTree(){return this.rawUrlTree}createBrowserPath({finalUrl:e,initialUrl:t,targetBrowserUrl:o}){let r=e!==void 0?this.urlHandlingStrategy.merge(e,t):t,a=o??r;return a instanceof qa?this.urlSerializer.serialize(a):a}commitTransition({targetRouterState:e,finalUrl:t,initialUrl:o}){t&&e?(this.currentUrlTree=t,this.rawUrlTree=this.urlHandlingStrategy.merge(t,o),this.routerState=e):this.rawUrlTree=o}routerState=N7(null,f(zl));getRouterState(){return this.routerState}_stateMemento=this.createStateMemento();get stateMemento(){return this._stateMemento}updateStateMemento(){this._stateMemento=this.createStateMemento()}createStateMemento(){return{rawUrlTree:this.rawUrlTree,currentUrlTree:this.currentUrlTree,routerState:this.routerState}}restoredState(){return this.location.getState()}static \u0275fac=function(t){return new(t||n)};static \u0275prov=K({token:n,factory:()=>f(qoe),providedIn:"root"})}return n})(),qoe=(()=>{class n extends Cy{currentPageId=0;lastSuccessfulId=-1;get browserPageId(){return this.canceledNavigationResolution!=="computed"?this.currentPageId:this.restoredState()?.\u0275routerPageId??this.currentPageId}registerNonRouterCurrentEntryChangeListener(e){return this.location.subscribe(t=>{t.type==="popstate"&&setTimeout(()=>{e(t.url,t.state,"popstate",{replaceUrl:!0})})})}handleRouterEvent(e,t){e instanceof Bc?this.updateStateMemento():e instanceof Vc?this.commitTransition(t):e instanceof xv?this.urlUpdateStrategy==="eager"&&(t.extras.skipLocationChange||this.setBrowserUrl(this.createBrowserPath(t),t)):e instanceof Tf?(this.commitTransition(t),this.urlUpdateStrategy==="deferred"&&!t.extras.skipLocationChange&&this.setBrowserUrl(this.createBrowserPath(t),t)):e instanceof fs&&!O7(e)?this.restoreHistory(t):e instanceof Id?this.restoreHistory(t,!0):e instanceof Mr&&(this.lastSuccessfulId=e.id,this.currentPageId=this.browserPageId)}setBrowserUrl(e,{extras:t,id:o}){let{replaceUrl:r,state:a}=t;if(this.location.isCurrentPathEqualTo(e)||r){let c=this.browserPageId,m=W(W({},a),this.generateNgRouterState(o,c));this.location.replaceState(e,"",m)}else{let c=W(W({},a),this.generateNgRouterState(o,this.browserPageId+1));this.location.go(e,"",c)}}restoreHistory(e,t=!1){if(this.canceledNavigationResolution==="computed"){let o=this.browserPageId,r=this.currentPageId-o;r!==0?this.location.historyGo(r):this.getCurrentUrlTree()===e.finalUrl&&r===0&&(this.resetInternalState(e),this.resetUrlToCurrentUrlTree())}else this.canceledNavigationResolution==="replace"&&(t&&this.resetInternalState(e),this.resetUrlToCurrentUrlTree())}resetInternalState({finalUrl:e}){this.routerState=this.stateMemento.routerState,this.currentUrlTree=this.stateMemento.currentUrlTree,this.rawUrlTree=this.urlHandlingStrategy.merge(this.currentUrlTree,e??this.rawUrlTree)}resetUrlToCurrentUrlTree(){this.location.replaceState(this.urlSerializer.serialize(this.getRawUrlTree()),"",this.generateNgRouterState(this.lastSuccessfulId,this.currentPageId))}generateNgRouterState(e,t){return this.canceledNavigationResolution==="computed"?{navigationId:e,\u0275routerPageId:t}:{navigationId:e}}static \u0275fac=(()=>{let e;return function(o){return(e||(e=bi(n)))(o||n)}})();static \u0275prov=K({token:n,factory:n.\u0275fac,providedIn:"root"})}return n})();function by(n,i){n.events.pipe(Yn(e=>e instanceof Mr||e instanceof fs||e instanceof Id||e instanceof Vc),xt(e=>e instanceof Mr||e instanceof Vc?0:(e instanceof fs?e.code===wa.Redirect||e.code===wa.SupersededByNewNavigation:!1)?2:1),Yn(e=>e!==2),Gi(1)).subscribe(()=>{i()})}var mt=(()=>{class n{get currentUrlTree(){return this.stateManager.getCurrentUrlTree()}get rawUrlTree(){return this.stateManager.getRawUrlTree()}disposed=!1;nonRouterCurrentEntryChangeSubscription;console=f(jN);stateManager=f(Cy);options=f(Bm,{optional:!0})||{};pendingTasks=f(RN);urlUpdateStrategy=this.options.urlUpdateStrategy||"deferred";navigationTransitions=f(vy);urlSerializer=f(Lm);location=f(dc);urlHandlingStrategy=f(_y);injector=f(zl);_events=new je;get events(){return this._events}get routerState(){return this.stateManager.getRouterState()}navigated=!1;routeReuseStrategy=f(tB);injectorCleanup=f(eB,{optional:!0});onSameUrlNavigation=this.options.onSameUrlNavigation||"ignore";config=f(Of,{optional:!0})?.flat()??[];componentInputBindingEnabled=!!f(Ev,{optional:!0});currentNavigation=this.navigationTransitions.currentNavigation.asReadonly();constructor(){this.resetConfig(this.config),this.navigationTransitions.setupNavigations(this).subscribe({error:e=>{}}),this.subscribeToNavigationEvents()}eventsSubscription=new go;subscribeToNavigationEvents(){let e=this.navigationTransitions.events.subscribe(t=>{try{let o=this.navigationTransitions.currentTransition,r=rr(this.navigationTransitions.currentNavigation);if(o!==null&&r!==null){if(this.stateManager.handleRouterEvent(t,r),t instanceof fs&&t.code!==wa.Redirect&&t.code!==wa.SupersededByNewNavigation)this.navigated=!0;else if(t instanceof Mr)this.navigated=!0,this.injectorCleanup?.(this.routeReuseStrategy,this.routerState,this.config);else if(t instanceof Ef){let a=t.navigationBehaviorOptions,c=this.urlHandlingStrategy.merge(t.url,o.currentRawUrl),m=W({scroll:o.extras.scroll,browserUrl:o.extras.browserUrl,info:o.extras.info,skipLocationChange:o.extras.skipLocationChange,replaceUrl:o.extras.replaceUrl||this.urlUpdateStrategy==="eager"||Goe(o.source)},a);this.scheduleNavigation(c,Sf,null,m,{resolve:o.resolve,reject:o.reject,promise:o.promise})}}Wie(t)&&this._events.next(t)}catch(o){this.navigationTransitions.transitionAbortWithErrorSubject.next(o)}});this.eventsSubscription.add(e)}resetRootComponentType(e){this.routerState.root.component=e,this.navigationTransitions.rootComponentType=e}initialNavigation(){this.setUpLocationChangeListener(),this.navigationTransitions.hasRequestedNavigation||this.navigateToSyncWithBrowser(this.location.path(!0),Sf,this.stateManager.restoredState(),{replaceUrl:!0})}setUpLocationChangeListener(){this.nonRouterCurrentEntryChangeSubscription??=this.stateManager.registerNonRouterCurrentEntryChangeListener((e,t,o,r)=>{this.navigateToSyncWithBrowser(e,o,t,r)})}navigateToSyncWithBrowser(e,t,o,r){let a=o?.navigationId?o:null;if(o){let m=W({},o);delete m.navigationId,delete m.\u0275routerPageId,Object.keys(m).length!==0&&(r.state=m)}let c=this.parseUrl(e);this.scheduleNavigation(c,t,a,r).catch(m=>{this.disposed||this.injector.get(i1)(m)})}get url(){return this.serializeUrl(this.currentUrlTree)}getCurrentNavigation(){return rr(this.navigationTransitions.currentNavigation)}get lastSuccessfulNavigation(){return this.navigationTransitions.lastSuccessfulNavigation}resetConfig(e){this.config=e.map(qI),this.navigated=!1}ngOnDestroy(){this.dispose()}dispose(){this._events.unsubscribe(),this.navigationTransitions.complete(),this.nonRouterCurrentEntryChangeSubscription?.unsubscribe(),this.nonRouterCurrentEntryChangeSubscription=void 0,this.disposed=!0,this.eventsSubscription.unsubscribe()}createUrlTree(e,t={}){let{relativeTo:o,queryParams:r,fragment:a,queryParamsHandling:c,preserveFragment:m}=t,p=m?this.currentUrlTree.fragment:a,h=null;switch(c??this.options.defaultQueryParamsHandling){case"merge":h=W(W({},this.currentUrlTree.queryParams),r);break;case"preserve":h=this.currentUrlTree.queryParams;break;default:h=r||null}h!==null&&(h=this.removeEmptyProps(h));let g;try{let S=o?o.snapshot:this.routerState.snapshot.root;g=D7(S)}catch{(typeof e[0]!="string"||e[0][0]!=="/")&&(e=[]),g=this.currentUrlTree.root}return P7(g,e,h,p??null,this.urlSerializer)}navigateByUrl(e,t={skipLocationChange:!1}){let o=Fm(e)?e:this.parseUrl(e),r=this.urlHandlingStrategy.merge(o,this.rawUrlTree);return this.scheduleNavigation(r,Sf,null,t)}navigate(e,t={skipLocationChange:!1}){return Qoe(e),this.navigateByUrl(this.createUrlTree(e,t),t)}serializeUrl(e){return this.urlSerializer.serialize(e)}parseUrl(e){try{return this.urlSerializer.parse(e)}catch{return this.console.warn(AN(4018,!1)),this.urlSerializer.parse("/")}}isActive(e,t){let o;if(t===!0?o=W({},jI):t===!1?o=W({},Cv):o=W(W({},Cv),t),Fm(e))return EI(this.currentUrlTree,e,o);let r=this.parseUrl(e);return EI(this.currentUrlTree,r,o)}removeEmptyProps(e){return Object.entries(e).reduce((t,[o,r])=>(r!=null&&(t[o]=r),t),{})}scheduleNavigation(e,t,o,r,a){if(this.disposed)return Promise.resolve(!1);let c,m,p;a?(c=a.resolve,m=a.reject,p=a.promise):p=new Promise((g,S)=>{c=g,m=S});let h=this.pendingTasks.add();return by(this,()=>{queueMicrotask(()=>this.pendingTasks.remove(h))}),this.navigationTransitions.handleNavigationRequest({source:t,restoredState:o,currentUrlTree:this.currentUrlTree,currentRawUrl:this.currentUrlTree,rawUrl:e,extras:r,resolve:c,reject:m,promise:p,currentSnapshot:this.routerState.snapshot,currentRouterState:this.routerState}),p.catch(Promise.reject.bind(Promise))}static \u0275fac=function(t){return new(t||n)};static \u0275prov=K({token:n,factory:n.\u0275fac,providedIn:"root"})}return n})();function Qoe(n){for(let i=0;i{class n{router=f(mt);stateManager=f(Cy);fragment=se("");queryParams=se({});path=se("");serializer=f(Lm);constructor(){this.updateState(),this.router.events?.subscribe(e=>{e instanceof Mr&&this.updateState()})}updateState(){let{fragment:e,root:t,queryParams:o}=this.stateManager.getCurrentUrlTree();this.fragment.set(e),this.queryParams.set(o),this.path.set(this.serializer.serialize(new qa(t)))}static \u0275fac=function(t){return new(t||n)};static \u0275prov=K({token:n,factory:n.\u0275fac,providedIn:"root"})}return n})(),pn=(()=>{class n{router;route;tabIndexAttribute;renderer;el;locationStrategy;hrefAttributeValue=f(new Ks("href"),{optional:!0});reactiveHref=YN(()=>this.isAnchorElement?this.computeHref(this._urlTree()):this.hrefAttributeValue);get href(){return rr(this.reactiveHref)}set href(e){this.reactiveHref.set(e)}set target(e){this._target.set(e)}get target(){return rr(this._target)}_target=se(void 0);set queryParams(e){this._queryParams.set(e)}get queryParams(){return rr(this._queryParams)}_queryParams=se(void 0,{equal:()=>!1});set fragment(e){this._fragment.set(e)}get fragment(){return rr(this._fragment)}_fragment=se(void 0);set queryParamsHandling(e){this._queryParamsHandling.set(e)}get queryParamsHandling(){return rr(this._queryParamsHandling)}_queryParamsHandling=se(void 0);set state(e){this._state.set(e)}get state(){return rr(this._state)}_state=se(void 0,{equal:()=>!1});set info(e){this._info.set(e)}get info(){return rr(this._info)}_info=se(void 0,{equal:()=>!1});set relativeTo(e){this._relativeTo.set(e)}get relativeTo(){return rr(this._relativeTo)}_relativeTo=se(void 0);set preserveFragment(e){this._preserveFragment.set(e)}get preserveFragment(){return rr(this._preserveFragment)}_preserveFragment=se(!1);set skipLocationChange(e){this._skipLocationChange.set(e)}get skipLocationChange(){return rr(this._skipLocationChange)}_skipLocationChange=se(!1);set replaceUrl(e){this._replaceUrl.set(e)}get replaceUrl(){return rr(this._replaceUrl)}_replaceUrl=se(!1);isAnchorElement;onChanges=new je;applicationErrorHandler=f(i1);options=f(Bm,{optional:!0});reactiveRouterState=f(Xoe);constructor(e,t,o,r,a,c){this.router=e,this.route=t,this.tabIndexAttribute=o,this.renderer=r,this.el=a,this.locationStrategy=c;let m=a.nativeElement.tagName?.toLowerCase();this.isAnchorElement=m==="a"||m==="area"||!!(typeof customElements=="object"&&customElements.get(m)?.observedAttributes?.includes?.("href"))}setTabIndexIfNotOnNativeEl(e){this.tabIndexAttribute!=null||this.isAnchorElement||this.applyAttributeValue("tabindex",e)}ngOnChanges(e){this.onChanges.next(this)}routerLinkInput=se(null);set routerLink(e){e==null?(this.routerLinkInput.set(null),this.setTabIndexIfNotOnNativeEl(null)):(Fm(e)?this.routerLinkInput.set(e):this.routerLinkInput.set(Array.isArray(e)?e:[e]),this.setTabIndexIfNotOnNativeEl("0"))}onClick(e,t,o,r,a){let c=this._urlTree();if(c===null||this.isAnchorElement&&(e!==0||t||o||r||a||typeof this.target=="string"&&this.target!="_self"))return!0;let m={skipLocationChange:this.skipLocationChange,replaceUrl:this.replaceUrl,state:this.state,info:this.info};return this.router.navigateByUrl(c,m)?.catch(p=>{this.applicationErrorHandler(p)}),!this.isAnchorElement}ngOnDestroy(){}applyAttributeValue(e,t){let o=this.renderer,r=this.el.nativeElement;t!==null?o.setAttribute(r,e,t):o.removeAttribute(r,e)}_urlTree=mn(()=>{this.reactiveRouterState.path(),this._preserveFragment()&&this.reactiveRouterState.fragment();let e=o=>o==="preserve"||o==="merge";(e(this._queryParamsHandling())||e(this.options?.defaultQueryParamsHandling))&&this.reactiveRouterState.queryParams();let t=this.routerLinkInput();return t===null||!this.router.createUrlTree?null:Fm(t)?t:this.router.createUrlTree(t,{relativeTo:this._relativeTo()!==void 0?this._relativeTo():this.route,queryParams:this._queryParams(),fragment:this._fragment(),queryParamsHandling:this._queryParamsHandling(),preserveFragment:this._preserveFragment()})},{equal:(e,t)=>this.computeHref(e)===this.computeHref(t)});get urlTree(){return rr(this._urlTree)}computeHref(e){return e!==null&&this.locationStrategy?this.locationStrategy?.prepareExternalUrl(this.router.serializeUrl(e))??"":null}static \u0275fac=function(t){return new(t||n)(rt(mt),rt(it),LN("tabindex"),rt(pi),rt(Qt),rt(w_))};static \u0275dir=ft({type:n,selectors:[["","routerLink",""]],hostVars:2,hostBindings:function(t,o){t&1&&_("click",function(a){return o.onClick(a.button,a.ctrlKey,a.shiftKey,a.altKey,a.metaKey)}),t&2&&Xt("href",o.reactiveHref(),VN)("target",o._target())},inputs:{target:"target",queryParams:"queryParams",fragment:"fragment",queryParamsHandling:"queryParamsHandling",state:"state",info:"info",relativeTo:"relativeTo",preserveFragment:[2,"preserveFragment","preserveFragment",gt],skipLocationChange:[2,"skipLocationChange","skipLocationChange",gt],replaceUrl:[2,"replaceUrl","replaceUrl",gt],routerLink:"routerLink"},features:[dn]})}return n})(),JI=(()=>{class n{router;element;renderer;cdr;links;classes=[];routerEventsSubscription;linkInputChangesSubscription;_isActive=!1;get isActive(){return this._isActive}routerLinkActiveOptions={exact:!1};ariaCurrentWhenActive;isActiveChange=new _e;link=f(pn,{optional:!0});constructor(e,t,o,r){this.router=e,this.element=t,this.renderer=o,this.cdr=r,this.routerEventsSubscription=e.events.subscribe(a=>{a instanceof Mr&&this.update()})}ngAfterContentInit(){_t(this.links.changes,_t(null)).pipe(v_()).subscribe(e=>{this.update(),this.subscribeToEachLinkOnChanges()})}subscribeToEachLinkOnChanges(){this.linkInputChangesSubscription?.unsubscribe();let e=[...this.links.toArray(),this.link].filter(t=>!!t).map(t=>t.onChanges);this.linkInputChangesSubscription=nr(e).pipe(v_()).subscribe(t=>{this._isActive!==this.isLinkActive(this.router)(t)&&this.update()})}set routerLinkActive(e){let t=Array.isArray(e)?e:e.split(" ");this.classes=t.filter(o=>!!o)}ngOnChanges(e){this.update()}ngOnDestroy(){this.routerEventsSubscription.unsubscribe(),this.linkInputChangesSubscription?.unsubscribe()}update(){!this.links||!this.router.navigated||queueMicrotask(()=>{let e=this.hasActiveLinks();this.classes.forEach(t=>{e?this.renderer.addClass(this.element.nativeElement,t):this.renderer.removeClass(this.element.nativeElement,t)}),e&&this.ariaCurrentWhenActive!==void 0?this.renderer.setAttribute(this.element.nativeElement,"aria-current",this.ariaCurrentWhenActive.toString()):this.renderer.removeAttribute(this.element.nativeElement,"aria-current"),this._isActive!==e&&(this._isActive=e,this.cdr.markForCheck(),this.isActiveChange.emit(e))})}isLinkActive(e){let t=Yoe(this.routerLinkActiveOptions)?this.routerLinkActiveOptions:this.routerLinkActiveOptions.exact??!1?W({},jI):W({},Cv);return o=>{let r=o.urlTree;return r?rr($I(r,e,t)):!1}}hasActiveLinks(){let e=this.isLinkActive(this.router);return this.link&&e(this.link)||this.links.some(e)}static \u0275fac=function(t){return new(t||n)(rt(mt),rt(Qt),rt(pi),rt(X))};static \u0275dir=ft({type:n,selectors:[["","routerLinkActive",""]],contentQueries:function(t,o,r){if(t&1&&Vi(r,pn,5),t&2){let a;pt(a=ut())&&(o.links=a)}},inputs:{routerLinkActiveOptions:"routerLinkActiveOptions",ariaCurrentWhenActive:"ariaCurrentWhenActive",routerLinkActive:"routerLinkActive"},outputs:{isActiveChange:"isActiveChange"},exportAs:["routerLinkActive"],features:[dn]})}return n})();function Yoe(n){let i=n;return!!(i.paths||i.matrixParams||i.queryParams||i.fragment)}var Pv=class{};var nB=(()=>{class n{router;injector;preloadingStrategy;loader;subscription;constructor(e,t,o,r){this.router=e,this.injector=t,this.preloadingStrategy=o,this.loader=r}setUpPreloading(){this.subscription=this.router.events.pipe(Yn(e=>e instanceof Mr),Jd(()=>this.preload())).subscribe(()=>{})}preload(){return this.processRoutes(this.injector,this.router.config)}ngOnDestroy(){this.subscription?.unsubscribe()}processRoutes(e,t){let o=[];for(let r of t){r.providers&&!r._injector&&(r._injector=a1(r.providers,e,""));let a=r._injector??e;r._loadedNgModuleFactory&&!r._loadedInjector&&(r._loadedInjector=r._loadedNgModuleFactory.create(a).injector);let c=r._loadedInjector??a;(r.loadChildren&&!r._loadedRoutes&&r.canLoad===void 0||r.loadComponent&&!r._loadedComponent)&&o.push(this.preloadConfig(a,r)),(r.children||r._loadedRoutes)&&o.push(this.processRoutes(c,r.children??r._loadedRoutes))}return nr(o).pipe(v_())}preloadConfig(e,t){return this.preloadingStrategy.preload(t,()=>{if(e.destroyed)return _t(null);let o;t.loadChildren&&t.canLoad===void 0?o=nr(this.loader.loadChildren(e,t)):o=_t(null);let r=o.pipe(vr(a=>a===null?_t(void 0):(t._loadedRoutes=a.routes,t._loadedInjector=a.injector,t._loadedNgModuleFactory=a.factory,this.processRoutes(a.injector??e,a.routes))));if(t.loadComponent&&!t._loadedComponent){let a=this.loader.loadComponent(e,t);return nr([r,a]).pipe(v_())}else return r})}static \u0275fac=function(t){return new(t||n)(ge(mt),ge(zl),ge(Pv),ge(gy))};static \u0275prov=K({token:n,factory:n.\u0275fac,providedIn:"root"})}return n})(),iB=new $t(""),Koe=(()=>{class n{options;routerEventsSubscription;scrollEventsSubscription;lastId=0;lastSource=Sf;restoredId=0;store={};urlSerializer=f(Lm);zone=f(Pi);viewportScroller=f(UT);transitions=f(vy);constructor(e){this.options=e,this.options.scrollPositionRestoration||="disabled",this.options.anchorScrolling||="disabled"}init(){this.options.scrollPositionRestoration!=="disabled"&&this.viewportScroller.setHistoryScrollRestoration("manual"),this.routerEventsSubscription=this.createScrollEvents(),this.scrollEventsSubscription=this.consumeScrollEvents()}createScrollEvents(){return this.transitions.events.subscribe(e=>{e instanceof Bc?(this.store[this.lastId]=this.viewportScroller.getScrollPosition(),this.lastSource=e.navigationTrigger,this.restoredId=e.restoredState?e.restoredState.navigationId:0):e instanceof Mr?(this.lastId=e.id,this.scheduleScrollEvent(e,this.urlSerializer.parse(e.urlAfterRedirects).fragment)):e instanceof Vc&&e.code===Mf.IgnoredSameUrlNavigation&&(this.lastSource=void 0,this.restoredId=0,this.scheduleScrollEvent(e,this.urlSerializer.parse(e.url).fragment))})}consumeScrollEvents(){return this.transitions.events.subscribe(e=>{if(!(e instanceof kf)||e.scrollBehavior==="manual")return;let t={behavior:"instant"};e.position?this.options.scrollPositionRestoration==="top"?this.viewportScroller.scrollToPosition([0,0],t):this.options.scrollPositionRestoration==="enabled"&&this.viewportScroller.scrollToPosition(e.position,t):e.anchor&&this.options.anchorScrolling==="enabled"?this.viewportScroller.scrollToAnchor(e.anchor):this.options.scrollPositionRestoration!=="disabled"&&this.viewportScroller.scrollToPosition([0,0])})}scheduleScrollEvent(e,t){let o=rr(this.transitions.currentNavigation)?.extras.scroll;this.zone.runOutsideAngular(async()=>{await new Promise(r=>{setTimeout(r),typeof requestAnimationFrame<"u"&&requestAnimationFrame(r)}),this.zone.run(()=>{this.transitions.events.next(new kf(e,this.lastSource==="popstate"?this.store[this.restoredId]:null,t,o))})})}ngOnDestroy(){this.routerEventsSubscription?.unsubscribe(),this.scrollEventsSubscription?.unsubscribe()}static \u0275fac=function(t){r1()};static \u0275prov=K({token:n,factory:n.\u0275fac})}return n})();function Zoe(){return f(mt).routerState.root}function Iv(n,i){return{\u0275kind:n,\u0275providers:i}}function Joe(){let n=f(Wo);return i=>{let e=n.get($T);if(i!==e.components[0])return;let t=n.get(mt),o=n.get(oB);n.get(e3)===1&&t.initialNavigation(),n.get(sB,null,{optional:!0})?.setUpPreloading(),n.get(iB,null,{optional:!0})?.init(),t.resetRootComponentType(e.componentTypes[0]),o.closed||(o.next(),o.complete(),o.unsubscribe())}}var oB=new $t("",{factory:()=>new je}),e3=new $t("",{factory:()=>1});function rB(){let n=[{provide:BN,useValue:!0},{provide:e3,useValue:0},jT(()=>{let i=f(Wo);return i.get(e5,Promise.resolve()).then(()=>new Promise(t=>{let o=i.get(mt),r=i.get(oB);by(o,()=>{t(!0)}),i.get(vy).afterPreactivation=()=>(t(!0),r.closed?_t(void 0):r),o.initialNavigation()}))})];return Iv(2,n)}function aB(){let n=[jT(()=>{f(mt).setUpLocationChangeListener()}),{provide:e3,useValue:2}];return Iv(3,n)}var sB=new $t("");function lB(n){return Iv(0,[{provide:sB,useExisting:nB},{provide:Pv,useExisting:n}])}function cB(){return Iv(8,[GI,{provide:Ev,useExisting:GI}])}function dB(n){zT("NgRouterViewTransitions");let i=[{provide:XI,useValue:J7},{provide:YI,useValue:W({skipNextTransition:!!n?.skipInitialTransition},n)}];return Iv(9,i)}var mB=[dc,{provide:Lm,useClass:Pd},mt,Iu,{provide:it,useFactory:Zoe},gy,[]],dt=(()=>{class n{constructor(){}static forRoot(e,t){return{ngModule:n,providers:[mB,[],{provide:Of,multi:!0,useValue:e},[],t?.errorHandler?{provide:KI,useValue:t.errorHandler}:[],{provide:Bm,useValue:t||{}},t?.useHash?tre():nre(),ere(),t?.preloadingStrategy?lB(t.preloadingStrategy).\u0275providers:[],t?.initialNavigation?ire(t):[],t?.bindToComponentInputs?cB().\u0275providers:[],t?.enableViewTransitions?dB().\u0275providers:[],ore()]}}static forChild(e){return{ngModule:n,providers:[{provide:Of,multi:!0,useValue:e}]}}static \u0275fac=function(t){return new(t||n)};static \u0275mod=Gt({type:n});static \u0275inj=Ut({})}return n})();function ere(){return{provide:iB,useFactory:()=>{let n=f(UT),i=f(Bm);return i.scrollOffset&&n.setOffset(i.scrollOffset),new Koe(i)}}}function tre(){return{provide:w_,useClass:n5}}function nre(){return{provide:w_,useClass:t5}}function ire(n){return[n.initialNavigation==="disabled"?aB().\u0275providers:[],n.initialNavigation==="enabledBlocking"?rB().\u0275providers:[]]}var ZI=new $t("");function ore(){return[{provide:ZI,useFactory:Joe},{provide:UN,multi:!0,useExisting:ZI}]}var Au=class{visible;error;clear;constructor(i,e,t=!1){this.visible=i,this.error=e,this.clear=t}},so=(()=>{class n{state=new zt(new Au(!1));constructor(){}setError(e){this.state.next(new Au(!1,e.error))}clear(){this.state.next(new Au(!1,null,!0))}activate(){this.state.next(new Au(!0))}deactivate(){this.state.next(new Au(!1))}static \u0275fac=function(t){return new(t||n)};static \u0275prov=K({token:n,factory:n.\u0275fac})}return n})();function rre(n,i){n&1&&(s(0,"div",1),B(1,"mat-spinner",3),l())}function are(n,i){if(n&1){let e=z();s(0,"div",2)(1,"div",4)(2,"mat-icon"),d(3,"error_outline"),l()(),s(4,"div"),d(5),l(),s(6,"div")(7,"button",5),_("click",function(){T(e);let o=C(2);return E(o.refresh())}),s(8,"mat-icon"),d(9,"refresh"),l()(),s(10,"button",6)(11,"mat-icon"),d(12,"home"),l()()()()}if(n&2){let e,t=C(2);u(5),te("Error occurred: ",(e=t.error())==null?null:e.message)}}function sre(n,i){if(n&1&&(s(0,"div",0),A(1,rre,2,0,"div",1),A(2,are,13,1,"div",2),l()),n&2){let e=C();u(),O(e.visible()&&!e.error()?1:-1),u(),O(e.error()?2:-1)}}var Vm=(()=>{class n{progressService=f(so);router=f(mt);visible=se(!1);error=se(null);routerSubscription;ngOnInit(){this.progressService.state.subscribe(e=>{this.visible.set(e.visible),e.error&&!this.error()&&this.error.set(e.error),e.clear&&this.error.set(null)}),this.routerSubscription=this.router.events.subscribe(()=>{this.progressService.clear()})}refresh(){this.router.navigateByUrl(this.router.url)}ngOnDestroy(){this.routerSubscription.unsubscribe()}static \u0275fac=function(t){return new(t||n)};static \u0275cmp=F({type:n,selectors:[["app-progress"]],decls:1,vars:1,consts:[[1,"overlay"],[1,"loading-spinner"],[1,"error-state"],["color","primary"],[1,"error-icon"],["mat-button","","matTooltip","Refresh page","matTooltipClass","custom-tooltip",3,"click"],["mat-button","","routerLink","/","matTooltip","Go to home","matTooltipClass","custom-tooltip"]],template:function(t,o){t&1&&A(0,sre,3,2,"div",0),t&2&&O(o.visible()||o.error()?0:-1)},dependencies:[ne,Mn,zi,re,ce,U,pe,Et,Vt,pn],styles:[".overlay[_ngcontent-%COMP%]{position:fixed;width:100%;height:100%;inset:0;background-color:color-mix(in srgb,var(--mat-sys-shadow) 50%,transparent);z-index:2000}.loading-spinner[_ngcontent-%COMP%], .error-state[_ngcontent-%COMP%]{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%)}.error-state[_ngcontent-%COMP%] div[_ngcontent-%COMP%]{text-align:center}.error-icon[_ngcontent-%COMP%] mat-icon[_ngcontent-%COMP%]{font-size:64px;width:64px;height:64px}"],changeDetection:0})}return n})();var xy=(()=>{class n{document;router=f(mt);controllerService=f(Je);progressService=f(so);constructor(e){this.document=e}ngOnInit(){this.progressService.activate(),setTimeout(()=>{let e;parseInt(this.document.location.port,10)?e=parseInt(this.document.location.port,10):this.document.location.protocol=="https:"?e=443:e=80,this.controllerService.getLocalController(this.document.location.hostname,e).then(t=>{this.router.navigate(["/controller",t.id,"projects"]),this.progressService.deactivate()})},100)}static \u0275fac=function(t){return new(t||n)(rt(co))};static \u0275cmp=F({type:n,selectors:[["app-bundled-controller-finder"]],decls:1,vars:0,template:function(t,o){t&1&&B(0,"app-progress")},dependencies:[Vm],encapsulation:2,changeDetection:0})}return n})();var lre=["mat-menu-item",""],cre=[[["mat-icon"],["","matMenuItemIcon",""]],"*"],dre=["mat-icon, [matMenuItemIcon]","*"];function mre(n,i){n&1&&(Jn(),s(0,"svg",2),B(1,"polygon",3),l())}var pre=["*"];function ure(n,i){if(n&1){let e=z();yo(0,"div",0),s1("click",function(){T(e);let o=C();return E(o.closed.emit("click"))})("animationstart",function(o){T(e);let r=C();return E(r._onAnimationStart(o.animationName))})("animationend",function(o){T(e);let r=C();return E(r._onAnimationDone(o.animationName))})("animationcancel",function(o){T(e);let r=C();return E(r._onAnimationDone(o.animationName))}),yo(1,"div",1),nn(2),Eo()()}if(n&2){let e=C();or(e._classList),Ue("mat-menu-panel-animations-disabled",e._animationsDisabled)("mat-menu-panel-exit-animation",e._panelAnimationState==="void")("mat-menu-panel-animating",e._isAnimating()),qo("id",e.panelId),Xt("aria-label",e.ariaLabel||null)("aria-labelledby",e.ariaLabelledby||null)("aria-describedby",e.ariaDescribedby||null)}}var n3=new $t("MAT_MENU_PANEL"),et=(()=>{class n{_elementRef=f(Qt);_document=f(co);_focusMonitor=f(Fa);_parentMenu=f(n3,{optional:!0});_changeDetectorRef=f(X);role="menuitem";disabled=!1;disableRipple=!1;_hovered=new je;_focused=new je;_highlighted=!1;_triggersSubmenu=!1;constructor(){f(pr).load(sa),this._parentMenu?.addItem?.(this)}focus(e,t){this._focusMonitor&&e?this._focusMonitor.focusVia(this._getHostElement(),e,t):this._getHostElement().focus(t),this._focused.next(this)}ngAfterViewInit(){this._focusMonitor&&this._focusMonitor.monitor(this._elementRef,!1)}ngOnDestroy(){this._focusMonitor&&this._focusMonitor.stopMonitoring(this._elementRef),this._parentMenu&&this._parentMenu.removeItem&&this._parentMenu.removeItem(this),this._hovered.complete(),this._focused.complete()}_getTabIndex(){return this.disabled?"-1":"0"}_getHostElement(){return this._elementRef.nativeElement}_checkDisabled(e){this.disabled&&(e.preventDefault(),e.stopPropagation())}_handleMouseEnter(){this._hovered.next(this)}getLabel(){let e=this._elementRef.nativeElement.cloneNode(!0),t=e.querySelectorAll("mat-icon, .material-icons");for(let o=0;o({overlapTrigger:!1,xPosition:"after",yPosition:"below",backdropClass:"cdk-overlay-transparent-backdrop"})}),t3="_mat-menu-enter",yy="_mat-menu-exit",ti=(()=>{class n{_elementRef=f(Qt);_changeDetectorRef=f(X);_injector=f(Wo);_keyManager;_xPosition;_yPosition;_firstItemFocusRef;_exitFallbackTimeout;_animationsDisabled=Qo();_allItems;_directDescendantItems=new jl;_classList={};_panelAnimationState="void";_animationDone=new je;_isAnimating=se(!1);parentMenu;direction;overlayPanelClass;backdropClass;ariaLabel;ariaLabelledby;ariaDescribedby;get xPosition(){return this._xPosition}set xPosition(e){this._xPosition=e,this.setPositionClasses()}get yPosition(){return this._yPosition}set yPosition(e){this._yPosition=e,this.setPositionClasses()}templateRef;items;lazyContent;overlapTrigger=!1;hasBackdrop;set panelClass(e){let t=this._previousPanelClass,o=W({},this._classList);t&&t.length&&t.split(" ").forEach(r=>{o[r]=!1}),this._previousPanelClass=e,e&&e.length&&(e.split(" ").forEach(r=>{o[r]=!0}),this._elementRef.nativeElement.className=""),this._classList=o}_previousPanelClass;get classList(){return this.panelClass}set classList(e){this.panelClass=e}closed=new _e;close=this.closed;panelId=f(Do).getId("mat-menu-panel-");constructor(){let e=f(fre);this.overlayPanelClass=e.overlayPanelClass||"",this._xPosition=e.xPosition,this._yPosition=e.yPosition,this.backdropClass=e.backdropClass,this.overlapTrigger=e.overlapTrigger,this.hasBackdrop=e.hasBackdrop}ngOnInit(){this.setPositionClasses()}ngAfterContentInit(){this._updateDirectDescendants(),this._keyManager=new rm(this._directDescendantItems).withWrap().withTypeAhead().withHomeAndEnd(),this._keyManager.tabOut.subscribe(()=>this.closed.emit("tab")),this._directDescendantItems.changes.pipe(xi(this._directDescendantItems),hn(e=>En(...e.map(t=>t._focused)))).subscribe(e=>this._keyManager.updateActiveItem(e)),this._directDescendantItems.changes.subscribe(e=>{let t=this._keyManager;if(this._panelAnimationState==="enter"&&t.activeItem?._hasFocus()){let o=e.toArray(),r=Math.max(0,Math.min(o.length-1,t.activeItemIndex||0));o[r]&&!o[r].disabled?t.setActiveItem(r):t.setNextItemActive()}})}ngOnDestroy(){this._keyManager?.destroy(),this._directDescendantItems.destroy(),this.closed.complete(),this._firstItemFocusRef?.destroy(),clearTimeout(this._exitFallbackTimeout)}_hovered(){return this._directDescendantItems.changes.pipe(xi(this._directDescendantItems),hn(t=>En(...t.map(o=>o._hovered))))}addItem(e){}removeItem(e){}_handleKeydown(e){let t=e.keyCode,o=this._keyManager;switch(t){case 27:Ca(e)||(e.preventDefault(),this.closed.emit("keydown"));break;case 37:this.parentMenu&&this.direction==="ltr"&&this.closed.emit("keydown");break;case 39:this.parentMenu&&this.direction==="rtl"&&this.closed.emit("keydown");break;default:(t===38||t===40)&&o.setFocusOrigin("keyboard"),o.onKeydown(e);return}}focusFirstItem(e="program"){this._firstItemFocusRef?.destroy(),this._firstItemFocusRef=aa(()=>{let t=this._resolvePanel();if(!t||!t.contains(document.activeElement)){let o=this._keyManager;o.setFocusOrigin(e).setFirstItemActive(),!o.activeItem&&t&&t.focus()}},{injector:this._injector})}resetActiveItem(){this._keyManager.setActiveItem(-1)}setElevation(e){}setPositionClasses(e=this.xPosition,t=this.yPosition){this._classList=Qe(W({},this._classList),{"mat-menu-before":e==="before","mat-menu-after":e==="after","mat-menu-above":t==="above","mat-menu-below":t==="below"}),this._changeDetectorRef.markForCheck()}_onAnimationDone(e){let t=e===yy;(t||e===t3)&&(t&&(clearTimeout(this._exitFallbackTimeout),this._exitFallbackTimeout=void 0),this._animationDone.next(t?"void":"enter"),this._isAnimating.set(!1))}_onAnimationStart(e){(e===t3||e===yy)&&this._isAnimating.set(!0)}_setIsOpen(e){if(this._panelAnimationState=e?"enter":"void",e){if(this._keyManager.activeItemIndex===0){let t=this._resolvePanel();t&&(t.scrollTop=0)}}else this._animationsDisabled||(this._exitFallbackTimeout=setTimeout(()=>this._onAnimationDone(yy),200));this._animationsDisabled&&setTimeout(()=>{this._onAnimationDone(e?t3:yy)}),this._changeDetectorRef.markForCheck()}_updateDirectDescendants(){this._allItems.changes.pipe(xi(this._allItems)).subscribe(e=>{this._directDescendantItems.reset(e.filter(t=>t._parentMenu===this)),this._directDescendantItems.notifyOnChanges()})}_resolvePanel(){let e=null;return this._directDescendantItems.length&&(e=this._directDescendantItems.first._getHostElement().closest('[role="menu"]')),e}static \u0275fac=function(t){return new(t||n)};static \u0275cmp=F({type:n,selectors:[["mat-menu"]],contentQueries:function(t,o,r){if(t&1&&Vi(r,hre,5)(r,et,5)(r,et,4),t&2){let a;pt(a=ut())&&(o.lazyContent=a.first),pt(a=ut())&&(o._allItems=a),pt(a=ut())&&(o.items=a)}},viewQuery:function(t,o){if(t&1&&Dn(jo,5),t&2){let r;pt(r=ut())&&(o.templateRef=r.first)}},hostVars:3,hostBindings:function(t,o){t&2&&Xt("aria-label",null)("aria-labelledby",null)("aria-describedby",null)},inputs:{backdropClass:"backdropClass",ariaLabel:[0,"aria-label","ariaLabel"],ariaLabelledby:[0,"aria-labelledby","ariaLabelledby"],ariaDescribedby:[0,"aria-describedby","ariaDescribedby"],xPosition:"xPosition",yPosition:"yPosition",overlapTrigger:[2,"overlapTrigger","overlapTrigger",gt],hasBackdrop:[2,"hasBackdrop","hasBackdrop",e=>e==null?null:gt(e)],panelClass:[0,"class","panelClass"],classList:"classList"},outputs:{closed:"closed",close:"close"},exportAs:["matMenu"],features:[Cn([{provide:n3,useExisting:n}])],ngContentSelectors:pre,decls:1,vars:0,consts:[["tabindex","-1","role","menu",1,"mat-mdc-menu-panel",3,"click","animationstart","animationend","animationcancel","id"],[1,"mat-mdc-menu-content"]],template:function(t,o){t&1&&(ii(),dh(0,ure,3,12,"ng-template"))},styles:[`mat-menu{display:none}.mat-mdc-menu-content{margin:0;padding:8px 0;outline:0}.mat-mdc-menu-content,.mat-mdc-menu-content .mat-mdc-menu-item .mat-mdc-menu-item-text{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;flex:1;white-space:normal;font-family:var(--mat-menu-item-label-text-font, var(--mat-sys-label-large-font));line-height:var(--mat-menu-item-label-text-line-height, var(--mat-sys-label-large-line-height));font-size:var(--mat-menu-item-label-text-size, var(--mat-sys-label-large-size));letter-spacing:var(--mat-menu-item-label-text-tracking, var(--mat-sys-label-large-tracking));font-weight:var(--mat-menu-item-label-text-weight, var(--mat-sys-label-large-weight))}@keyframes _mat-menu-enter{from{opacity:0;transform:scale(0.8)}to{opacity:1;transform:none}}@keyframes _mat-menu-exit{from{opacity:1}to{opacity:0}}.mat-mdc-menu-panel{min-width:112px;max-width:280px;overflow:auto;box-sizing:border-box;outline:0;animation:_mat-menu-enter 120ms cubic-bezier(0, 0, 0.2, 1);border-radius:var(--mat-menu-container-shape, var(--mat-sys-corner-extra-small));background-color:var(--mat-menu-container-color, var(--mat-sys-surface-container));box-shadow:var(--mat-menu-container-elevation-shadow, 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12));will-change:transform,opacity}.mat-mdc-menu-panel.mat-menu-panel-exit-animation{animation:_mat-menu-exit 100ms 25ms linear forwards}.mat-mdc-menu-panel.mat-menu-panel-animations-disabled{animation:none}.mat-mdc-menu-panel.mat-menu-panel-animating{pointer-events:none}.mat-mdc-menu-panel.mat-menu-panel-animating:has(.mat-mdc-menu-content:empty){display:none}@media(forced-colors: active){.mat-mdc-menu-panel{outline:solid 1px}}.mat-mdc-menu-panel .mat-divider{border-top-color:var(--mat-menu-divider-color, var(--mat-sys-surface-variant));margin-bottom:var(--mat-menu-divider-bottom-spacing, 8px);margin-top:var(--mat-menu-divider-top-spacing, 8px)}.mat-mdc-menu-item{display:flex;position:relative;align-items:center;justify-content:flex-start;overflow:hidden;padding:0;cursor:pointer;width:100%;text-align:left;box-sizing:border-box;color:inherit;font-size:inherit;background:none;text-decoration:none;margin:0;min-height:48px;padding-left:var(--mat-menu-item-leading-spacing, 12px);padding-right:var(--mat-menu-item-trailing-spacing, 12px);-webkit-user-select:none;user-select:none;cursor:pointer;outline:none;border:none;-webkit-tap-highlight-color:rgba(0,0,0,0)}.mat-mdc-menu-item::-moz-focus-inner{border:0}[dir=rtl] .mat-mdc-menu-item{padding-left:var(--mat-menu-item-trailing-spacing, 12px);padding-right:var(--mat-menu-item-leading-spacing, 12px)}.mat-mdc-menu-item:has(.material-icons,mat-icon,[matButtonIcon]){padding-left:var(--mat-menu-item-with-icon-leading-spacing, 12px);padding-right:var(--mat-menu-item-with-icon-trailing-spacing, 12px)}[dir=rtl] .mat-mdc-menu-item:has(.material-icons,mat-icon,[matButtonIcon]){padding-left:var(--mat-menu-item-with-icon-trailing-spacing, 12px);padding-right:var(--mat-menu-item-with-icon-leading-spacing, 12px)}.mat-mdc-menu-item,.mat-mdc-menu-item:visited,.mat-mdc-menu-item:link{color:var(--mat-menu-item-label-text-color, var(--mat-sys-on-surface))}.mat-mdc-menu-item .mat-icon-no-color,.mat-mdc-menu-item .mat-mdc-menu-submenu-icon{color:var(--mat-menu-item-icon-color, var(--mat-sys-on-surface-variant))}.mat-mdc-menu-item[disabled]{cursor:default;opacity:.38}.mat-mdc-menu-item[disabled]::after{display:block;position:absolute;content:"";top:0;left:0;bottom:0;right:0}.mat-mdc-menu-item:focus{outline:0}.mat-mdc-menu-item .mat-icon{flex-shrink:0;margin-right:var(--mat-menu-item-spacing, 12px);height:var(--mat-menu-item-icon-size, 24px);width:var(--mat-menu-item-icon-size, 24px)}[dir=rtl] .mat-mdc-menu-item{text-align:right}[dir=rtl] .mat-mdc-menu-item .mat-icon{margin-right:0;margin-left:var(--mat-menu-item-spacing, 12px)}.mat-mdc-menu-item:not([disabled]):hover{background-color:var(--mat-menu-item-hover-state-layer-color, color-mix(in srgb, var(--mat-sys-on-surface) calc(var(--mat-sys-hover-state-layer-opacity) * 100%), transparent))}.mat-mdc-menu-item:not([disabled]).cdk-program-focused,.mat-mdc-menu-item:not([disabled]).cdk-keyboard-focused,.mat-mdc-menu-item:not([disabled]).mat-mdc-menu-item-highlighted{background-color:var(--mat-menu-item-focus-state-layer-color, color-mix(in srgb, var(--mat-sys-on-surface) calc(var(--mat-sys-focus-state-layer-opacity) * 100%), transparent))}@media(forced-colors: active){.mat-mdc-menu-item{margin-top:1px}}.mat-mdc-menu-submenu-icon{width:var(--mat-menu-item-icon-size, 24px);height:10px;fill:currentColor;padding-left:var(--mat-menu-item-spacing, 12px)}[dir=rtl] .mat-mdc-menu-submenu-icon{padding-right:var(--mat-menu-item-spacing, 12px);padding-left:0}[dir=rtl] .mat-mdc-menu-submenu-icon polygon{transform:scaleX(-1);transform-origin:center}@media(forced-colors: active){.mat-mdc-menu-submenu-icon{fill:CanvasText}}.mat-mdc-menu-item .mat-mdc-menu-ripple{top:0;left:0;right:0;bottom:0;position:absolute;pointer-events:none} -`],encapsulation:2,changeDetection:0})}return n})(),gre=new $t("mat-menu-scroll-strategy",{providedIn:"root",factory:()=>{let n=f(Wo);return()=>v1(n)}});var Nf=new WeakMap,_re=(()=>{class n{_canHaveBackdrop;_element=f(Qt);_viewContainerRef=f(Ji);_menuItemInstance=f(et,{optional:!0,self:!0});_dir=f(ts,{optional:!0});_focusMonitor=f(Fa);_ngZone=f(Pi);_injector=f(Wo);_scrollStrategy=f(gre);_changeDetectorRef=f(X);_animationsDisabled=Qo();_portal;_overlayRef=null;_menuOpen=!1;_closingActionsSubscription=go.EMPTY;_menuCloseSubscription=go.EMPTY;_pendingRemoval;_parentMaterialMenu;_parentInnerPadding;_openedBy=void 0;get _menu(){return this._menuInternal}set _menu(e){e!==this._menuInternal&&(this._menuInternal=e,this._menuCloseSubscription.unsubscribe(),e&&(this._parentMaterialMenu,this._menuCloseSubscription=e.close.subscribe(t=>{this._destroyMenu(t),(t==="click"||t==="tab")&&this._parentMaterialMenu&&this._parentMaterialMenu.closed.emit(t)})),this._menuItemInstance?._setTriggersSubmenu(this._triggersSubmenu()))}_menuInternal=null;constructor(e){this._canHaveBackdrop=e;let t=f(n3,{optional:!0});this._parentMaterialMenu=t instanceof ti?t:void 0}ngOnDestroy(){this._menu&&this._ownsMenu(this._menu)&&Nf.delete(this._menu),this._pendingRemoval?.unsubscribe(),this._menuCloseSubscription.unsubscribe(),this._closingActionsSubscription.unsubscribe(),this._overlayRef&&(this._overlayRef.dispose(),this._overlayRef=null)}get menuOpen(){return this._menuOpen}get dir(){return this._dir&&this._dir.value==="rtl"?"rtl":"ltr"}_triggersSubmenu(){return!!(this._menuItemInstance&&this._parentMaterialMenu&&this._menu)}_closeMenu(){this._menu?.close.emit()}_openMenu(e){if(this._triggerIsAriaDisabled())return;let t=this._menu;if(this._menuOpen||!t)return;this._pendingRemoval?.unsubscribe();let o=Nf.get(t);Nf.set(t,this),o&&o!==this&&o._closeMenu();let r=this._createOverlay(t),a=r.getConfig(),c=a.positionStrategy;this._setPosition(t,c),this._canHaveBackdrop?a.hasBackdrop=t.hasBackdrop==null?!this._triggersSubmenu():t.hasBackdrop:a.hasBackdrop=t.hasBackdrop??!1,r.hasAttached()||(r.attach(this._getPortal(t)),t.lazyContent?.attach(this.menuData)),this._closingActionsSubscription=this._menuClosingActions().subscribe(()=>this._closeMenu()),t.parentMenu=this._triggersSubmenu()?this._parentMaterialMenu:void 0,t.direction=this.dir,e&&t.focusFirstItem(this._openedBy||"program"),this._setIsMenuOpen(!0),t instanceof ti&&(t._setIsOpen(!0),t._directDescendantItems.changes.pipe(tt(t.close)).subscribe(()=>{c.withLockedPosition(!1).reapplyLastPosition(),c.withLockedPosition(!0)}))}focus(e,t){this._focusMonitor&&e?this._focusMonitor.focusVia(this._element,e,t):this._element.nativeElement.focus(t)}_destroyMenu(e){let t=this._overlayRef,o=this._menu;!t||!this.menuOpen||(this._closingActionsSubscription.unsubscribe(),this._pendingRemoval?.unsubscribe(),o instanceof ti&&this._ownsMenu(o)?(this._pendingRemoval=o._animationDone.pipe(Gi(1)).subscribe(()=>{t.detach(),Nf.has(o)||o.lazyContent?.detach()}),o._setIsOpen(!1)):(t.detach(),o?.lazyContent?.detach()),o&&this._ownsMenu(o)&&Nf.delete(o),this.restoreFocus&&(e==="keydown"||!this._openedBy||!this._triggersSubmenu())&&this.focus(this._openedBy),this._openedBy=void 0,this._setIsMenuOpen(!1))}_setIsMenuOpen(e){e!==this._menuOpen&&(this._menuOpen=e,this._menuOpen?this.menuOpened.emit():this.menuClosed.emit(),this._triggersSubmenu()&&this._menuItemInstance._setHighlighted(e),this._changeDetectorRef.markForCheck())}_createOverlay(e){if(!this._overlayRef){let t=this._getOverlayConfig(e);this._subscribeToPositions(e,t.positionStrategy),this._overlayRef=x1(this._injector,t),this._overlayRef.keydownEvents().subscribe(o=>{this._menu instanceof ti&&this._menu._handleKeydown(o)})}return this._overlayRef}_getOverlayConfig(e){return new C1({positionStrategy:b1(this._injector,this._getOverlayOrigin()).withLockedPosition().withGrowAfterOpen().withTransformOriginOn(".mat-menu-panel, .mat-mdc-menu-panel"),backdropClass:e.backdropClass||"cdk-overlay-transparent-backdrop",panelClass:e.overlayPanelClass,scrollStrategy:this._scrollStrategy(),direction:this._dir||"ltr",disableAnimations:this._animationsDisabled})}_subscribeToPositions(e,t){e.setPositionClasses&&t.positionChanges.subscribe(o=>{this._ngZone.run(()=>{let r=o.connectionPair.overlayX==="start"?"after":"before",a=o.connectionPair.overlayY==="top"?"below":"above";e.setPositionClasses(r,a)})})}_setPosition(e,t){let[o,r]=e.xPosition==="before"?["end","start"]:["start","end"],[a,c]=e.yPosition==="above"?["bottom","top"]:["top","bottom"],[m,p]=[a,c],[h,g]=[o,r],S=0;if(this._triggersSubmenu()){if(g=o=e.xPosition==="before"?"start":"end",r=h=o==="end"?"start":"end",this._parentMaterialMenu){if(this._parentInnerPadding==null){let x=this._parentMaterialMenu.items.first;this._parentInnerPadding=x?x._getHostElement().offsetTop:0}S=a==="bottom"?this._parentInnerPadding:-this._parentInnerPadding}}else e.overlapTrigger||(m=a==="top"?"bottom":"top",p=c==="top"?"bottom":"top");t.withPositions([{originX:o,originY:m,overlayX:h,overlayY:a,offsetY:S},{originX:r,originY:m,overlayX:g,overlayY:a,offsetY:S},{originX:o,originY:p,overlayX:h,overlayY:c,offsetY:-S},{originX:r,originY:p,overlayX:g,overlayY:c,offsetY:-S}])}_menuClosingActions(){let e=this._getOutsideClickStream(this._overlayRef),t=this._overlayRef.detachments(),o=this._parentMaterialMenu?this._parentMaterialMenu.closed:_t(),r=this._parentMaterialMenu?this._parentMaterialMenu._hovered().pipe(Yn(a=>this._menuOpen&&a!==this._menuItemInstance)):_t();return En(e,o,r,t)}_getPortal(e){return(!this._portal||this._portal.templateRef!==e.templateRef)&&(this._portal=new im(e.templateRef,this._viewContainerRef)),this._portal}_ownsMenu(e){return Nf.get(e)===this}_triggerIsAriaDisabled(){return gt(this._element.nativeElement.getAttribute("aria-disabled"))}static \u0275fac=function(t){r1()};static \u0275dir=ft({type:n})}return n})(),An=(()=>{class n extends _re{_cleanupTouchstart;_hoverSubscription=go.EMPTY;get _deprecatedMatMenuTriggerFor(){return this.menu}set _deprecatedMatMenuTriggerFor(e){this.menu=e}get menu(){return this._menu}set menu(e){this._menu=e}menuData;restoreFocus=!0;menuOpened=new _e;onMenuOpen=this.menuOpened;menuClosed=new _e;onMenuClose=this.menuClosed;constructor(){super(!0);let e=f(pi);this._cleanupTouchstart=e.listen(this._element.nativeElement,"touchstart",t=>{_1(t)||(this._openedBy="touch")},{passive:!0})}triggersSubmenu(){return super._triggersSubmenu()}toggleMenu(){return this.menuOpen?this.closeMenu():this.openMenu()}openMenu(){this._openMenu(!0)}closeMenu(){this._closeMenu()}updatePosition(){this._overlayRef?.updatePosition()}ngAfterContentInit(){this._handleHover()}ngOnDestroy(){super.ngOnDestroy(),this._cleanupTouchstart(),this._hoverSubscription.unsubscribe()}_getOverlayOrigin(){return this._element}_getOutsideClickStream(e){return e.backdropClick()}_handleMousedown(e){g1(e)||(this._openedBy=e.button===0?"mouse":void 0,this.triggersSubmenu()&&e.preventDefault())}_handleKeydown(e){let t=e.keyCode;(t===13||t===32)&&(this._openedBy="keyboard"),this.triggersSubmenu()&&(t===39&&this.dir==="ltr"||t===37&&this.dir==="rtl")&&(this._openedBy="keyboard",this.openMenu())}_handleClick(e){this.triggersSubmenu()?(e.stopPropagation(),this.openMenu()):this.toggleMenu()}_handleHover(){this.triggersSubmenu()&&this._parentMaterialMenu&&(this._hoverSubscription=this._parentMaterialMenu._hovered().subscribe(e=>{e===this._menuItemInstance&&!e.disabled&&this._parentMaterialMenu?._panelAnimationState!=="void"&&(this._openedBy="mouse",this._openMenu(!1))}))}static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["","mat-menu-trigger-for",""],["","matMenuTriggerFor",""]],hostAttrs:[1,"mat-mdc-menu-trigger"],hostVars:3,hostBindings:function(t,o){t&1&&_("click",function(a){return o._handleClick(a)})("mousedown",function(a){return o._handleMousedown(a)})("keydown",function(a){return o._handleKeydown(a)}),t&2&&Xt("aria-haspopup",o.menu?"menu":null)("aria-expanded",o.menuOpen)("aria-controls",o.menuOpen?o.menu==null?null:o.menu.panelId:null)},inputs:{_deprecatedMatMenuTriggerFor:[0,"mat-menu-trigger-for","_deprecatedMatMenuTriggerFor"],menu:[0,"matMenuTriggerFor","menu"],menuData:[0,"matMenuTriggerData","menuData"],restoreFocus:[0,"matMenuTriggerRestoreFocus","restoreFocus"]},outputs:{menuOpened:"menuOpened",onMenuOpen:"onMenuOpen",menuClosed:"menuClosed",onMenuClose:"onMenuClose"},exportAs:["matMenuTrigger"],features:[ci]})}return n})();var qe=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275mod=Gt({type:n});static \u0275inj=Ut({imports:[uc,vh,ui,fd]})}return n})();var vre=[[["caption"]],[["colgroup"],["col"]],"*"],Cre=["caption","colgroup, col","*"];function bre(n,i){n&1&&nn(0,2)}function xre(n,i){n&1&&(s(0,"thead",0),mo(1,1),l(),s(2,"tbody",2),mo(3,3)(4,4),l(),s(5,"tfoot",0),mo(6,5),l())}function yre(n,i){n&1&&mo(0,1)(1,3)(2,4)(3,5)}var On=(()=>{class n extends RP{stickyCssClass="mat-mdc-table-sticky";needsPositionStickyOnElement=!1;static \u0275fac=(()=>{let e;return function(o){return(e||(e=bi(n)))(o||n)}})();static \u0275cmp=F({type:n,selectors:[["mat-table"],["table","mat-table",""]],hostAttrs:[1,"mat-mdc-table","mdc-data-table__table"],hostVars:2,hostBindings:function(t,o){t&2&&Ue("mat-table-fixed-layout",o.fixedLayout)},exportAs:["matTable"],features:[Cn([{provide:RP,useExisting:n},{provide:Xl,useExisting:n},{provide:nv,useValue:null}]),ci],ngContentSelectors:Cre,decls:5,vars:2,consts:[["role","rowgroup"],["headerRowOutlet",""],["role","rowgroup",1,"mdc-data-table__content"],["rowOutlet",""],["noDataRowOutlet",""],["footerRowOutlet",""]],template:function(t,o){t&1&&(ii(vre),nn(0),nn(1,1),A(2,bre,1,0),A(3,xre,7,0)(4,yre,4,0)),t&2&&(u(2),O(o._isServer?2:-1),u(),O(o._isNativeHtmlTable?3:4))},dependencies:[AP,IP,NP,OP],styles:[`.mat-mdc-table-sticky{position:sticky !important}mat-table{display:block}mat-header-row{min-height:var(--mat-table-header-container-height, 56px)}mat-row{min-height:var(--mat-table-row-item-container-height, 52px)}mat-footer-row{min-height:var(--mat-table-footer-container-height, 52px)}mat-row,mat-header-row,mat-footer-row{display:flex;border-width:0;border-bottom-width:1px;border-style:solid;align-items:center;box-sizing:border-box}mat-cell:first-of-type,mat-header-cell:first-of-type,mat-footer-cell:first-of-type{padding-left:24px}[dir=rtl] mat-cell:first-of-type:not(:only-of-type),[dir=rtl] mat-header-cell:first-of-type:not(:only-of-type),[dir=rtl] mat-footer-cell:first-of-type:not(:only-of-type){padding-left:0;padding-right:24px}mat-cell:last-of-type,mat-header-cell:last-of-type,mat-footer-cell:last-of-type{padding-right:24px}[dir=rtl] mat-cell:last-of-type:not(:only-of-type),[dir=rtl] mat-header-cell:last-of-type:not(:only-of-type),[dir=rtl] mat-footer-cell:last-of-type:not(:only-of-type){padding-right:0;padding-left:24px}mat-cell,mat-header-cell,mat-footer-cell{flex:1;display:flex;align-items:center;overflow:hidden;word-wrap:break-word;min-height:inherit}.mat-mdc-table{min-width:100%;border:0;border-spacing:0;table-layout:auto;white-space:normal;background-color:var(--mat-table-background-color, var(--mat-sys-surface))}.mat-table-fixed-layout{table-layout:fixed}.mdc-data-table__cell{box-sizing:border-box;overflow:hidden;text-align:start;text-overflow:ellipsis}.mdc-data-table__cell,.mdc-data-table__header-cell{padding:0 16px}.mat-mdc-header-row{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;height:var(--mat-table-header-container-height, 56px);color:var(--mat-table-header-headline-color, var(--mat-sys-on-surface, rgba(0, 0, 0, 0.87)));font-family:var(--mat-table-header-headline-font, var(--mat-sys-title-small-font, Roboto, sans-serif));line-height:var(--mat-table-header-headline-line-height, var(--mat-sys-title-small-line-height));font-size:var(--mat-table-header-headline-size, var(--mat-sys-title-small-size, 14px));font-weight:var(--mat-table-header-headline-weight, var(--mat-sys-title-small-weight, 500))}.mat-mdc-row{height:var(--mat-table-row-item-container-height, 52px);color:var(--mat-table-row-item-label-text-color, var(--mat-sys-on-surface, rgba(0, 0, 0, 0.87)))}.mat-mdc-row,.mdc-data-table__content{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:var(--mat-table-row-item-label-text-font, var(--mat-sys-body-medium-font, Roboto, sans-serif));line-height:var(--mat-table-row-item-label-text-line-height, var(--mat-sys-body-medium-line-height));font-size:var(--mat-table-row-item-label-text-size, var(--mat-sys-body-medium-size, 14px));font-weight:var(--mat-table-row-item-label-text-weight, var(--mat-sys-body-medium-weight))}.mat-mdc-footer-row{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;height:var(--mat-table-footer-container-height, 52px);color:var(--mat-table-row-item-label-text-color, var(--mat-sys-on-surface, rgba(0, 0, 0, 0.87)));font-family:var(--mat-table-footer-supporting-text-font, var(--mat-sys-body-medium-font, Roboto, sans-serif));line-height:var(--mat-table-footer-supporting-text-line-height, var(--mat-sys-body-medium-line-height));font-size:var(--mat-table-footer-supporting-text-size, var(--mat-sys-body-medium-size, 14px));font-weight:var(--mat-table-footer-supporting-text-weight, var(--mat-sys-body-medium-weight));letter-spacing:var(--mat-table-footer-supporting-text-tracking, var(--mat-sys-body-medium-tracking))}.mat-mdc-header-cell{border-bottom-color:var(--mat-table-row-item-outline-color, var(--mat-sys-outline, rgba(0, 0, 0, 0.12)));border-bottom-width:var(--mat-table-row-item-outline-width, 1px);border-bottom-style:solid;letter-spacing:var(--mat-table-header-headline-tracking, var(--mat-sys-title-small-tracking));font-weight:inherit;line-height:inherit;box-sizing:border-box;text-overflow:ellipsis;overflow:hidden;outline:none;text-align:start}.mdc-data-table__row:last-child>.mat-mdc-header-cell{border-bottom:none}.mat-mdc-cell{border-bottom-color:var(--mat-table-row-item-outline-color, var(--mat-sys-outline, rgba(0, 0, 0, 0.12)));border-bottom-width:var(--mat-table-row-item-outline-width, 1px);border-bottom-style:solid;letter-spacing:var(--mat-table-row-item-label-text-tracking, var(--mat-sys-body-medium-tracking));line-height:inherit}.mdc-data-table__row:last-child>.mat-mdc-cell{border-bottom:none}.mat-mdc-footer-cell{letter-spacing:var(--mat-table-row-item-label-text-tracking, var(--mat-sys-body-medium-tracking))}mat-row.mat-mdc-row,mat-header-row.mat-mdc-header-row,mat-footer-row.mat-mdc-footer-row{border-bottom:none}.mat-mdc-table tbody,.mat-mdc-table tfoot,.mat-mdc-table thead,.mat-mdc-cell,.mat-mdc-footer-cell,.mat-mdc-header-row,.mat-mdc-row,.mat-mdc-footer-row,.mat-mdc-table .mat-mdc-header-cell{background:inherit}.mat-mdc-table mat-header-row.mat-mdc-header-row,.mat-mdc-table mat-row.mat-mdc-row,.mat-mdc-table mat-footer-row.mat-mdc-footer-cell{height:unset}mat-header-cell.mat-mdc-header-cell,mat-cell.mat-mdc-cell,mat-footer-cell.mat-mdc-footer-cell{align-self:stretch} -`],encapsulation:2})}return n})(),Nn=(()=>{class n extends bx{static \u0275fac=(()=>{let e;return function(o){return(e||(e=bi(n)))(o||n)}})();static \u0275dir=ft({type:n,selectors:[["","matCellDef",""]],features:[Cn([{provide:bx,useExisting:n}]),ci]})}return n})(),Rn=(()=>{class n extends xx{static \u0275fac=(()=>{let e;return function(o){return(e||(e=bi(n)))(o||n)}})();static \u0275dir=ft({type:n,selectors:[["","matHeaderCellDef",""]],features:[Cn([{provide:xx,useExisting:n}]),ci]})}return n})();var Fn=(()=>{class n extends Om{get name(){return this._name}set name(e){this._setNameInput(e)}_updateColumnCssClassName(){super._updateColumnCssClassName(),this._columnCssClassName.push(`mat-column-${this.cssClassFriendlyName}`)}static \u0275fac=(()=>{let e;return function(o){return(e||(e=bi(n)))(o||n)}})();static \u0275dir=ft({type:n,selectors:[["","matColumnDef",""]],inputs:{name:[0,"matColumnDef","name"]},features:[Cn([{provide:Om,useExisting:n}]),ci]})}return n})(),Ln=(()=>{class n extends n8{static \u0275fac=(()=>{let e;return function(o){return(e||(e=bi(n)))(o||n)}})();static \u0275dir=ft({type:n,selectors:[["mat-header-cell"],["th","mat-header-cell",""]],hostAttrs:["role","columnheader",1,"mat-mdc-header-cell","mdc-data-table__header-cell"],features:[ci]})}return n})();var Bn=(()=>{class n extends i8{static \u0275fac=(()=>{let e;return function(o){return(e||(e=bi(n)))(o||n)}})();static \u0275dir=ft({type:n,selectors:[["mat-cell"],["td","mat-cell",""]],hostAttrs:[1,"mat-mdc-cell","mdc-data-table__cell"],features:[ci]})}return n})();var Vn=(()=>{class n extends iv{static \u0275fac=(()=>{let e;return function(o){return(e||(e=bi(n)))(o||n)}})();static \u0275dir=ft({type:n,selectors:[["","matHeaderRowDef",""]],inputs:{columns:[0,"matHeaderRowDef","columns"],sticky:[2,"matHeaderRowDefSticky","sticky",gt]},features:[Cn([{provide:iv,useExisting:n}]),ci]})}return n})();var zn=(()=>{class n extends yx{static \u0275fac=(()=>{let e;return function(o){return(e||(e=bi(n)))(o||n)}})();static \u0275dir=ft({type:n,selectors:[["","matRowDef",""]],inputs:{columns:[0,"matRowDefColumns","columns"],when:[0,"matRowDefWhen","when"]},features:[Cn([{provide:yx,useExisting:n}]),ci]})}return n})(),jn=(()=>{class n extends DP{static \u0275fac=(()=>{let e;return function(o){return(e||(e=bi(n)))(o||n)}})();static \u0275cmp=F({type:n,selectors:[["mat-header-row"],["tr","mat-header-row",""]],hostAttrs:["role","row",1,"mat-mdc-header-row","mdc-data-table__header-row"],exportAs:["matHeaderRow"],features:[Cn([{provide:DP,useExisting:n}]),ci],decls:1,vars:0,consts:[["cdkCellOutlet",""]],template:function(t,o){t&1&&mo(0,0)},dependencies:[bu],encapsulation:2})}return n})();var $n=(()=>{class n extends PP{static \u0275fac=(()=>{let e;return function(o){return(e||(e=bi(n)))(o||n)}})();static \u0275cmp=F({type:n,selectors:[["mat-row"],["tr","mat-row",""]],hostAttrs:["role","row",1,"mat-mdc-row","mdc-data-table__row"],exportAs:["matRow"],features:[Cn([{provide:PP,useExisting:n}]),ci],decls:1,vars:0,consts:[["cdkCellOutlet",""]],template:function(t,o){t&1&&mo(0,0)},dependencies:[bu],encapsulation:2})}return n})();var yn=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275mod=Gt({type:n});static \u0275inj=Ut({imports:[Sx,ui]})}return n})(),Sre=9007199254740991,fr=class extends Hl{_data;_renderData=new zt([]);_filter=new zt("");_internalPageChanges=new je;_renderChangesSubscription=null;filteredData;get data(){return this._data.value}set data(i){i=Array.isArray(i)?i:[],this._data.next(i),this._renderChangesSubscription||this._filterData(i)}get filter(){return this._filter.value}set filter(i){this._filter.next(i),this._renderChangesSubscription||this._filterData(this.data)}get sort(){return this._sort}set sort(i){this._sort=i,this._updateChangeSubscription()}_sort;get paginator(){return this._paginator}set paginator(i){this._paginator=i,this._updateChangeSubscription()}_paginator;sortingDataAccessor=(i,e)=>{let t=i[e];if(h5(t)){let o=Number(t);return o{let t=e.active,o=e.direction;return!t||o==""?i:i.sort((r,a)=>{let c=this.sortingDataAccessor(r,t),m=this.sortingDataAccessor(a,t),p=typeof c,h=typeof m;p!==h&&(p==="number"&&(c+=""),h==="number"&&(m+=""));let g=0;return c!=null&&m!=null?c>m?g=1:c{let t=e.trim().toLowerCase();return Object.values(i).some(o=>`${o}`.toLowerCase().includes(t))};constructor(i=[]){super(),this._data=new zt(i),this._updateChangeSubscription()}_updateChangeSubscription(){let i=this._sort?En(this._sort.sortChange,this._sort.initialized):_t(null),e=this._paginator?En(this._paginator.page,this._internalPageChanges,this._paginator.initialized):_t(null),t=this._data,o=ir([t,this._filter]).pipe(xt(([c])=>this._filterData(c))),r=ir([o,i]).pipe(xt(([c])=>this._orderData(c))),a=ir([r,e]).pipe(xt(([c])=>this._pageData(c)));this._renderChangesSubscription?.unsubscribe(),this._renderChangesSubscription=a.subscribe(c=>this._renderData.next(c))}_filterData(i){return this.filteredData=this.filter==null||this.filter===""?i:i.filter(e=>this.filterPredicate(e,this.filter)),this.paginator&&this._updatePaginator(this.filteredData.length),this.filteredData}_orderData(i){return this.sort?this.sortData(i.slice(),this.sort):i}_pageData(i){if(!this.paginator)return i;let e=this.paginator.pageIndex*this.paginator.pageSize;return i.slice(e,e+this.paginator.pageSize)}_updatePaginator(i){Promise.resolve().then(()=>{let e=this.paginator;if(e&&(e.length=i,e.pageIndex>0)){let t=Math.ceil(e.length/e.pageSize)-1||0,o=Math.min(e.pageIndex,t);o!==e.pageIndex&&(e.pageIndex=o,this._internalPageChanges.next())}})}connect(){return this._renderChangesSubscription||this._updateChangeSubscription(),this._renderData}disconnect(){this._renderChangesSubscription?.unsubscribe(),this._renderChangesSubscription=null}};var wre=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275cmp=F({type:n,selectors:[["ng-component"]],hostAttrs:["cdk-text-field-style-loader",""],decls:0,vars:0,template:function(t,o){},styles:[`textarea.cdk-textarea-autosize{resize:none}textarea.cdk-textarea-autosize-measuring{padding:2px 0 !important;box-sizing:content-box !important;height:auto !important;overflow:hidden !important}textarea.cdk-textarea-autosize-measuring-firefox{padding:2px 0 !important;box-sizing:content-box !important;height:0 !important}@keyframes cdk-text-field-autofill-start{/*!*/}@keyframes cdk-text-field-autofill-end{/*!*/}.cdk-text-field-autofill-monitored:-webkit-autofill{animation:cdk-text-field-autofill-start 0s 1ms}.cdk-text-field-autofill-monitored:not(:-webkit-autofill){animation:cdk-text-field-autofill-end 0s 1ms} -`],encapsulation:2,changeDetection:0})}return n})(),Mre={passive:!0},hB=(()=>{class n{_platform=f(Zs);_ngZone=f(Pi);_renderer=f(pd).createRenderer(null,null);_styleLoader=f(pr);_monitoredElements=new Map;constructor(){}monitor(e){if(!this._platform.isBrowser)return $r;this._styleLoader.load(wre);let t=$l(e),o=this._monitoredElements.get(t);if(o)return o.subject;let r=new je,a="cdk-text-field-autofilled",c=p=>{p.animationName==="cdk-text-field-autofill-start"&&!t.classList.contains(a)?(t.classList.add(a),this._ngZone.run(()=>r.next({target:p.target,isAutofilled:!0}))):p.animationName==="cdk-text-field-autofill-end"&&t.classList.contains(a)&&(t.classList.remove(a),this._ngZone.run(()=>r.next({target:p.target,isAutofilled:!1})))},m=this._ngZone.runOutsideAngular(()=>(t.classList.add("cdk-text-field-autofill-monitored"),this._renderer.listen(t,"animationstart",c,Mre)));return this._monitoredElements.set(t,{subject:r,unlisten:m}),r}stopMonitoring(e){let t=$l(e),o=this._monitoredElements.get(t);o&&(o.unlisten(),o.subject.complete(),t.classList.remove("cdk-text-field-autofill-monitored"),t.classList.remove("cdk-text-field-autofilled"),this._monitoredElements.delete(t))}ngOnDestroy(){this._monitoredElements.forEach((e,t)=>this.stopMonitoring(t))}static \u0275fac=function(t){return new(t||n)};static \u0275prov=K({token:n,factory:n.\u0275fac,providedIn:"root"})}return n})();var fB=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275mod=Gt({type:n});static \u0275inj=Ut({})}return n})();var gB=new $t("MAT_INPUT_VALUE_ACCESSOR");var kre=["button","checkbox","file","hidden","image","radio","range","reset","submit"],Tre=new $t("MAT_INPUT_CONFIG"),Ee=(()=>{class n{_elementRef=f(Qt);_platform=f(Zs);ngControl=f(S1,{optional:!0,self:!0});_autofillMonitor=f(hB);_ngZone=f(Pi);_formField=f(yh,{optional:!0});_renderer=f(pi);_uid=f(Do).getId("mat-input-");_previousNativeValue;_inputValueAccessor;_signalBasedValueAccessor;_previousPlaceholder=null;_errorStateTracker;_config=f(Tre,{optional:!0});_cleanupIosKeyup;_cleanupWebkitWheel;_isServer=!1;_isNativeSelect=!1;_isTextarea=!1;_isInFormField=!1;focused=!1;stateChanges=new je;controlType="mat-input";autofilled=!1;get disabled(){return this._disabled}set disabled(e){this._disabled=P1(e),this.focused&&(this.focused=!1,this.stateChanges.next())}_disabled=!1;get id(){return this._id}set id(e){this._id=e||this._uid}_id;placeholder;name;get required(){return this._required??this.ngControl?.control?.hasValidator($e.required)??!1}set required(e){this._required=P1(e)}_required;get type(){return this._type}set type(e){this._type=e||"text",this._validateType(),!this._isTextarea&&qT().has(this._type)&&(this._elementRef.nativeElement.type=this._type)}_type="text";get errorStateMatcher(){return this._errorStateTracker.matcher}set errorStateMatcher(e){this._errorStateTracker.matcher=e}userAriaDescribedBy;get value(){return this._signalBasedValueAccessor?this._signalBasedValueAccessor.value():this._inputValueAccessor.value}set value(e){e!==this.value&&(this._signalBasedValueAccessor?this._signalBasedValueAccessor.value.set(e):this._inputValueAccessor.value=e,this.stateChanges.next())}get readonly(){return this._readonly}set readonly(e){this._readonly=P1(e)}_readonly=!1;disabledInteractive;get errorState(){return this._errorStateTracker.errorState}set errorState(e){this._errorStateTracker.errorState=e}_neverEmptyInputTypes=["date","datetime","datetime-local","month","time","week"].filter(e=>qT().has(e));constructor(){let e=f(Gn,{optional:!0}),t=f(Nt,{optional:!0}),o=f(vd),r=f(gB,{optional:!0,self:!0}),a=this._elementRef.nativeElement,c=a.nodeName.toLowerCase();r?$N(r.value)?this._signalBasedValueAccessor=r:this._inputValueAccessor=r:this._inputValueAccessor=a,this._previousNativeValue=this.value,this.id=this.id,this._platform.IOS&&this._ngZone.runOutsideAngular(()=>{this._cleanupIosKeyup=this._renderer.listen(a,"keyup",this._iOSKeyupListener)}),this._errorStateTracker=new O1(o,this.ngControl,t,e,this.stateChanges),this._isServer=!this._platform.isBrowser,this._isNativeSelect=c==="select",this._isTextarea=c==="textarea",this._isInFormField=!!this._formField,this.disabledInteractive=this._config?.disabledInteractive||!1,this._isNativeSelect&&(this.controlType=a.multiple?"mat-native-select-multiple":"mat-native-select"),this._signalBasedValueAccessor&&ks(()=>{this._signalBasedValueAccessor.value(),this.stateChanges.next()})}ngAfterViewInit(){this._platform.isBrowser&&this._autofillMonitor.monitor(this._elementRef.nativeElement).subscribe(e=>{this.autofilled=e.isAutofilled,this.stateChanges.next()})}ngOnChanges(){this.stateChanges.next()}ngOnDestroy(){this.stateChanges.complete(),this._platform.isBrowser&&this._autofillMonitor.stopMonitoring(this._elementRef.nativeElement),this._cleanupIosKeyup?.(),this._cleanupWebkitWheel?.()}ngDoCheck(){this.ngControl&&(this.updateErrorState(),this.ngControl.disabled!==null&&this.ngControl.disabled!==this.disabled&&(this.disabled=this.ngControl.disabled,this.stateChanges.next())),this._dirtyCheckNativeValue(),this._dirtyCheckPlaceholder()}focus(e){this._elementRef.nativeElement.focus(e)}updateErrorState(){this._errorStateTracker.updateErrorState()}_focusChanged(e){if(e!==this.focused){if(!this._isNativeSelect&&e&&this.disabled&&this.disabledInteractive){let t=this._elementRef.nativeElement;t.type==="number"?(t.type="text",t.setSelectionRange(0,0),t.type="number"):t.setSelectionRange(0,0)}this.focused=e,this.stateChanges.next()}}_onInput(){}_dirtyCheckNativeValue(){let e=this._elementRef.nativeElement.value;this._previousNativeValue!==e&&(this._previousNativeValue=e,this.stateChanges.next())}_dirtyCheckPlaceholder(){let e=this._getPlaceholder();if(e!==this._previousPlaceholder){let t=this._elementRef.nativeElement;this._previousPlaceholder=e,e?t.setAttribute("placeholder",e):t.removeAttribute("placeholder")}}_getPlaceholder(){return this.placeholder||null}_validateType(){kre.indexOf(this._type)>-1}_isNeverEmpty(){return this._neverEmptyInputTypes.indexOf(this._type)>-1}_isBadInput(){let e=this._elementRef.nativeElement.validity;return e&&e.badInput}get empty(){return!this._isNeverEmpty()&&!this._elementRef.nativeElement.value&&!this._isBadInput()&&!this.autofilled}get shouldLabelFloat(){if(this._isNativeSelect){let e=this._elementRef.nativeElement,t=e.options[0];return this.focused||e.multiple||!this.empty||!!(e.selectedIndex>-1&&t&&t.label)}else return this.focused&&!this.disabled||!this.empty}get describedByIds(){return this._elementRef.nativeElement.getAttribute("aria-describedby")?.split(" ")||[]}setDescribedByIds(e){let t=this._elementRef.nativeElement;e.length?t.setAttribute("aria-describedby",e.join(" ")):t.removeAttribute("aria-describedby")}onContainerClick(){this.focused||this.focus()}_isInlineSelect(){let e=this._elementRef.nativeElement;return this._isNativeSelect&&(e.multiple||e.size>1)}_iOSKeyupListener=e=>{let t=e.target;!t.value&&t.selectionStart===0&&t.selectionEnd===0&&(t.setSelectionRange(1,1),t.setSelectionRange(0,0))};_getReadonlyAttribute(){return this._isNativeSelect?null:this.readonly||this.disabled&&this.disabledInteractive?"true":null}static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["input","matInput",""],["textarea","matInput",""],["select","matNativeControl",""],["input","matNativeControl",""],["textarea","matNativeControl",""]],hostAttrs:[1,"mat-mdc-input-element"],hostVars:21,hostBindings:function(t,o){t&1&&_("focus",function(){return o._focusChanged(!0)})("blur",function(){return o._focusChanged(!1)})("input",function(){return o._onInput()}),t&2&&(qo("id",o.id)("disabled",o.disabled&&!o.disabledInteractive)("required",o.required),Xt("name",o.name||null)("readonly",o._getReadonlyAttribute())("aria-disabled",o.disabled&&o.disabledInteractive?"true":null)("aria-invalid",o.empty&&o.required?null:o.errorState)("aria-required",o.required)("id",o.id),Ue("mat-input-server",o._isServer)("mat-mdc-form-field-textarea-control",o._isInFormField&&o._isTextarea)("mat-mdc-form-field-input-control",o._isInFormField)("mat-mdc-input-disabled-interactive",o.disabledInteractive)("mdc-text-field__input",o._isInFormField)("mat-mdc-native-select-inline",o._isInlineSelect()))},inputs:{disabled:"disabled",id:"id",placeholder:"placeholder",name:"name",required:"required",type:"type",errorStateMatcher:"errorStateMatcher",userAriaDescribedBy:[0,"aria-describedby","userAriaDescribedBy"],value:"value",readonly:"readonly",disabledInteractive:[2,"disabledInteractive","disabledInteractive",gt]},exportAs:["matInput"],features:[Cn([{provide:A1,useExisting:n}]),dn]})}return n})(),ye=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275mod=Gt({type:n});static \u0275inj=Ut({imports:[we,we,fB,ui]})}return n})();var jm=(()=>{class n{data;dialogRef=f(Ie);templateName=se("");constructor(e){this.data=e,this.templateName.set(e.templateName)}onNoClick(){this.dialogRef.close(!1)}onYesClick(){this.dialogRef.close(!0)}static \u0275fac=function(t){return new(t||n)(rt(Mt))};static \u0275cmp=F({type:n,selectors:[["app-delete-confirmation-dialog"]],decls:9,vars:1,consts:[["mat-dialog-title",""],[1,"mat-mdc-dialog-content"],["mat-dialog-actions",""],["mat-button","",3,"click"],["mat-button","","tabindex","2","mat-raised-button","","color","primary",3,"click"]],template:function(t,o){t&1&&(s(0,"h2",0),d(1,"Delete template"),l(),s(2,"div",1),d(3),l(),s(4,"div",2)(5,"button",3),_("click",function(){return o.onNoClick()}),d(6,"No, cancel"),l(),s(7,"button",4),_("click",function(){return o.onYesClick()}),d(8,"Yes, delete!"),l()()),t&2&&(u(3),te("Are you sure you want to delete template ",o.templateName(),"?"))},dependencies:[ve,Fe,Re,U,pe],encapsulation:2,changeDetection:0})}return n})();var Ere=(n,i)=>i.compute_id;function Dre(n,i){if(n&1&&(s(0,"button",3)(1,"mat-icon"),d(2,"arrow_back"),l()()),n&2){let e=C();b("routerLink","/controller/"+e.controller.id+"/projects")}}function Pre(n,i){if(n&1){let e=z();s(0,"button",13),_("click",function(){T(e);let o=C();return E(o.openAddDialog())}),s(1,"mat-icon"),d(2,"add_circle_outline"),l()()}}function Ire(n,i){n&1&&(s(0,"div",10),d(1,"Loading..."),l())}function Are(n,i){n&1&&(s(0,"div",11)(1,"mat-icon",14),d(2,"cloud_off"),l(),s(3,"p"),d(4,"No computes found. Click + to add one."),l()())}function Ore(n,i){if(n&1&&(s(0,"span",22)(1,"mat-icon",23),d(2,"memory"),l(),d(3),l(),s(4,"span",22)(5,"mat-icon",23),d(6,"storage"),l(),d(7),l(),s(8,"span",22)(9,"mat-icon",23),d(10,"disc_full"),l(),d(11),l()),n&2){let e=C().$implicit,t=C(2);u(3),te(" ",t.formatPercent(e.cpu_usage_percent)," "),u(4),te(" ",t.formatPercent(e.memory_usage_percent)," "),u(4),te(" ",t.formatPercent(e.disk_usage_percent)," ")}}function Nre(n,i){n&1&&(s(0,"span",21),d(1,"Offline"),l())}function Rre(n,i){if(n&1){let e=z();s(0,"button",24),_("click",function(o){return o.stopPropagation()}),s(1,"mat-icon"),d(2,"more_vert"),l()(),s(3,"mat-menu",25,0)(5,"button",26),_("click",function(){T(e);let o=C().$implicit,r=C(2);return E(r.openEditDialog(o))}),s(6,"mat-icon"),d(7,"edit"),l(),s(8,"span"),d(9,"Edit"),l()(),s(10,"button",26),_("click",function(){T(e);let o=C().$implicit,r=C(2);return E(r.connectCompute(o))}),s(11,"mat-icon"),d(12,"link"),l(),s(13,"span"),d(14,"Connect"),l()(),s(15,"button",26),_("click",function(){T(e);let o=C().$implicit,r=C(2);return E(r.deleteCompute(o))}),s(16,"mat-icon"),d(17,"delete"),l(),s(18,"span"),d(19,"Delete"),l()()()}if(n&2){let e=Pe(4);b("matMenuTriggerFor",e)}}function Fre(n,i){if(n&1&&(s(0,"div",15)(1,"mat-icon",16),d(2),l(),s(3,"div",17)(4,"span",18),d(5),l(),s(6,"span",19),d(7),l()(),s(8,"div",20),A(9,Ore,12,3)(10,Nre,2,0,"span",21),l(),A(11,Rre,20,1),l()),n&2){let e=i.$implicit,t=C(2);u(),wn("color",t.getStatusColor(e)),b("matTooltip",e.connected?"Connected":"Disconnected"),u(),te(" ",t.getStatusIcon(e)," "),u(3),$(e.name||e.compute_id),u(2),$(t.formatHost(e)),u(2),O(e.connected?9:10),u(2),O(e.compute_id!=="local"?11:-1)}}function Lre(n,i){if(n&1&&(s(0,"nav",12),Z(1,Fre,12,8,"div",15,Ere),l()),n&2){let e=C();u(),J(e.computes())}}var Bre=(n,i)=>i.key;function Vre(n,i){if(n&1&&(s(0,"mat-option",6),d(1),l()),n&2){let e=i.$implicit;b("value",e.key),u(),$(e.name)}}function zre(n,i){n&1&&(s(0,"mat-error"),d(1,"You must select a protocol"),l())}function jre(n,i){n&1&&(s(0,"mat-error"),d(1,"You must enter a host"),l())}function $re(n,i){n&1&&(s(0,"mat-error"),d(1,"You must enter a port"),l())}function Hre(n,i){n&1&&(s(0,"mat-error"),d(1,"Port must be between 1 and 65535"),l())}var vB=(()=>{class n{route=f(it);controllerService=f(Je);computeService=f(Po);notificationService=f(fc);toasterService=f(ee);dialog=f(nt);cd=f(X);controller;_computes=se([]);computes=mn(()=>[...this._computes()].sort((t,o)=>t.compute_id==="local"?-1:o.compute_id==="local"?1:(t.name||"").localeCompare(o.name||"")));displayedColumns=["status","name","host","connected","cpu","memory","disk","actions"];loading=se(!0);subscription=new go;ngOnInit(){this.loadControllerAndComputes()}ngOnDestroy(){this.subscription.unsubscribe()}loadControllerAndComputes(){let e=this.route.snapshot.paramMap.get("controller_id");this.controllerService.get(parseInt(e,10)).then(t=>{if(this.controller=t,this.cd.markForCheck(),this.notificationService.hasCachedData()){let o=this.notificationService.getCachedComputes();this._computes.set(o),this.loading.set(!1),this.cd.markForCheck()}else this.loadComputes();this.subscription.add(this.notificationService.computeNotificationEmitter.subscribe(o=>{this.handleComputeNotification(o)})),this.subscription.add(this.notificationService.computeCacheUpdated.subscribe(o=>{this._computes.set(o),this.loading.set(!1),this.cd.markForCheck()}))},t=>{let o=t.error?.message||t.message||"Failed to load controller";this.toasterService.error(o),this.loading.set(!1),this.cd.markForCheck()})}handleComputeNotification(e){switch(e.action){case"compute.created":this._computes.update(t=>[...t,e.event]),this.toasterService.success(`Compute "${e.event.name}" added`);break;case"compute.updated":this._computes.update(t=>t.map(o=>o.compute_id===e.event.compute_id?e.event:o));break;case"compute.deleted":this._computes.update(t=>t.filter(o=>o.compute_id!==e.event.compute_id)),this.toasterService.success(`Compute "${e.event.name}" deleted`);break}this.cd.markForCheck()}loadComputes(){this.loading.set(!0),this.computeService.getComputes(this.controller).subscribe({next:e=>{this.notificationService.setInitialComputes(e),this._computes.set(e),this.loading.set(!1),this.cd.markForCheck()},error:e=>{let t=e.error?.message||e.message||"Failed to load computes";this.loading.set(!1),this.toasterService.error(t),this.cd.markForCheck()}})}openAddDialog(){this.dialog.open(_B,{panelClass:["base-dialog-panel","simple-dialog-panel"],autoFocus:!1,disableClose:!0,data:{controller:this.controller}}).afterClosed().subscribe(t=>{t&&this.computeService.createCompute(this.controller,t).subscribe({next:()=>{this.toasterService.success("Compute added successfully"),this.loadComputes()},error:o=>{let r=o.error?.message||o.message||"Failed to add compute";this.toasterService.error(r),this.cd.markForCheck()}})})}openEditDialog(e){this.computeService.getCompute(this.controller,e.compute_id).subscribe({next:t=>{this.dialog.open(_B,{panelClass:["base-dialog-panel","simple-dialog-panel"],autoFocus:!1,disableClose:!0,data:{controller:this.controller,compute:t}}).afterClosed().subscribe(r=>{r&&this.computeService.updateCompute(this.controller,e.compute_id,r).subscribe({next:()=>{this.toasterService.success("Compute updated successfully"),this.loadComputes()},error:a=>{let c=a.error?.message||a.message||"Failed to update compute";this.toasterService.error(c),this.cd.markForCheck()}})})},error:t=>{let o=t.error?.message||t.message||"Failed to load compute details";this.toasterService.error(o),this.cd.markForCheck()}})}deleteCompute(e){this.dialog.open(jm,{panelClass:["base-confirmation-dialog-panel","confirmation-danger-panel"],autoFocus:!1,disableClose:!0,data:{templateName:e.name||e.compute_id}}).afterClosed().subscribe(o=>{o&&this.computeService.deleteCompute(this.controller,e.compute_id).subscribe({next:()=>{this.toasterService.success("Compute deleted successfully"),this.loadComputes()},error:r=>{let a=r.error?.message||r.message||"Failed to delete compute";this.toasterService.error(a),this.cd.markForCheck()}})})}connectCompute(e){this.computeService.connectCompute(this.controller,e.compute_id).subscribe({next:()=>{this.toasterService.success("Connection request sent"),this.computeService.getCompute(this.controller,e.compute_id).subscribe({next:t=>{let o=this.computes().map(r=>r.compute_id===t.compute_id?t:r);this._computes.set(o),this.cd.markForCheck()},error:()=>{this.loadComputes()}})},error:t=>{let o=t.error?.message||t.message||"Failed to connect compute";this.toasterService.error(o),this.cd.markForCheck()}})}getStatusIcon(e){return e.connected?"check_circle":"cancel"}getStatusColor(e){return e.connected?"var(--mat-sys-primary)":"var(--mat-sys-error)"}formatPercent(e){return e!=null?`${e.toFixed(1)}%`:"--"}formatHost(e){return`${e.host}:${e.port}`}static \u0275fac=function(t){return new(t||n)};static \u0275cmp=F({type:n,selectors:[["app-computes"]],decls:15,vars:5,consts:[["menu","matMenu"],[1,"computes"],[1,"computes__header"],["mat-icon-button","",1,"computes__back-btn",3,"routerLink"],[1,"computes__title"],["matTooltip","Add Compute","matTooltipClass","custom-tooltip","mat-icon-button","",1,"computes__add-btn"],[1,"computes__content"],[1,"computes__info"],[1,"computes__info-icon"],[1,"computes__info-text"],[1,"computes__loading"],[1,"computes__empty"],[1,"computes__list"],["matTooltip","Add Compute","matTooltipClass","custom-tooltip","mat-icon-button","",1,"computes__add-btn",3,"click"],[1,"computes__empty-icon"],[1,"computes__list-item"],[1,"computes__list-icon",3,"matTooltip"],[1,"computes__list-info"],[1,"computes__list-name"],[1,"computes__list-host"],[1,"computes__list-stats"],[1,"computes__stat","computes__stat--offline"],[1,"computes__stat"],[1,"computes__stat-icon"],["mat-icon-button","",1,"computes__menu-btn",3,"click","matMenuTriggerFor"],["xPosition","before"],["mat-menu-item","",3,"click"]],template:function(t,o){t&1&&(s(0,"div",1)(1,"header",2),A(2,Dre,3,1,"button",3),s(3,"h1",4),d(4,"Computes"),l(),A(5,Pre,3,0,"button",5),l(),s(6,"main",6)(7,"div",7)(8,"mat-icon",8),d(9,"info"),l(),s(10,"p",9),d(11," Once configured and connected, the backend maintains the connection to Compute nodes automatically. This page is used to add/delete/update node configurations and view node status. "),l()(),A(12,Ire,2,0,"div",10),A(13,Are,5,0,"div",11),A(14,Lre,3,0,"nav",12),l()()),t&2&&(u(2),O(o.controller?2:-1),u(3),O(o.controller?5:-1),u(7),O(o.loading()?12:-1),u(),O(!o.loading()&&!o.computes().length?13:-1),u(),O(!o.loading()&&o.computes().length?14:-1))},dependencies:[ne,dt,pn,U,ze,re,ce,qe,ti,et,An,yn,Et,Vt,ve,we,ye,bt,At],styles:["[_nghost-%COMP%]{display:block;width:100%;background:transparent}.computes__header[_ngcontent-%COMP%]{max-width:800px;margin:0 auto;padding:32px 20px 16px;display:flex;align-items:center;gap:16px;animation:_ngcontent-%COMP%_fadeInSlideIn .4s ease-out}.computes__back-btn[_ngcontent-%COMP%]{width:48px;height:48px;border-radius:12px;color:var(--mat-sys-on-surface);transition:all .2s cubic-bezier(.4,0,.2,1)}.computes__back-btn[_ngcontent-%COMP%]:hover{background-color:color-mix(in srgb,var(--mat-sys-on-surface) 8%,transparent)}.computes__title[_ngcontent-%COMP%]{font-size:32px;font-weight:500;color:var(--mat-sys-on-surface);margin:0;padding-bottom:8px}.computes__add-btn[_ngcontent-%COMP%]{margin-left:auto;color:var(--mat-sys-primary)}.computes__content[_ngcontent-%COMP%]{max-width:800px;margin:0 auto;padding:0 20px 20px;animation:_ngcontent-%COMP%_fadeInSlideIn .4s ease-out .1s both}.computes__info[_ngcontent-%COMP%]{display:flex;gap:12px;padding:16px;margin-bottom:20px;background:var(--mat-sys-primary-container);border-radius:12px;border-left:4px solid var(--mat-sys-primary);animation:_ngcontent-%COMP%_fadeInSlideIn .4s ease-out .15s both}.computes__info-icon[_ngcontent-%COMP%]{color:var(--mat-sys-on-primary-container);flex-shrink:0;font-size:20px;width:20px;height:20px}.computes__info-text[_ngcontent-%COMP%]{margin:0;font-size:14px;color:var(--mat-sys-on-primary-container);line-height:1.5}.computes__loading[_ngcontent-%COMP%], .computes__empty[_ngcontent-%COMP%]{display:flex;flex-direction:column;align-items:center;justify-content:center;height:200px;color:var(--mat-sys-on-surface-variant);background:var(--mat-sys-surface);border-radius:16px;box-shadow:0 8px 32px color-mix(in srgb,var(--mat-sys-shadow) 20%,transparent),0 2px 8px color-mix(in srgb,var(--mat-sys-shadow) 10%,transparent)}.computes__empty-icon[_ngcontent-%COMP%]{font-size:48px;width:48px;height:48px;margin-bottom:16px;color:var(--mat-sys-on-surface-variant)}.computes__list[_ngcontent-%COMP%]{background:var(--mat-sys-surface);border-radius:16px;overflow:hidden;box-shadow:0 8px 32px color-mix(in srgb,var(--mat-sys-shadow) 20%,transparent),0 2px 8px color-mix(in srgb,var(--mat-sys-shadow) 10%,transparent)}.computes__list-item[_ngcontent-%COMP%]{display:flex;align-items:center;gap:16px;padding:16px 24px;border-bottom:1px solid var(--mat-sys-outline-variant);transition:all .2s cubic-bezier(.4,0,.2,1);cursor:pointer}.computes__list-item[_ngcontent-%COMP%]:last-child{border-bottom:none}.computes__list-item[_ngcontent-%COMP%]:hover{background-color:color-mix(in srgb,var(--mat-sys-on-surface) 5%,transparent)}.computes__list-icon[_ngcontent-%COMP%]{flex-shrink:0}.computes__list-info[_ngcontent-%COMP%]{display:flex;flex-direction:column;flex:1;min-width:0}.computes__list-name[_ngcontent-%COMP%]{font-size:16px;font-weight:500;color:var(--mat-sys-on-surface);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.computes__list-host[_ngcontent-%COMP%]{font-size:13px;color:var(--mat-sys-on-surface-variant)}.computes__list-stats[_ngcontent-%COMP%]{display:flex;gap:16px;flex-shrink:0}.computes__stat[_ngcontent-%COMP%]{display:flex;align-items:center;gap:4px;font-size:13px;color:var(--mat-sys-on-surface)}.computes__stat--offline[_ngcontent-%COMP%]{color:var(--mat-sys-on-surface-variant);font-style:italic}.computes__stat-icon[_ngcontent-%COMP%]{font-size:16px;width:16px;height:16px;color:var(--mat-sys-on-surface-variant)}.computes__menu-btn[_ngcontent-%COMP%]{flex-shrink:0}@keyframes _ngcontent-%COMP%_fadeInSlideIn{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@media(max-width:768px){.computes__header[_ngcontent-%COMP%]{padding:24px 16px 12px;gap:12px}.computes__back-btn[_ngcontent-%COMP%]{width:44px;height:44px}.computes__title[_ngcontent-%COMP%]{font-size:24px}.computes__content[_ngcontent-%COMP%]{padding:0 16px 16px}.computes__list-item[_ngcontent-%COMP%]{padding:14px 16px}.computes__list-stats[_ngcontent-%COMP%]{display:none}}"],changeDetection:0})}return n})(),_B=(()=>{class n{dialogRef=f(Ie);data=f(Mt);protocols=[{key:"http",name:"HTTP"},{key:"https",name:"HTTPS"}];computeForm=new br({name:new Ke(""),protocol:new Ke("http",[$e.required]),host:new Ke("",[$e.required]),port:new Ke(3080,[$e.required,$e.min(1),$e.max(65535)]),user:new Ke("gns3"),password:new Ke("gns3")});isEditMode=!1;constructor(){this.data.compute&&(this.isEditMode=!0,this.computeForm.patchValue({name:this.data.compute.name,protocol:this.data.compute.protocol,host:this.data.compute.host,port:this.data.compute.port,user:this.data.compute.user,password:""}))}onSaveClick(){if(!this.computeForm.valid)return;let e=this.computeForm.value,t={protocol:e.protocol,host:e.host,port:e.port,user:e.user||void 0,name:e.name||void 0};e.password&&e.password.trim()!==""&&(t.password=e.password),this.dialogRef.close(t)}onCancelClick(){this.dialogRef.close()}static \u0275fac=function(t){return new(t||n)};static \u0275cmp=F({type:n,selectors:[["app-add-compute-dialog"]],decls:39,vars:8,consts:[["mat-dialog-title",""],[3,"formGroup"],["mat-dialog-content",""],["appearance","fill",1,"full-width-field"],["matInput","","tabindex","1","formControlName","name","placeholder","My Compute"],["formControlName","protocol"],[3,"value"],["matInput","","tabindex","1","formControlName","host","placeholder","192.168.1.100"],["matInput","","type","number","tabindex","1","formControlName","port","placeholder","3080"],["matInput","","tabindex","1","formControlName","user","placeholder","gns3"],["matInput","","type","password","tabindex","1","formControlName","password","placeholder","gns3"],["mat-dialog-actions","","align","end"],["mat-button","","tabindex","-1","color","accent",3,"click"],["mat-button","","tabindex","2","mat-raised-button","","color","primary",3,"click","disabled"]],template:function(t,o){t&1&&(s(0,"h2",0),d(1),l(),s(2,"form",1)(3,"div",2)(4,"mat-form-field",3)(5,"mat-label"),d(6,"Name (optional)"),l(),B(7,"input",4),l(),s(8,"mat-form-field",3)(9,"mat-label"),d(10,"Protocol"),l(),s(11,"mat-select",5),Z(12,Vre,2,2,"mat-option",6,Bre),l(),A(14,zre,2,0,"mat-error"),l(),s(15,"mat-form-field",3)(16,"mat-label"),d(17,"Host"),l(),B(18,"input",7),A(19,jre,2,0,"mat-error"),l(),s(20,"mat-form-field",3)(21,"mat-label"),d(22,"Port"),l(),B(23,"input",8),A(24,$re,2,0,"mat-error"),A(25,Hre,2,0,"mat-error"),l(),s(26,"mat-form-field",3)(27,"mat-label"),d(28,"User"),l(),B(29,"input",9),l(),s(30,"mat-form-field",3)(31,"mat-label"),d(32,"Password"),l(),B(33,"input",10),l()(),s(34,"div",11)(35,"button",12),_("click",function(){return o.onCancelClick()}),d(36,"Cancel"),l(),s(37,"button",13),_("click",function(){return o.onSaveClick()}),d(38),l()()()),t&2&&(u(),$(o.isEditMode?"Edit Compute":"Add Compute"),u(),b("formGroup",o.computeForm),u(10),J(o.protocols),u(2),O(o.computeForm.get("protocol").hasError("required")?14:-1),u(5),O(o.computeForm.get("host").hasError("required")?19:-1),u(5),O(o.computeForm.get("port").hasError("required")?24:-1),u(),O(o.computeForm.get("port").hasError("min")||o.computeForm.get("port").hasError("max")?25:-1),u(12),b("disabled",o.computeForm.invalid),u(),te(" ",o.isEditMode?"Update":"Add"," "))},dependencies:[ne,At,st,Lt,xr,Ot,at,Nt,Bt,ve,Fe,Re,Rt,U,pe,we,ke,ot,hi,ye,Ee,bt,Dt,vt],encapsulation:2,changeDetection:0})}return n})();var Ure=["*"];var Gre=[[["","mat-card-avatar",""],["","matCardAvatar",""]],[["mat-card-title"],["mat-card-subtitle"],["","mat-card-title",""],["","mat-card-subtitle",""],["","matCardTitle",""],["","matCardSubtitle",""]],"*"],Wre=["[mat-card-avatar], [matCardAvatar]",`mat-card-title, mat-card-subtitle, - [mat-card-title], [mat-card-subtitle], - [matCardTitle], [matCardSubtitle]`,"*"],qre=new $t("MAT_CARD_CONFIG"),Sn=(()=>{class n{appearance;constructor(){let e=f(qre,{optional:!0});this.appearance=e?.appearance||"raised"}static \u0275fac=function(t){return new(t||n)};static \u0275cmp=F({type:n,selectors:[["mat-card"]],hostAttrs:[1,"mat-mdc-card","mdc-card"],hostVars:8,hostBindings:function(t,o){t&2&&Ue("mat-mdc-card-outlined",o.appearance==="outlined")("mdc-card--outlined",o.appearance==="outlined")("mat-mdc-card-filled",o.appearance==="filled")("mdc-card--filled",o.appearance==="filled")},inputs:{appearance:"appearance"},exportAs:["matCard"],ngContentSelectors:Ure,decls:1,vars:0,template:function(t,o){t&1&&(ii(),nn(0))},styles:[`.mat-mdc-card{display:flex;flex-direction:column;box-sizing:border-box;position:relative;border-style:solid;border-width:0;background-color:var(--mat-card-elevated-container-color, var(--mat-sys-surface-container-low));border-color:var(--mat-card-elevated-container-color, var(--mat-sys-surface-container-low));border-radius:var(--mat-card-elevated-container-shape, var(--mat-sys-corner-medium));box-shadow:var(--mat-card-elevated-container-elevation, var(--mat-sys-level1))}.mat-mdc-card::after{position:absolute;top:0;left:0;width:100%;height:100%;border:solid 1px rgba(0,0,0,0);content:"";display:block;pointer-events:none;box-sizing:border-box;border-radius:var(--mat-card-elevated-container-shape, var(--mat-sys-corner-medium))}.mat-mdc-card-outlined{background-color:var(--mat-card-outlined-container-color, var(--mat-sys-surface));border-radius:var(--mat-card-outlined-container-shape, var(--mat-sys-corner-medium));border-width:var(--mat-card-outlined-outline-width, 1px);border-color:var(--mat-card-outlined-outline-color, var(--mat-sys-outline-variant));box-shadow:var(--mat-card-outlined-container-elevation, var(--mat-sys-level0))}.mat-mdc-card-outlined::after{border:none}.mat-mdc-card-filled{background-color:var(--mat-card-filled-container-color, var(--mat-sys-surface-container-highest));border-radius:var(--mat-card-filled-container-shape, var(--mat-sys-corner-medium));box-shadow:var(--mat-card-filled-container-elevation, var(--mat-sys-level0))}.mdc-card__media{position:relative;box-sizing:border-box;background-repeat:no-repeat;background-position:center;background-size:cover}.mdc-card__media::before{display:block;content:""}.mdc-card__media:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.mdc-card__media:last-child{border-bottom-left-radius:inherit;border-bottom-right-radius:inherit}.mat-mdc-card-actions{display:flex;flex-direction:row;align-items:center;box-sizing:border-box;min-height:52px;padding:8px}.mat-mdc-card-title{font-family:var(--mat-card-title-text-font, var(--mat-sys-title-large-font));line-height:var(--mat-card-title-text-line-height, var(--mat-sys-title-large-line-height));font-size:var(--mat-card-title-text-size, var(--mat-sys-title-large-size));letter-spacing:var(--mat-card-title-text-tracking, var(--mat-sys-title-large-tracking));font-weight:var(--mat-card-title-text-weight, var(--mat-sys-title-large-weight))}.mat-mdc-card-subtitle{color:var(--mat-card-subtitle-text-color, var(--mat-sys-on-surface));font-family:var(--mat-card-subtitle-text-font, var(--mat-sys-title-medium-font));line-height:var(--mat-card-subtitle-text-line-height, var(--mat-sys-title-medium-line-height));font-size:var(--mat-card-subtitle-text-size, var(--mat-sys-title-medium-size));letter-spacing:var(--mat-card-subtitle-text-tracking, var(--mat-sys-title-medium-tracking));font-weight:var(--mat-card-subtitle-text-weight, var(--mat-sys-title-medium-weight))}.mat-mdc-card-title,.mat-mdc-card-subtitle{display:block;margin:0}.mat-mdc-card-avatar~.mat-mdc-card-header-text .mat-mdc-card-title,.mat-mdc-card-avatar~.mat-mdc-card-header-text .mat-mdc-card-subtitle{padding:16px 16px 0}.mat-mdc-card-header{display:flex;padding:16px 16px 0}.mat-mdc-card-content{display:block;padding:0 16px}.mat-mdc-card-content:first-child{padding-top:16px}.mat-mdc-card-content:last-child{padding-bottom:16px}.mat-mdc-card-title-group{display:flex;justify-content:space-between;width:100%}.mat-mdc-card-avatar{height:40px;width:40px;border-radius:50%;flex-shrink:0;margin-bottom:16px;object-fit:cover}.mat-mdc-card-avatar~.mat-mdc-card-header-text .mat-mdc-card-subtitle,.mat-mdc-card-avatar~.mat-mdc-card-header-text .mat-mdc-card-title{line-height:normal}.mat-mdc-card-sm-image{width:80px;height:80px}.mat-mdc-card-md-image{width:112px;height:112px}.mat-mdc-card-lg-image{width:152px;height:152px}.mat-mdc-card-xl-image{width:240px;height:240px}.mat-mdc-card-subtitle~.mat-mdc-card-title,.mat-mdc-card-title~.mat-mdc-card-subtitle,.mat-mdc-card-header .mat-mdc-card-header-text .mat-mdc-card-title,.mat-mdc-card-header .mat-mdc-card-header-text .mat-mdc-card-subtitle,.mat-mdc-card-title-group .mat-mdc-card-title,.mat-mdc-card-title-group .mat-mdc-card-subtitle{padding-top:0}.mat-mdc-card-content>:last-child:not(.mat-mdc-card-footer){margin-bottom:0}.mat-mdc-card-actions-align-end{justify-content:flex-end} -`],encapsulation:2,changeDetection:0})}return n})(),zc=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["mat-card-title"],["","mat-card-title",""],["","matCardTitle",""]],hostAttrs:[1,"mat-mdc-card-title"]})}return n})();var _l=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["mat-card-content"]],hostAttrs:[1,"mat-mdc-card-content"]})}return n})();var Sy=(()=>{class n{align="start";static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["mat-card-actions"]],hostAttrs:[1,"mat-mdc-card-actions","mdc-card__actions"],hostVars:2,hostBindings:function(t,o){t&2&&Ue("mat-mdc-card-actions-align-end",o.align==="end")},inputs:{align:"align"},exportAs:["matCardActions"]})}return n})(),jc=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275cmp=F({type:n,selectors:[["mat-card-header"]],hostAttrs:[1,"mat-mdc-card-header"],ngContentSelectors:Wre,decls:4,vars:0,consts:[[1,"mat-mdc-card-header-text"]],template:function(t,o){t&1&&(ii(Gre),nn(0),yo(1,"div",0),nn(2,1),Eo(),nn(3,2))},encapsulation:2,changeDetection:0})}return n})();var St=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275mod=Gt({type:n});static \u0275inj=Ut({imports:[ui]})}return n})();var $m=(()=>{class n{dataChange=new zt([]);constructor(){}get data(){return this.dataChange.value}addController(e){let t=this.data.slice();t.push(e),this.dataChange.next(t)}addControllers(e){this.dataChange.next(e)}remove(e){let t=this.data.indexOf(e);t>=0&&(this.data.splice(t,1),this.dataChange.next(this.data.slice()))}find(e){return this.data.find(t=>t.name===e)}findById(e){return this.data.find(t=>t.id===e)}findIndex(e){return this.data.findIndex(t=>t.name===e)}findIndexById(e){return this.data.findIndex(t=>t.id===e)}update(e){let t=this.findIndexById(e.id);t>=0&&(this.data[t]=e,this.dataChange.next(this.data.slice()))}static \u0275fac=function(t){return new(t||n)};static \u0275prov=K({token:n,factory:n.\u0275fac})}return n})();var CB=(n,i)=>i.key;function Qre(n,i){n&1&&(s(0,"mat-error"),d(1,"You must enter a value"),l())}function Xre(n,i){if(n&1&&(s(0,"mat-option",9),d(1),l()),n&2){let e=i.$implicit;b("value",e.key),u(),te(" ",e.name," ")}}function Yre(n,i){if(n&1&&(s(0,"mat-option",9),d(1),l()),n&2){let e=i.$implicit;b("value",e.key),u(),te(" ",e.name," ")}}var wy=(()=>{class n{controllerService=f(Je);controllerDatabase=f($m);route=f(it);router=f(mt);toasterService=f(ee);cdr=f(X);controllerOptionsVisibility=se(!1);controllerIp;controllerPort;projectId;protocols=[{key:"http:",name:"HTTP"},{key:"https:",name:"HTTPS"}];locations=[{key:"local",name:"Local"},{key:"remote",name:"Remote"}];controllerForm=new br({name:new Ke("",[$e.required]),location:new Ke(""),protocol:new Ke("http:")});constructor(){}async ngOnInit(){this.controllerService.isServiceInitialized&&this.getControllers(),this.controllerService.serviceInitialized.subscribe(async e=>{e&&this.getControllers()})}async getControllers(){this.controllerIp=this.route.snapshot.paramMap.get("controller_ip"),this.controllerPort=+this.route.snapshot.paramMap.get("controller_port"),this.projectId=this.route.snapshot.paramMap.get("project_id");try{let t=(await this.controllerService.findAll()).filter(o=>o.host===this.controllerIp&&o.port===this.controllerPort)[0];t?this.router.navigate(["/controller",t.id,"project",this.projectId]):(this.controllerOptionsVisibility.set(!0),this.cdr.markForCheck())}catch(e){let t=e.error?.message||e.message||"Failed to load controllers";this.toasterService.error(t),this.cdr.markForCheck()}}createController(){if(!this.controllerForm.get("name").hasError&&!this.controllerForm.get("location").hasError&&!this.controllerForm.get("protocol").hasError){this.toasterService.error("Please use correct values");return}let e=new F5;e.host=this.controllerIp,e.port=this.controllerPort,e.name=this.controllerForm.get("name").value,e.location=this.controllerForm.get("location").value,e.protocol=this.controllerForm.get("protocol").value,this.controllerService.create(e).then(t=>{this.router.navigate(["/controller",t.id,"project",this.projectId])},t=>{let o=t.error?.message||t.message||"Failed to create controller";this.toasterService.error(o),this.cdr.markForCheck()})}static \u0275fac=function(t){return new(t||n)};static \u0275cmp=F({type:n,selectors:[["app-direct-link"]],decls:22,vars:3,consts:[[1,"content",3,"hidden"],[1,"default-header"],[1,"row"],[1,"col"],[1,"default-content"],[1,"matCard"],[3,"formGroup"],["matInput","","tabindex","1","formControlName","name","placeholder","Name"],["placeholder","Location","formControlName","location"],[3,"value"],["placeholder","Protocol","formControlName","protocol"],[1,"buttons-bar"],["mat-raised-button","","color","primary",3,"click"]],template:function(t,o){t&1&&(s(0,"div",0)(1,"div",1)(2,"div",2)(3,"h1",3),d(4,"Add new controller"),l()()(),s(5,"div",4)(6,"mat-card",5)(7,"form",6)(8,"mat-form-field"),B(9,"input",7),A(10,Qre,2,0,"mat-error"),l(),s(11,"mat-form-field")(12,"mat-select",8),Z(13,Xre,2,2,"mat-option",9,CB),l()(),s(15,"mat-form-field")(16,"mat-select",10),Z(17,Yre,2,2,"mat-option",9,CB),l()()()(),s(19,"div",11)(20,"button",12),_("click",function(){return o.createController()}),d(21,"Add controller"),l()()()()),t&2&&(b("hidden",!o.controllerOptionsVisibility()),u(7),b("formGroup",o.controllerForm),u(3),O(o.controllerForm.get("name").hasError("required")?10:-1),u(3),J(o.locations),u(4),J(o.protocols))},dependencies:[ne,At,st,Lt,Ot,at,Nt,Bt,dt,St,Sn,we,ke,hi,ye,Ee,bt,Dt,vt,Xo,U,pe],styles:["mat-form-field[_ngcontent-%COMP%]{width:100%}"],changeDetection:0})}return n})();var i3=new $t("CdkAccordion"),bB=(()=>{class n{_stateChanges=new je;_openCloseAllActions=new je;id=f(Do).getId("cdk-accordion-");multi=!1;openAll(){this.multi&&this._openCloseAllActions.next(!0)}closeAll(){this._openCloseAllActions.next(!1)}ngOnChanges(e){this._stateChanges.next(e)}ngOnDestroy(){this._stateChanges.complete(),this._openCloseAllActions.complete()}static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["cdk-accordion"],["","cdkAccordion",""]],inputs:{multi:[2,"multi","multi",gt]},exportAs:["cdkAccordion"],features:[Cn([{provide:i3,useExisting:n}]),dn]})}return n})(),xB=(()=>{class n{accordion=f(i3,{optional:!0,skipSelf:!0});_changeDetectorRef=f(X);_expansionDispatcher=f(xh);_openCloseAllSubscription=go.EMPTY;closed=new _e;opened=new _e;destroyed=new _e;expandedChange=new _e;id=f(Do).getId("cdk-accordion-child-");get expanded(){return this._expanded}set expanded(e){if(this._expanded!==e){if(this._expanded=e,this.expandedChange.emit(e),e){this.opened.emit();let t=this.accordion?this.accordion.id:this.id;this._expansionDispatcher.notify(this.id,t)}else this.closed.emit();this._changeDetectorRef.markForCheck()}}_expanded=!1;get disabled(){return this._disabled()}set disabled(e){this._disabled.set(e)}_disabled=se(!1);_removeUniqueSelectionListener=()=>{};constructor(){}ngOnInit(){this._removeUniqueSelectionListener=this._expansionDispatcher.listen((e,t)=>{this.accordion&&!this.accordion.multi&&this.accordion.id===t&&this.id!==e&&(this.expanded=!1)}),this.accordion&&(this._openCloseAllSubscription=this._subscribeToOpenCloseAllActions())}ngOnDestroy(){this.opened.complete(),this.closed.complete(),this.destroyed.emit(),this.destroyed.complete(),this._removeUniqueSelectionListener(),this._openCloseAllSubscription.unsubscribe()}toggle(){this.disabled||(this.expanded=!this.expanded)}close(){this.disabled||(this.expanded=!1)}open(){this.disabled||(this.expanded=!0)}_subscribeToOpenCloseAllActions(){return this.accordion._openCloseAllActions.subscribe(e=>{this.disabled||(this.expanded=e)})}static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["cdk-accordion-item"],["","cdkAccordionItem",""]],inputs:{expanded:[2,"expanded","expanded",gt],disabled:[2,"disabled","disabled",gt]},outputs:{closed:"closed",opened:"opened",destroyed:"destroyed",expandedChange:"expandedChange"},exportAs:["cdkAccordionItem"],features:[Cn([{provide:i3,useValue:void 0}])]})}return n})(),My=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275mod=Gt({type:n});static \u0275inj=Ut({})}return n})();var Kre=["body"],Zre=["bodyWrapper"],Jre=[[["mat-expansion-panel-header"]],"*",[["mat-action-row"]]],eae=["mat-expansion-panel-header","*","mat-action-row"];function tae(n,i){}var nae=[[["mat-panel-title"]],[["mat-panel-description"]],"*"],iae=["mat-panel-title","mat-panel-description","*"];function oae(n,i){n&1&&(yo(0,"span",1),Jn(),yo(1,"svg",2),Ts(2,"path",3),Eo()())}var o3=new $t("MAT_ACCORDION"),yB=new $t("MAT_EXPANSION_PANEL"),rae=(()=>{class n{_template=f(jo);_expansionPanel=f(yB,{optional:!0});constructor(){}static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["ng-template","matExpansionPanelContent",""]]})}return n})(),SB=new $t("MAT_EXPANSION_PANEL_DEFAULT_OPTIONS"),Od=(()=>{class n extends xB{_viewContainerRef=f(Ji);_animationsDisabled=Qo();_document=f(co);_ngZone=f(Pi);_elementRef=f(Qt);_renderer=f(pi);_cleanupTransitionEnd;get hideToggle(){return this._hideToggle||this.accordion&&this.accordion.hideToggle}set hideToggle(e){this._hideToggle=e}_hideToggle=!1;get togglePosition(){return this._togglePosition||this.accordion&&this.accordion.togglePosition}set togglePosition(e){this._togglePosition=e}_togglePosition;afterExpand=new _e;afterCollapse=new _e;_inputChanges=new je;accordion=f(o3,{optional:!0,skipSelf:!0});_lazyContent;_body;_bodyWrapper;_portal;_headerId=f(Do).getId("mat-expansion-panel-header-");constructor(){super();let e=f(SB,{optional:!0});this._expansionDispatcher=f(xh),e&&(this.hideToggle=e.hideToggle)}_hasSpacing(){return this.accordion?this.expanded&&this.accordion.displayMode==="default":!1}_getExpandedState(){return this.expanded?"expanded":"collapsed"}toggle(){this.expanded=!this.expanded}close(){this.expanded=!1}open(){this.expanded=!0}ngAfterContentInit(){this._lazyContent&&this._lazyContent._expansionPanel===this&&this.opened.pipe(xi(null),Yn(()=>this.expanded&&!this._portal),Gi(1)).subscribe(()=>{this._portal=new im(this._lazyContent._template,this._viewContainerRef)}),this._setupAnimationEvents()}ngOnChanges(e){this._inputChanges.next(e)}ngOnDestroy(){super.ngOnDestroy(),this._cleanupTransitionEnd?.(),this._inputChanges.complete()}_containsFocus(){if(this._body){let e=this._document.activeElement,t=this._body.nativeElement;return e===t||t.contains(e)}return!1}_transitionEndListener=({target:e,propertyName:t})=>{e===this._bodyWrapper?.nativeElement&&t==="grid-template-rows"&&this._ngZone.run(()=>{this.expanded?this.afterExpand.emit():this.afterCollapse.emit()})};_setupAnimationEvents(){this._ngZone.runOutsideAngular(()=>{this._animationsDisabled?(this.opened.subscribe(()=>this._ngZone.run(()=>this.afterExpand.emit())),this.closed.subscribe(()=>this._ngZone.run(()=>this.afterCollapse.emit()))):setTimeout(()=>{let e=this._elementRef.nativeElement;this._cleanupTransitionEnd=this._renderer.listen(e,"transitionend",this._transitionEndListener),e.classList.add("mat-expansion-panel-animations-enabled")},200)})}static \u0275fac=function(t){return new(t||n)};static \u0275cmp=F({type:n,selectors:[["mat-expansion-panel"]],contentQueries:function(t,o,r){if(t&1&&Vi(r,rae,5),t&2){let a;pt(a=ut())&&(o._lazyContent=a.first)}},viewQuery:function(t,o){if(t&1&&Dn(Kre,5)(Zre,5),t&2){let r;pt(r=ut())&&(o._body=r.first),pt(r=ut())&&(o._bodyWrapper=r.first)}},hostAttrs:[1,"mat-expansion-panel"],hostVars:4,hostBindings:function(t,o){t&2&&Ue("mat-expanded",o.expanded)("mat-expansion-panel-spacing",o._hasSpacing())},inputs:{hideToggle:[2,"hideToggle","hideToggle",gt],togglePosition:"togglePosition"},outputs:{afterExpand:"afterExpand",afterCollapse:"afterCollapse"},exportAs:["matExpansionPanel"],features:[Cn([{provide:o3,useValue:void 0},{provide:yB,useExisting:n}]),ci,dn],ngContentSelectors:eae,decls:9,vars:4,consts:[["bodyWrapper",""],["body",""],[1,"mat-expansion-panel-content-wrapper"],["role","region",1,"mat-expansion-panel-content",3,"id"],[1,"mat-expansion-panel-body"],[3,"cdkPortalOutlet"]],template:function(t,o){t&1&&(ii(Jre),nn(0),s(1,"div",2,0)(3,"div",3,1)(5,"div",4),nn(6,1),Se(7,tae,0,0,"ng-template",5),l(),nn(8,2),l()()),t&2&&(u(),Xt("inert",o.expanded?null:""),u(2),b("id",o.id),Xt("aria-labelledby",o._headerId),u(4),b("cdkPortalOutlet",o._portal))},dependencies:[fh],styles:[`.mat-expansion-panel{box-sizing:content-box;display:block;margin:0;overflow:hidden}.mat-expansion-panel.mat-expansion-panel-animations-enabled{transition:margin 225ms cubic-bezier(0.4, 0, 0.2, 1),box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1)}.mat-expansion-panel{position:relative;background:var(--mat-expansion-container-background-color, var(--mat-sys-surface));color:var(--mat-expansion-container-text-color, var(--mat-sys-on-surface));border-radius:var(--mat-expansion-container-shape, 12px)}.mat-expansion-panel:not([class*=mat-elevation-z]){box-shadow:var(--mat-expansion-container-elevation-shadow, 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12))}.mat-accordion .mat-expansion-panel:not(.mat-expanded),.mat-accordion .mat-expansion-panel:not(.mat-expansion-panel-spacing){border-radius:0}.mat-accordion .mat-expansion-panel:first-of-type{border-top-right-radius:var(--mat-expansion-container-shape, 12px);border-top-left-radius:var(--mat-expansion-container-shape, 12px)}.mat-accordion .mat-expansion-panel:last-of-type{border-bottom-right-radius:var(--mat-expansion-container-shape, 12px);border-bottom-left-radius:var(--mat-expansion-container-shape, 12px)}@media(forced-colors: active){.mat-expansion-panel{outline:solid 1px}}.mat-expansion-panel-content-wrapper{display:grid;grid-template-rows:0fr;grid-template-columns:100%}.mat-expansion-panel-animations-enabled .mat-expansion-panel-content-wrapper{transition:grid-template-rows 225ms cubic-bezier(0.4, 0, 0.2, 1)}.mat-expansion-panel.mat-expanded>.mat-expansion-panel-content-wrapper{grid-template-rows:1fr}@supports not (grid-template-rows: 0fr){.mat-expansion-panel-content-wrapper{height:0}.mat-expansion-panel.mat-expanded>.mat-expansion-panel-content-wrapper{height:auto}}@media print{.mat-expansion-panel-content-wrapper{height:0}.mat-expansion-panel.mat-expanded>.mat-expansion-panel-content-wrapper{height:auto}}.mat-expansion-panel-content{display:flex;flex-direction:column;overflow:visible;min-height:0;visibility:hidden}.mat-expansion-panel-animations-enabled .mat-expansion-panel-content{transition:visibility 190ms linear}.mat-expansion-panel.mat-expanded>.mat-expansion-panel-content-wrapper>.mat-expansion-panel-content{visibility:visible}.mat-expansion-panel-content{font-family:var(--mat-expansion-container-text-font, var(--mat-sys-body-large-font));font-size:var(--mat-expansion-container-text-size, var(--mat-sys-body-large-size));font-weight:var(--mat-expansion-container-text-weight, var(--mat-sys-body-large-weight));line-height:var(--mat-expansion-container-text-line-height, var(--mat-sys-body-large-line-height));letter-spacing:var(--mat-expansion-container-text-tracking, var(--mat-sys-body-large-tracking))}.mat-expansion-panel-body{padding:0 24px 16px}.mat-expansion-panel-spacing{margin:16px 0}.mat-accordion>.mat-expansion-panel-spacing:first-child,.mat-accordion>*:first-child:not(.mat-expansion-panel) .mat-expansion-panel-spacing{margin-top:0}.mat-accordion>.mat-expansion-panel-spacing:last-child,.mat-accordion>*:last-child:not(.mat-expansion-panel) .mat-expansion-panel-spacing{margin-bottom:0}.mat-action-row{border-top-style:solid;border-top-width:1px;display:flex;flex-direction:row;justify-content:flex-end;padding:16px 8px 16px 24px;border-top-color:var(--mat-expansion-actions-divider-color, var(--mat-sys-outline))}.mat-action-row .mat-button-base,.mat-action-row .mat-mdc-button-base{margin-left:8px}[dir=rtl] .mat-action-row .mat-button-base,[dir=rtl] .mat-action-row .mat-mdc-button-base{margin-left:0;margin-right:8px} -`],encapsulation:2,changeDetection:0})}return n})();var Nd=(()=>{class n{panel=f(Od,{host:!0});_element=f(Qt);_focusMonitor=f(Fa);_changeDetectorRef=f(X);_parentChangeSubscription=go.EMPTY;constructor(){f(pr).load(sa);let e=this.panel,t=f(SB,{optional:!0}),o=f(new Ks("tabindex"),{optional:!0}),r=e.accordion?e.accordion._stateChanges.pipe(Yn(a=>!!(a.hideToggle||a.togglePosition))):$r;this.tabIndex=parseInt(o||"")||0,this._parentChangeSubscription=En(e.opened,e.closed,r,e._inputChanges.pipe(Yn(a=>!!(a.hideToggle||a.disabled||a.togglePosition)))).subscribe(()=>this._changeDetectorRef.markForCheck()),e.closed.pipe(Yn(()=>e._containsFocus())).subscribe(()=>this._focusMonitor.focusVia(this._element,"program")),t&&(this.expandedHeight=t.expandedHeight,this.collapsedHeight=t.collapsedHeight)}expandedHeight;collapsedHeight;tabIndex=0;get disabled(){return this.panel.disabled}_toggle(){this.disabled||this.panel.toggle()}_isExpanded(){return this.panel.expanded}_getExpandedState(){return this.panel._getExpandedState()}_getPanelId(){return this.panel.id}_getTogglePosition(){return this.panel.togglePosition}_showToggle(){return!this.panel.hideToggle&&!this.panel.disabled}_getHeaderHeight(){let e=this._isExpanded();return e&&this.expandedHeight?this.expandedHeight:!e&&this.collapsedHeight?this.collapsedHeight:null}_keydown(e){switch(e.keyCode){case 32:case 13:Ca(e)||(e.preventDefault(),this._toggle());break;default:this.panel.accordion&&this.panel.accordion._handleHeaderKeydown(e);return}}focus(e,t){e?this._focusMonitor.focusVia(this._element,e,t):this._element.nativeElement.focus(t)}ngAfterViewInit(){this._focusMonitor.monitor(this._element).subscribe(e=>{e&&this.panel.accordion&&this.panel.accordion._handleHeaderFocus(this)})}ngOnDestroy(){this._parentChangeSubscription.unsubscribe(),this._focusMonitor.stopMonitoring(this._element)}static \u0275fac=function(t){return new(t||n)};static \u0275cmp=F({type:n,selectors:[["mat-expansion-panel-header"]],hostAttrs:["role","button",1,"mat-expansion-panel-header","mat-focus-indicator"],hostVars:13,hostBindings:function(t,o){t&1&&_("click",function(){return o._toggle()})("keydown",function(a){return o._keydown(a)}),t&2&&(Xt("id",o.panel._headerId)("tabindex",o.disabled?-1:o.tabIndex)("aria-controls",o._getPanelId())("aria-expanded",o._isExpanded())("aria-disabled",o.panel.disabled),wn("height",o._getHeaderHeight()),Ue("mat-expanded",o._isExpanded())("mat-expansion-toggle-indicator-after",o._getTogglePosition()==="after")("mat-expansion-toggle-indicator-before",o._getTogglePosition()==="before"))},inputs:{expandedHeight:"expandedHeight",collapsedHeight:"collapsedHeight",tabIndex:[2,"tabIndex","tabIndex",e=>e==null?0:Lo(e)]},ngContentSelectors:iae,decls:5,vars:3,consts:[[1,"mat-content"],[1,"mat-expansion-indicator"],["xmlns","http://www.w3.org/2000/svg","viewBox","0 -960 960 960","aria-hidden","true","focusable","false"],["d","M480-345 240-585l56-56 184 184 184-184 56 56-240 240Z"]],template:function(t,o){t&1&&(ii(nae),yo(0,"span",0),nn(1),nn(2,1),nn(3,2),Eo(),A(4,oae,3,0,"span",1)),t&2&&(Ue("mat-content-hide-toggle",!o._showToggle()),u(4),O(o._showToggle()?4:-1))},styles:[`.mat-expansion-panel-header{display:flex;flex-direction:row;align-items:center;padding:0 24px;border-radius:inherit}.mat-expansion-panel-animations-enabled .mat-expansion-panel-header{transition:height 225ms cubic-bezier(0.4, 0, 0.2, 1)}.mat-expansion-panel-header::before{border-radius:inherit}.mat-expansion-panel-header{height:var(--mat-expansion-header-collapsed-state-height, 48px);font-family:var(--mat-expansion-header-text-font, var(--mat-sys-title-medium-font));font-size:var(--mat-expansion-header-text-size, var(--mat-sys-title-medium-size));font-weight:var(--mat-expansion-header-text-weight, var(--mat-sys-title-medium-weight));line-height:var(--mat-expansion-header-text-line-height, var(--mat-sys-title-medium-line-height));letter-spacing:var(--mat-expansion-header-text-tracking, var(--mat-sys-title-medium-tracking))}.mat-expansion-panel-header.mat-expanded{height:var(--mat-expansion-header-expanded-state-height, 64px)}.mat-expansion-panel-header[aria-disabled=true]{color:var(--mat-expansion-header-disabled-state-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent))}.mat-expansion-panel-header:not([aria-disabled=true]){cursor:pointer}.mat-expansion-panel:not(.mat-expanded) .mat-expansion-panel-header:not([aria-disabled=true]):hover{background:var(--mat-expansion-header-hover-state-layer-color, color-mix(in srgb, var(--mat-sys-on-surface) calc(var(--mat-sys-hover-state-layer-opacity) * 100%), transparent))}@media(hover: none){.mat-expansion-panel:not(.mat-expanded) .mat-expansion-panel-header:not([aria-disabled=true]):hover{background:var(--mat-expansion-container-background-color, var(--mat-sys-surface))}}.mat-expansion-panel .mat-expansion-panel-header:not([aria-disabled=true]).cdk-keyboard-focused,.mat-expansion-panel .mat-expansion-panel-header:not([aria-disabled=true]).cdk-program-focused{background:var(--mat-expansion-header-focus-state-layer-color, color-mix(in srgb, var(--mat-sys-on-surface) calc(var(--mat-sys-focus-state-layer-opacity) * 100%), transparent))}.mat-expansion-panel-header._mat-animation-noopable{transition:none}.mat-expansion-panel-header:focus,.mat-expansion-panel-header:hover{outline:none}.mat-expansion-panel-header.mat-expanded:focus,.mat-expansion-panel-header.mat-expanded:hover{background:inherit}.mat-expansion-panel-header.mat-expansion-toggle-indicator-before{flex-direction:row-reverse}.mat-expansion-panel-header.mat-expansion-toggle-indicator-before .mat-expansion-indicator{margin:0 16px 0 0}[dir=rtl] .mat-expansion-panel-header.mat-expansion-toggle-indicator-before .mat-expansion-indicator{margin:0 0 0 16px}.mat-content{display:flex;flex:1;flex-direction:row;overflow:hidden}.mat-content.mat-content-hide-toggle{margin-right:8px}[dir=rtl] .mat-content.mat-content-hide-toggle{margin-right:0;margin-left:8px}.mat-expansion-toggle-indicator-before .mat-content.mat-content-hide-toggle{margin-left:24px;margin-right:0}[dir=rtl] .mat-expansion-toggle-indicator-before .mat-content.mat-content-hide-toggle{margin-right:24px;margin-left:0}.mat-expansion-panel-header-title{color:var(--mat-expansion-header-text-color, var(--mat-sys-on-surface))}.mat-expansion-panel-header-title,.mat-expansion-panel-header-description{display:flex;flex-grow:1;flex-basis:0;margin-right:16px;align-items:center}[dir=rtl] .mat-expansion-panel-header-title,[dir=rtl] .mat-expansion-panel-header-description{margin-right:0;margin-left:16px}.mat-expansion-panel-header[aria-disabled=true] .mat-expansion-panel-header-title,.mat-expansion-panel-header[aria-disabled=true] .mat-expansion-panel-header-description{color:inherit}.mat-expansion-panel-header-description{flex-grow:2;color:var(--mat-expansion-header-description-color, var(--mat-sys-on-surface-variant))}.mat-expansion-panel-animations-enabled .mat-expansion-indicator{transition:transform 225ms cubic-bezier(0.4, 0, 0.2, 1)}.mat-expansion-panel-header.mat-expanded .mat-expansion-indicator{transform:rotate(180deg)}.mat-expansion-indicator::after{border-style:solid;border-width:0 2px 2px 0;content:"";padding:3px;transform:rotate(45deg);vertical-align:middle;color:var(--mat-expansion-header-indicator-color, var(--mat-sys-on-surface-variant));display:var(--mat-expansion-legacy-header-indicator-display, none)}.mat-expansion-indicator svg{width:24px;height:24px;margin:0 -8px;vertical-align:middle;fill:var(--mat-expansion-header-indicator-color, var(--mat-sys-on-surface-variant));display:var(--mat-expansion-header-indicator-display, inline-block)}@media(forced-colors: active){.mat-expansion-panel-content{border-top:1px solid;border-top-left-radius:0;border-top-right-radius:0}} -`],encapsulation:2,changeDetection:0})}return n})(),ky=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["mat-panel-description"]],hostAttrs:[1,"mat-expansion-panel-header-description"]})}return n})(),Hm=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275dir=ft({type:n,selectors:[["mat-panel-title"]],hostAttrs:[1,"mat-expansion-panel-header-title"]})}return n})(),Um=(()=>{class n extends bB{_keyManager;_ownHeaders=new jl;_headers;hideToggle=!1;displayMode="default";togglePosition="after";ngAfterContentInit(){this._headers.changes.pipe(xi(this._headers)).subscribe(e=>{this._ownHeaders.reset(e.filter(t=>t.panel.accordion===this)),this._ownHeaders.notifyOnChanges()}),this._keyManager=new rm(this._ownHeaders).withWrap().withHomeAndEnd()}_handleHeaderKeydown(e){this._keyManager.onKeydown(e)}_handleHeaderFocus(e){this._keyManager.updateActiveItem(e)}ngOnDestroy(){super.ngOnDestroy(),this._keyManager?.destroy(),this._ownHeaders.destroy()}static \u0275fac=(()=>{let e;return function(o){return(e||(e=bi(n)))(o||n)}})();static \u0275dir=ft({type:n,selectors:[["mat-accordion"]],contentQueries:function(t,o,r){if(t&1&&Vi(r,Nd,5),t&2){let a;pt(a=ut())&&(o._headers=a)}},hostAttrs:[1,"mat-accordion"],hostVars:2,hostBindings:function(t,o){t&2&&Ue("mat-accordion-multi",o.multi)},inputs:{hideToggle:[2,"hideToggle","hideToggle",gt],displayMode:"displayMode",togglePosition:"togglePosition"},exportAs:["matAccordion"],features:[Cn([{provide:o3,useExisting:n}]),ci]})}return n})(),vl=(()=>{class n{static \u0275fac=function(t){return new(t||n)};static \u0275mod=Gt({type:n});static \u0275inj=Ut({imports:[My,gh,ui]})}return n})();var Ey=(()=>{class n{httpClient=f(mc);sanitizer=f(Cr);toasterService=f(ee);cd=f(X);thirdpartylicenses=se("");releasenotes=se("");ngOnInit(){this.httpClient.get(window.location.href+"/3rdpartylicenses.txt",{responseType:"text"}).subscribe({next:e=>{let t=e.replace(new RegExp(` -`,"g"),"
");this.thirdpartylicenses.set(this.sanitizer.bypassSecurityTrustHtml(t))},error:e=>{if(e.status===404)this.thirdpartylicenses.set("Download Solar-PuTTY");else{let t=e.error?.message||e.message||"Failed to load third party licenses";this.toasterService.error(t)}this.cd.markForCheck()}}),this.httpClient.get("ReleaseNotes.txt",{responseType:"text"}).subscribe({next:e=>{let t=e.replace(new RegExp(` -`,"g"),"
");this.releasenotes.set(this.sanitizer.bypassSecurityTrustHtml(t))},error:e=>{let t=e.error?.message||e.message||"Failed to load release notes";this.toasterService.error(t),this.cd.markForCheck()}})}goToDocumentation(){window.location.href="https://docs.gns3.com/docs/"}static \u0275fac=function(t){return new(t||n)};static \u0275cmp=F({type:n,selectors:[["app-help"]],decls:39,vars:2,consts:[[1,"help"],[1,"help__header"],[1,"help__title"],[1,"help__content"],[1,"help__card"],["href","https://downloads.solarwinds.com/solarwinds/GNS3/Solar-PuTTY/Solar-PuTTY-Optional.exe",1,"help__link"],[3,"innerHTML"],["mat-button","","color","primary",1,"help__doc-button",3,"click"]],template:function(t,o){t&1&&(s(0,"div",0)(1,"header",1)(2,"h1",2),d(3,"Help"),l()(),s(4,"main",3)(5,"section",4)(6,"mat-accordion")(7,"mat-expansion-panel")(8,"mat-expansion-panel-header")(9,"mat-panel-title"),d(10," Useful shortcuts "),l()(),s(11,"mat-list")(12,"mat-list-item"),d(13," ctrl + + to zoom in "),l(),s(14,"mat-list-item"),d(15," ctrl + - to zoom out "),l(),s(16,"mat-list-item"),d(17," ctrl + 0 to reset zoom "),l(),s(18,"mat-list-item"),d(19," ctrl + h to hide toolbar "),l(),s(20,"mat-list-item"),d(21," ctrl + a to select all items on map "),l(),s(22,"mat-list-item"),d(23," ctrl + shift + a to deselect all items on map "),l(),s(24,"mat-list-item"),d(25," ctrl + shift + s to go to preferences "),l()()(),s(26,"mat-expansion-panel")(27,"mat-expansion-panel-header")(28,"mat-panel-title"),d(29," Third party components "),l()(),s(30,"a",5),B(31,"div",6),l()(),s(32,"mat-expansion-panel")(33,"mat-expansion-panel-header")(34,"mat-panel-title"),d(35," Release notes "),l()(),B(36,"div",6),l()()(),s(37,"button",7),_("click",function(){return o.goToDocumentation()}),d(38," Go to documentation "),l()()()),t&2&&(u(31),b("innerHTML",o.thirdpartylicenses(),Lp),u(5),b("innerHTML",o.releasenotes(),Lp))},dependencies:[U,pe,vl,Um,Od,Nd,Hm,no,X5,am],styles:["[_nghost-%COMP%]{display:block;width:100%;background:transparent}.help__header[_ngcontent-%COMP%]{max-width:800px;margin:0 auto;padding:32px 20px 16px;animation:_ngcontent-%COMP%_fadeInSlideIn .4s ease-out}.help__title[_ngcontent-%COMP%]{font-size:32px;font-weight:500;color:var(--mat-sys-on-surface);margin:0;padding-bottom:8px}.help__content[_ngcontent-%COMP%]{max-width:800px;margin:0 auto;padding:0 20px 20px;animation:_ngcontent-%COMP%_fadeInSlideIn .4s ease-out .1s both}.help__card[_ngcontent-%COMP%]{background:var(--mat-sys-surface);border-radius:16px;overflow:hidden;box-shadow:0 8px 32px color-mix(in srgb,var(--mat-sys-shadow) 20%,transparent),0 2px 8px color-mix(in srgb,var(--mat-sys-shadow) 10%,transparent)}.mat-expansion-panel[_ngcontent-%COMP%]{box-shadow:none;border-bottom:1px solid var(--mat-sys-outline-variant)}.mat-expansion-panel[_ngcontent-%COMP%]:first-of-type{border-top:none}.mat-expansion-panel-header[_ngcontent-%COMP%]{font-weight:500;color:var(--mat-sys-on-surface)}.mat-expansion-panel-header-title[_ngcontent-%COMP%]{color:var(--mat-sys-on-surface)}.help__doc-button[_ngcontent-%COMP%]{display:block;width:100%;margin-top:20px;height:48px;border-radius:12px;font-size:16px;font-weight:500;text-transform:none;letter-spacing:.5px}.help__link[_ngcontent-%COMP%]{color:var(--mat-sys-primary);text-decoration:none;font-size:14px;font-weight:400}.help__link[_ngcontent-%COMP%]:hover{text-decoration:underline}@keyframes _ngcontent-%COMP%_fadeInSlideIn{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@media(max-width:768px){.help__header[_ngcontent-%COMP%]{padding:24px 16px 12px}.help__title[_ngcontent-%COMP%]{font-size:24px}.help__content[_ngcontent-%COMP%]{padding:0 16px 16px}.help__doc-button[_ngcontent-%COMP%]{height:44px;font-size:15px}}"],changeDetection:0})}return n})();var Dy=(()=>{class n{constructor(){}isWindows(){return navigator.platform.indexOf("Win")>-1}isLinux(){return navigator.platform.indexOf("Linux")>-1}isDarwin(){return navigator.platform.indexOf("Mac")>-1}static \u0275fac=function(t){return new(t||n)};static \u0275prov=K({token:n,factory:n.\u0275fac})}return n})();var Py=(()=>{class n{platformService;constructor(e){this.platformService=e}get(){return this.platformService.isWindows()?this.getForWindows():this.platformService.isDarwin()?this.getForDarwin():this.getForLinux()}getForWindows(){let e=[{name:"Wireshark",locations:["C:\\Program Files\\Wireshark\\Wireshark.exe"],type:"web",resource:"https://1.na.dl.wireshark.org/win64/all-versions/Wireshark-win64-2.6.3.exe",binary:"Wireshark.exe",sudo:!0,installation_arguments:[],installed:!1,installer:!0}],t={name:"SolarPuTTY",locations:["SolarPuTTY.exe","external\\SolarPuTTY.exe"],type:"web",resource:"",binary:"SolarPuTTY.exe",sudo:!1,installation_arguments:["--only-ask"],installed:!1,installer:!1};return Fi.solarputty_download_url&&(t.resource=Fi.solarputty_download_url,e.push(t)),e}getForLinux(){return[]}getForDarwin(){return[]}static \u0275fac=function(t){return new(t||n)(ge(Dy))};static \u0275prov=K({token:n,factory:n.\u0275fac})}return n})();var Iy=(()=>{class n{externalSoftwareDefinition;constructor(e){this.externalSoftwareDefinition=e}list(){return this.externalSoftwareDefinition.get().map(t=>(t.installed=!1,t))}static \u0275fac=function(t){return new(t||n)(ge(Py))};static \u0275prov=K({token:n,factory:n.\u0275fac})}return n})();var lae=(n,i)=>({hidden:n,lightTheme:i}),cae=/(.*)<\/a>(.*)\s*