diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5a87a2a..1bb70ec 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,13 +11,28 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Install pnpm + uses: pnpm/action-setup@v6 + + - name: Install Node.js + uses: actions/setup-node@v7 + with: + node-version: 24 + cache: pnpm + + - name: Build frontend plugin + run: | + pnpm install --frozen-lockfile + pnpm check + pnpm build + - name: Install uv uses: astral-sh/setup-uv@v5 with: enable-cache: true - - name: Install dependencies - run: uv sync --locked + - name: Install Python dependencies + run: uv sync --locked --all-packages - name: Run unit and integration tests run: uv run pytest diff --git a/.gitignore b/.gitignore index f68a799..95aa316 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,10 @@ # Automatically generated directories and files output cache + +# tangram frontend plugin builds (example_plugins/tangram/*) +node_modules/ +dist-frontend/ bluesky/resources/grib bluesky/resources/netcdf diff --git a/AGENTS.md b/AGENTS.md index 96b26de..7dc8769 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,7 +18,13 @@ uv run pytest -m api tests/test_api.py # REST API tests — spawn a separate uv run ruff check . # lint uv run ruff format . # format (line-length 100) -uv run pyright # type check (standard mode) +uv run pyright # type check (standard mode; covers example_plugins EXCEPT example_plugins/tangram) + +# tangram frontend plugin — its own self-contained uv + pnpm workspace (separate .venv); +# all commands run from example_plugins/tangram/ (see its justfile) +cd example_plugins/tangram && just check # ruff + pyright + pnpm (eslint + vue-tsc + tsc) +cd example_plugins/tangram && just fmt # ruff --fix/format + pnpm lint:fix +cd example_plugins/tangram && pnpm build # bundle each plugin into dist-frontend/ uv run minisky commands docs # regenerate docs/reference/commands.md after changing commands uv run minisky docs serve # docs live preview @@ -41,7 +47,7 @@ The FastAPI app lives in `minisky/server.py`; `minisky server` is the CLI entry Full details in `docs/architecture.md` — read it before making structural changes. The essentials: -**Singletons.** `minisky.init()` constructs module-level singletons everything else references: `sim` (clock/state machine), `traf` (all aircraft state + flight-dynamics update), `runner` (async loop stepping at a controllable rate), `scr` (`ConsoleIO` output buffer), `navdb` (waypoints/airports/airways from parquet). They are `None` until `init()` runs. Call `load_plugins()` after `init()` to activate plugins from `settings.yml`. +**Singletons.** `minisky.init()` constructs module-level singletons everything else references: `sim` (clock/state machine), `traf` (all aircraft state + flight-dynamics update), `runner` (async loop stepping at a controllable rate), `scr` (`ConsoleIO` output buffer), `navdb` (waypoints/airports/airways from parquet). They are `None` until `init()` runs. Call `load_plugins()` after `init()` to activate plugins from `settings.toml`. **Import order in `minisky/__init__.py` is load-bearing.** `traffic` is imported last and separately because the performance model runs module-level code touching `minisky.data` (set up by the settings import). Reordering causes a circular import. @@ -75,4 +81,4 @@ Every text command — scenario file, REST `stack/` endpoint, or console — goe - Package/dependency management is **uv**. Prefix Python invocations with `uv run`. - After adding or changing a stack command, regenerate `docs/reference/commands.md` with the gen script. -- `settings.yml` holds runtime config (ASAS protected-zone margins, plugin path, `enabled_plugins`). +- `settings.toml` holds runtime config (ASAS protected-zone margins, plugin path, `enabled_plugins`). diff --git a/docs/api/tangram-minisky.md b/docs/api/tangram-minisky.md new file mode 100644 index 0000000..3367acc --- /dev/null +++ b/docs/api/tangram-minisky.md @@ -0,0 +1,13 @@ +# `tangram_minisky` + +The tangram-side frontend plugin that renders live MiniSky traffic on a +[tangram](https://github.com/open-aviation/tangram) map. It is a separately +packaged member of MiniSky's root +[uv workspace](https://docs.astral.sh/uv/concepts/projects/workspaces/), +deliberately frontend-only: all +simulator-side logic lives in MiniSky's `TANGRAM` plugin. See the +[tangram streaming guide](../guides/tangram.md) for the full setup. + +## Plugin and configuration + +::: tangram_minisky diff --git a/docs/architecture.md b/docs/architecture.md index da14d6b..ccf9e53 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -21,7 +21,7 @@ of the code refers to: import minisky minisky.init() # create the singletons -minisky.load_plugins() # optional: load plugins enabled in settings.yml +minisky.load_plugins() # optional: load plugins enabled in settings.toml ``` ## The simulation loop @@ -104,7 +104,7 @@ subsystems that act on it each timestep: LNAV/VNAV logic following a [`Route`][minisky.traffic.route.Route] of waypoints. - **Conflict detection** (`traffic/asas/detection.py`) — pairwise state-based detection within a lookahead time against a protected zone (default 5 NM / 1000 ft, configurable - in `settings.yml`). Candidate pairs are pre-selected with a KD-tree on projected + in `settings.toml`). Candidate pairs are pre-selected with a KD-tree on projected positions plus a vertical reachability filter, so cost scales with local traffic density rather than N². - **Conflict resolution** (`traffic/asas/mvp.py`) — Modified Voltage Potential resolution diff --git a/docs/getting-started.md b/docs/getting-started.md index abc9273..dda1ee6 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -80,7 +80,7 @@ and stepping the simulation yourself. ## Configuration -Runtime settings live in `settings.yml` at the repository root, e.g. conflict-detection +Runtime settings live in `settings.toml` at the repository root, e.g. conflict-detection lookahead time and protected-zone sizes, the plugin search directory, and which plugins to load at startup: diff --git a/docs/guides/plugins.md b/docs/guides/plugins.md index 82e1228..885a605 100644 --- a/docs/guides/plugins.md +++ b/docs/guides/plugins.md @@ -6,7 +6,7 @@ The `example_plugins/` directory contains working examples. ## Anatomy of a plugin -A plugin is a Python file in the plugin directory (`plugin_path` in `settings.yml`, +A plugin is a Python file in the plugin directory (`plugin_path` in `settings.toml`, default `example_plugins`) that defines an `init_plugin()` function: ```python @@ -26,7 +26,7 @@ def init_plugin(): example = Example() return { - "plugin_name": "EXAMPLE", # name used by PLUGIN LOAD / settings.yml + "plugin_name": "EXAMPLE", # name used by PLUGIN LOAD / settings.toml "update_interval": 5, # seconds of sim time between update calls "update": example.update, # called every update_interval # "preupdate": ..., # called before traf.update() @@ -106,11 +106,11 @@ the plugin is loaded), so a broken plugin can't crash the simulator at startup. Load plugins in any of three ways: -- **At startup** — list them in `settings.yml`: +- **At startup** — list them in `settings.toml`: - ```yaml - plugin_path: example_plugins - enabled_plugins: ['EXAMPLE'] + ```toml + plugin_path = "example_plugins" + enabled_plugins = ["EXAMPLE"] ``` (Requires the host program to call [`minisky.load_plugins()`][minisky.load_plugins] diff --git a/docs/guides/running-scenarios.md b/docs/guides/running-scenarios.md index 777295d..838ba40 100644 --- a/docs/guides/running-scenarios.md +++ b/docs/guides/running-scenarios.md @@ -14,7 +14,7 @@ uv run minisky run --scenario scenarios/kl204.scn --speed 10 | `--speed` | `1` | Simulation speed multiplier relative to wall time | The script initialises the simulator with the scenario, loads any plugins enabled in -`settings.yml`, and runs the [`Runner`][minisky.simulation.runner.Runner] loop until the +`settings.toml`, and runs the [`Runner`][minisky.simulation.runner.Runner] loop until the scenario ends the simulation. ## Scenario files diff --git a/docs/guides/tangram.md b/docs/guides/tangram.md new file mode 100644 index 0000000..417629b --- /dev/null +++ b/docs/guides/tangram.md @@ -0,0 +1,240 @@ +# Streaming to a tangram map + +MiniSky can act as an *external simulator* for +[tangram](https://github.com/open-aviation/tangram), the open aviation data +visualisation framework: live simulated traffic appears on tangram's map, and +the simulator can be controlled (run/hold/reset, speed, arbitrary stack +commands) from a tangram sidebar widget. + +Nothing is added to the tangram source tree. Tangram discovers plugins +through Python entry points, so its side of the integration is a package you +`pip install` into whatever environment runs `tangram serve`, plus one line +of configuration. Both halves of the integration live in this repository: + +``` +minisky process (TANGRAM plugin) tangram process + publishes to:minisky:new-data ─▶ Redis ─▶ Channel service ─▶ browser + publishes to:minisky:console ─▶ (WebSocket) │ + listens on from:minisky:command ◀─ Redis ◀────────────────────── ┘ +``` + +- **`example_plugins/tangram.py`** (the `TANGRAM` MiniSky plugin) owns all the + logic: it converts each simulation snapshot to aviation units, publishes it + to Redis, relays console output, and executes stack commands pushed from the + browser. MiniSky talks to tangram *only* through Redis pub/sub — tangram's + transport convention (`to::` / `from::`) that + has stayed stable across its plugin API changes. +- **`example_plugins/tangram/tangram_minisky/`** is a separately packaged, thin + tangram frontend plugin: it registers a `minisky_aircraft` entity type, a + deck.gl layer, trail rendering via tangram's shared trajectory store, and + the control widget. No business logic lives there, so it is cheap to rewrite + when tangram's frontend plugin API changes. + +## Step-by-step setup + +The steps below assume Redis in a container, MiniSky and tangram on the host +— the common development setup. (Tangram's own container image only bundles +its in-tree plugins, so a dockerised `tangram serve` cannot load +`tangram_minisky` without image changes; run it on the host instead.) + +### 1. Redis + +Any Redis 5+ reachable by both processes. With docker/podman, publish the +port to the host: + +```bash +docker run -d --rm -p 6379:6379 --name redis redis:8-alpine +``` + +If you already run Redis for a tangram deployment, reuse it — the two sides +meet on the channel name, not on any shared configuration. + +### 2. MiniSky side (the producer) + +```bash +just sync +``` + + + +In `settings.toml`: + +```toml +enabled_plugins = ["TANGRAM"] + +# [tangram] is optional; uncomment to override the defaults shown here. +# [tangram] +# redis_url = "redis://127.0.0.1:6379" +# channel = "minisky" +# max_hz = 5 +``` + +Start MiniSky (any front — the bridge works the same in all of them): + +```bash +uv run minisky server +# or: uv run minisky run --scenario scenarios/kl204.scn +``` + +Startup should print +`Tangram bridge publishing to to:minisky:* at redis://127.0.0.1:6379`. + +**Verify the transport now, before touching any frontend:** + +```bash +# watch everything MiniSky publishes (prefix with `docker exec -it ` +# if redis-cli is not installed on the host) +redis-cli psubscribe "to:*" + +# drive the simulator from outside +redis-cli publish "from:minisky:command" '{"command": "CRE KL204 B744 52 4 90 FL300 250"}' +redis-cli publish "from:minisky:command" '{"command": "HOLD"}' +redis-cli publish "from:minisky:command" '{"command": "OP"}' +``` + +Expect `to:minisky:new-data` snapshots (~`[tangram].max_hz`/s while running, +1/s heartbeat otherwise) reacting to the commands, plus `to:minisky:console` +lines. If this works, the simulator side is done; everything after this point +is tangram-side only. + +### 3. Run tangram + +There are two options: + +#### uv tool + +```bash +uv tool install tangram_core \ + --with ./example_plugins/tangram/tangram_minisky \ + --force +tangram serve --config /path/to/tangram.toml +``` + +Rerun the install after rebuilding the frontend so the tool receives the new +`dist-frontend` bundle. + +#### execution project (recommended) + +Expected layout: + +```text +./ +├── minisky/ +├── tangram/ +└── tangram_minisky_exe/ +``` + +`tangram_minisky_exe/pyproject.toml` pins the published core and keeps only the +plugin editable: + +```toml +[project] +name = "tangram-minisky-exe" +version = "0.1.0" +requires-python = ">=3.11" +dependencies = ["tangram-core", "tangram-minisky"] + +[tool.uv.sources] +tangram-minisky = { path = "../minisky/example_plugins/tangram/tangram_minisky", editable = true } +``` + +Run it with: + +```bash +cd ../tangram_minisky_exe +uv sync +uv run tangram serve --config tangram.toml +``` + +Open . Note that this runs on the published tangram package. + +## Local tangram checkout + +To test an unpublished local checkout instead, make these temporary development-only changes. + +In `tangram_minisky_exe/pyproject.toml`, unpin core and add its editable source: + +```toml +[project] +dependencies = ["tangram-core", "tangram-minisky"] + +[tool.uv.sources] +tangram-core = { path = "../tangram/packages/tangram_core", editable = true } +tangram-minisky = { path = "../minisky/example_plugins/tangram/tangram_minisky", editable = true } +``` + +In MiniSky's root `pnpm-workspace.yaml`, temporarily add: + +```yaml +overrides: + "@open-aviation/tangram-core": "link:../tangram/packages/tangram_core" +``` + +Then refresh both environments: + +```bash +pnpm install +pnpm build +cd ../tangram_minisky_exe +uv sync +``` + +Do not commit the temporary overrides or their lockfile changes. Comment them and +run `pnpm install` again. + +## Troubleshooting + +Work upstream-to-downstream: + +1. **No `to:minisky:new-data` in `redis-cli psubscribe "to:*"`** — MiniSky + side. Check the plugin loaded at startup, and run the `TANGRAM` stack + command in the MiniSky console: it reports connection state, message count + and the last Redis error. The bridge heartbeats once per second even when + the simulation is idle, so *silence means it is not connected*. +2. **Snapshots flow, but tangram logs `fail to decode JWT` / + `InvalidSignature` on joins** — the browser holds tokens signed under a + different `jwt_secret` than the running channel service. Almost always a + stale tab auto-reconnecting after a restart with a changed secret: + hard-reload the page. Also check nothing else (an old `tangram serve`, a + tangram container) is squatting ports 2346/2347: `lsof -i :2346 -i :2347`. +3. **Channel joins succeed but the widget says "Simulator offline"** — no + snapshot or heartbeat arrived for 5 seconds. Almost always a Redis URL + mismatch: `tangram.toml`'s `redis_url` and `settings.toml`'s + `[tangram].redis_url` must point at the *same* Redis instance (mind + host-vs-container addressing: a dockerised tangram reaches a compose + Redis at `redis://redis:6379`, a host process at `redis://127.0.0.1:6379`). + A channel-name mismatch between the two sides has the same symptom. + +## Wire contract + +All payloads are JSON. Aircraft fields use aviation units (altitude in ft, +speeds in kt, vertical rate in fpm) under jet1090-style names, so tangram-side +consumers see familiar shapes; the conversion from MiniSky's internal SI state +happens in the MiniSky plugin, keeping `minisky.streaming` consumer-agnostic. + +- `to::new-data`: + `{"aircraft": [{id, callsign, typecode, latitude, longitude, altitude, + groundspeed, tas, ias, vertical_rate, track, inconf, timestamp}], + "count": n, "siminfo": {simt, simdt, simutc, speed, ntraf, state, + state_name, scenname, nconf_cur, nlos_cur}}`. + Published on every simulation step (wall-clock capped at `[tangram].max_hz`). + Whenever the simulation is not advancing — including a freshly started + simulator with no scenario — a heartbeat with refreshed `siminfo` (and the + last aircraft list) is republished every second, so the frontend always + sees the simulator and its state changes. +- `to::console`: `{"lines": [...]}` — everything echoed to the + MiniSky console (the bridge tees the console, it does not consume it). +- `from::command`: `{"command": "..."}` — one stack command, + executed on the next simulation step (works while paused, so `OP` can + un-pause). Bare strings are also accepted for redis-cli convenience. + +## Known limitations + +- tangram's playback timeline (`api.time`) is client-side only; scrubbing or + pausing it does not drive the simulator clock. Use the control widget (or + stack commands) instead. +- Console output relayed to tangram is a tee of everything echoed by the + simulator, not a per-command response stream. +- Commands published before the bridge's Redis subscription is live are lost + (pub/sub has no replay); the bridge logs its status via the `TANGRAM` + stack command. diff --git a/example_plugins/example.py b/example_plugins/example.py index 6fccefa..1cb6e68 100644 --- a/example_plugins/example.py +++ b/example_plugins/example.py @@ -6,6 +6,8 @@ - Stack commands """ +from __future__ import annotations + from random import randint import numpy as np @@ -14,7 +16,7 @@ from minisky import plugin, stack # Global reference to the example instance -example = None +example: Example | None = None def init_plugin(): @@ -26,13 +28,13 @@ def init_plugin(): global example # Instantiate our example entity - example = Example() + example = instance = Example() # Configuration parameters config = { "plugin_name": "EXAMPLE", "update_interval": 5, # Update every 5 seconds - "update": example.update, # Register update function via config + "update": instance.update, # Register update function via config } return config @@ -48,7 +50,7 @@ def __init__(self): with self.settrafarrays(): self.npassengers = np.array([]) - def create(self, n=1): + def create(self, n: int = 1): """Called automatically when new aircraft are created.""" super().create(n) # Set passenger count for new aircraft @@ -70,7 +72,8 @@ def passengers(callsign: str, count: int = -1): - callsign: Aircraft callsign - count: Number of passengers (optional, omit to query) """ - global example + if example is None: + return False, "Example plugin not initialised" callsign = callsign.upper() diff --git a/example_plugins/tangram.py b/example_plugins/tangram.py new file mode 100644 index 0000000..f2780a9 --- /dev/null +++ b/example_plugins/tangram.py @@ -0,0 +1,427 @@ +"""Tangram bridge: stream MiniSky state to a tangram map over Redis pub/sub. + +This plugin makes a running MiniSky process act as an *external simulator* +for `tangram `_. It talks to +tangram exclusively through Redis, using tangram's stable channel +convention (see `docs/architecture/channel.md` in the tangram repo): + +- `to::` -- published by us, pushed to the browser by + tangram's Channel service over WebSocket. +- `from::` -- pushed by the browser, re-published to + Redis by the Channel service, consumed by us. + +Wire contract (all published payloads are JSON): + +- `to::new-data`: `{"aircraft": [...], "count": n, "siminfo": {...}}` + with per-aircraft fields in aviation units (altitude ft, speeds kt, + vertical rate fpm) under jet1090-style names. +- `to::console`: `{"lines": [...]}` -- echoed simulator output. +- `from::command`: `{"command": "OP"}` -- a stack command to run. + +All Redis I/O happens on a background thread so the simulation loop never +blocks on the network, and so commands are still received while the +simulation is paused (plugin update hooks only fire in the OP state; the +command stack itself is processed in every state). + +Settings (optional, under a ``[tangram]`` table in `settings.toml`): + +- `redis_url`: Redis connection URL (default `redis://127.0.0.1:6379`). +- `channel`: channel/topic name (default `minisky`). +- `max_hz`: wall-clock cap on snapshot publish rate (default 5). + +Debug the transport without any frontend:: + + redis-cli psubscribe "to:*" + redis-cli publish "from:minisky:command" '{"command": "ECHO hello"}' +""" + +import json +import queue +import threading +import time +from collections import deque +from collections.abc import Callable +from datetime import UTC, datetime +from typing import Any, TypedDict, cast + +from pydantic import BaseModel, ConfigDict, Field + +import minisky +from minisky import stack +from minisky.core import settings +from minisky.streaming import Snapshot, build_snapshot +from minisky.tools.aero import fpm, ft, kts + + +class TangramSettings(BaseModel): + """Validated ``[tangram]`` config from ``settings.toml``.""" + + model_config = ConfigDict(extra="forbid", frozen=True) + + redis_url: str = "redis://127.0.0.1:6379" + channel: str = "minisky" + max_hz: float = 5.0 + + +class TangramPluginSettings(BaseModel): + tangram: TangramSettings = Field(default_factory=TangramSettings) + + +# How often the background thread republishes state while the simulation is +# not advancing (paused/init), so the frontend still sees state changes. +HEARTBEAT_SECS = 1.0 + +SIM_STATE_NAMES = {0: "INIT", 1: "HOLD", 2: "OP", 3: "END"} + +bridge = None + + +class TangramSimInfo(TypedDict): + """Simulation status block of the `new-data` wire payload.""" + + simt: float # s + simdt: float # s + simutc: str # ISO-8601 + speed: float # runner speed multiplier (x realtime) + ntraf: int + state: int # 0=INIT, 1=HOLD, 2=OP, 3=END + state_name: str + scenname: str # "" when no scenario is loaded + nconf_cur: int + nlos_cur: int + + +class TangramAircraft(TypedDict): + """One aircraft in the `new-data` wire payload (jet1090-style fields).""" + + id: str + callsign: str + typecode: str + latitude: float # deg + longitude: float # deg + altitude: int # ft + groundspeed: float # kt + tas: float # kt + ias: float # kt + vertical_rate: int # fpm + track: float # deg + inconf: bool + timestamp: float | None # epoch seconds; None when simutc fails to parse + + +class TangramPayload(TypedDict): + """Full `to::new-data` wire payload.""" + + aircraft: list[TangramAircraft] + count: int + siminfo: TangramSimInfo + + +def convert_snapshot(snapshot: Snapshot) -> TangramPayload: + """Convert a MiniSky SI-unit snapshot into the tangram wire payload. + + Aircraft come out with jet1090-style field names and aviation units + (altitude in ft, speeds in kt, vertical rate in fpm). Pure function so + it can be unit-tested without a running simulator. + """ + siminfo = snapshot["siminfo"] + acdata = snapshot["acdata"] + + state = siminfo["state"] + simutc = siminfo["simutc"] + try: + utc = datetime.fromisoformat(simutc) + except ValueError: + utc = None + if utc is not None and utc.tzinfo is None: + # sim.utc is UTC by definition; never let a naive string be read as local time. + utc = utc.replace(tzinfo=UTC) + timestamp = utc.timestamp() if utc is not None else None + + out_siminfo: TangramSimInfo = { + "simt": siminfo["simt"], + "simdt": siminfo["simdt"], + "simutc": simutc, + "speed": siminfo["speed"], + "ntraf": siminfo["ntraf"], + "state": state, + "state_name": SIM_STATE_NAMES.get(state, "?"), + "scenname": siminfo["scenname"], + "nconf_cur": acdata["nconf_cur"], + "nlos_cur": acdata["nlos_cur"], + } + + aircraft: list[TangramAircraft] = [] + for i, callsign in enumerate(acdata["callsign"]): + aircraft.append( + { + "id": callsign, + "callsign": callsign, + "typecode": acdata["typecode"][i], + "latitude": acdata["lat"][i], + "longitude": acdata["lon"][i], + "altitude": round(acdata["alt"][i] / ft), + "groundspeed": round(acdata["gs"][i] / kts, 1), + "tas": round(acdata["tas"][i] / kts, 1), + "ias": round(acdata["cas"][i] / kts, 1), + "vertical_rate": round(acdata["vs"][i] / fpm), + "track": acdata["trk"][i], + "inconf": acdata["inconf"][i], + "timestamp": timestamp, + } + ) + + return {"aircraft": aircraft, "count": len(aircraft), "siminfo": out_siminfo} + + +def extract_command(payload: str | bytes) -> str | None: + """Extract the stack command from a `from::command` payload. + + Accepts the JSON envelope pushed by the tangram frontend + (`{"command": "..."}`) and, for convenience when testing with + redis-cli, a bare string. + """ + if isinstance(payload, bytes): + payload = payload.decode("utf-8", errors="replace") + text = payload.strip() + if not text: + return None + try: + data = json.loads(text) + except ValueError: + return text + if isinstance(data, dict): + cmd = cast("dict[str, Any]", data).get("command") + return str(cmd).strip() or None if cmd is not None else None + if isinstance(data, str): + return data.strip() or None + return None + + +class TangramBridge: + """Owns the Redis connection and shuttles data between it and the sim. + + The simulation thread only ever touches thread-safe queues/deques: the + `update` hook enqueues converted snapshots, and a tee on `scr.echo` + enqueues console lines. A daemon thread does all Redis I/O: draining + those queues, republishing a heartbeat while the sim is not advancing, + and listening for browser commands on `from::*`. + """ + + def __init__( + self, + redis_url: str, + channel: str, + max_hz: float, + redis_factory: Callable[[str], Any] | None = None, + ) -> None: + self.redis_url = redis_url + self.channel = channel + self.min_interval = 1.0 / max_hz if max_hz > 0 else 0.0 + self.redis_factory = redis_factory + + self.connected = False + self.published = 0 + self.last_error = "" + + self._last_build = 0.0 + self._last_payload: TangramPayload | None = None + self._snapshots: queue.Queue[TangramPayload] = queue.Queue(maxsize=4) + self._console: deque[str] = deque(maxlen=200) + self._stop = threading.Event() + self._thread: threading.Thread | None = None + self.ready = threading.Event() + """Set once the command subscription is live (commands published + before this are lost -- Redis pub/sub has no replay).""" + + # -- simulation-thread side ------------------------------------------- + + def start(self) -> tuple[bool, str]: + """Install the console tee and start the Redis I/O thread.""" + try: + if self.redis_factory is None: + import redis + + # cast: from_url's untyped **kwargs would leak Unknown under strict mode. + self.redis_factory = cast( + "Callable[[str], Any]", + redis.Redis.from_url, # pyright: ignore[reportUnknownMemberType] + ) + except ImportError: + return False, ( + "TANGRAM plugin needs the redis package; run `just sync` from the " + "MiniSky repository root" + ) + + self._tee_console() + self._thread = threading.Thread(target=self._run, name="tangram-bridge", daemon=True) + self._thread.start() + return True, f"Tangram bridge publishing to to:{self.channel}:* at {self.redis_url}" + + def stop(self) -> None: + self._stop.set() + if self._thread is not None: + self._thread.join(timeout=2.0) + + def tick(self) -> None: + """Update hook: build and enqueue a snapshot (rate-capped). Runs in OP.""" + now = time.monotonic() + if now - self._last_build < self.min_interval: + return + self._last_build = now + self._enqueue(convert_snapshot(build_snapshot())) + + def reset(self) -> None: + """Reset hook: push an empty payload so the frontend clears the map.""" + self._last_payload = None + self._enqueue(convert_snapshot(build_snapshot())) + + def _enqueue(self, payload: TangramPayload) -> None: + self._last_payload = payload + try: + self._snapshots.put_nowait(payload) + except queue.Full: + # Drop the oldest snapshot; each payload is a full state anyway. + try: + self._snapshots.get_nowait() + self._snapshots.put_nowait(payload) + except (queue.Empty, queue.Full): + pass + + def _tee_console(self) -> None: + """Also capture everything echoed to the console, without consuming it.""" + scr = minisky.scr + original_echo = scr.echo + + def echo(text: str = "", flag: int = 0) -> None: + original_echo(text, flag) + if text: + self._console.extend(text.splitlines()) + + scr.echo = echo # type: ignore[method-assign] + + # -- Redis-thread side ------------------------------------------------- + + def _siminfo_heartbeat(self) -> TangramPayload: + """Refresh the cheap scalar fields of the last payload. + + Only reads scalar attributes of the singletons (safe enough from a + second thread); the aircraft list and conflict counters are reused + from the last snapshot built on the simulation thread. + """ + last = self._last_payload + sim = minisky.sim + state = int(sim.state) + siminfo: TangramSimInfo = { + "simt": float(sim.simt), + "simdt": float(sim.simdt), + "simutc": sim.utc.isoformat(), + "speed": float(minisky.runner.speed) if minisky.runner else 1.0, + "ntraf": int(minisky.traf.ntraf), + "state": state, + "state_name": SIM_STATE_NAMES.get(state, "?"), + "scenname": stack.get_scenname(), + "nconf_cur": last["siminfo"]["nconf_cur"] if last is not None else 0, + "nlos_cur": last["siminfo"]["nlos_cur"] if last is not None else 0, + } + if last is None: + return {"aircraft": [], "count": 0, "siminfo": siminfo} + return {"aircraft": last["aircraft"], "count": last["count"], "siminfo": siminfo} + + def _run(self) -> None: + assert self.redis_factory is not None + data_topic = f"to:{self.channel}:new-data" + console_topic = f"to:{self.channel}:console" + command_pattern = f"from:{self.channel}:*" + command_topic = f"from:{self.channel}:command" + + while not self._stop.is_set(): + try: + client = self.redis_factory(self.redis_url) + pubsub = client.pubsub(ignore_subscribe_messages=True) + pubsub.psubscribe(command_pattern) + self.connected = True + self.last_error = "" + self.ready.set() + last_publish = 0.0 + + while not self._stop.is_set(): + message = pubsub.get_message(timeout=0.05) + if message is not None and message.get("type") == "pmessage": + topic = message.get("channel", b"") + if isinstance(topic, bytes): + topic = topic.decode("utf-8", errors="replace") + if topic == command_topic: + cmd = extract_command(message.get("data", "")) + if cmd: + stack.stack(cmd) + + published = False + while True: + try: + payload = self._snapshots.get_nowait() + except queue.Empty: + break + client.publish(data_topic, json.dumps(payload)) + self.published += 1 + published = True + + if published: + last_publish = time.monotonic() + elif time.monotonic() - last_publish > HEARTBEAT_SECS: + # Publish even before the first snapshot (INIT state, no + # traffic yet) so the frontend sees the simulator at all. + client.publish(data_topic, json.dumps(self._siminfo_heartbeat())) + self.published += 1 + last_publish = time.monotonic() + + if self._console: + lines: list[str] = [] + while self._console: + lines.append(self._console.popleft()) + client.publish(console_topic, json.dumps({"lines": lines})) + except Exception as e: # noqa: BLE001 - reconnect on any Redis failure + self.connected = False + self.ready.clear() + self.last_error = str(e) + if self._stop.wait(timeout=2.0): + return + + +@stack.command(name="TANGRAM") +def tangram_status() -> tuple[bool, str]: + """Show the status of the tangram Redis bridge.""" + if bridge is None: + return False, "Tangram bridge not initialised" + status = "connected" if bridge.connected else "disconnected" + text = ( + f"Tangram bridge: {status} to {bridge.redis_url}\n" + f"Channel: to:{bridge.channel}:new-data ({bridge.published} messages published)" + ) + if bridge.last_error: + text += f"\nLast error: {bridge.last_error}" + return True, text + + +def init_plugin() -> dict[str, Any]: + """Create the bridge and register its simulation hooks.""" + global bridge + + cfg = TangramPluginSettings.model_validate(settings.config).tangram + bridge = TangramBridge( + redis_url=cfg.redis_url, + channel=cfg.channel, + max_hz=cfg.max_hz, + ) + success, msg = bridge.start() + minisky.scr.echo(msg) + if not success: + raise RuntimeError(msg) + + config = { + "plugin_name": "TANGRAM", + "update_interval": 0.0, + "update": bridge.tick, + "reset": bridge.reset, + } + return config diff --git a/example_plugins/tangram/tangram_minisky/eslint.config.js b/example_plugins/tangram/tangram_minisky/eslint.config.js new file mode 100644 index 0000000..ac603b8 --- /dev/null +++ b/example_plugins/tangram/tangram_minisky/eslint.config.js @@ -0,0 +1,18 @@ +import js from "@eslint/js"; +import pluginVue from "eslint-plugin-vue"; +import globals from "globals"; +import tseslint from "typescript-eslint"; + +export default tseslint.config( + { ignores: ["dist-frontend/", "node_modules/"] }, + js.configs.recommended, + tseslint.configs.recommended, + pluginVue.configs["flat/recommended"], + { + languageOptions: { globals: globals.browser }, + }, + { + files: ["**/*.vue"], + languageOptions: { parserOptions: { parser: tseslint.parser } }, + }, +); diff --git a/example_plugins/tangram/tangram_minisky/package.json b/example_plugins/tangram/tangram_minisky/package.json new file mode 100644 index 0000000..e93fd0f --- /dev/null +++ b/example_plugins/tangram/tangram_minisky/package.json @@ -0,0 +1,33 @@ +{ + "name": "@amvlab/tangram-minisky", + "version": "0.1.0", + "private": true, + "type": "module", + "main": "src/tangram_minisky/index.ts", + "scripts": { + "build": "vite build", + "lint": "eslint", + "lint:fix": "eslint --fix", + "typecheck": "vue-tsc --noEmit -p tsconfig.json", + "typecheck:vite": "tsc --noEmit -p tsconfig.vite.json", + "check": "pnpm run lint && pnpm run typecheck && pnpm run typecheck:vite" + }, + "dependencies": { + "@open-aviation/tangram-core": "^0.5.0" + }, + "devDependencies": { + "@deck.gl/core": "^9.3.3", + "@deck.gl/layers": "^9.3.3", + "@eslint/js": "^9.39.0", + "@types/node": "^24.10.1", + "@types/phoenix": "^1.6.7", + "eslint": "^9.39.0", + "eslint-plugin-vue": "^10.6.2", + "globals": "^17.7.0", + "typescript": "^5.9.3", + "typescript-eslint": "^8.61.0", + "vite": "^8.0.16", + "vue": "^3.5.35", + "vue-tsc": "^3.3.4" + } +} diff --git a/example_plugins/tangram/tangram_minisky/pyproject.toml b/example_plugins/tangram/tangram_minisky/pyproject.toml new file mode 100644 index 0000000..86632a8 --- /dev/null +++ b/example_plugins/tangram/tangram_minisky/pyproject.toml @@ -0,0 +1,26 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "tangram_minisky" +version = "0.1.0" +description = "tangram frontend plugin rendering live traffic from a MiniSky simulator" +readme = "readme.md" +requires-python = ">=3.11" +# NOTE: we depend on redis and pydantic which is already included in tangram core. +dependencies = ["tangram_core>=0.5.0"] +classifiers = ["Private :: Do Not Upload"] +authors = [{ name = "Andres Morfin Veytia" }] + +[project.entry-points."tangram_core.plugins"] +tangram_minisky = "tangram_minisky:plugin" + +[tool.hatch.build.targets.sdist] +ignore-vcs = true +include = ["dist-frontend/*", "src/*", "package.json"] + +[tool.hatch.build.targets.wheel.force-include] +"dist-frontend" = "tangram_minisky/dist-frontend" +# not required, but allows `uvx tangram check-plugin` to work +"package.json" = "tangram_minisky/package.json" diff --git a/example_plugins/tangram/tangram_minisky/readme.md b/example_plugins/tangram/tangram_minisky/readme.md new file mode 100644 index 0000000..76a60bc --- /dev/null +++ b/example_plugins/tangram/tangram_minisky/readme.md @@ -0,0 +1,83 @@ +# tangram_minisky + +A thin [tangram](https://github.com/open-aviation/tangram) frontend plugin that +renders live traffic from a MiniSky simulator. + +This package is **frontend-only** and deliberately disposable: it contains no +business logic and no simulation state. Everything simulator-side (unit +conversion, snapshot publishing, command handling) lives in MiniSky's own +`TANGRAM` plugin (`example_plugins/tangram.py`), which talks to tangram +exclusively over Redis pub/sub — tangram's one transport layer that has been +stable across its recent plugin API churn. If tangram's frontend API breaks +again, only this package needs touching. + +## What it registers + +- a `minisky_aircraft` entity type (own type — it does not impersonate + `jet1090_aircraft`) +- a deck.gl aircraft layer (`entities` slot) and a trail layer for selected + aircraft (`live_trails` slot) +- a top-bar aircraft/state counter and a sidebar simulator control widget + (run/hold/reset, speed, a stack command console) +- a trajectory authority on `api.bus` / `api.trajectory`: it appends points + for selected aircraft and answers `TrajectoryApi.TOPIC_GET` requests, so + other plugins can consume the simulator feed without depending on this + package + +## Wire contract (Redis, via tangram's Channel service) + +- `to::new-data` — `{aircraft, count, siminfo}` full snapshots, + aviation units (ft/kt/fpm), jet1090-style field names +- `to::console` — `{lines: [...]}` echoed simulator output +- `from::command` — `{command: "OP"}` stack commands from the browser + +`` defaults to `minisky` and is configurable on both sides +(`[tangram].channel` in MiniSky's `settings.toml`, `channel` in tangram's +`tangram.toml` under `[plugins.tangram_minisky]`). + +## Build and run + +From the MiniSky repository root: + +```bash +just sync +just check +``` + +Run with published tangram using either route: + +```bash +uv tool install tangram_core \ + --with ./example_plugins/tangram/tangram_minisky \ + --force +tangram serve --config ../tangram_minisky_exe/tangram.toml +``` + +or: + +```bash +cd ../tangram_minisky_exe +uv sync +uv run tangram serve --config tangram.toml +``` + +The complete setup and temporary local-checkout overrides are documented in +[Streaming to a tangram map](../../../docs/guides/tangram.md). + +## Run the simulator side + +In the MiniSky repo: + +```bash +just sync +# settings.toml: enabled_plugins = ["TANGRAM"], and (optionally) a [tangram] table +# with redis_url pointing at the same Redis instance tangram uses +minisky server # or: minisky run --scenario scenarios/kl204.scn +``` + +Debug the transport without any frontend: + +```bash +redis-cli psubscribe "to:*" +redis-cli publish "from:minisky:command" '{"command": "CRE KL204 B744 52 4 90 FL300 250"}' +``` diff --git a/example_plugins/tangram/tangram_minisky/src/tangram_minisky/AircraftCountWidget.vue b/example_plugins/tangram/tangram_minisky/src/tangram_minisky/AircraftCountWidget.vue new file mode 100644 index 0000000..86a34bc --- /dev/null +++ b/example_plugins/tangram/tangram_minisky/src/tangram_minisky/AircraftCountWidget.vue @@ -0,0 +1,124 @@ + + + + + diff --git a/example_plugins/tangram/tangram_minisky/src/tangram_minisky/AircraftLayer.vue b/example_plugins/tangram/tangram_minisky/src/tangram_minisky/AircraftLayer.vue new file mode 100644 index 0000000..972ca6d --- /dev/null +++ b/example_plugins/tangram/tangram_minisky/src/tangram_minisky/AircraftLayer.vue @@ -0,0 +1,218 @@ + + + + + diff --git a/example_plugins/tangram/tangram_minisky/src/tangram_minisky/AircraftTrailLayer.vue b/example_plugins/tangram/tangram_minisky/src/tangram_minisky/AircraftTrailLayer.vue new file mode 100644 index 0000000..a51b6d9 --- /dev/null +++ b/example_plugins/tangram/tangram_minisky/src/tangram_minisky/AircraftTrailLayer.vue @@ -0,0 +1,67 @@ + + + diff --git a/example_plugins/tangram/tangram_minisky/src/tangram_minisky/SimControlWidget.vue b/example_plugins/tangram/tangram_minisky/src/tangram_minisky/SimControlWidget.vue new file mode 100644 index 0000000..eb37218 --- /dev/null +++ b/example_plugins/tangram/tangram_minisky/src/tangram_minisky/SimControlWidget.vue @@ -0,0 +1,284 @@ + + + + + diff --git a/example_plugins/tangram/tangram_minisky/src/tangram_minisky/__init__.py b/example_plugins/tangram/tangram_minisky/src/tangram_minisky/__init__.py new file mode 100644 index 0000000..cfd31f0 --- /dev/null +++ b/example_plugins/tangram/tangram_minisky/src/tangram_minisky/__init__.py @@ -0,0 +1,61 @@ +"""tangram frontend plugin rendering live traffic from a MiniSky simulator. + +This package is deliberately frontend-only: it registers no API routes and +runs no background services inside tangram. All simulator-side logic (unit +conversion, snapshot publishing, command handling) lives in MiniSky's own +`TANGRAM` plugin (`example_plugins/tangram.py` in the MiniSky repo), +which talks to tangram exclusively over Redis pub/sub: + +- `to::new-data` full state snapshots (aviation units) +- `to::console` echoed simulator console output +- `from::command` stack commands pushed from the browser + +The only thing tangram needs from this package is the compiled frontend +bundle in `dist-frontend` and the configuration schema below. +""" + +from dataclasses import dataclass +from typing import Annotated + +import tangram_core +from tangram_core.config import FrontendMutable + + +@dataclass(frozen=True) +class MiniskyConfig: + channel: str = "minisky" + """Redis channel name; must match `[tangram].channel` in MiniSky's settings.toml.""" + topbar_order: int = 45 + """Ordering hint for the sim-control widget in tangram's topbar (lower is earlier).""" + sidebar_order: int = 45 + """Ordering hint for the aircraft-layer entry in tangram's sidebar (lower is earlier).""" + + +@dataclass(frozen=True) +class MiniskyFrontendConfig: + """The subset of [`MiniskyConfig`][tangram_minisky.MiniskyConfig] served to the + browser. `topbar_order` and `sidebar_order` are marked `FrontendMutable` so + tangram can let the user reorder the widgets at runtime.""" + + channel: str + topbar_order: Annotated[int, FrontendMutable()] + sidebar_order: Annotated[int, FrontendMutable()] + + +def into_frontend(config: MiniskyConfig) -> MiniskyFrontendConfig: + """Lower a [`MiniskyConfig`][tangram_minisky.MiniskyConfig] into the + frontend-facing [`MiniskyFrontendConfig`][tangram_minisky.MiniskyFrontendConfig] + that tangram serves to the browser.""" + return MiniskyFrontendConfig( + channel=config.channel, + topbar_order=config.topbar_order, + sidebar_order=config.sidebar_order, + ) + + +plugin = tangram_core.Plugin( + frontend_path="dist-frontend", + config_class=MiniskyConfig, + frontend_config_class=MiniskyFrontendConfig, + into_frontend_config_function=into_frontend, +) diff --git a/example_plugins/tangram/tangram_minisky/src/tangram_minisky/index.ts b/example_plugins/tangram/tangram_minisky/src/tangram_minisky/index.ts new file mode 100644 index 0000000..1760665 --- /dev/null +++ b/example_plugins/tangram/tangram_minisky/src/tangram_minisky/index.ts @@ -0,0 +1,171 @@ +import type { Entity, PluginContext } from "@open-aviation/tangram-core/api"; +import { + TrajectoryApi, + type BusResultEnvelope, + type EntityKey, + type TrajectoryGetRequest, + type TrajectoryGetResult +} from "@open-aviation/tangram-core/api"; +import AircraftLayer from "./AircraftLayer.vue"; +import AircraftTrailLayer from "./AircraftTrailLayer.vue"; +import AircraftCountWidget from "./AircraftCountWidget.vue"; +import SimControlWidget from "./SimControlWidget.vue"; +import { miniskyStore, pushLog, type MiniskyAircraft, type SimInfo } from "./store"; + +export const ENTITY_TYPE = "minisky_aircraft"; +const SOURCE = "tangram_minisky"; + +// If no snapshot or heartbeat arrives for this long, the simulator is gone. +// The producer heartbeats every second even while paused. +const STALE_AFTER_MS = 5000; + +interface MiniskyFrontendConfig { + channel: string; + topbar_order: number; + sidebar_order: number; +} + +interface StreamPayload { + aircraft: MiniskyAircraft[]; + count: number; + siminfo: SimInfo; +} + +export function install(ctx: PluginContext, config?: MiniskyFrontendConfig) { + const api = ctx.api; + const channel = config?.channel || "minisky"; + miniskyStore.channel = channel; + + api.state.registerEntityType(ENTITY_TYPE, { pluginId: ctx.id }); + + api.ui.registerWidget("minisky-count-widget", "TopBar", AircraftCountWidget, { + pluginId: ctx.id, + priority: config?.topbar_order + }); + api.ui.registerWidget("minisky-aircraft-layer", "MapOverlay", AircraftLayer, { + pluginId: ctx.id + }); + api.ui.registerWidget("minisky-trail-layer", "MapOverlay", AircraftTrailLayer, { + pluginId: ctx.id + }); + api.ui.registerWidget("minisky-control-widget", "SideBar", SimControlWidget, { + pluginId: ctx.id, + priority: config?.sidebar_order, + title: "MiniSky Simulator" + }); + + const staleTimer = setInterval(() => { + if (miniskyStore.connected && Date.now() - miniskyStore.lastUpdate > STALE_AFTER_MS) { + miniskyStore.connected = false; + } + }, 1000); + ctx.onDispose({ dispose: () => clearInterval(staleTimer) }); + + // Trajectories are tracked for selected aircraft only and shared through + // the core trajectory store (api.bus / api.trajectory), so other plugins + // can consume the simulator feed without depending on this package. + let selectedIds: ReadonlySet = new Set(); + + const trajectoryKey = (id: string): EntityKey => ({ id, type: ENTITY_TYPE }); + + ctx.onDispose( + api.selection.onChanged(selection => { + const next = selection.get(ENTITY_TYPE) ?? new Set(); + for (const id of next) { + if (selectedIds.has(id)) continue; + const entity = api.state.getEntity(trajectoryKey(id)); + const points = + entity && entity.state.latitude != null && entity.state.longitude != null + ? [entity.state] + : []; + api.bus.publish(TrajectoryApi.TOPIC_INIT, { + key: trajectoryKey(id), + points, + source: SOURCE + }); + } + selectedIds = next; + }) + ); + + // Serve trajectory requests from other plugins (bus authority). + ctx.onDispose( + api.bus.subscribe(TrajectoryApi.TOPIC_GET, req => { + if (req.key.type !== ENTITY_TYPE) return; + api.bus.publish>( + `${TrajectoryApi.TOPIC_GET}:result`, + { + request_id: req.request_id, + data: { + key: req.key, + points: api.trajectory.get(req.key).value, + source: SOURCE + } + } + ); + }) + ); + + let lastSimt = Number.NEGATIVE_INFINITY; + + void (async () => { + try { + await api.realtime.ensureConnected(); + + ctx.onDispose( + await api.realtime.subscribe(`${channel}:new-data`, payload => { + // Simulation reset (or a new scenario): drop stale trails. + if (payload.siminfo.simt < lastSimt) { + for (const id of selectedIds) { + api.bus.publish(TrajectoryApi.TOPIC_INIT, { + key: trajectoryKey(id), + points: [], + source: SOURCE + }); + } + } + lastSimt = payload.siminfo.simt; + + const entities: Entity[] = payload.aircraft + .filter(ac => ac.latitude != null && ac.longitude != null) + .map(ac => ({ id: ac.id, type: ENTITY_TYPE, state: ac })); + api.state.replaceAllEntitiesByType(ENTITY_TYPE, entities); + api.state.setTotalCount(ENTITY_TYPE, payload.count); + + miniskyStore.siminfo = payload.siminfo; + miniskyStore.connected = true; + miniskyStore.lastUpdate = Date.now(); + + for (const id of selectedIds) { + const ac = payload.aircraft.find(a => a.id === id); + if (!ac || ac.latitude == null || ac.longitude == null) continue; + const trajectory = api.trajectory.get(trajectoryKey(id)).value; + const last = trajectory[trajectory.length - 1] as + | MiniskyAircraft + | undefined; + // Heartbeats repeat the last snapshot; skip duplicate points. + if (ac.timestamp != null && last?.timestamp === ac.timestamp) continue; + api.bus.publish(TrajectoryApi.TOPIC_APPEND, { + key: trajectoryKey(id), + points: [ac], + source: SOURCE + }); + } + }) + ); + + ctx.onDispose( + await api.realtime.subscribe<{ lines: string[] }>( + `${channel}:console`, + payload => { + for (const line of payload.lines ?? []) { + pushLog("msg", line); + } + } + ) + ); + } catch (e) { + console.error("tangram_minisky: realtime subscription failed", e); + } + })(); +} diff --git a/example_plugins/tangram/tangram_minisky/src/tangram_minisky/store.ts b/example_plugins/tangram/tangram_minisky/src/tangram_minisky/store.ts new file mode 100644 index 0000000..fc9d716 --- /dev/null +++ b/example_plugins/tangram/tangram_minisky/src/tangram_minisky/store.ts @@ -0,0 +1,59 @@ +import { reactive } from "vue"; + +export interface MiniskyAircraft { + id: string; + callsign: string; + typecode?: string; + latitude?: number; + longitude?: number; + /** feet */ + altitude?: number; + /** knots */ + groundspeed?: number; + /** knots */ + tas?: number; + /** knots */ + ias?: number; + /** feet per minute */ + vertical_rate?: number; + /** degrees */ + track?: number; + /** aircraft currently in a detected conflict */ + inconf: boolean; + /** epoch seconds of the simulated UTC clock */ + timestamp?: number; +} + +export interface SimInfo { + simt: number; + simdt: number; + simutc?: string; + speed: number; + ntraf: number; + state: number; + state_name: string; + scenname?: string; + nconf_cur: number; + nlos_cur: number; +} + +export interface ConsoleEntry { + kind: "cmd" | "msg" | "err"; + text: string; +} + +export const miniskyStore = reactive({ + channel: "minisky", + siminfo: null as SimInfo | null, + connected: false, + lastUpdate: 0, + log: [] as ConsoleEntry[] +}); + +export function pushLog(kind: ConsoleEntry["kind"], text: string) { + if (!text) return; + miniskyStore.log.push({ kind, text }); + if (miniskyStore.log.length > 100) { + miniskyStore.log.splice(0, miniskyStore.log.length - 100); + } +} diff --git a/example_plugins/tangram/tangram_minisky/tsconfig.json b/example_plugins/tangram/tangram_minisky/tsconfig.json new file mode 100644 index 0000000..3b429a7 --- /dev/null +++ b/example_plugins/tangram/tangram_minisky/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@open-aviation/tangram-core/tsconfig.plugin.json", + "include": ["src/**/*.ts", "src/**/*.vue"] +} diff --git a/example_plugins/tangram/tangram_minisky/tsconfig.vite.json b/example_plugins/tangram/tangram_minisky/tsconfig.vite.json new file mode 100644 index 0000000..8f03f9f --- /dev/null +++ b/example_plugins/tangram/tangram_minisky/tsconfig.vite.json @@ -0,0 +1,5 @@ +{ + // typechecks vite.config.ts (node context), separate from the browser plugin code + "extends": "@open-aviation/tangram-core/tsconfig.node.json", + "include": ["vite.config.ts"] +} diff --git a/example_plugins/tangram/tangram_minisky/vite.config.ts b/example_plugins/tangram/tangram_minisky/vite.config.ts new file mode 100644 index 0000000..e99f9fc --- /dev/null +++ b/example_plugins/tangram/tangram_minisky/vite.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from "vite"; +// Published tangram-core v0.5.0 ships no declaration for the vite-plugin subpath, but a +// linked local checkout does — @ts-expect-error would be "unused" in one of the two modes. +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +import { tangramPlugin } from "@open-aviation/tangram-core/vite-plugin"; + +export default defineConfig({ + plugins: [tangramPlugin()] +}); diff --git a/justfile b/justfile new file mode 100644 index 0000000..391811f --- /dev/null +++ b/justfile @@ -0,0 +1,17 @@ +plugin_dir := "example_plugins/tangram/tangram_minisky" + +sync: + pnpm install + pnpm build + uv sync --all-packages + +fmt: + uv run ruff check minisky example_plugins tests --fix + uv run ruff format example_plugins/tangram.py {{plugin_dir}} + pnpm lint:fix + +check: + uv run ruff check minisky example_plugins tests + uv run ruff format example_plugins/tangram.py {{plugin_dir}} --check + uv run pyright + pnpm check diff --git a/minisky/core/settings.py b/minisky/core/settings.py index b628660..d3e37a7 100644 --- a/minisky/core/settings.py +++ b/minisky/core/settings.py @@ -1,22 +1,22 @@ """MiniSky settings loader. -Reads settings.yml from the project root at import time and exposes every +Reads settings.toml from the project root at import time and exposes every key/value pair as a module-level attribute (e.g., -``minisky.core.settings.prefer_compiled``). Also provides the data() -helper that resolves paths inside the package data directory. +``minisky.core.settings.prefer_compiled``). Nested tables (e.g. ``[tangram]``) +are exposed as the corresponding ``dict``. Also provides the data() helper +that resolves paths inside the package data directory. """ # %% +import tomllib from pathlib import Path -import yaml +filename_settings = Path(__file__).parent.parent.parent / "settings.toml" -filename_settings = Path(__file__).parent.parent.parent / "settings.yml" +with open(filename_settings, "rb") as file: + config = tomllib.load(file) -with open(filename_settings, encoding="utf-8") as file: - _settings = yaml.safe_load(file) - -for key, value in _settings.items(): +for key, value in config.items(): globals()[key] = value # Explicit type declarations for pyright (set dynamically above via globals()) diff --git a/minisky/plugin/plugin_decorators.py b/minisky/plugin/plugin_decorators.py index 6430270..755a863 100644 --- a/minisky/plugin/plugin_decorators.py +++ b/minisky/plugin/plugin_decorators.py @@ -9,9 +9,9 @@ def command( - func: Callable | None = None, + func: Callable[..., Any] | None = None, name: str = "", - aliases: tuple = (), + aliases: tuple[str, ...] = (), brief: str = "", help: str = "", arguments: str = "", diff --git a/minisky/simulation/simulation.py b/minisky/simulation/simulation.py index 93c93f4..32762e9 100644 --- a/minisky/simulation/simulation.py +++ b/minisky/simulation/simulation.py @@ -61,8 +61,8 @@ def __init__(self) -> None: # System time [seconds] self.syst: float = 0 - # Simulated UTC clock time, can be set by setutc() - self.utc: datetime.datetime = datetime.datetime.today() + # Simulated UTC clock time (timezone-aware), can be set by setutc() + self.utc: datetime.datetime = datetime.datetime.now(datetime.UTC) # Flag indicating running at fixed rate or fast time @@ -165,7 +165,7 @@ def reset(self) -> None: self.simt = 0 self.simdt = 1 self.utc = datetime.datetime.now(datetime.UTC).replace( - hour=0, minute=0, second=0, microsecond=0, tzinfo=None + hour=0, minute=0, second=0, microsecond=0 ) minisky.navdb.reset() minisky.traf.reset() @@ -262,29 +262,27 @@ def setutc(self, *args: str) -> tuple[bool, str]: elif len(args) == 1: if args[0].upper() == "RUN": self.utc = datetime.datetime.now(datetime.UTC).replace( - hour=0, minute=0, second=0, microsecond=0, tzinfo=None + hour=0, minute=0, second=0, microsecond=0 ) elif args[0].upper() == "REAL": - self.utc = datetime.datetime.today().replace(microsecond=0) + self.utc = datetime.datetime.now(datetime.UTC).astimezone().replace(microsecond=0) elif args[0].upper() == "UTC": - self.utc = datetime.datetime.now(datetime.UTC).replace( - microsecond=0, tzinfo=None - ) + self.utc = datetime.datetime.now(datetime.UTC).replace(microsecond=0) else: try: self.utc = datetime.datetime.strptime( args[0], "%H:%M:%S.%f" if "." in args[0] else "%H:%M:%S" - ) + ).replace(tzinfo=datetime.UTC) except ValueError: return False, "Input time invalid" elif len(args) == 3: day, month, year = args try: - self.utc = datetime.datetime(int(year), int(month), int(day)) + self.utc = datetime.datetime(int(year), int(month), int(day), tzinfo=datetime.UTC) except ValueError: return False, "Input date invalid." elif len(args) == 4: @@ -293,7 +291,7 @@ def setutc(self, *args: str) -> tuple[bool, str]: self.utc = datetime.datetime.strptime( f"{year},{month},{day},{timestring}", ("%Y,%m,%d,%H:%M:%S.%f" if "." in timestring else "%Y,%m,%d,%H:%M:%S"), - ) + ).replace(tzinfo=datetime.UTC) except ValueError: return False, "Input date invalid." else: diff --git a/minisky/stack/__init__.py b/minisky/stack/__init__.py index 5a45fec..037b456 100644 --- a/minisky/stack/__init__.py +++ b/minisky/stack/__init__.py @@ -346,17 +346,18 @@ def reset(cls) -> None: @classmethod def commands(cls) -> Iterator[str]: - """Generator function to iterate over stack commands.""" - # Return commands from PCALL if passed, otherwise own command stack + """Generator function to iterate over stack commands. + + Detaches the pending command list before iterating so that a + stack() call from another thread (e.g. a plugin I/O thread) can + never race with processing: a command lands either on the old + list (processed this step) or on the fresh one (next step). + """ + pending, cls.cmdstack = cls.cmdstack, [] # (assigning to cls attributes on purpose, so cls.current tracks the loop) - for cls.current, cls.sender_rte in cls.cmdstack: # noqa: B020 + for cls.current, cls.sender_rte in pending: # noqa: B020 yield cls.current - @classmethod - def clear(cls) -> None: - """Remove all commands from the command stack.""" - cls.cmdstack.clear() - def delete_element(*arg): """DEL: Delete an element (aircraft, wind field, area shape, or group). @@ -402,7 +403,9 @@ def process() -> None: in which case the second word is the command, defaulting to POS), the remaining text is passed to the Command object for argument parsing and execution, and any resulting message is echoed to the screen. The - command stack is cleared afterwards. + pending commands are detached from the stack up front (see + Stack.commands), so commands stacked while processing runs — including + from other threads — are kept for the next step instead of being lost. """ # First check for commands in scenario file checkscen() @@ -459,9 +462,6 @@ def process() -> None: if echotext: minisky.scr.echo(echotext) - # Clear the processed commands - Stack.clear() - def readscn(scn: "str | Path | StringIO") -> Iterator[tuple[float, str]]: """Read a scenario file and yield its timestamped commands. diff --git a/minisky/streaming.py b/minisky/streaming.py index af10b83..2be870f 100644 --- a/minisky/streaming.py +++ b/minisky/streaming.py @@ -10,13 +10,8 @@ any particular client or wire contract. Unit conversion and field mapping to a specific consumer's format happen downstream, in that consumer, not here. -Snapshot shape:: - - { - "siminfo": {speed, simdt, simt, simutc, ntraf, state, scenname}, - "acdata": {callsign, lat, lon, alt, trk, vs, tas, cas, gs, typecode, - inconf, tcpamax, nconf_cur, nconf_tot, nlos_cur, nlos_tot}, - } +The snapshot shape is defined by the :class:`Snapshot` / :class:`SimInfo` / +:class:`AcData` TypedDicts below. Units on the wire here are SI: positions in decimal degrees, ``alt`` in metres, speeds (``tas``/``cas``/``gs``) in m/s, ``vs`` in m/s, ``trk`` in degrees, @@ -27,7 +22,7 @@ import asyncio import time -from typing import Any, cast +from typing import Any, TypedDict, cast import numpy as np @@ -39,28 +34,67 @@ STREAM_MAX_HZ = 10.0 -def _tolist(arr: Any) -> list: +class SimInfo(TypedDict): + """Simulation-level snapshot fields.""" + + speed: float # runner speed multiplier (x realtime) + simdt: float # s + simt: float # s + simutc: str # ISO-8601, timezone-aware + ntraf: int + state: int # 0=INIT, 1=HOLD, 2=OP, 3=END + scenname: str # "" when no scenario is loaded + + +class AcData(TypedDict): + """Per-aircraft snapshot columns (one element per aircraft, SI units).""" + + callsign: list[str] + lat: list[float] # deg + lon: list[float] # deg + alt: list[float] # m + trk: list[float] # deg + vs: list[float] # m/s + tas: list[float] # m/s + cas: list[float] # m/s + gs: list[float] # m/s + typecode: list[str] + inconf: list[bool] + tcpamax: list[float] # s + nconf_cur: int + nconf_tot: int + nlos_cur: int + nlos_tot: int + + +class Snapshot(TypedDict): + """Full per-tick simulation snapshot.""" + + siminfo: SimInfo + acdata: AcData + + +def _tolist(arr: Any) -> list[float]: """Convert a numpy array (or list) into a plain JSON-serialisable list.""" if isinstance(arr, np.ndarray): - return cast(list, arr.tolist()) + return cast(list[float], arr.tolist()) return list(arr) -def build_snapshot() -> dict[str, Any]: +def build_snapshot() -> Snapshot: """Build a full snapshot of the current simulation state (SI units). Reads ``minisky.sim`` and ``minisky.traf`` and returns a plain dict of Python scalars and lists (no numpy types), safe to serialise as JSON. Returns: - A dict with ``siminfo`` and ``acdata`` keys as described in the module - docstring. + A :class:`Snapshot` with ``siminfo`` and ``acdata`` keys. """ sim = minisky.sim traf = minisky.traf cd = traf.cd - siminfo = { + siminfo: SimInfo = { "speed": float(minisky.runner.speed), "simdt": float(sim.simdt), "simt": float(sim.simt), @@ -70,7 +104,7 @@ def build_snapshot() -> dict[str, Any]: "scenname": minisky.stack.get_scenname(), } - acdata = { + acdata: AcData = { "callsign": [str(c) for c in traf.callsign], "lat": _tolist(traf.lat), "lon": _tolist(traf.lon), @@ -83,7 +117,7 @@ def build_snapshot() -> dict[str, Any]: "typecode": [str(t) for t in traf.typecode], # Conflict data (traf.cd). The per-pair counters are derived from the # detection object's current/cumulative unique-pair collections. - "inconf": _tolist(cd.inconf), + "inconf": [bool(v) for v in cd.inconf], "tcpamax": _tolist(cd.tcpamax), "nconf_cur": len(cd.confpairs_unique), "nconf_tot": len(cd.confpairs_all), @@ -117,7 +151,7 @@ def __init__(self, max_hz: float = STREAM_MAX_HZ) -> None: self._event = asyncio.Event() self._min_interval = 1.0 / max_hz if max_hz > 0 else 0.0 self._last_publish = 0.0 - self.latest: dict[str, Any] | None = None + self.latest: Snapshot | None = None self.generation = 0 @property @@ -152,7 +186,7 @@ def publish_tick(self) -> None: return self.publish(build_snapshot()) - def publish(self, snapshot: dict[str, Any]) -> None: + def publish(self, snapshot: Snapshot) -> None: """Store a snapshot as :attr:`latest` and wake awaiting consumers.""" self.latest = snapshot self.generation += 1 diff --git a/minisky/traffic/traffic.py b/minisky/traffic/traffic.py index 14885c3..f5a525b 100644 --- a/minisky/traffic/traffic.py +++ b/minisky/traffic/traffic.py @@ -136,8 +136,8 @@ def __init__(self) -> None: with self.settrafarrays(): # Aircraft Info - self.callsign = [] # identifier (string) - self.typecode = [] # aircaft type (string) + self.callsign: list[str] = [] # identifier (string) + self.typecode: list[str] = [] # aircaft type (string) # Positions self.lat = np.array([]) # latitude [deg] diff --git a/mkdocs.yml b/mkdocs.yml index 0ad1b63..b157eec 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -33,6 +33,10 @@ plugins: - mkdocstrings: handlers: python: + # tangram_minisky lives in its own workspace/.venv and is not installed + # in the docs env; point griffe at its source so it can analyse it + # statically (no import, so tangram_core is not required here). + paths: [example_plugins/tangram/tangram_minisky/src] options: docstring_style: google docstring_section_style: table @@ -69,6 +73,7 @@ nav: - Control console: guides/console.md - Python library: guides/python-api.md - Writing plugins: guides/plugins.md + - Streaming to tangram: guides/tangram.md - Reference: - Stack commands: reference/commands.md - API: @@ -79,3 +84,4 @@ nav: - plugin: api/plugin.md - core: api/core.md - tools: api/tools.md + - tangram_minisky: api/tangram-minisky.md diff --git a/package.json b/package.json new file mode 100644 index 0000000..c777a4d --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "name": "minisky-workspace", + "private": true, + "packageManager": "pnpm@11.17.0", + "scripts": { + "build": "pnpm --filter './example_plugins/tangram/*' run build", + "check": "pnpm --filter './example_plugins/tangram/*' run check", + "lint:fix": "pnpm --filter './example_plugins/tangram/*' run lint:fix" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..51e6dc8 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,3132 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: {} + + example_plugins/tangram/tangram_minisky: + dependencies: + '@open-aviation/tangram-core': + specifier: ^0.5.0 + version: 0.5.0(@loaders.gl/core@4.4.3)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/gltf@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6))(@math.gl/web-mercator@4.1.0)(typescript@5.9.3)(vite@8.1.5(@types/node@24.13.3)) + devDependencies: + '@deck.gl/core': + specifier: ^9.3.3 + version: 9.3.7 + '@deck.gl/layers': + specifier: ^9.3.3 + version: 9.3.7(@deck.gl/core@9.3.7)(@loaders.gl/core@4.4.3)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6))) + '@eslint/js': + specifier: ^9.39.0 + version: 9.39.5 + '@types/node': + specifier: ^24.10.1 + version: 24.13.3 + '@types/phoenix': + specifier: ^1.6.7 + version: 1.6.7 + eslint: + specifier: ^9.39.0 + version: 9.39.5(supports-color@7.2.0) + eslint-plugin-vue: + specifier: ^10.6.2 + version: 10.10.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3))(eslint@9.39.5(supports-color@7.2.0))(vue-eslint-parser@10.4.1(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)) + globals: + specifier: ^17.7.0 + version: 17.7.0 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + typescript-eslint: + specifier: ^8.61.0 + version: 8.65.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + vite: + specifier: ^8.0.16 + version: 8.1.5(@types/node@24.13.3) + vue: + specifier: ^3.5.35 + version: 3.5.40(typescript@5.9.3) + vue-tsc: + specifier: ^3.3.4 + version: 3.3.8(typescript@5.9.3) + +packages: + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + + '@deck.gl/aggregation-layers@9.3.7': + resolution: {integrity: sha512-eRzddMlHuBBFeSfhBig5V/32psav5JLvRYjuMqHgcwWXKfanAcxsKBfDSA4tzwWpnDU4id9sfdGW1RDzbVgY5Q==} + peerDependencies: + '@deck.gl/core': ~9.3.0 + '@deck.gl/layers': ~9.3.0 + '@luma.gl/core': ~9.3.3 + '@luma.gl/engine': ~9.3.3 + + '@deck.gl/core@9.3.7': + resolution: {integrity: sha512-l+YPm1I0Fwj+b7xsu8FvyDT5MGPgEPJe2cQJUpQNgG742b2goB4tyYpteUhNAOeVo1gmenvqv36ZJGNsGxipJA==} + + '@deck.gl/extensions@9.3.7': + resolution: {integrity: sha512-CY4Hi7yeVNMOjYfbrxNZ5PF3ftH16zGwV4JSH6g9opCTfLFxBgT2Rxbs3cwywanAs5yO7RraDDM9WyQSzAU7qg==} + peerDependencies: + '@deck.gl/core': ~9.3.0 + '@luma.gl/core': ~9.3.3 + '@luma.gl/engine': ~9.3.3 + + '@deck.gl/geo-layers@9.3.7': + resolution: {integrity: sha512-BPa0iaiDGvs3C9Ptu5zDXyYpm4NMUCB1LQOVvxFRuIEqup9nZpBmCsLx+HSeF4l1aQ0c9K4GG9EdM3iwwbqLAw==} + peerDependencies: + '@deck.gl/core': ~9.3.0 + '@deck.gl/extensions': ~9.3.0 + '@deck.gl/layers': ~9.3.0 + '@deck.gl/mesh-layers': ~9.3.0 + '@loaders.gl/core': ^4.4.3 + '@luma.gl/core': ~9.3.3 + '@luma.gl/engine': ~9.3.3 + + '@deck.gl/json@9.3.7': + resolution: {integrity: sha512-ohLW3cOYr+ptHSTxgfQSJ4rNNpVJdoHk9iHOU+AK5oK8MP/ISfySA+zWqgb/VS5HKMJGpgYXJWBZGnTnw/Cl0A==} + peerDependencies: + '@deck.gl/core': ~9.3.0 + + '@deck.gl/layers@9.3.7': + resolution: {integrity: sha512-T2CnkEU1QEqmusrSdot7+8Nl09gzntG5DvhaogyBbTjmABhTh4rUnS6dLOjf5/ju3aJ+Q58/FvDzgBBcK4V7kg==} + peerDependencies: + '@deck.gl/core': ~9.3.0 + '@loaders.gl/core': ^4.4.3 + '@luma.gl/core': ~9.3.3 + '@luma.gl/engine': ~9.3.3 + + '@deck.gl/mapbox@9.3.7': + resolution: {integrity: sha512-hamgwBS2BbB8DHOjYWwPMrFki+KhcedjoHv/9cUpQHnjfXUswzmyhAURuyowO/tAVJb3vF+CmMzaDVUTlI40Sw==} + peerDependencies: + '@deck.gl/core': ~9.3.0 + '@luma.gl/core': ~9.3.3 + '@math.gl/web-mercator': ^4.1.0 + + '@deck.gl/mesh-layers@9.3.7': + resolution: {integrity: sha512-Rc88vaOd/isTw8sZS44xrJOA2vSMYXcO0n9QAEm6AYRBwneZ3+l1/6WIiMIs3Vo8xUytMRyZthdNV3hn2Qv/lg==} + peerDependencies: + '@deck.gl/core': ~9.3.0 + '@luma.gl/core': ~9.3.3 + '@luma.gl/engine': ~9.3.3 + '@luma.gl/gltf': ~9.3.3 + '@luma.gl/shadertools': ~9.3.3 + + '@deck.gl/widgets@9.3.7': + resolution: {integrity: sha512-Ygvn6XRQfh8hDnVczZNrh/nmKXdptavT/KmDzrBJbJaP8Upi/4bgo9p8pe1u7qZagCprzSfo/tKumRN1BNOWhg==} + peerDependencies: + '@deck.gl/core': ~9.3.0 + '@luma.gl/core': ~9.3.3 + + '@emnapi/core@1.11.1': + resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} + + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + + '@eslint-community/eslint-utils@4.10.1': + resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.6': + resolution: {integrity: sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.5': + resolution: {integrity: sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@floating-ui/core@1.8.0': + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} + + '@floating-ui/dom@1.8.0': + resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} + + '@floating-ui/utils@0.2.12': + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + + '@fontsource/b612@5.3.0': + resolution: {integrity: sha512-2AQPrSFDI9K00w/lPlgfcqve+ZPdRSCttvVUxcmv+UrnwwaKLMym+/uXmM7+dgPCVBIV9omoXUabFoyBFpc1Vg==} + + '@fontsource/inconsolata@5.3.0': + resolution: {integrity: sha512-12UZvQzR9rcgFv1Wb2vy9NvMqleh4fVZ9W5Ww/S3CamaRLeUfCkfXersbiqcyCkH82etGTTRyHd5cJYDJ2W27w==} + + '@fontsource/roboto-condensed@5.3.0': + resolution: {integrity: sha512-Vbh5pesre/M/Jspd10btdKRqLSTe3yn4l6B2tXHGElCeASfA7Pgdpu1mb8wTG8E72J4Xk6FUfqnSe46UN2QAog==} + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@loaders.gl/3d-tiles@4.4.3': + resolution: {integrity: sha512-trKDXRYE7xIyH0g2tvDG0SHo/J5sCUhOec70Ne1a5t64/yY+yifQ4B67n4AnSOnZ+l4sxjUypjxhHUqoAeWieQ==} + peerDependencies: + '@loaders.gl/core': ~4.4.0 + + '@loaders.gl/compression@4.4.3': + resolution: {integrity: sha512-v3feEE48FblxBPaILwejV48plcLmjvOh2yBQrgvKvLCaQdQ9bVz7PzhrUdLW0VDVlGnoR1NUJtI833627U8Heg==} + peerDependencies: + '@loaders.gl/core': ~4.4.0 + + '@loaders.gl/core@4.4.3': + resolution: {integrity: sha512-D6kXFdpUtYwoqS5OQLLaLELkHlS3qIdcSgzVvsh/MinRteeVIITJojqEc/lmDx1zVR3j6WND7yPGF4i4aOwiOg==} + + '@loaders.gl/crypto@4.4.3': + resolution: {integrity: sha512-zratEvtj/Mdbu0NwwwzdbP1oyY4FNxLcOY2JRcYqe3wzw+0kyeK7THdaVPcduZAnQ06HtpwHls61ONZunjMtXA==} + peerDependencies: + '@loaders.gl/core': ~4.4.0 + + '@loaders.gl/draco@4.4.3': + resolution: {integrity: sha512-YNI1MUDDIbrJBamgU1emLBC2kQUbESNIOZv9bcS8xwxr9SNMfnAMZWgdUJkYcC5xzV2q6ty2I/b2eGhXQkd9EQ==} + peerDependencies: + '@loaders.gl/core': ~4.4.0 + + '@loaders.gl/geoarrow@4.4.3': + resolution: {integrity: sha512-P0TSbtsw1UI1rZnp1tGHuqPVHcH7Yc14q9jZLIcrjhQOzol55YDI0/hYSXpA73794nR6o8ALxNwiy7/4HlBlcA==} + peerDependencies: + '@loaders.gl/core': ~4.4.0 + + '@loaders.gl/gis@4.4.3': + resolution: {integrity: sha512-2dhhzfCT1cXQQLZ1lMtb2Y+pX414qaeLL8Wpx7FJu/ar1HJfKL6Fb3juTR+2WzMybkAOX5zLxYh6+y7LqiPjgA==} + peerDependencies: + '@loaders.gl/core': ~4.4.0 + + '@loaders.gl/gltf@4.4.3': + resolution: {integrity: sha512-OYE/0nGLYm3weiCb78+ROwdNVyuLS8IZwN8NvGqctqt0HS4ZD40biu7b9L8xkFbVTZQkQZXDpyZ61n5PSQeU1A==} + peerDependencies: + '@loaders.gl/core': ~4.4.0 + + '@loaders.gl/images@4.4.3': + resolution: {integrity: sha512-a4C6x+4GZUchf+IwpdBOvyGeLGSbGOVob+WSWfGUezEQ4gqu1zS1pn3lCX3ZTL5P+Ch3v6KKwPGdO5YdTd52Ew==} + peerDependencies: + '@loaders.gl/core': ~4.4.0 + + '@loaders.gl/loader-utils@4.4.3': + resolution: {integrity: sha512-5yt1OqZPYR3d+xFAqtzSKO24Hd7019KqN+iOWp7XBKPQBS+sAEhAnmSTnO1axo45IEi14J3FhMDk+ndqBoSKEQ==} + + '@loaders.gl/math@4.4.3': + resolution: {integrity: sha512-EdEsZGqo1AX3sqgXt8bSBmdo+8ncURXjWucyQ8eeIJ2h0VIqM3bLkOGKk/D1ngeqK4hJXcjiturYHBW1B286lw==} + peerDependencies: + '@loaders.gl/core': ~4.4.0 + + '@loaders.gl/mvt@4.4.3': + resolution: {integrity: sha512-s2R8GRlaWDfZ7oKDFiY0c5EJ8elkf2VbPvdC0eoh69DN71y+AKngDSB+7h4veH/oueLEjmV7tNlF0+Snv58CPw==} + peerDependencies: + '@loaders.gl/core': ~4.4.0 + + '@loaders.gl/schema-utils@4.4.3': + resolution: {integrity: sha512-TggzQWkzf9pIYbj8I9RlTk33kwXslvDkY1nnw/OL/hika6qTls1G127qK5m9skiAjElk5ZkvQLQcwhOboZXZeA==} + peerDependencies: + '@loaders.gl/core': ~4.4.0 + + '@loaders.gl/schema@4.4.3': + resolution: {integrity: sha512-tDb/rOn44nHWvMFSOaAk1/pQcQcusxirGCM1O5BMJR0PQ71UrCHey56rsbcf7/wGhvyRwPtX+oiMzlJ9TmN8vQ==} + + '@loaders.gl/terrain@4.4.3': + resolution: {integrity: sha512-wu7O3FVXE6D1kSm5inRu8M8V1Vv5AYlnlNB0EJIOSSXBLhfNbPL5oHjlD2wIlag0gSxOVlhWjCUcWS3/Nb0P8Q==} + peerDependencies: + '@loaders.gl/core': ~4.4.0 + + '@loaders.gl/textures@4.4.3': + resolution: {integrity: sha512-QNC+anwJJcHsLsaAzXozJ+IUNxBnqWsZLUMkUZIorVey+XQcp2hcYng5iOmUQnCZdIR9gYe53VJa0j1C0JXRSg==} + peerDependencies: + '@loaders.gl/core': ~4.4.0 + + '@loaders.gl/tiles@4.4.3': + resolution: {integrity: sha512-8ZkPMe+daMV5mHKTEU591mJSp6pswdWliI+PcNhSOkpeuvyjecaYKSzuzMFMmaywfgOLpYl+lpfzKUEmCcE5vQ==} + peerDependencies: + '@loaders.gl/core': ~4.4.0 + + '@loaders.gl/wms@4.4.3': + resolution: {integrity: sha512-fKYCdIp6xQcucbm42bztprlfMJ5SZKx5f2ZwWLcj+kwv53U7hjcbIl04uMAk/i/7Xw/I4QEyA/XnPnxquuIFHQ==} + peerDependencies: + '@loaders.gl/core': ~4.4.0 + + '@loaders.gl/worker-utils@4.4.3': + resolution: {integrity: sha512-RZt/NwFXm6Kx3Fn/rqiW3Alse9Z0XtBktH/EFAp8W6LvsKuxpRMjULqGfybTu2waRXRYUNYZ6zGs9aOWbtDRLw==} + peerDependencies: + '@loaders.gl/core': ~4.4.0 + + '@loaders.gl/xml@4.4.3': + resolution: {integrity: sha512-/CtpIWaPKBs/AaX7MqUGSSE1OAhq78nDrJ5xPExYdIQAw5gl5OSmLZobUSSxV5FGaqkJRVbtC/sj5sUpci7doA==} + peerDependencies: + '@loaders.gl/core': ~4.4.0 + + '@loaders.gl/zip@4.4.3': + resolution: {integrity: sha512-HH/JLulJJVyqmbQYp4e/9MPjaTnUFnWfhgza8sMGIBYZ/YuZXOc6Ad2vvmyFQHvNbVr+NLrRZODASEjNZQ5rfQ==} + peerDependencies: + '@loaders.gl/core': ~4.4.0 + + '@luma.gl/core@9.3.6': + resolution: {integrity: sha512-eqHnCPh2xHYkdd9rEkiIIkGMixNiJkq1ROrnTob6npvmZNVHXL0ubIw5KueL7rqW0+J1tm+p2+TXZaCmn0sP7Q==} + + '@luma.gl/engine@9.3.6': + resolution: {integrity: sha512-NYTdhn2NaH/MjN8qXCl2ukrT1Ac9iTKu1mgN0wz11XRd1QYnlMNBXswRROD7IZ2PUsBWdmYHc9qE8wKBQqMuIA==} + peerDependencies: + '@luma.gl/core': ~9.3.0 + '@luma.gl/shadertools': ~9.3.0 + + '@luma.gl/gltf@9.3.6': + resolution: {integrity: sha512-9R27aYwvu6KBJzd9Kxs4cqIJpgJsI1AyL6Pn6+5CrmGbaCqMtgWi7Sf9Wo+bqv0PRxwzH0j1oAv7qRk0hGbstw==} + peerDependencies: + '@luma.gl/core': ~9.3.0 + '@luma.gl/engine': ~9.3.0 + '@luma.gl/shadertools': ~9.3.0 + + '@luma.gl/shadertools@9.3.6': + resolution: {integrity: sha512-dDuD8lCOkAE1L7hJGZEyZAi7upR1HG3wEEIQ1gCLaTTbJWC7tNtnVz853lqUA+VXgjgS9NiQFFRcosiIZmW4Vg==} + peerDependencies: + '@luma.gl/core': ~9.3.0 + + '@luma.gl/webgl@9.3.6': + resolution: {integrity: sha512-tGm7FGWPmJKxGZMvkRPRi219x3tKmBxR7Uke09nTp2ujNak/O5V9xPIQ9lUBGTxqAb8U2yjKkXG8j+f/4b2+sw==} + peerDependencies: + '@luma.gl/core': ~9.3.0 + + '@mapbox/jsonlint-lines-primitives@2.0.3': + resolution: {integrity: sha512-0SElaV0uMxEnxzBhhX9WTuPyUeMsAN/SS0i16tjuba4/mio63MG9khjC1a0JAiPGXAwvwm4UfHJURCN7nyudQg==} + engines: {node: '>= 22'} + + '@mapbox/martini@0.2.0': + resolution: {integrity: sha512-7hFhtkb0KTLEls+TRw/rWayq5EeHtTaErgm/NskVoXmtgAQu/9D299aeyj6mzAR/6XUnYRp2lU+4IcrYRFjVsQ==} + + '@mapbox/point-geometry@0.1.0': + resolution: {integrity: sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==} + + '@mapbox/point-geometry@1.1.0': + resolution: {integrity: sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==} + + '@mapbox/tiny-sdf@2.2.0': + resolution: {integrity: sha512-LVL4wgI9YAum5V+LNVQO6QgFBPw7/MIIY4XJPNsPDMrjEwcE+JfKk1LuIl8GnF197ejVdC9QdPaxrx5gfgdGXg==} + + '@mapbox/unitbezier@0.0.1': + resolution: {integrity: sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==} + + '@mapbox/unitbezier@1.0.0': + resolution: {integrity: sha512-fqd515fjBmANKGGsQ286E2Wvj/XvDFpGzwJxq4CI6jMQue6Oy04uCKp+JWKF00xRTmk6cEu1jPJ9p3xqH8YWqQ==} + + '@mapbox/vector-tile@1.3.1': + resolution: {integrity: sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==} + + '@mapbox/vector-tile@2.0.5': + resolution: {integrity: sha512-pXj8m7KTsqZt+1jsE0xIpGvqTSbblfkuEJL/NJmNePMtEwxO8V3XMDo9WMSfDeqHvCtBI9Lmt4mGcGR10zecmw==} + + '@mapbox/whoots-js@3.1.0': + resolution: {integrity: sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==} + engines: {node: '>=6.0.0'} + + '@maplibre/geojson-vt@6.1.1': + resolution: {integrity: sha512-FVMOcmSP/yqol45t7StApEyTL5/vmqBCuFhH9n+fFuINenhaX+YgHHIt1yJ86S8kln3uJLcMvmEU2cfn6E2eCQ==} + + '@maplibre/maplibre-gl-style-spec@24.10.0': + resolution: {integrity: sha512-lichxSiagMEBBrqHF0trtMQH9RKh+9jUlIJl0qW0QHvt2H/tbvUWdE+ZzI2Jd0/pT7j/iavLonlPu7EQ/ixTOw==} + hasBin: true + + '@maplibre/mlt@1.1.12': + resolution: {integrity: sha512-ZeK5w2TTeHOajcLaEQs1KZXw2V9wIKo1PmThlxlsHoXsQsYlBqLJzPOd6tJHRtGTChUY3DPPmjXRArYVvAbmZw==} + + '@maplibre/vt-pbf@4.3.2': + resolution: {integrity: sha512-j6p0AdjvAR19Z3XaCysle7A4ZSo08tYOzxD0Y9NQylwPAkwJJeYub5b2eVucdeDh7erhv69DahoLOevDRERRUw==} + + '@math.gl/core@4.1.0': + resolution: {integrity: sha512-FrdHBCVG3QdrworwrUSzXIaK+/9OCRLscxI2OUy6sLOHyHgBMyfnEGs99/m3KNvs+95BsnQLWklVfpKfQzfwKA==} + + '@math.gl/culling@4.1.0': + resolution: {integrity: sha512-jFmjFEACnP9kVl8qhZxFNhCyd47qPfSVmSvvjR0/dIL6R9oD5zhR1ub2gN16eKDO/UM7JF9OHKU3EBIfeR7gtg==} + + '@math.gl/geospatial@4.1.0': + resolution: {integrity: sha512-BzsUhpVvnmleyYF6qdqJIip6FtIzJmnWuPTGhlBuPzh7VBHLonCFSPtQpbkRuoyAlbSyaGXcVt6p6lm9eK2vtg==} + + '@math.gl/polygon@4.1.0': + resolution: {integrity: sha512-YA/9PzaCRHbIP5/0E9uTYrqe+jsYTQoqoDWhf6/b0Ixz8bPZBaGDEafLg3z7ffBomZLacUty9U3TlPjqMtzPjA==} + + '@math.gl/sun@4.1.0': + resolution: {integrity: sha512-i3q6OCBLSZ5wgZVhXg+X7gsjY/TUtuFW/2KBiq/U1ypLso3S4sEykoU/MGjxUv1xiiGtr+v8TeMbO1OBIh/HmA==} + + '@math.gl/types@4.1.0': + resolution: {integrity: sha512-clYZdHcmRvMzVK5fjeDkQlHUzXQSNdZ7s4xOqC3nJPgz4C/TZkUecTo9YS4PruZqtDda/ag4erndP0MIn40dGA==} + + '@math.gl/web-mercator@4.1.0': + resolution: {integrity: sha512-HZo3vO5GCMkXJThxRJ5/QYUYRr3XumfT8CzNNCwoJfinxy5NtKUd7dusNTXn7yJ40UoB8FMIwkVwNlqaiRZZAw==} + + '@napi-rs/wasm-runtime@1.1.6': + resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@nodable/entities@3.0.0': + resolution: {integrity: sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==} + + '@open-aviation/tangram-core@0.5.0': + resolution: {integrity: sha512-0fusOdFqdzJA6yQGFPsM9DqUR+GVNL09Ttmf0F01ETqD+KnEj7Axv6eid/JD5OKsmOf2XIcP4g7iar1++B/ceA==} + + '@oxc-project/types@0.139.0': + resolution: {integrity: sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==} + + '@probe.gl/env@4.1.1': + resolution: {integrity: sha512-+68seNDMVsEegRB47pFA/Ws1Fjy8agcFYXxzorKToyPcD6zd+gZ5uhwoLd7TzsSw6Ydns//2KEszWn+EnNHTbA==} + + '@probe.gl/log@4.1.1': + resolution: {integrity: sha512-kcZs9BT44pL7hS1OkRGKYRXI/SN9KejUlPD+BY40DguRLzdC5tLG/28WGMyfKdn/51GT4a0p+0P8xvDn1Ez+Kg==} + + '@probe.gl/stats@4.1.1': + resolution: {integrity: sha512-4VpAyMHOqydSvPlEyHwXaE+AkIdR03nX+Qhlxsk2D/IW4OVmDZgIsvJB1cDzyEEtcfKcnaEbfXeiPgejBceT6g==} + + '@protomaps/basemaps@5.7.2': + resolution: {integrity: sha512-K1Yk6bWdULulYg+R2QRVXx4NzJZan5YQhpejEG0c1/sXruJrfPIPZuakpf3jwAgVmjIRVQwAv+yRafDeN0aaUQ==} + hasBin: true + + '@rolldown/binding-android-arm64@1.1.5': + resolution: {integrity: sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.1.5': + resolution: {integrity: sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.1.5': + resolution: {integrity: sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.1.5': + resolution: {integrity: sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.1.5': + resolution: {integrity: sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.1.5': + resolution: {integrity: sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.1.5': + resolution: {integrity: sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.1.5': + resolution: {integrity: sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.1.5': + resolution: {integrity: sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.1.5': + resolution: {integrity: sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.1.5': + resolution: {integrity: sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.1.5': + resolution: {integrity: sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.1.5': + resolution: {integrity: sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.1.5': + resolution: {integrity: sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.1.5': + resolution: {integrity: sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@turf/boolean-clockwise@5.1.5': + resolution: {integrity: sha512-FqbmEEOJ4rU4/2t7FKx0HUWmjFEVqR+NJrFP7ymGSjja2SQ7Q91nnBihGuT+yuHHl6ElMjQ3ttsB/eTmyCycxA==} + + '@turf/clone@5.1.5': + resolution: {integrity: sha512-//pITsQ8xUdcQ9pVb4JqXiSqG4dos5Q9N4sYFoWghX21tfOV2dhc5TGqYOhnHrQS7RiKQL1vQ48kIK34gQ5oRg==} + + '@turf/helpers@5.1.5': + resolution: {integrity: sha512-/lF+JR+qNDHZ8bF9d+Cp58nxtZWJ3sqFe6n3u3Vpj+/0cqkjk4nXKYBSY0azm+GIYB5mWKxUXvuP/m0ZnKj1bw==} + + '@turf/invariant@5.2.0': + resolution: {integrity: sha512-28RCBGvCYsajVkw2EydpzLdcYyhSA77LovuOvgCJplJWaNVyJYH6BOR3HR9w50MEkPqb/Vc/jdo6I6ermlRtQA==} + + '@turf/meta@5.2.0': + resolution: {integrity: sha512-ZjQ3Ii62X9FjnK4hhdsbT+64AYRpaI8XMBMcyftEOGSmPMUVnkbvuv3C9geuElAXfQU7Zk1oWGOcrGOD9zr78Q==} + + '@turf/rewind@5.1.5': + resolution: {integrity: sha512-Gdem7JXNu+G4hMllQHXRFRihJl3+pNl7qY+l4qhQFxq+hiU1cQoVFnyoleIqWKIrdK/i2YubaSwc3SCM7N5mMw==} + + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + + '@types/brotli@1.3.5': + resolution: {integrity: sha512-9xoNr+bcxT236/7ZgcWw/6Pb2RRetE13p4bFy1xYSckKwyOiRfmInay8baUWZgH7/284Wl6IPe7+nOI9+OQg/A==} + + '@types/crypto-js@4.2.2': + resolution: {integrity: sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==} + + '@types/esrecurse@4.3.1': + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@24.13.3': + resolution: {integrity: sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==} + + '@types/node@25.9.5': + resolution: {integrity: sha512-OScDchr2fwuUmWdf4kZ9h7PcJiYDVInhJizG/biAq3cAvqwYktuy/TYGGdZNMtNTFUP7rnb0NU4TUdm82kt4Rg==} + + '@types/offscreencanvas@2019.7.3': + resolution: {integrity: sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==} + + '@types/pako@1.0.7': + resolution: {integrity: sha512-YBtzT2ztNF6R/9+UXj2wTGFnC9NklAnASt3sC0h2m1bbH7G6FyBIkt4AN8ThZpNfxUo1b2iMVO0UawiJymEt8A==} + + '@types/phoenix@1.6.7': + resolution: {integrity: sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + + '@typescript-eslint/eslint-plugin@8.65.0': + resolution: {integrity: sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.65.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.65.0': + resolution: {integrity: sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.65.0': + resolution: {integrity: sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.65.0': + resolution: {integrity: sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.65.0': + resolution: {integrity: sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.65.0': + resolution: {integrity: sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.65.0': + resolution: {integrity: sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.65.0': + resolution: {integrity: sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.65.0': + resolution: {integrity: sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.65.0': + resolution: {integrity: sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@vitejs/plugin-vue@6.0.8': + resolution: {integrity: sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + vue: ^3.2.25 + + '@volar/language-core@2.4.28': + resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==} + + '@volar/source-map@2.4.28': + resolution: {integrity: sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==} + + '@volar/typescript@2.4.28': + resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==} + + '@vue/compiler-core@3.5.40': + resolution: {integrity: sha512-39E8IgOhTbVDnoJFMKc2DvYnypcZwUqgUhQkccva/0m6FUwtIKSGV7n1hpVmYcFaoRAwf9pBcwnKlCEsN63ZEQ==} + + '@vue/compiler-dom@3.5.40': + resolution: {integrity: sha512-pwkx4vqlqOspFstrcmzwkKLePVMD3PT65imRzLhanU2V1Fj4K13g6OXjanOyzw3aTAuRk84BOmY8f3rEHqPaVA==} + + '@vue/compiler-sfc@3.5.40': + resolution: {integrity: sha512-gIf497P4kpuALcvs5n3AEg1Vdn0pSY4XbjASIfHNYF1/MP3T2Mf2STERTubysBxCRxzJGJYtF/O7vwJrxFB3Vw==} + + '@vue/compiler-ssr@3.5.40': + resolution: {integrity: sha512-rrE5xiXG663+vHCHa3J9p2z5OcBRjXmoqenprJxAFQxg5pSshzeBiCE6pu46axapRJ2Adk0YDA2BRZVjiHXnhg==} + + '@vue/language-core@3.3.8': + resolution: {integrity: sha512-ieGT8jJdhhy0mGzStZhsg/qPw5bQZJg5yF+3+XU6saf4sM7yo9ZXy3h+nCwrm2+b4qS/SypkNdR2jAF3uei9tA==} + + '@vue/reactivity@3.5.40': + resolution: {integrity: sha512-B7ot9UlUZOi1zbq61/LvE88ZLTV8IlajTdiZTAEiDQgrnIMIZoPr9kGw0Zw46ObW62O9+H/Be3kMbfb7kYPQZA==} + + '@vue/runtime-core@3.5.40': + resolution: {integrity: sha512-KAZLweuZ6uUJPK1PMSQPgBU5gCjgrrfjUhSglmU9NhH+Zjepa8cnwSydPWDWHDwOgY4g3VcZ+PljbiHlURNCbw==} + + '@vue/runtime-dom@3.5.40': + resolution: {integrity: sha512-ZfrX8ssZQds900L9pr8AuK05ddnMsR4MPMZr8cPN9GoqoPWcXLhjvvbIA2SMv+7a97sJ1vv9pj/zxK0Cq/eEFQ==} + + '@vue/server-renderer@3.5.40': + resolution: {integrity: sha512-XNJym9WpevhTVt1HuwOrCRJ5Q+9z4BjTMrDtjTrvx74SmUll8spNTw6whWJa9mEkO4PKn5TihI/bm/8ds2QVJw==} + + '@vue/shared@3.5.40': + resolution: {integrity: sha512-WxnBtruIqOoV3rA4jeKDWzrYI5h7Cp4+pjwDi8kWGHz+IslhiN+wguLVVhtv2l8VoU02rzDCVfDjgCl1lNpZVg==} + + a5-js@0.7.3: + resolution: {integrity: sha512-3aoMwHmNkyuMDHS4q6GRRInpOawamen2pokIbc0MQmR9cqG0Y9+B0bZpzswwetjrSG2ckbYtShH+nKru6+3O5Q==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.17.0: + resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + alien-signals@3.2.1: + resolution: {integrity: sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + anynum@1.0.1: + resolution: {integrity: sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==} + + apache-arrow@21.2.0: + resolution: {integrity: sha512-Hxe6Agq26gQOM954qpzYSllJBPJl+e16U5CkfuMUhLrNba+5nKkttIVlflaovN6oaTratqMGAO8H5u/aNhmHWQ==} + hasBin: true + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + brace-expansion@1.1.16: + resolution: {integrity: sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==} + + brace-expansion@5.0.8: + resolution: {integrity: sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==} + engines: {node: 20 || >=22} + + brotli@1.3.3: + resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==} + + buf-compare@1.0.1: + resolution: {integrity: sha512-Bvx4xH00qweepGc43xFvMs5BKASXTbHaHm6+kDYIK9p/4iFwjATQkmPKHQSgJZzKbAymhztRbXUf1Nqhzl73/Q==} + engines: {node: '>=0.10.0'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + charenc@0.0.2: + resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + core-assert@0.2.1: + resolution: {integrity: sha512-IG97qShIP+nrJCXMCgkNZgH7jZQ4n8RpPyPeXX++T6avR/KhLhgLiHKoEn5Rc1KjfycSfA9DMa6m+4C4eguHhw==} + engines: {node: '>=0.10.0'} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + crypt@0.0.2: + resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + d3-hexbin@0.2.2: + resolution: {integrity: sha512-KS3fUT2ReD4RlGCjvCEm1RgMtp2NFZumdMu4DBzQK8AZv3fXRM6Xm8I4fSU07UXvH4xxg03NwWKWdvxfS/yc4w==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deep-strict-equal@0.2.0: + resolution: {integrity: sha512-3daSWyvZ/zwJvuMGlzG1O+Ow0YSadGfb3jsh9xoCutv2tWyB9dA4YvR9L9/fSdDZa2dByYQe+TqapSGUrjnkoA==} + engines: {node: '>=0.10.0'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + draco3d@1.5.7: + resolution: {integrity: sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ==} + + earcut@2.2.4: + resolution: {integrity: sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==} + + earcut@3.2.3: + resolution: {integrity: sha512-vnS4AVwp1KHAF13i1vp1/2D5evWy3k5u/iW/B81QVsUZtV8cv2tU0b2VNFlqvh4kYwrFMDdjPCfAmfyJW9y14Q==} + + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-plugin-vue@10.10.0: + resolution: {integrity: sha512-dL9x9rBHqqNcByWiLOHK6L0SB97V82/NC0cZRn9cXPjM7pCuWlpQQP9bFH4vjBv80ej1ZpzAkuD8zWH1o9bZbA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@stylistic/eslint-plugin': ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + '@typescript-eslint/parser': ^7.0.0 || ^8.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + vue-eslint-parser: ^10.3.0 + peerDependenciesMeta: + '@stylistic/eslint-plugin': + optional: true + '@typescript-eslint/parser': + optional: true + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-scope@9.1.2: + resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.39.5: + resolution: {integrity: sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + espree@11.2.0: + resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-xml-builder@1.3.0: + resolution: {integrity: sha512-F74cZEdCvuw9P41GAC3rod4X04jjWGM1JPEv/GWSqFTWLsdyMSBMBMlm9Hk3GLBgLBbdBNY8yee0pQh2RBVESQ==} + + fast-xml-parser@5.10.1: + resolution: {integrity: sha512-IEMIf7298kXuZSRFoGfMYrl7is8LpavODgbNz1cwIudv7KwVFnuU+UsMporfq6PD6aXSlawZlARiA3UywCTfMw==} + hasBin: true + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fflate@0.7.4: + resolution: {integrity: sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==} + + fflate@0.8.3: + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatbuffers@25.9.23: + resolution: {integrity: sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ==} + + flatted@3.4.3: + resolution: {integrity: sha512-/zipXxyO6rGvuNGDiULY9MvEGSkb2gaG4GGH4ygMi0ZZzyMHdUZBmntJmx5x1G2VuPytCwGN4xsJP6cw+sK+vQ==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + gl-matrix@3.4.4: + resolution: {integrity: sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@17.7.0: + resolution: {integrity: sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==} + engines: {node: '>=18'} + + h3-js@4.5.0: + resolution: {integrity: sha512-uKmdPc+DuarnR+XqZxEuWOMw7KzzKROrx3MLeJpFnMOs78S9M5eZ+X5RieS9UcSFQqbeXzbfWuX/W9Pyajinqw==} + engines: {node: '>=4', npm: '>=3', yarn: '>=1.3.0'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.6: + resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} + engines: {node: '>= 4'} + + image-size@0.7.5: + resolution: {integrity: sha512-Hiyv+mXHfFEP7LzUL/llg9RwFxxY+o9N3JVLIeG5E7iFIFAalxvRU9UZthBdYDEVnzHMgjnKJPPpay5BWf1g9g==} + engines: {node: '>=6.9.0'} + hasBin: true + + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + is-buffer@1.1.6: + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} + + is-error@2.2.2: + resolution: {integrity: sha512-IOQqts/aHWbiisY5DuPJQ0gcbvaLFCa7fBa9xoLfxBZvQ+ZI/Zh9xoI7Gk+G64N0FdK4AbibytHht2tWgpJWLg==} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-unsafe@2.0.0: + resolution: {integrity: sha512-2LdV822R+wmI86unXA93WCFpL6g+av8ynWk0nrHyJqGop5VoocYsSLFgN8jrfalT6iGeLNM4KXuVSsULP53kEA==} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + js-yaml@4.3.0: + resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==} + hasBin: true + + jsep@0.3.5: + resolution: {integrity: sha512-AoRLBDc6JNnKjNcmonituEABS5bcfqDhQAWWXNTFrqu6nVXBpBAGfcoTGZMFlIrh9FjmE1CQyX9CTNwZrXMMDA==} + engines: {node: '>= 6.0.0'} + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json-stringify-pretty-compact@4.0.0: + resolution: {integrity: sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==} + + json-with-bigint@3.5.10: + resolution: {integrity: sha512-Vcx+JVNEBts/xfcoCS69sKrOhOk/3TVlvlT+XzUOefVKnnrbYSCKpDCm10pohsJFtsJVYnwa/cXRZ4eElzaM6w==} + + jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + + kdbush@4.1.0: + resolution: {integrity: sha512-e9vurzrXJQrFX6ckpHP3bvj5l+9CnYzkxDNnNQ1h2QTqdWsUAJgXiKdGNcOa1EY85dU8KbQ+z/FdQdB7P+9yfQ==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + ktx-parse@0.7.1: + resolution: {integrity: sha512-FeA3g56ksdFNwjXJJsc1CCc7co+AJYDp6ipIp878zZ2bU8kWROatLYf39TQEd4/XRSUvBXovQ8gaVKWPXsCLEQ==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + + lit-html@3.3.3: + resolution: {integrity: sha512-el8M6jK2o3RXBnrSHX3ZKrsN8zEV63pSExTO1wYJz7QndGYZ8353e2a5PPX+qHe2aGayfnchQmkAojaWAREOIA==} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + long@3.2.0: + resolution: {integrity: sha512-ZYvPPOMqUwPoDsbJaR10iQJYnMuZhRTvHYl62ErLIEX7RgFlziSBUUvrt3OVfc47QlHHpzPZYP17g3Fv7oeJkg==} + engines: {node: '>=0.6'} + + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + + lz4js@0.2.0: + resolution: {integrity: sha512-gY2Ia9Lm7Ep8qMiuGRhvUq0Q7qUereeldZPP1PMEJxPtEWHJLqw9pgX68oHajBH0nzJK4MaZEA/YNV3jT8u8Bg==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + maplibre-gl@5.24.0: + resolution: {integrity: sha512-ALyFxgtd5R+65UqZ/++lOqwWcC0SNho9c27fYSyLmG7AfnAul2o46F05aDJGPbFU57wos9dgcIySHs0Xe6ia3A==} + engines: {node: '>=16.14.0', npm: '>=8.1.0'} + + md5@2.3.0: + resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==} + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + mjolnir.js@3.0.1: + resolution: {integrity: sha512-/RMi8Jm3NKleOkVI8D2ai+1OVwtfRJsSVBVjbTXNm83nfsN4uORYaN3u1/hsg5CqVI+di8enTkvgDNDOywn6cQ==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + + murmurhash-js@1.0.0: + resolution: {integrity: sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==} + + nanoid@3.3.16: + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parquet-wasm@0.7.2: + resolution: {integrity: sha512-XfzVFW7REEYzt/MF5pG2fVjF0lyArXkk4x7m4wuE7R5OEoUfXq5wdI5DQi/UsB8wWW3ncx2z6xMjq5dM9qTYgg==} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-expression-matcher@1.6.2: + resolution: {integrity: sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==} + engines: {node: '>=14.0.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + pbf@3.3.0: + resolution: {integrity: sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==} + hasBin: true + + pbf@4.0.2: + resolution: {integrity: sha512-J0ajxARhZfpUEebxYs1vhMGMuLSXtBe1e+fFPDrf2uA2hgo+UshKfNUWOz92HJNz6/NFEXseQPddnHkTreWRqg==} + hasBin: true + + pbf@5.1.2: + resolution: {integrity: sha512-mnvGdvOrIvJOBGUEdGkrVXjN8E/VkIJCkf2eS1DH2yv82ORUlLttmDt0rWY38yYZmVwciZwBUvHM20qxBZf40w==} + hasBin: true + + phoenix@1.8.9: + resolution: {integrity: sha512-/2qzAZB3P2s08fFAYaG65lqaNFmVXUSlXdY4/JDdDKIC81y2cFWkPwI8gycy4VLpv197JwZ5PpBf3VhoG32yGA==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} + + pmtiles@4.4.1: + resolution: {integrity: sha512-5oTeQc/yX/ft1evbpIlnoCZugQuug/iYIAj/ZTqIqzdGek4uZEho99En890EE6NOSI3JTI3IG8R7r8+SltphxA==} + + postcss-selector-parser@7.1.4: + resolution: {integrity: sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==} + engines: {node: '>=4'} + + postcss@8.5.23: + resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==} + engines: {node: ^10 || ^12 || >=14} + + potpack@2.1.0: + resolution: {integrity: sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ==} + + preact@10.29.7: + resolution: {integrity: sha512-DCHYrK/B10yUD3ZjLfhZ3WIE/9Vf9VFUODcRE2dRomTYDpJk6z6L9wecSfhfE6M9ZTHUdyQkoC46arIDhEV84Q==} + peerDependencies: + preact-render-to-string: '>=5' + peerDependenciesMeta: + preact-render-to-string: + optional: true + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + protocol-buffers-schema@3.6.1: + resolution: {integrity: sha512-VG2K63Igkiv9p76tk1lilczEK1cT+kCjKtkdhw1dQZV3k3IXJbd3o6Ho8b9zJZaHSnT2hKe4I+ObmX9w6m5SmQ==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + quickselect@3.0.0: + resolution: {integrity: sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-protobuf-schema@2.1.0: + resolution: {integrity: sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==} + + rolldown@1.1.5: + resolution: {integrity: sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rs1090-wasm@0.4.14: + resolution: {integrity: sha512-bvCduWWjVjdPbjnjxBZr5HdD/upUyuZKInOmtxgQ99m9y1YDxR4wLtgoyXYGVXAuQFosxv/nEABBeqDT3qjc+Q==} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + snappyjs@0.6.1: + resolution: {integrity: sha512-YIK6I2lsH072UE0aOFxxY1dPDCS43I5ktqHpeAsuLNYWkE5pGxRGWfDM4/vSUfNzXjC1Ivzt3qx31PCLmc9yqg==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strnum@2.4.1: + resolution: {integrity: sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg==} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + texture-compressor@1.0.2: + resolution: {integrity: sha512-dStVgoaQ11mA5htJ+RzZ51ZxIZqNOgWKAIvtjLrW1AliQQLCmrDqNzQZ8Jh91YealQ95DXt4MEduLzJmbs6lig==} + hasBin: true + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tinyqueue@3.0.0: + resolution: {integrity: sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typescript-eslint@8.65.0: + resolution: {integrity: sha512-/ggrHAwyjENDusvyxbuqxAC2dTnZg/Z8F+fgQtYIz+L6n/9HfSlEZcFGV/NsMNa6CkGk0xUjUAFwC0vHOflvIA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vite@8.1.5: + resolution: {integrity: sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.3.0 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vscode-uri@3.1.0: + resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + + vue-eslint-parser@10.4.1: + resolution: {integrity: sha512-Gk6gRDj0n/fkRa3C3l0bBheoBckUq/Rs0F/TvMWIS6nzzx67amAViMe9CkNgsP2tXyQONvGiHQESHwFtZ3aYDA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + + vue-tsc@3.3.8: + resolution: {integrity: sha512-xXmYlVQpcwJDWyGlqbHrGVOl1h3UOsASymRibrHc+iy9j/UNnOrOn4u+fntHz4D6Cs74RtapeqVV6CzJeg+UlA==} + hasBin: true + peerDependencies: + typescript: '>=5.0.0' + + vue@3.5.40: + resolution: {integrity: sha512-+8PJ4SJXdn/cHGImF4CKdxlWHIN5Dkt7DoufRREM6h6uVCx2m7QxgcEQmmzyOK8A9mcafg7sFbJFYsdFVubTig==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xml-naming@0.3.0: + resolution: {integrity: sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==} + engines: {node: '>=16.0.0'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zstd-codec@0.1.5: + resolution: {integrity: sha512-v3fyjpK8S/dpY/X5WxqTK3IoCnp/ZOLxn144GZVlNUjtwAchzrVo03h+oMATFhCIiJ5KTr4V3vDQQYz4RU684g==} + +snapshots: + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@deck.gl/aggregation-layers@9.3.7(@deck.gl/core@9.3.7)(@deck.gl/layers@9.3.7(@deck.gl/core@9.3.7)(@loaders.gl/core@4.4.3)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6))))(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))': + dependencies: + '@deck.gl/core': 9.3.7 + '@deck.gl/layers': 9.3.7(@deck.gl/core@9.3.7)(@loaders.gl/core@4.4.3)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6))) + '@luma.gl/core': 9.3.6 + '@luma.gl/engine': 9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)) + '@luma.gl/shadertools': 9.3.6(@luma.gl/core@9.3.6) + '@math.gl/core': 4.1.0 + '@math.gl/web-mercator': 4.1.0 + d3-hexbin: 0.2.2 + + '@deck.gl/core@9.3.7': + dependencies: + '@loaders.gl/core': 4.4.3 + '@loaders.gl/images': 4.4.3(@loaders.gl/core@4.4.3) + '@luma.gl/core': 9.3.6 + '@luma.gl/engine': 9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)) + '@luma.gl/shadertools': 9.3.6(@luma.gl/core@9.3.6) + '@luma.gl/webgl': 9.3.6(@luma.gl/core@9.3.6) + '@math.gl/core': 4.1.0 + '@math.gl/sun': 4.1.0 + '@math.gl/types': 4.1.0 + '@math.gl/web-mercator': 4.1.0 + '@probe.gl/env': 4.1.1 + '@probe.gl/log': 4.1.1 + '@probe.gl/stats': 4.1.1 + '@types/offscreencanvas': 2019.7.3 + gl-matrix: 3.4.4 + mjolnir.js: 3.0.1 + + '@deck.gl/extensions@9.3.7(@deck.gl/core@9.3.7)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))': + dependencies: + '@deck.gl/core': 9.3.7 + '@luma.gl/core': 9.3.6 + '@luma.gl/engine': 9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)) + '@luma.gl/shadertools': 9.3.6(@luma.gl/core@9.3.6) + '@luma.gl/webgl': 9.3.6(@luma.gl/core@9.3.6) + '@math.gl/core': 4.1.0 + + '@deck.gl/geo-layers@9.3.7(@deck.gl/core@9.3.7)(@deck.gl/extensions@9.3.7(@deck.gl/core@9.3.7)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6))))(@deck.gl/layers@9.3.7(@deck.gl/core@9.3.7)(@loaders.gl/core@4.4.3)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6))))(@deck.gl/mesh-layers@9.3.7(@deck.gl/core@9.3.7)(@loaders.gl/core@4.4.3)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/gltf@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@loaders.gl/core@4.4.3)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))': + dependencies: + '@deck.gl/core': 9.3.7 + '@deck.gl/extensions': 9.3.7(@deck.gl/core@9.3.7)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6))) + '@deck.gl/layers': 9.3.7(@deck.gl/core@9.3.7)(@loaders.gl/core@4.4.3)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6))) + '@deck.gl/mesh-layers': 9.3.7(@deck.gl/core@9.3.7)(@loaders.gl/core@4.4.3)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/gltf@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)) + '@loaders.gl/3d-tiles': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/core': 4.4.3 + '@loaders.gl/gis': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/loader-utils': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/mvt': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/schema': 4.4.3 + '@loaders.gl/terrain': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/tiles': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/wms': 4.4.3(@loaders.gl/core@4.4.3) + '@luma.gl/core': 9.3.6 + '@luma.gl/engine': 9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)) + '@luma.gl/gltf': 9.3.6(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)) + '@luma.gl/shadertools': 9.3.6(@luma.gl/core@9.3.6) + '@math.gl/core': 4.1.0 + '@math.gl/culling': 4.1.0 + '@math.gl/web-mercator': 4.1.0 + '@types/geojson': 7946.0.16 + a5-js: 0.7.3 + h3-js: 4.5.0 + long: 3.2.0 + + '@deck.gl/json@9.3.7(@deck.gl/core@9.3.7)': + dependencies: + '@deck.gl/core': 9.3.7 + jsep: 0.3.5 + + '@deck.gl/layers@9.3.7(@deck.gl/core@9.3.7)(@loaders.gl/core@4.4.3)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))': + dependencies: + '@deck.gl/core': 9.3.7 + '@loaders.gl/core': 4.4.3 + '@loaders.gl/images': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/schema': 4.4.3 + '@luma.gl/core': 9.3.6 + '@luma.gl/engine': 9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)) + '@luma.gl/shadertools': 9.3.6(@luma.gl/core@9.3.6) + '@mapbox/tiny-sdf': 2.2.0 + '@math.gl/core': 4.1.0 + '@math.gl/polygon': 4.1.0 + '@math.gl/web-mercator': 4.1.0 + '@types/geojson': 7946.0.16 + earcut: 2.2.4 + + '@deck.gl/mapbox@9.3.7(@deck.gl/core@9.3.7)(@luma.gl/core@9.3.6)(@math.gl/web-mercator@4.1.0)': + dependencies: + '@deck.gl/core': 9.3.7 + '@luma.gl/core': 9.3.6 + '@math.gl/web-mercator': 4.1.0 + + '@deck.gl/mesh-layers@9.3.7(@deck.gl/core@9.3.7)(@loaders.gl/core@4.4.3)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/gltf@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6))': + dependencies: + '@deck.gl/core': 9.3.7 + '@loaders.gl/gltf': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/schema': 4.4.3 + '@luma.gl/core': 9.3.6 + '@luma.gl/engine': 9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)) + '@luma.gl/gltf': 9.3.6(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)) + '@luma.gl/shadertools': 9.3.6(@luma.gl/core@9.3.6) + transitivePeerDependencies: + - '@loaders.gl/core' + + '@deck.gl/widgets@9.3.7(@deck.gl/core@9.3.7)(@luma.gl/core@9.3.6)': + dependencies: + '@deck.gl/core': 9.3.7 + '@floating-ui/dom': 1.8.0 + '@luma.gl/core': 9.3.6 + preact: 10.29.7 + transitivePeerDependencies: + - preact-render-to-string + + '@emnapi/core@1.11.1': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@eslint-community/eslint-utils@4.10.1(eslint@9.39.5(supports-color@7.2.0))': + dependencies: + eslint: 9.39.5(supports-color@7.2.0) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.2(supports-color@7.2.0)': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3(supports-color@7.2.0) + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.6(supports-color@7.2.0)': + dependencies: + ajv: 6.15.0 + debug: 4.4.3(supports-color@7.2.0) + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.3.0 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.5': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@floating-ui/core@1.8.0': + dependencies: + '@floating-ui/utils': 0.2.12 + + '@floating-ui/dom@1.8.0': + dependencies: + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 + + '@floating-ui/utils@0.2.12': {} + + '@fontsource/b612@5.3.0': {} + + '@fontsource/inconsolata@5.3.0': {} + + '@fontsource/roboto-condensed@5.3.0': {} + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@loaders.gl/3d-tiles@4.4.3(@loaders.gl/core@4.4.3)': + dependencies: + '@loaders.gl/compression': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/core': 4.4.3 + '@loaders.gl/crypto': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/draco': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/gltf': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/images': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/loader-utils': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/math': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/tiles': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/zip': 4.4.3(@loaders.gl/core@4.4.3) + '@math.gl/core': 4.1.0 + '@math.gl/culling': 4.1.0 + '@math.gl/geospatial': 4.1.0 + '@probe.gl/log': 4.1.1 + long: 5.3.2 + + '@loaders.gl/compression@4.4.3(@loaders.gl/core@4.4.3)': + dependencies: + '@loaders.gl/core': 4.4.3 + '@loaders.gl/loader-utils': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/worker-utils': 4.4.3(@loaders.gl/core@4.4.3) + '@types/pako': 1.0.7 + fflate: 0.7.4 + pako: 1.0.11 + snappyjs: 0.6.1 + optionalDependencies: + '@types/brotli': 1.3.5 + brotli: 1.3.3 + lz4js: 0.2.0 + zstd-codec: 0.1.5 + + '@loaders.gl/core@4.4.3': + dependencies: + '@loaders.gl/loader-utils': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/schema': 4.4.3 + '@loaders.gl/schema-utils': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/worker-utils': 4.4.3(@loaders.gl/core@4.4.3) + '@probe.gl/log': 4.1.1 + + '@loaders.gl/crypto@4.4.3(@loaders.gl/core@4.4.3)': + dependencies: + '@loaders.gl/core': 4.4.3 + '@loaders.gl/loader-utils': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/worker-utils': 4.4.3(@loaders.gl/core@4.4.3) + '@types/crypto-js': 4.2.2 + + '@loaders.gl/draco@4.4.3(@loaders.gl/core@4.4.3)': + dependencies: + '@loaders.gl/core': 4.4.3 + '@loaders.gl/loader-utils': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/schema': 4.4.3 + '@loaders.gl/schema-utils': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/worker-utils': 4.4.3(@loaders.gl/core@4.4.3) + draco3d: 1.5.7 + + '@loaders.gl/geoarrow@4.4.3(@loaders.gl/core@4.4.3)': + dependencies: + '@loaders.gl/core': 4.4.3 + '@math.gl/polygon': 4.1.0 + apache-arrow: 21.2.0 + + '@loaders.gl/gis@4.4.3(@loaders.gl/core@4.4.3)': + dependencies: + '@loaders.gl/core': 4.4.3 + '@loaders.gl/geoarrow': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/loader-utils': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/schema': 4.4.3 + '@loaders.gl/schema-utils': 4.4.3(@loaders.gl/core@4.4.3) + '@mapbox/vector-tile': 1.3.1 + '@math.gl/polygon': 4.1.0 + pbf: 3.3.0 + + '@loaders.gl/gltf@4.4.3(@loaders.gl/core@4.4.3)': + dependencies: + '@loaders.gl/core': 4.4.3 + '@loaders.gl/draco': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/images': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/loader-utils': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/schema': 4.4.3 + '@loaders.gl/textures': 4.4.3(@loaders.gl/core@4.4.3) + '@math.gl/core': 4.1.0 + + '@loaders.gl/images@4.4.3(@loaders.gl/core@4.4.3)': + dependencies: + '@loaders.gl/core': 4.4.3 + '@loaders.gl/loader-utils': 4.4.3(@loaders.gl/core@4.4.3) + + '@loaders.gl/loader-utils@4.4.3(@loaders.gl/core@4.4.3)': + dependencies: + '@loaders.gl/schema': 4.4.3 + '@loaders.gl/worker-utils': 4.4.3(@loaders.gl/core@4.4.3) + '@probe.gl/log': 4.1.1 + '@probe.gl/stats': 4.1.1 + transitivePeerDependencies: + - '@loaders.gl/core' + + '@loaders.gl/math@4.4.3(@loaders.gl/core@4.4.3)': + dependencies: + '@loaders.gl/core': 4.4.3 + '@math.gl/core': 4.1.0 + + '@loaders.gl/mvt@4.4.3(@loaders.gl/core@4.4.3)': + dependencies: + '@loaders.gl/core': 4.4.3 + '@loaders.gl/gis': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/images': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/loader-utils': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/schema': 4.4.3 + '@math.gl/polygon': 4.1.0 + '@probe.gl/stats': 4.1.1 + pbf: 3.3.0 + + '@loaders.gl/schema-utils@4.4.3(@loaders.gl/core@4.4.3)': + dependencies: + '@loaders.gl/core': 4.4.3 + '@loaders.gl/schema': 4.4.3 + '@types/geojson': 7946.0.16 + apache-arrow: 21.2.0 + + '@loaders.gl/schema@4.4.3': + dependencies: + '@types/geojson': 7946.0.16 + apache-arrow: 21.2.0 + + '@loaders.gl/terrain@4.4.3(@loaders.gl/core@4.4.3)': + dependencies: + '@loaders.gl/core': 4.4.3 + '@loaders.gl/images': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/loader-utils': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/schema': 4.4.3 + '@mapbox/martini': 0.2.0 + + '@loaders.gl/textures@4.4.3(@loaders.gl/core@4.4.3)': + dependencies: + '@loaders.gl/core': 4.4.3 + '@loaders.gl/images': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/loader-utils': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/schema': 4.4.3 + '@loaders.gl/worker-utils': 4.4.3(@loaders.gl/core@4.4.3) + '@math.gl/types': 4.1.0 + ktx-parse: 0.7.1 + texture-compressor: 1.0.2 + + '@loaders.gl/tiles@4.4.3(@loaders.gl/core@4.4.3)': + dependencies: + '@loaders.gl/core': 4.4.3 + '@loaders.gl/loader-utils': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/math': 4.4.3(@loaders.gl/core@4.4.3) + '@math.gl/core': 4.1.0 + '@math.gl/culling': 4.1.0 + '@math.gl/geospatial': 4.1.0 + '@math.gl/web-mercator': 4.1.0 + '@probe.gl/stats': 4.1.1 + + '@loaders.gl/wms@4.4.3(@loaders.gl/core@4.4.3)': + dependencies: + '@loaders.gl/core': 4.4.3 + '@loaders.gl/images': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/loader-utils': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/schema': 4.4.3 + '@loaders.gl/xml': 4.4.3(@loaders.gl/core@4.4.3) + '@turf/rewind': 5.1.5 + deep-strict-equal: 0.2.0 + + '@loaders.gl/worker-utils@4.4.3(@loaders.gl/core@4.4.3)': + dependencies: + '@loaders.gl/core': 4.4.3 + + '@loaders.gl/xml@4.4.3(@loaders.gl/core@4.4.3)': + dependencies: + '@loaders.gl/core': 4.4.3 + '@loaders.gl/loader-utils': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/schema': 4.4.3 + fast-xml-parser: 5.10.1 + + '@loaders.gl/zip@4.4.3(@loaders.gl/core@4.4.3)': + dependencies: + '@loaders.gl/compression': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/core': 4.4.3 + '@loaders.gl/crypto': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/loader-utils': 4.4.3(@loaders.gl/core@4.4.3) + jszip: 3.10.1 + md5: 2.3.0 + + '@luma.gl/core@9.3.6': + dependencies: + '@math.gl/types': 4.1.0 + '@probe.gl/env': 4.1.1 + '@probe.gl/log': 4.1.1 + '@probe.gl/stats': 4.1.1 + '@types/offscreencanvas': 2019.7.3 + + '@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6))': + dependencies: + '@luma.gl/core': 9.3.6 + '@luma.gl/shadertools': 9.3.6(@luma.gl/core@9.3.6) + '@math.gl/core': 4.1.0 + '@math.gl/types': 4.1.0 + '@probe.gl/log': 4.1.1 + '@probe.gl/stats': 4.1.1 + + '@luma.gl/gltf@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6))': + dependencies: + '@loaders.gl/core': 4.4.3 + '@loaders.gl/gltf': 4.4.3(@loaders.gl/core@4.4.3) + '@loaders.gl/textures': 4.4.3(@loaders.gl/core@4.4.3) + '@luma.gl/core': 9.3.6 + '@luma.gl/engine': 9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)) + '@luma.gl/shadertools': 9.3.6(@luma.gl/core@9.3.6) + '@math.gl/core': 4.1.0 + + '@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)': + dependencies: + '@luma.gl/core': 9.3.6 + '@math.gl/core': 4.1.0 + '@math.gl/types': 4.1.0 + + '@luma.gl/webgl@9.3.6(@luma.gl/core@9.3.6)': + dependencies: + '@luma.gl/core': 9.3.6 + '@math.gl/types': 4.1.0 + '@probe.gl/env': 4.1.1 + + '@mapbox/jsonlint-lines-primitives@2.0.3': {} + + '@mapbox/martini@0.2.0': {} + + '@mapbox/point-geometry@0.1.0': {} + + '@mapbox/point-geometry@1.1.0': {} + + '@mapbox/tiny-sdf@2.2.0': {} + + '@mapbox/unitbezier@0.0.1': {} + + '@mapbox/unitbezier@1.0.0': {} + + '@mapbox/vector-tile@1.3.1': + dependencies: + '@mapbox/point-geometry': 0.1.0 + + '@mapbox/vector-tile@2.0.5': + dependencies: + '@mapbox/point-geometry': 1.1.0 + '@types/geojson': 7946.0.16 + pbf: 4.0.2 + + '@mapbox/whoots-js@3.1.0': {} + + '@maplibre/geojson-vt@6.1.1': + dependencies: + kdbush: 4.1.0 + + '@maplibre/maplibre-gl-style-spec@24.10.0': + dependencies: + '@mapbox/jsonlint-lines-primitives': 2.0.3 + '@mapbox/unitbezier': 1.0.0 + json-stringify-pretty-compact: 4.0.0 + minimist: 1.2.8 + quickselect: 3.0.0 + tinyqueue: 3.0.0 + + '@maplibre/mlt@1.1.12': + dependencies: + '@mapbox/point-geometry': 1.1.0 + + '@maplibre/vt-pbf@4.3.2': + dependencies: + '@mapbox/point-geometry': 1.1.0 + '@types/geojson': 7946.0.16 + pbf: 5.1.2 + + '@math.gl/core@4.1.0': + dependencies: + '@math.gl/types': 4.1.0 + + '@math.gl/culling@4.1.0': + dependencies: + '@math.gl/core': 4.1.0 + '@math.gl/types': 4.1.0 + + '@math.gl/geospatial@4.1.0': + dependencies: + '@math.gl/core': 4.1.0 + '@math.gl/types': 4.1.0 + + '@math.gl/polygon@4.1.0': + dependencies: + '@math.gl/core': 4.1.0 + + '@math.gl/sun@4.1.0': {} + + '@math.gl/types@4.1.0': {} + + '@math.gl/web-mercator@4.1.0': + dependencies: + '@math.gl/core': 4.1.0 + + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@nodable/entities@3.0.0': {} + + '@open-aviation/tangram-core@0.5.0(@loaders.gl/core@4.4.3)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/gltf@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6))(@math.gl/web-mercator@4.1.0)(typescript@5.9.3)(vite@8.1.5(@types/node@24.13.3))': + dependencies: + '@deck.gl/aggregation-layers': 9.3.7(@deck.gl/core@9.3.7)(@deck.gl/layers@9.3.7(@deck.gl/core@9.3.7)(@loaders.gl/core@4.4.3)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6))))(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6))) + '@deck.gl/core': 9.3.7 + '@deck.gl/extensions': 9.3.7(@deck.gl/core@9.3.7)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6))) + '@deck.gl/geo-layers': 9.3.7(@deck.gl/core@9.3.7)(@deck.gl/extensions@9.3.7(@deck.gl/core@9.3.7)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6))))(@deck.gl/layers@9.3.7(@deck.gl/core@9.3.7)(@loaders.gl/core@4.4.3)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6))))(@deck.gl/mesh-layers@9.3.7(@deck.gl/core@9.3.7)(@loaders.gl/core@4.4.3)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/gltf@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@loaders.gl/core@4.4.3)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6))) + '@deck.gl/json': 9.3.7(@deck.gl/core@9.3.7) + '@deck.gl/layers': 9.3.7(@deck.gl/core@9.3.7)(@loaders.gl/core@4.4.3)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6))) + '@deck.gl/mapbox': 9.3.7(@deck.gl/core@9.3.7)(@luma.gl/core@9.3.6)(@math.gl/web-mercator@4.1.0) + '@deck.gl/mesh-layers': 9.3.7(@deck.gl/core@9.3.7)(@loaders.gl/core@4.4.3)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/gltf@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)) + '@deck.gl/widgets': 9.3.7(@deck.gl/core@9.3.7)(@luma.gl/core@9.3.6) + '@fontsource/b612': 5.3.0 + '@fontsource/inconsolata': 5.3.0 + '@fontsource/roboto-condensed': 5.3.0 + '@protomaps/basemaps': 5.7.2 + '@vitejs/plugin-vue': 6.0.8(vite@8.1.5(@types/node@24.13.3))(vue@3.5.40(typescript@5.9.3)) + lit-html: 3.3.3 + maplibre-gl: 5.24.0 + parquet-wasm: 0.7.2 + phoenix: 1.8.9 + pmtiles: 4.4.1 + rs1090-wasm: 0.4.14 + vue: 3.5.40(typescript@5.9.3) + transitivePeerDependencies: + - '@loaders.gl/core' + - '@luma.gl/core' + - '@luma.gl/engine' + - '@luma.gl/gltf' + - '@luma.gl/shadertools' + - '@math.gl/web-mercator' + - preact-render-to-string + - typescript + - vite + + '@oxc-project/types@0.139.0': {} + + '@probe.gl/env@4.1.1': {} + + '@probe.gl/log@4.1.1': + dependencies: + '@probe.gl/env': 4.1.1 + + '@probe.gl/stats@4.1.1': {} + + '@protomaps/basemaps@5.7.2': {} + + '@rolldown/binding-android-arm64@1.1.5': + optional: true + + '@rolldown/binding-darwin-arm64@1.1.5': + optional: true + + '@rolldown/binding-darwin-x64@1.1.5': + optional: true + + '@rolldown/binding-freebsd-x64@1.1.5': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.1.5': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.1.5': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.1.5': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.1.5': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.1.5': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.1.5': + optional: true + + '@rolldown/binding-linux-x64-musl@1.1.5': + optional: true + + '@rolldown/binding-openharmony-arm64@1.1.5': + optional: true + + '@rolldown/binding-wasm32-wasi@1.1.5': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.1.5': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.1.5': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + '@turf/boolean-clockwise@5.1.5': + dependencies: + '@turf/helpers': 5.1.5 + '@turf/invariant': 5.2.0 + + '@turf/clone@5.1.5': + dependencies: + '@turf/helpers': 5.1.5 + + '@turf/helpers@5.1.5': {} + + '@turf/invariant@5.2.0': + dependencies: + '@turf/helpers': 5.1.5 + + '@turf/meta@5.2.0': + dependencies: + '@turf/helpers': 5.1.5 + + '@turf/rewind@5.1.5': + dependencies: + '@turf/boolean-clockwise': 5.1.5 + '@turf/clone': 5.1.5 + '@turf/helpers': 5.1.5 + '@turf/invariant': 5.2.0 + '@turf/meta': 5.2.0 + + '@tybys/wasm-util@0.10.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/brotli@1.3.5': + dependencies: + '@types/node': 24.13.3 + optional: true + + '@types/crypto-js@4.2.2': {} + + '@types/esrecurse@4.3.1': {} + + '@types/estree@1.0.9': {} + + '@types/geojson@7946.0.16': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@24.13.3': + dependencies: + undici-types: 7.18.2 + + '@types/node@25.9.5': + dependencies: + undici-types: 7.24.6 + + '@types/offscreencanvas@2019.7.3': {} + + '@types/pako@1.0.7': {} + + '@types/phoenix@1.6.7': {} + + '@types/trusted-types@2.0.7': {} + + '@typescript-eslint/eslint-plugin@8.65.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3))(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.65.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.65.0 + '@typescript-eslint/type-utils': 8.65.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/utils': 8.65.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.65.0 + eslint: 9.39.5(supports-color@7.2.0) + ignore: 7.0.6 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.65.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.65.0 + '@typescript-eslint/types': 8.65.0 + '@typescript-eslint/typescript-estree': 8.65.0(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.65.0 + debug: 4.4.3(supports-color@7.2.0) + eslint: 9.39.5(supports-color@7.2.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.65.0(supports-color@7.2.0)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.65.0(typescript@5.9.3) + '@typescript-eslint/types': 8.65.0 + debug: 4.4.3(supports-color@7.2.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.65.0': + dependencies: + '@typescript-eslint/types': 8.65.0 + '@typescript-eslint/visitor-keys': 8.65.0 + + '@typescript-eslint/tsconfig-utils@8.65.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.65.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.65.0 + '@typescript-eslint/typescript-estree': 8.65.0(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/utils': 8.65.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + debug: 4.4.3(supports-color@7.2.0) + eslint: 9.39.5(supports-color@7.2.0) + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.65.0': {} + + '@typescript-eslint/typescript-estree@8.65.0(supports-color@7.2.0)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.65.0(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.65.0(typescript@5.9.3) + '@typescript-eslint/types': 8.65.0 + '@typescript-eslint/visitor-keys': 8.65.0 + debug: 4.4.3(supports-color@7.2.0) + minimatch: 10.2.5 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.65.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(supports-color@7.2.0)) + '@typescript-eslint/scope-manager': 8.65.0 + '@typescript-eslint/types': 8.65.0 + '@typescript-eslint/typescript-estree': 8.65.0(supports-color@7.2.0)(typescript@5.9.3) + eslint: 9.39.5(supports-color@7.2.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.65.0': + dependencies: + '@typescript-eslint/types': 8.65.0 + eslint-visitor-keys: 5.0.1 + + '@vitejs/plugin-vue@6.0.8(vite@8.1.5(@types/node@24.13.3))(vue@3.5.40(typescript@5.9.3))': + dependencies: + '@rolldown/pluginutils': 1.0.1 + vite: 8.1.5(@types/node@24.13.3) + vue: 3.5.40(typescript@5.9.3) + + '@volar/language-core@2.4.28': + dependencies: + '@volar/source-map': 2.4.28 + + '@volar/source-map@2.4.28': {} + + '@volar/typescript@2.4.28': + dependencies: + '@volar/language-core': 2.4.28 + path-browserify: 1.0.1 + vscode-uri: 3.1.0 + + '@vue/compiler-core@3.5.40': + dependencies: + '@babel/parser': 7.29.7 + '@vue/shared': 3.5.40 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.40': + dependencies: + '@vue/compiler-core': 3.5.40 + '@vue/shared': 3.5.40 + + '@vue/compiler-sfc@3.5.40': + dependencies: + '@babel/parser': 7.29.7 + '@vue/compiler-core': 3.5.40 + '@vue/compiler-dom': 3.5.40 + '@vue/compiler-ssr': 3.5.40 + '@vue/shared': 3.5.40 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.23 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.40': + dependencies: + '@vue/compiler-dom': 3.5.40 + '@vue/shared': 3.5.40 + + '@vue/language-core@3.3.8': + dependencies: + '@volar/language-core': 2.4.28 + '@vue/compiler-dom': 3.5.40 + '@vue/shared': 3.5.40 + alien-signals: 3.2.1 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + picomatch: 4.0.5 + + '@vue/reactivity@3.5.40': + dependencies: + '@vue/shared': 3.5.40 + + '@vue/runtime-core@3.5.40': + dependencies: + '@vue/reactivity': 3.5.40 + '@vue/shared': 3.5.40 + + '@vue/runtime-dom@3.5.40': + dependencies: + '@vue/reactivity': 3.5.40 + '@vue/runtime-core': 3.5.40 + '@vue/shared': 3.5.40 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.40': + dependencies: + '@vue/compiler-ssr': 3.5.40 + '@vue/runtime-dom': 3.5.40 + '@vue/shared': 3.5.40 + + '@vue/shared@3.5.40': {} + + a5-js@0.7.3: + dependencies: + gl-matrix: 3.4.4 + + acorn-jsx@5.3.2(acorn@8.17.0): + dependencies: + acorn: 8.17.0 + + acorn@8.17.0: {} + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + alien-signals@3.2.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + anynum@1.0.1: {} + + apache-arrow@21.2.0: + dependencies: + '@types/node': 25.9.5 + flatbuffers: 25.9.23 + json-with-bigint: 3.5.10 + tslib: 2.8.1 + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + base64-js@1.5.1: + optional: true + + boolbase@1.0.0: {} + + brace-expansion@1.1.16: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@5.0.8: + dependencies: + balanced-match: 4.0.4 + + brotli@1.3.3: + dependencies: + base64-js: 1.5.1 + optional: true + + buf-compare@1.0.1: {} + + callsites@3.1.0: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + charenc@0.0.2: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + + core-assert@0.2.1: + dependencies: + buf-compare: 1.0.1 + is-error: 2.2.2 + + core-util-is@1.0.3: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + crypt@0.0.2: {} + + cssesc@3.0.0: {} + + csstype@3.2.3: {} + + d3-hexbin@0.2.2: {} + + debug@4.4.3(supports-color@7.2.0): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 7.2.0 + + deep-is@0.1.4: {} + + deep-strict-equal@0.2.0: + dependencies: + core-assert: 0.2.1 + + detect-libc@2.1.2: {} + + draco3d@1.5.7: {} + + earcut@2.2.4: {} + + earcut@3.2.3: {} + + entities@7.0.1: {} + + escape-string-regexp@4.0.0: {} + + eslint-plugin-vue@10.10.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3))(eslint@9.39.5(supports-color@7.2.0))(vue-eslint-parser@10.4.1(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)): + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(supports-color@7.2.0)) + eslint: 9.39.5(supports-color@7.2.0) + natural-compare: 1.4.0 + nth-check: 2.1.1 + postcss-selector-parser: 7.1.4 + semver: 7.8.5 + vue-eslint-parser: 10.4.1(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0) + xml-name-validator: 5.0.0 + optionalDependencies: + '@typescript-eslint/parser': 8.65.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-scope@9.1.2: + dependencies: + '@types/esrecurse': 4.3.1 + '@types/estree': 1.0.9 + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.5(supports-color@7.2.0): + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(supports-color@7.2.0)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2(supports-color@7.2.0) + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.6(supports-color@7.2.0) + '@eslint/js': 9.39.5 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@7.2.0) + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.17.0 + acorn-jsx: 5.3.2(acorn@8.17.0) + eslint-visitor-keys: 4.2.1 + + espree@11.2.0: + dependencies: + acorn: 8.17.0 + acorn-jsx: 5.3.2(acorn@8.17.0) + eslint-visitor-keys: 5.0.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@2.0.2: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-xml-builder@1.3.0: + dependencies: + path-expression-matcher: 1.6.2 + xml-naming: 0.3.0 + + fast-xml-parser@5.10.1: + dependencies: + '@nodable/entities': 3.0.0 + fast-xml-builder: 1.3.0 + is-unsafe: 2.0.0 + path-expression-matcher: 1.6.2 + strnum: 2.4.1 + xml-naming: 0.3.0 + + fdir@6.5.0(picomatch@4.0.5): + optionalDependencies: + picomatch: 4.0.5 + + fflate@0.7.4: {} + + fflate@0.8.3: {} + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.3 + keyv: 4.5.4 + + flatbuffers@25.9.23: {} + + flatted@3.4.3: {} + + fsevents@2.3.3: + optional: true + + gl-matrix@3.4.4: {} + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@17.7.0: {} + + h3-js@4.5.0: {} + + has-flag@4.0.0: {} + + ieee754@1.2.1: {} + + ignore@5.3.2: {} + + ignore@7.0.6: {} + + image-size@0.7.5: {} + + immediate@3.0.6: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inherits@2.0.4: {} + + is-buffer@1.1.6: {} + + is-error@2.2.2: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-unsafe@2.0.0: {} + + isarray@1.0.0: {} + + isexe@2.0.0: {} + + js-yaml@4.3.0: + dependencies: + argparse: 2.0.1 + + jsep@0.3.5: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json-stringify-pretty-compact@4.0.0: {} + + json-with-bigint@3.5.10: {} + + jszip@3.10.1: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + + kdbush@4.1.0: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + ktx-parse@0.7.1: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lie@3.3.0: + dependencies: + immediate: 3.0.6 + + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + + lit-html@3.3.3: + dependencies: + '@types/trusted-types': 2.0.7 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + long@3.2.0: {} + + long@5.3.2: {} + + lz4js@0.2.0: + optional: true + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + maplibre-gl@5.24.0: + dependencies: + '@mapbox/jsonlint-lines-primitives': 2.0.3 + '@mapbox/point-geometry': 1.1.0 + '@mapbox/tiny-sdf': 2.2.0 + '@mapbox/unitbezier': 0.0.1 + '@mapbox/vector-tile': 2.0.5 + '@mapbox/whoots-js': 3.1.0 + '@maplibre/geojson-vt': 6.1.1 + '@maplibre/maplibre-gl-style-spec': 24.10.0 + '@maplibre/mlt': 1.1.12 + '@maplibre/vt-pbf': 4.3.2 + '@types/geojson': 7946.0.16 + earcut: 3.2.3 + gl-matrix: 3.4.4 + kdbush: 4.1.0 + murmurhash-js: 1.0.0 + pbf: 4.0.2 + potpack: 2.1.0 + quickselect: 3.0.0 + tinyqueue: 3.0.0 + + md5@2.3.0: + dependencies: + charenc: 0.0.2 + crypt: 0.0.2 + is-buffer: 1.1.6 + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.8 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.16 + + minimist@1.2.8: {} + + mjolnir.js@3.0.1: {} + + ms@2.1.3: {} + + muggle-string@0.4.1: {} + + murmurhash-js@1.0.0: {} + + nanoid@3.3.16: {} + + natural-compare@1.4.0: {} + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + pako@1.0.11: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parquet-wasm@0.7.2: {} + + path-browserify@1.0.1: {} + + path-exists@4.0.0: {} + + path-expression-matcher@1.6.2: {} + + path-key@3.1.1: {} + + pbf@3.3.0: + dependencies: + ieee754: 1.2.1 + resolve-protobuf-schema: 2.1.0 + + pbf@4.0.2: + dependencies: + resolve-protobuf-schema: 2.1.0 + + pbf@5.1.2: + dependencies: + resolve-protobuf-schema: 2.1.0 + + phoenix@1.8.9: {} + + picocolors@1.1.1: {} + + picomatch@4.0.5: {} + + pmtiles@4.4.1: + dependencies: + fflate: 0.8.3 + + postcss-selector-parser@7.1.4: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss@8.5.23: + dependencies: + nanoid: 3.3.16 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + potpack@2.1.0: {} + + preact@10.29.7: {} + + prelude-ls@1.2.1: {} + + process-nextick-args@2.0.1: {} + + protocol-buffers-schema@3.6.1: {} + + punycode@2.3.1: {} + + quickselect@3.0.0: {} + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + resolve-from@4.0.0: {} + + resolve-protobuf-schema@2.1.0: + dependencies: + protocol-buffers-schema: 3.6.1 + + rolldown@1.1.5: + dependencies: + '@oxc-project/types': 0.139.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.1.5 + '@rolldown/binding-darwin-arm64': 1.1.5 + '@rolldown/binding-darwin-x64': 1.1.5 + '@rolldown/binding-freebsd-x64': 1.1.5 + '@rolldown/binding-linux-arm-gnueabihf': 1.1.5 + '@rolldown/binding-linux-arm64-gnu': 1.1.5 + '@rolldown/binding-linux-arm64-musl': 1.1.5 + '@rolldown/binding-linux-ppc64-gnu': 1.1.5 + '@rolldown/binding-linux-s390x-gnu': 1.1.5 + '@rolldown/binding-linux-x64-gnu': 1.1.5 + '@rolldown/binding-linux-x64-musl': 1.1.5 + '@rolldown/binding-openharmony-arm64': 1.1.5 + '@rolldown/binding-wasm32-wasi': 1.1.5 + '@rolldown/binding-win32-arm64-msvc': 1.1.5 + '@rolldown/binding-win32-x64-msvc': 1.1.5 + + rs1090-wasm@0.4.14: {} + + safe-buffer@5.1.2: {} + + semver@7.8.5: {} + + setimmediate@1.0.5: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + snappyjs@0.6.1: {} + + source-map-js@1.2.1: {} + + sprintf-js@1.0.3: {} + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + strip-json-comments@3.1.1: {} + + strnum@2.4.1: + dependencies: + anynum: 1.0.1 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + texture-compressor@1.0.2: + dependencies: + argparse: 1.0.10 + image-size: 0.7.5 + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + + tinyqueue@3.0.0: {} + + ts-api-utils@2.5.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + tslib@2.8.1: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typescript-eslint@8.65.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.65.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3))(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/parser': 8.65.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.65.0(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/utils': 8.65.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + eslint: 9.39.5(supports-color@7.2.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + typescript@5.9.3: {} + + undici-types@7.18.2: {} + + undici-types@7.24.6: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + vite@8.1.5(@types/node@24.13.3): + dependencies: + lightningcss: 1.33.0 + picomatch: 4.0.5 + postcss: 8.5.23 + rolldown: 1.1.5 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 24.13.3 + fsevents: 2.3.3 + + vscode-uri@3.1.0: {} + + vue-eslint-parser@10.4.1(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0): + dependencies: + debug: 4.4.3(supports-color@7.2.0) + eslint: 9.39.5(supports-color@7.2.0) + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + espree: 11.2.0 + esquery: 1.7.0 + semver: 7.8.5 + transitivePeerDependencies: + - supports-color + + vue-tsc@3.3.8(typescript@5.9.3): + dependencies: + '@volar/typescript': 2.4.28 + '@vue/language-core': 3.3.8 + typescript: 5.9.3 + + vue@3.5.40(typescript@5.9.3): + dependencies: + '@vue/compiler-dom': 3.5.40 + '@vue/compiler-sfc': 3.5.40 + '@vue/runtime-dom': 3.5.40 + '@vue/server-renderer': 3.5.40 + '@vue/shared': 3.5.40 + optionalDependencies: + typescript: 5.9.3 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + xml-name-validator@5.0.0: {} + + xml-naming@0.3.0: {} + + yocto-queue@0.1.0: {} + + zstd-codec@0.1.5: + optional: true diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..fc89cec --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,9 @@ +packages: + - example_plugins/tangram/* + +# to use a local version of tangram-core instead, uncomment this. +# link paths are relative to this workspace root; the deck.gl links point at the +# checkout's own copies so typechecking sees a single deck.gl installation. +# before committing, remember to re-comment and run `pnpm i`! +# overrides: +# "@open-aviation/tangram-core": "link:../tangram/packages/tangram_core" \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 274447c..7bd5f9f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,6 @@ dependencies = [ "pandas>=2.2.3", "prompt-toolkit>=3.0.50", "pyarrow>=19.0.1", - "pyyaml>=6.0.2", "requests>=2.32.3", "rtree>=1.3.0", "scipy>=1.15.1", @@ -30,12 +29,16 @@ build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] packages = ["minisky"] +[tool.uv.workspace] +members = ["example_plugins/tangram/tangram_minisky"] + [dependency-groups] dev = [ "ipykernel>=6.29.5", "pytest>=9.1.1", "ruff>=0.8.0", "pyright>=1.1.390", + "fakeredis>=2.26", ] docs = [ "mkdocs-material>=9.5.0", @@ -56,8 +59,9 @@ target-version = "py311" [tool.ruff.lint] # E/W: pycodestyle, F: pyflakes, I: isort, UP: pyupgrade, -# B: flake8-bugbear, SIM: flake8-simplify, C4: flake8-comprehensions -select = ["E", "W", "F", "I", "UP", "B", "SIM", "C4"] +# B: flake8-bugbear, SIM: flake8-simplify, C4: flake8-comprehensions, +# DTZ: flake8-datetimez (no naive datetimes; sim.utc is timezone-aware UTC) +select = ["E", "W", "F", "I", "UP", "B", "SIM", "C4", "DTZ"] ignore = [ "E501", # line length is handled by the formatter "B008", # FastAPI relies on function calls (Depends/File) in argument defaults @@ -71,10 +75,12 @@ ignore = [ "minisky/plugin/*.py" = ["F401"] # plugin API surface re-exports [tool.pyright] -include = ["minisky"] -exclude = ["**/__pycache__", ".venv", "build", "site"] +include = ["minisky", "example_plugins"] +exclude = ["**/__pycache__", "**/node_modules", ".venv", "build", "site"] pythonVersion = "3.11" # "standard" catches useful bugs while staying practical for this numpy/scipy-heavy fork. typeCheckingMode = "standard" +# Fully-typed files where any leftover implicit Any should be an error. +strict = ["example_plugins/*.py"] reportMissingTypeStubs = false reportWildcardImportFromLibrary = false diff --git a/settings.toml b/settings.toml new file mode 100644 index 0000000..0cc7e8a --- /dev/null +++ b/settings.toml @@ -0,0 +1,20 @@ +prefer_compiled = true # geo functions + +asas_dtlookahead = 300 # ASAS lookahead time [sec] +asas_pzr = 5 # ASAS horizontal PZ margin [nm] +asas_pzh = 1000 # ASAS vertical PZ margin [ft] +asas_marh = 1.05 # ASAS protected zone factors - horizontal +asas_marv = 1.05 # ASAS protected zone factors - vertical + +# Plugin settings +plugin_path = "example_plugins" # Directory to search for plugins +enabled_plugins = ["TANGRAM"] # Plugins to load at startup + +# Tangram bridge plugin (enabled_plugins = ["TANGRAM"], installed by `just sync`). +# Uncomment [tangram] and any key below to override its default. The whole table is +# optional; when absent the defaults shown here apply. Keep it last in the file — in +# TOML every bare key after a [table] header belongs to that table. +# [tangram] +# redis_url = "redis://127.0.0.1:6379" # Redis used by the tangram deployment +# channel = "minisky" # publishes to::*, listens on from::* +# max_hz = 5 # wall-clock cap on snapshot publish rate diff --git a/settings.yml b/settings.yml deleted file mode 100644 index b4f5077..0000000 --- a/settings.yml +++ /dev/null @@ -1,11 +0,0 @@ -prefer_compiled: True # geo functions - -asas_dtlookahead: 300 # ASAS lookahead time [sec] -asas_pzr: 5 # ASAS horizontal PZ margin [nm] -asas_pzh: 1000 # ASAS vertical PZ margin [ft] -asas_marh: 1.05 # ASAS protected zone factors - horizontal -asas_marv: 1.05 # ASAS protected zone factors - vertical - -# Plugin settings -plugin_path: example_plugins # Directory to search for plugins -# enabled_plugins: ['CUSTOMAUTOPILOT', 'EXAMPLE'] # Plugins to load at startup (e.g., ['EXAMPLE']) diff --git a/tests/integration/test_stack.py b/tests/integration/test_stack.py index 0c8d93d..2fd631a 100644 --- a/tests/integration/test_stack.py +++ b/tests/integration/test_stack.py @@ -21,6 +21,19 @@ def test_command_executes_on_step(self, bs, sim): assert bs.traf.ntraf == 1 assert bs.traf.callsign[0] == "KL204" + def test_command_stacked_during_processing_is_kept(self, bs, sim): + # A stack() call that lands while process() is draining the stack + # (e.g. from a plugin I/O thread) must not be lost: commands() detaches + # the pending list up front, so late arrivals run on the next step. + minisky.stack.stack("ECHO first") + drain = minisky.stack.Stack.commands() + assert next(drain) == "ECHO first" + minisky.stack.stack("CRE KL204,B744,52,4,45,FL250,350") # racing append + with pytest.raises(StopIteration): + next(drain) + bs.sim.step() + assert bs.traf.ntraf == 1 + class TestCommands: def test_cre_via_stack(self, bs, run_cmd): diff --git a/tests/integration/test_tangram_bridge.py b/tests/integration/test_tangram_bridge.py new file mode 100644 index 0000000..6a5e722 --- /dev/null +++ b/tests/integration/test_tangram_bridge.py @@ -0,0 +1,153 @@ +"""Integration tests for the tangram Redis bridge (against fakeredis).""" + +import json +import time +from collections.abc import Callable, Iterator +from types import ModuleType +from typing import Any + +import fakeredis +import pytest +from redis.client import PubSub + +from example_plugins.tangram import TangramBridge +from minisky.simulation import Simulation + +Observer = tuple[fakeredis.FakeRedis, PubSub] +StepUntil = Callable[[Callable[[], bool]], int] + + +@pytest.fixture +def redis_server() -> fakeredis.FakeServer: + return fakeredis.FakeServer() + + +@pytest.fixture +def bridge( + bs: ModuleType, sim: Simulation, redis_server: fakeredis.FakeServer +) -> Iterator[TangramBridge]: + bridge = TangramBridge( + "redis://fake", + "minisky", + max_hz=1000, + redis_factory=lambda url: fakeredis.FakeRedis(server=redis_server), + ) + ok, msg = bridge.start() + assert ok, msg + # The I/O thread subscribes asynchronously; commands published before the + # subscription is live would be silently lost (pub/sub has no replay). + assert bridge.ready.wait(timeout=5.0), "bridge did not subscribe in time" + yield bridge + bridge.stop() + + +@pytest.fixture +def observer(redis_server: fakeredis.FakeServer) -> Observer: + """A second Redis client playing the role of tangram's Channel service.""" + client = fakeredis.FakeRedis(server=redis_server) + pubsub = client.pubsub(ignore_subscribe_messages=True) + pubsub.psubscribe("to:*") + return client, pubsub + + +def wait_for( + pubsub: PubSub, + topic_suffix: str, + pred: Callable[[dict[str, Any]], bool] = lambda payload: True, + timeout: float = 5.0, +) -> dict[str, Any]: + """Read pattern messages until one on the given topic satisfies pred.""" + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + message = pubsub.get_message(timeout=0.05) + if message is None or message["type"] != "pmessage": + continue + if not message["channel"].decode().endswith(topic_suffix): + continue + payload = json.loads(message["data"]) + if pred(payload): + return payload + pytest.fail(f"no message on *{topic_suffix} satisfying predicate within {timeout}s") + + +def test_snapshot_published( + bs: ModuleType, + sim: Simulation, + bridge: TangramBridge, + observer: Observer, + step_until: StepUntil, +) -> None: + _, pubsub = observer + bs.stack.stack("CRE KL204 B744 52 4 90 FL300 250") + step_until(lambda: bs.traf.ntraf == 1) + bridge.tick() + + payload = wait_for(pubsub, ":new-data", lambda p: p["count"] == 1) + (ac,) = payload["aircraft"] + assert ac["callsign"] == "KL204" + assert ac["altitude"] == 30000 + assert payload["siminfo"]["ntraf"] == 1 + + +def test_command_roundtrip( + bs: ModuleType, + sim: Simulation, + bridge: TangramBridge, + observer: Observer, + step_until: StepUntil, +) -> None: + client, _ = observer + bs.stack.stack("CRE KL204 B744 52 4 90 FL300 250") + step_until(lambda: int(bs.sim.state) == bs.OP) + + client.publish("from:minisky:command", json.dumps({"command": "HOLD"})) + # The bridge thread stacks the command; the sim applies it on a step. + deadline = time.monotonic() + 5.0 + while time.monotonic() < deadline and int(bs.sim.state) != bs.HOLD: + bs.sim.step() + time.sleep(0.02) + assert int(bs.sim.state) == bs.HOLD + + +def test_heartbeat_while_paused( + bs: ModuleType, + sim: Simulation, + bridge: TangramBridge, + observer: Observer, + step_until: StepUntil, +) -> None: + _, pubsub = observer + bs.stack.stack("CRE KL204 B744 52 4 90 FL300 250") + step_until(lambda: bs.traf.ntraf == 1) + bridge.tick() + bs.stack.stack("HOLD") + bs.sim.step() + + # With no further ticks, the bridge must still republish state on its own, + # and the refreshed siminfo must reflect the pause. + payload = wait_for( + pubsub, ":new-data", lambda p: p["siminfo"]["state_name"] == "HOLD", timeout=5.0 + ) + assert payload["aircraft"], "heartbeat should retain the last aircraft list" + + +def test_heartbeat_before_any_traffic( + bs: ModuleType, sim: Simulation, bridge: TangramBridge, observer: Observer +) -> None: + """A freshly started, idle simulator (INIT, no aircraft, no ticks yet) must + still announce itself, or the frontend shows 'simulator offline'.""" + _, pubsub = observer + payload = wait_for(pubsub, ":new-data", timeout=5.0) + assert payload["count"] == 0 + assert payload["aircraft"] == [] + assert payload["siminfo"]["state_name"] == "INIT" + assert payload["siminfo"]["nconf_cur"] == 0 + + +def test_console_relay( + bs: ModuleType, sim: Simulation, bridge: TangramBridge, observer: Observer +) -> None: + _, pubsub = observer + bs.scr.echo("hello tangram") + payload = wait_for(pubsub, ":console", lambda p: "hello tangram" in p["lines"]) + assert payload["lines"] diff --git a/tests/unit/test_tangram_plugin.py b/tests/unit/test_tangram_plugin.py new file mode 100644 index 0000000..b196f76 --- /dev/null +++ b/tests/unit/test_tangram_plugin.py @@ -0,0 +1,148 @@ +"""Unit tests for the pure functions of the tangram bridge plugin.""" + +import pytest + +from example_plugins.tangram import convert_snapshot, extract_command +from minisky.streaming import Snapshot + + +def make_snapshot() -> Snapshot: + return { + "siminfo": { + "speed": 5.0, + "simdt": 1.0, + "simt": 42.0, + "simutc": "2026-01-01T00:00:00+00:00", + "ntraf": 1, + "state": 2, + "scenname": "kl204", + }, + "acdata": { + "callsign": ["KL204"], + "lat": [52.0], + "lon": [4.0], + "alt": [3048.0], # 10000 ft + "trk": [90.0], + "vs": [5.08], # 1000 fpm + "tas": [102.8888], # 200 kt + "cas": [51.4444], # 100 kt + "gs": [154.3332], # 300 kt + "typecode": ["B744"], + "inconf": [False], + "tcpamax": [0.0], + "nconf_cur": 0, + "nconf_tot": 0, + "nlos_cur": 0, + "nlos_tot": 0, + }, + } + + +def test_convert_snapshot_units_and_fields(): + payload = convert_snapshot(make_snapshot()) + + assert payload["count"] == 1 + siminfo = payload["siminfo"] + assert siminfo["state"] == 2 + assert siminfo["state_name"] == "OP" + assert siminfo["scenname"] == "kl204" + assert siminfo["speed"] == 5.0 + + (ac,) = payload["aircraft"] + assert ac["id"] == "KL204" + assert ac["callsign"] == "KL204" + assert ac["typecode"] == "B744" + assert ac["latitude"] == 52.0 + assert ac["longitude"] == 4.0 + assert ac["altitude"] == 10000 + assert ac["vertical_rate"] == 1000 + assert ac["tas"] == pytest.approx(200, abs=0.1) + assert ac["ias"] == pytest.approx(100, abs=0.1) + assert ac["groundspeed"] == pytest.approx(300, abs=0.1) + assert ac["track"] == 90.0 + assert ac["inconf"] is False + # simutc converted to an epoch timestamp usable as a trajectory point time + assert ac["timestamp"] == 1767225600.0 + + +def test_convert_snapshot_no_traffic(): + snapshot: Snapshot = { + "siminfo": { + "speed": 1.0, + "simdt": 1.0, + "simt": 0.0, + "simutc": "2026-01-01T00:00:00+00:00", + "ntraf": 0, + "state": 0, + "scenname": "", + }, + "acdata": { + "callsign": [], + "lat": [], + "lon": [], + "alt": [], + "trk": [], + "vs": [], + "tas": [], + "cas": [], + "gs": [], + "typecode": [], + "inconf": [], + "tcpamax": [], + "nconf_cur": 0, + "nconf_tot": 0, + "nlos_cur": 0, + "nlos_tot": 0, + }, + } + assert convert_snapshot(snapshot) == { + "aircraft": [], + "count": 0, + "siminfo": { + "simt": 0.0, + "simdt": 1.0, + "simutc": "2026-01-01T00:00:00+00:00", + "speed": 1.0, + "ntraf": 0, + "state": 0, + "state_name": "INIT", + "scenname": "", + "nconf_cur": 0, + "nlos_cur": 0, + }, + } + + +def test_convert_snapshot_naive_simutc_is_utc(): + # A simutc without tzinfo must be interpreted as UTC, never local time. + snapshot = make_snapshot() + snapshot["siminfo"]["simutc"] = "2026-01-01T00:00:00" + payload = convert_snapshot(snapshot) + assert payload["aircraft"][0]["timestamp"] == 1767225600.0 + + +def test_convert_snapshot_bad_simutc(): + snapshot = make_snapshot() + snapshot["siminfo"]["simutc"] = "not a date" + payload = convert_snapshot(snapshot) + assert payload["aircraft"][0]["timestamp"] is None + + +@pytest.mark.parametrize( + ("payload", "expected"), + [ + ('{"command": "OP"}', "OP"), + ('{"command": " DTMULT 5 "}', "DTMULT 5"), + (b'{"command": "HOLD"}', "HOLD"), + ('"RESET"', "RESET"), + ("ECHO hello", "ECHO hello"), # bare string for redis-cli convenience + ('{"command": ""}', None), + ('{"other": "x"}', None), + ('{"command": null}', None), + ("", None), + (" ", None), + ("[1, 2]", None), + ], +) +def test_extract_command(payload, expected): + assert extract_command(payload) == expected diff --git a/uv.lock b/uv.lock index ef0b1a8..9bad0bd 100644 --- a/uv.lock +++ b/uv.lock @@ -16,6 +16,12 @@ resolution-markers = [ "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] +[manifest] +members = [ + "minisky", + "tangram-minisky", +] + [[package]] name = "annotated-doc" version = "0.0.4" @@ -65,6 +71,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, ] +[[package]] +name = "async-timeout" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, +] + [[package]] name = "babel" version = "2.18.0" @@ -101,7 +116,7 @@ name = "cffi" version = "2.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy'" }, + { name = "pycparser" }, ] sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } wheels = [ @@ -436,6 +451,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, ] +[[package]] +name = "fakeredis" +version = "2.36.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "redis" }, + { name = "sortedcontainers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0a/ed/86ed74d8829c3bc565025c1c9efaf8518c9165fbf8c9cc2c026c8ed21bd9/fakeredis-2.36.2.tar.gz", hash = "sha256:c37a0b307fae3f27ec7c19e59519e57b8c52782e00303df9075361b5ba441be6", size = 213336, upload-time = "2026-06-17T13:25:38.934Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/4d/e6e40f93031adbf654d34e542bd396e9f3bc1c6209b40c920d58c9e1317a/fakeredis-2.36.2-py3-none-any.whl", hash = "sha256:84cbb9c74ca8946c0d2499daadf3a5d0bfe3cfbac71e3398316d1a1eab3421c4", size = 141039, upload-time = "2026-06-17T13:25:36.638Z" }, +] + [[package]] name = "fastapi" version = "0.139.0" @@ -685,6 +713,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, ] +[[package]] +name = "h2" +version = "4.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "hpack" }, + { name = "hyperframe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/30/d4/a7d6fb3f58be99d65cbf2d3f766896217a2921d0f3ab10711c45dc1519ee/h2-4.4.0.tar.gz", hash = "sha256:46b551bdcdc7e83cf5c04d0bf93badb8a939bd2287d9fee1abb23a445b9e0580", size = 2156691, upload-time = "2026-07-23T19:14:19.442Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/df/5b14a118322d6097cb9bb30ec6bacad268e546a8ecfcb1f6d0de618dac2f/h2-4.4.0-py3-none-any.whl", hash = "sha256:6acffe1aeab79098d7eb0f8385c1add11f2c7a94815f6fa2b7060eeddee3d87c", size = 62368, upload-time = "2026-07-23T19:14:16.143Z" }, +] + +[[package]] +name = "hpack" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/5b/fcabf6028144a8723726318b07a32c2f3314acdff6265743cf08a344b18e/hpack-4.2.0.tar.gz", hash = "sha256:0895cfa3b5531fc65fe439c05eb65144f123bf7a394fcaa56aa423548d8e45c0", size = 51300, upload-time = "2026-06-23T18:34:46.667Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/b4/4a9fcfb2aef6ba44d9073ecd301443aa00b3dac95de5619f2a7de7ec8a91/hpack-4.2.0-py3-none-any.whl", hash = "sha256:858ac0b02280fa582b5080d68db0899c62a80375e0e5413a74970c5e518b6986", size = 34246, upload-time = "2026-06-23T18:34:45.472Z" }, +] + [[package]] name = "httpcore" version = "1.0.9" @@ -756,6 +806,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] +[package.optional-dependencies] +http2 = [ + { name = "h2" }, +] + +[[package]] +name = "hyperframe" +version = "6.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566, upload-time = "2025-01-22T21:41:49.302Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, +] + [[package]] name = "idna" version = "3.18" @@ -1197,7 +1261,6 @@ dependencies = [ { name = "pandas" }, { name = "prompt-toolkit" }, { name = "pyarrow" }, - { name = "pyyaml" }, { name = "requests" }, { name = "rtree" }, { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, @@ -1207,6 +1270,7 @@ dependencies = [ [package.dev-dependencies] dev = [ + { name = "fakeredis" }, { name = "ipykernel" }, { name = "pyright" }, { name = "pytest" }, @@ -1227,7 +1291,6 @@ requires-dist = [ { name = "pandas", specifier = ">=2.2.3" }, { name = "prompt-toolkit", specifier = ">=3.0.50" }, { name = "pyarrow", specifier = ">=19.0.1" }, - { name = "pyyaml", specifier = ">=6.0.2" }, { name = "requests", specifier = ">=2.32.3" }, { name = "rtree", specifier = ">=1.3.0" }, { name = "scipy", specifier = ">=1.15.1" }, @@ -1236,6 +1299,7 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ + { name = "fakeredis", specifier = ">=2.26" }, { name = "ipykernel", specifier = ">=6.29.5" }, { name = "pyright", specifier = ">=1.1.390" }, { name = "pytest", specifier = ">=9.1.1" }, @@ -1547,6 +1611,74 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/66/b1/4263c069ca2177d9c71d2c19046e2b587b13b6936f22df9e082233b5ec64/openap-2.6.0-py3-none-any.whl", hash = "sha256:aed9a19f232f57744bbe3276e1e044f74f1984dc16a72d00797aa70f54800fab", size = 2699866, upload-time = "2026-07-02T14:42:25.368Z" }, ] +[[package]] +name = "orjson" +version = "3.11.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/0c/964746fcafbd16f8ff53219ad9f6b412b34f345c75f384ad434ceaadb538/orjson-3.11.9.tar.gz", hash = "sha256:4fef17e1f8722c11587a6ef18e35902450221da0028e65dbaaa543619e68e48f", size = 5599163, upload-time = "2026-05-06T15:11:08.309Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/51/3fb9e65ae76ee97bd611869a503fa3fc0a6e81dd8b737cf3003f682df7ff/orjson-3.11.9-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f01c4818b3fc9b0da8e096722a84318071eaa118df35f6ed2344da0e73a5444f", size = 228522, upload-time = "2026-05-06T15:09:35.362Z" }, + { url = "https://files.pythonhosted.org/packages/16/fa/9d54b07cb3f3b0bfd57841478e42d7a0ece4a9f49f9907eecf5a45461687/orjson-3.11.9-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:3ebca4179031ee716ed076ffadc29428e900512f6fccee8614c9983157fcf19c", size = 128463, upload-time = "2026-05-06T15:09:37.063Z" }, + { url = "https://files.pythonhosted.org/packages/88/b1/6ceafc2eefd0a553e3be77ce6c49d107e772485d9568629376171c50e634/orjson-3.11.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48ee05097750de0ff69ed5b7bbcf0732182fd57a24043dcc2a1da780a5ead3a5", size = 132306, upload-time = "2026-05-06T15:09:38.299Z" }, + { url = "https://files.pythonhosted.org/packages/ea/76/f11311285324a40aab1e3031385c50b635a7cd0734fdaf60c7e89a696f60/orjson-3.11.9-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6082706765a95a6680d812e1daf1c0cfe8adec7831b3ff3b625693f3b461b1c", size = 127988, upload-time = "2026-05-06T15:09:39.597Z" }, + { url = "https://files.pythonhosted.org/packages/9e/85/0ef63bcf1337f44031ce9b91b1919563f62a37527b3ea4368bb15a22e5d7/orjson-3.11.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:277fefe9d76ee17eb14debf399e3533d4d63b5f677a4d3719eb763536af1f4bd", size = 135188, upload-time = "2026-05-06T15:09:40.957Z" }, + { url = "https://files.pythonhosted.org/packages/05/94/b0d27090ea8a2095db3c2bd1b1c96f96f19bbb494d7fef33130e846e613d/orjson-3.11.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03db380e3780fa0015ed776a90f20e8e20bb11dde13b216ce19e5718e3dfba62", size = 145937, upload-time = "2026-05-06T15:09:42.249Z" }, + { url = "https://files.pythonhosted.org/packages/09/eb/75d50c29c05b8054013e221e598820a365c8e64065312e75e202ed880709/orjson-3.11.9-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33d7d766701847dc6729846362dc27895d2f2d2251264f9d10e7cb9878194877", size = 132758, upload-time = "2026-05-06T15:09:43.945Z" }, + { url = "https://files.pythonhosted.org/packages/49/bd/360686f39348aa88827cb6fbf7dc606fd41c831a35235e1abf1db8e3a9e6/orjson-3.11.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:147302878da387104b66bb4a8b0227d1d487e976ce41a8501916161072ed87b1", size = 133971, upload-time = "2026-05-06T15:09:45.239Z" }, + { url = "https://files.pythonhosted.org/packages/0e/30/3178eb16f3221aeef068b6f1f1ebe05f656ea5c6dffe9f6c917329fe17a3/orjson-3.11.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3513550321f8c8c811a7c3297b8a630e82dc08e4c10216d07703c997776236cd", size = 141685, upload-time = "2026-05-06T15:09:46.858Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f1/ff2f19ed0225f9680fafa42febca3570dd59444ebf190980738d376214c2/orjson-3.11.9-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c5d001196b89fa9cf0a4ab79766cd835b991a166e4b621ba95089edc50c429ff", size = 415167, upload-time = "2026-05-06T15:09:48.312Z" }, + { url = "https://files.pythonhosted.org/packages/9b/61/863bddf0da6e9e586765414debd54b4e58db05f560902b6d00658cb88636/orjson-3.11.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:16969c9d369c98eb084889c6e4d2d39b77c7eb38ceccf8da2a9fff62ae908980", size = 147913, upload-time = "2026-05-06T15:09:49.733Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8a/4081492586d75b073d60c5271a8d0f05a0955cabf1e34c8473f6fcd84235/orjson-3.11.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:63e0efbc991250c0b3143488fa57d95affcabbfc63c99c48d625dd37779aafe2", size = 136959, upload-time = "2026-05-06T15:09:51.311Z" }, + { url = "https://files.pythonhosted.org/packages/0d/bd/70b6ab193594d7abb875320c0a7c8335e846f28968c432c31042409c3c8d/orjson-3.11.9-cp311-cp311-win32.whl", hash = "sha256:14ed654580c1ed2bc217352ec82f91b047aef82951aa71c7f64e0dcb03c0e180", size = 131533, upload-time = "2026-05-06T15:09:52.637Z" }, + { url = "https://files.pythonhosted.org/packages/3f/17/1a1a228183d62d1b77e2c30d210f47dd4768b310ebe1607c63e3c0e3a71e/orjson-3.11.9-cp311-cp311-win_amd64.whl", hash = "sha256:57ea77fb70a448ce87d18fca050193202a3da5e54598f6501ca5476fb66cfe02", size = 127106, upload-time = "2026-05-06T15:09:54.204Z" }, + { url = "https://files.pythonhosted.org/packages/b8/95/285de5fa296d09681ee9c546cd4a8aeb773b701cf343dc125994f4d52953/orjson-3.11.9-cp311-cp311-win_arm64.whl", hash = "sha256:19b72ed11572a2ee51a67a903afbe5af504f84ed6f529c0fe44b0ab3fb5cc697", size = 126848, upload-time = "2026-05-06T15:09:55.551Z" }, + { url = "https://files.pythonhosted.org/packages/16/6d/11867a3ffa3a3608d84a4de51ef4dd0896d6b5cc9132fbe1daf593e677bc/orjson-3.11.9-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9ef6fe90aadef185c7b128859f40beb24720b4ecea95379fc9000931179c3a49", size = 228515, upload-time = "2026-05-06T15:09:57.265Z" }, + { url = "https://files.pythonhosted.org/packages/24/75/05912954c8b288f34fcf5cd4b9b071cb4f6e77b9961e175e56ebb258089f/orjson-3.11.9-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:e5c9b8f28e726e97d97696c826bc7bea5d71cecd63576dba92924a32c1961291", size = 128409, upload-time = "2026-05-06T15:09:59.063Z" }, + { url = "https://files.pythonhosted.org/packages/ab/86/1c3a47df3bc8191ea9ac51603bbb872a95167a364320c269f2557911f406/orjson-3.11.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a473dbb4162108b27901492546f83c76fdcea3d0eadff00ae7a07e18dcce09", size = 132106, upload-time = "2026-05-06T15:10:00.798Z" }, + { url = "https://files.pythonhosted.org/packages/d7/cf/b33b5f3e695ae7d63feef9d915c37cc3b8f465493dcd4f8e0b4c697a2366/orjson-3.11.9-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:011382e2a60fda9d46f1cdee31068cfc52ffe952b587d683ec0463002802a0f4", size = 127864, upload-time = "2026-05-06T15:10:02.15Z" }, + { url = "https://files.pythonhosted.org/packages/31/6a/6cf69385a58208024fcb8c014e2141b8ce838aba6492b589f8acfff97fab/orjson-3.11.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2d3dc759490128c5c1711a53eeaa8ee1d437fd0038ffd2b6008abf46db3f882", size = 135213, upload-time = "2026-05-06T15:10:03.515Z" }, + { url = "https://files.pythonhosted.org/packages/e8/f8/0b1bd3e8f2efcdd376af5c8cfd79eaf13f018080c0089c80ebd724e3c7fb/orjson-3.11.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8ea516b3726d190e1b4297e6f4e7a8650347ae053868a18163b4dd3641d1fff", size = 145994, upload-time = "2026-05-06T15:10:05.083Z" }, + { url = "https://files.pythonhosted.org/packages/f3/59/dab79f61044c529d2c81aecdc589b1f833a1c8dec11ba3b1c2498a02ca7e/orjson-3.11.9-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:380cdce7ba24989af81d0a7013d0aaec5d0e2a21734c0e2681b1bc4f141957fe", size = 132744, upload-time = "2026-05-06T15:10:06.853Z" }, + { url = "https://files.pythonhosted.org/packages/0e/a4/82b7a2fe5d8a67a59ed831b24d59a3d46ea7d207b66e1602d376541d94a6/orjson-3.11.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be4fa4f0af7fa18951f7ab3fc2148e223af211bf03f59e1c6034ec3f97f21d61", size = 134014, upload-time = "2026-05-06T15:10:08.213Z" }, + { url = "https://files.pythonhosted.org/packages/50/c7/375e83a76851b73b2e39f3bcf0e5a19e2b89bad13e5bca97d0b293d27f24/orjson-3.11.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a8f5f8bc7ce7d59f08d9f99fa510c06496164a24cb5f3d34537dbd9ca30132e2", size = 141509, upload-time = "2026-05-06T15:10:09.595Z" }, + { url = "https://files.pythonhosted.org/packages/7f/7c/49d5d82a3d3097f641f094f552131f1e2723b0b8cb0fa2874ab65ecfffa6/orjson-3.11.9-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:4d7fde5501b944f83b3e665e1b31343ff6e154b15560a16b7130ea1e594a4206", size = 415127, upload-time = "2026-05-06T15:10:11.049Z" }, + { url = "https://files.pythonhosted.org/packages/3a/dc/7446c538590d55f455647e5f3c61fc33f7108714e7afcffa6a2a033f8350/orjson-3.11.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cde1a448023ba7d5bb4c01c5afb48894380b5e4956e0627266526587ef4e535f", size = 148025, upload-time = "2026-05-06T15:10:12.842Z" }, + { url = "https://files.pythonhosted.org/packages/df/e5/4d2d8af06f788329b4f78f8cc3679bb395392fcaa1e4d8d3c33e85308fa4/orjson-3.11.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:71e63adb0e1f1ed5d9e168f50a91ceb93ae6420731d222dc7da5c69409aa47aa", size = 136943, upload-time = "2026-05-06T15:10:14.405Z" }, + { url = "https://files.pythonhosted.org/packages/06/69/850264ccf6d80f6b174620d30a87f65c9b1490aba33fe6b62798e618cad3/orjson-3.11.9-cp312-cp312-win32.whl", hash = "sha256:2d057a602cdd19a0ad680417527c45b6961a095081c0f46fe0e03e304aac6470", size = 131606, upload-time = "2026-05-06T15:10:15.791Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d5/973a43fc9c55e20f2051e9830997649f669be0cb3ca52192087c0143f118/orjson-3.11.9-cp312-cp312-win_amd64.whl", hash = "sha256:59e403b1cc5a676da8eaf31f6254801b7341b3e29efa85f92b48d272637e77be", size = 127101, upload-time = "2026-05-06T15:10:17.129Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ae/495470f0e4a18f73fa10b7f6b84b464ec4cc5291c4e0c7c2a6c400bef006/orjson-3.11.9-cp312-cp312-win_arm64.whl", hash = "sha256:9af678d6488357948f1f84c6cd1c1d397c014e1ae2f98ae082a44eb48f602624", size = 126736, upload-time = "2026-05-06T15:10:18.645Z" }, + { url = "https://files.pythonhosted.org/packages/32/33/93fcc25907235c344ae73122f8a4e01d2d393ef062b4af7d2e2487a32c37/orjson-3.11.9-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4bab1b2d6141fe7b32ae71dac905666ece4f94936efbfb13d55bb7739a3a6021", size = 228458, upload-time = "2026-05-06T15:10:20.079Z" }, + { url = "https://files.pythonhosted.org/packages/8f/27/b1e6dadb3c080313c03fdd8067b85e6a0460c7d8d6a1c3984ef77b904e4d/orjson-3.11.9-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:844417969855fc7a41be124aafe83dc424592a7f77cd4501900c67307122b92c", size = 128368, upload-time = "2026-05-06T15:10:21.549Z" }, + { url = "https://files.pythonhosted.org/packages/21/0f/c9ede0bf052f6b4051e64a7d4fa91b725cccf8321a6a786e86eb03519f00/orjson-3.11.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffe02797b5e9f3a9d8292ddcd289b474ad13e81ad83cd1891a240811f1d2cb81", size = 132070, upload-time = "2026-05-06T15:10:23.371Z" }, + { url = "https://files.pythonhosted.org/packages/fd/26/d398e28048dc18205bbe812f2c88cb9b40313db2470778e25964796458fe/orjson-3.11.9-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e4eed3b200023042814d2fc8a5d2e880f13b52e1ed2485e83da4f3962f7dc1a", size = 127892, upload-time = "2026-05-06T15:10:24.714Z" }, + { url = "https://files.pythonhosted.org/packages/66/60/52b0054c4c700d5aa7fc5b7ca96917400d8f061307778578e67a10e25852/orjson-3.11.9-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aff7da9952a5ad1cef8e68017724d96c7b9a66e99e91d6252e1b133d67a7b10", size = 135217, upload-time = "2026-05-06T15:10:26.084Z" }, + { url = "https://files.pythonhosted.org/packages/d5/97/1e3dc2b2a28b7b2528f403d2fc1d79ec5f39af3bc143ab65d3ec26426385/orjson-3.11.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d4e98d6f3b8afed8bc8cd9718ec0cdf46661826beefb53fe8eafb37f2bf0362", size = 145980, upload-time = "2026-05-06T15:10:28.062Z" }, + { url = "https://files.pythonhosted.org/packages/fc/39/31fbfe7850f2de32dee7e7e5c09f26d403ab01e440ac96001c6b01ad3c99/orjson-3.11.9-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a81d52442a7c99b3662333235b3adf96a1715864658b35bb797212be7bddb97", size = 132738, upload-time = "2026-05-06T15:10:29.727Z" }, + { url = "https://files.pythonhosted.org/packages/a1/08/dca0082dd2a194acb93e5457e73455388e2e2ca464a2672449a9ddbb679d/orjson-3.11.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e39364e726a8fff737309aff059ff67d8a8c8d5b677be7bb49a8b3e84b7e218", size = 134033, upload-time = "2026-05-06T15:10:31.152Z" }, + { url = "https://files.pythonhosted.org/packages/11/d4/5bdb0626801230139987385554c5d4c42255218ac906525bf4347f22cd95/orjson-3.11.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4fd66214623f1b17501df9f0543bef0b833979ab5b6ded1e1d123222866aa8c9", size = 141492, upload-time = "2026-05-06T15:10:32.641Z" }, + { url = "https://files.pythonhosted.org/packages/fa/88/a21fb53b3ede6703aede6dce4710ed4111e5b201cfa6bbff5e544f9d47d7/orjson-3.11.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8ecc30f10465fa1e0ce13fd01d9e22c316e5053a719a8d915d4545a09a5ff677", size = 415087, upload-time = "2026-05-06T15:10:34.438Z" }, + { url = "https://files.pythonhosted.org/packages/3d/57/1b30daf70f0d8180e9a73cefbfbdd99e4bf19eb020466502b01fba7e0e50/orjson-3.11.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:97db4c94a7db398a5bd636273324f0b3fd58b350bbbac8bb380ceb825a9b40f4", size = 148031, upload-time = "2026-05-06T15:10:36.358Z" }, + { url = "https://files.pythonhosted.org/packages/04/83/45fbb6d962e260807f99441db9613cee868ceda4baceda59b3720a563f97/orjson-3.11.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9f78cf8fec5bd627f4082b8dfeac7871b43d7f3274904492a43dab39f18a19a0", size = 136915, upload-time = "2026-05-06T15:10:38.013Z" }, + { url = "https://files.pythonhosted.org/packages/5f/cc/2d10025f9056d376e4127ec05a5808b218d46f035fdc08178a5411b34250/orjson-3.11.9-cp313-cp313-win32.whl", hash = "sha256:d4087e5c0209a0a8efe4de3303c234b9c44d1174161dcd851e8eea07c7560b32", size = 131613, upload-time = "2026-05-06T15:10:39.569Z" }, + { url = "https://files.pythonhosted.org/packages/67/bd/2775ff28bfe883b9aa1ff348300542eb2ef1ee18d8ae0e3a49846817a865/orjson-3.11.9-cp313-cp313-win_amd64.whl", hash = "sha256:051b102c93b4f634e89f3866b07b9a9a98915ada541f4ec30f177067b2694979", size = 127086, upload-time = "2026-05-06T15:10:41.262Z" }, + { url = "https://files.pythonhosted.org/packages/91/2b/d26799e580939e32a7da9a39531bc9e58e15ca32ffaa6a8cb3e9bb0d22cd/orjson-3.11.9-cp313-cp313-win_arm64.whl", hash = "sha256:cce9127885941bd28f080cecf1f1d288336b7e0d812c345b08be88b572796254", size = 126696, upload-time = "2026-05-06T15:10:42.651Z" }, + { url = "https://files.pythonhosted.org/packages/8e/eb/5da01e356015aee6ecfa1187ced87aef51364e306f5e695dd52719bf0e78/orjson-3.11.9-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b6ef1979adc4bc243523f1a2ba91418030a8e29b0a99cbe7e0e2d6807d4dce6e", size = 228465, upload-time = "2026-05-06T15:10:44.097Z" }, + { url = "https://files.pythonhosted.org/packages/64/62/3e0e0c14c957133bcd855395c62b55ed4e3b0af23ffea11b032cb1dcbdb1/orjson-3.11.9-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:f36b7f32c7c0db4a719f1fc5824db4a9c6f8bd1a354debb91faf26ebf3a4c71e", size = 128364, upload-time = "2026-05-06T15:10:45.839Z" }, + { url = "https://files.pythonhosted.org/packages/5a/5a/07d8aa117211a8ed7630bda80c8c0b14d04e0f8dcf99bcf49656e4a710eb/orjson-3.11.9-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08f4d8ebb44925c794e535b2bebc507cebf32209df81de22ae285fb0d8d66de0", size = 132063, upload-time = "2026-05-06T15:10:47.267Z" }, + { url = "https://files.pythonhosted.org/packages/d6/ec/4acaf21483e18aa945be74a474c74b434f284b549f275a0a39b9f98956e9/orjson-3.11.9-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6cc7923789694fd58f001cbcac7e47abc13af4d560ebbfcf3b41a8b1a0748124", size = 122356, upload-time = "2026-05-06T15:10:48.765Z" }, + { url = "https://files.pythonhosted.org/packages/13/d8/5f0555e7638801323b7a75850f92e7dfa891bc84fe27a1ba4449170d1200/orjson-3.11.9-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea5c46eb2d3af39e806b986f4b09d5c2706a1f5afde3cbf7544ce6616127173c", size = 129592, upload-time = "2026-05-06T15:10:50.13Z" }, + { url = "https://files.pythonhosted.org/packages/b6/30/ed9860412a3603ceb3c5955bfd72d28b9d0e7ba6ed81add14f83d7114236/orjson-3.11.9-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f5d89a2ed90731df3be64bab0aa44f78bff39fdc9d71c291f4a8023aa46425b7", size = 140491, upload-time = "2026-05-06T15:10:51.582Z" }, + { url = "https://files.pythonhosted.org/packages/d0/17/adc514dea7ac7c505527febf884934b815d34f0c7b8693c1a8b39c5c4a57/orjson-3.11.9-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:25e4aed0312d292c09f61af25bba34e0b2c88546041472b09088c39a4d828af1", size = 127309, upload-time = "2026-05-06T15:10:53.329Z" }, + { url = "https://files.pythonhosted.org/packages/76/3e/c0b690253f0b82d86e99949af13533363acfb5432ecb5d53dd5b3bce9c34/orjson-3.11.9-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaea64f3f467d22e70eeed68bdccb3bc4f83f650446c4a03c59f2cba28a108db", size = 134030, upload-time = "2026-05-06T15:10:54.988Z" }, + { url = "https://files.pythonhosted.org/packages/c1/7a/bc82a0bb25e9faaf92dc4d9ef002732efc09737706af83e346788641d4a7/orjson-3.11.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a028425d1b440c5d92a6be1e1a020739dfe67ea87d96c6dbe828c1b30041728b", size = 141482, upload-time = "2026-05-06T15:10:56.663Z" }, + { url = "https://files.pythonhosted.org/packages/01/55/e69188b939f77d5d32a9833745ace31ea5ccae3ab613a1ec185d3cd2c4fb/orjson-3.11.9-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5b192c6cf397e4455b11523c5cf2b18ed084c1bbd61b6c0926344d2129481972", size = 415178, upload-time = "2026-05-06T15:10:58.446Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1a/b8a5a7ac527e80b9cb11d51e3f6689b709279183264b9ec5c7bc680bb8b5/orjson-3.11.9-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ea407d4ccf5891d667d045fecae97a7a1e5e87b3b97f97ae1803c2e741130be0", size = 148089, upload-time = "2026-05-06T15:11:00.441Z" }, + { url = "https://files.pythonhosted.org/packages/97/4e/00503f64204bf859b37213a63927028f30fb6268cd8677fb0a5ad48155e1/orjson-3.11.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5f63aaf97afd9f6dec5b1a68e1b8da12bfccb4cb9a9a65c3e0b6c847849e7586", size = 136921, upload-time = "2026-05-06T15:11:02.176Z" }, + { url = "https://files.pythonhosted.org/packages/0d/ba/a23b82a0a8d0ed7bed4e5f5035aae751cad4ff6a1e8d2ecd14d8860f5929/orjson-3.11.9-cp314-cp314-win32.whl", hash = "sha256:e30ab17845bb9fa54ccf67fa4f9f5282652d54faa6d17452f47d0f369d038673", size = 131638, upload-time = "2026-05-06T15:11:03.696Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c3/0c6798456bade745c75c452342dabacce5798196483e77e643be1f53877d/orjson-3.11.9-cp314-cp314-win_amd64.whl", hash = "sha256:32ef5f4283a3be81913947d19608eacb7c6608026851123790cd9cc8982af34b", size = 127078, upload-time = "2026-05-06T15:11:05.123Z" }, + { url = "https://files.pythonhosted.org/packages/16/21/5a3f1e8913103b703a436a5664238e5b965ec392b555fe68943ea3691e6b/orjson-3.11.9-cp314-cp314-win_arm64.whl", hash = "sha256:eebdbdeef0094e4f5aefa20dcd4eb2368ab5e7a3b4edea27f1e7b2892e009cf9", size = 126687, upload-time = "2026-05-06T15:11:06.602Z" }, +] + [[package]] name = "packaging" version = "26.2" @@ -1649,7 +1781,7 @@ name = "pexpect" version = "4.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ptyprocess", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "ptyprocess" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } wheels = [ @@ -2240,6 +2372,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/01/1b/5dbe84eefc86f48473947e2f41711aded97eecef1231f4558f1f02713c12/pyzmq-27.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c9f7f6e13dff2e44a6afeaf2cf54cee5929ad64afaf4d40b50f93c58fc687355", size = 544862, upload-time = "2025-09-08T23:09:56.509Z" }, ] +[[package]] +name = "redis" +version = "8.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "async-timeout", marker = "python_full_version < '3.11.3'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/c3/928b290c2c0ca99ab96eea5b4ff8f30be8112b075301a7d3ba214a3c8c12/redis-8.0.1.tar.gz", hash = "sha256:afc5a7a2f5a084f5b1880dec548dd45be17db7e43c82a30d84f952aefb05cfb0", size = 5114170, upload-time = "2026-06-23T14:52:37.728Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/0a/c2345ebf1ebe70840ce3f6c6ee612f8fa749cfbd1b03069c53bf0c62aaad/redis-8.0.1-py3-none-any.whl", hash = "sha256:47daa35a058c23468d6437f17a8c76882cb316b838ef763036af99b96cedd743", size = 502406, upload-time = "2026-06-23T14:52:36.137Z" }, +] + [[package]] name = "requests" version = "2.34.2" @@ -2428,7 +2572,7 @@ resolution-markers = [ "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } wheels = [ @@ -2510,7 +2654,7 @@ resolution-markers = [ "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/a7/25/c2700dfaf6442b4effaa91af24ebce5dc9d31bb4a69706313aae70d72cd0/scipy-1.18.0.tar.gz", hash = "sha256:67b2ad2ad54c72ca6d04975a9b2df8c3638c34ddd5b28738e94fc2b57929d378", size = 30774447, upload-time = "2026-06-19T15:01:43.456Z" } wheels = [ @@ -2587,6 +2731,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] +[[package]] +name = "sortedcontainers" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, +] + [[package]] name = "stack-data" version = "0.6.3" @@ -2614,6 +2767,77 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/bb/2799cc2ede3ed41131f8975621e7213dfc7ef4acbbaadfa440f32500c370/starlette-1.3.1-py3-none-any.whl", hash = "sha256:c7372aae11c3c3f26a42df7bd626cec2f47d03483d261d369516a615a53714c6", size = 73632, upload-time = "2026-06-12T09:23:10.017Z" }, ] +[[package]] +name = "tangram-core" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fastapi" }, + { name = "httpx", extra = ["http2"] }, + { name = "orjson" }, + { name = "platformdirs" }, + { name = "pydantic" }, + { name = "redis" }, + { name = "typer" }, + { name = "uvicorn" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/72/9f5e56816c5e20ffe5584dcebeddeffd6754e28767436ff5acadd3ff0632/tangram_core-0.5.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:efb5bf10d6452abd2e6cac34c219f8873ce0a288a1ad8c64f5fb74b273cbb14f", size = 20272996, upload-time = "2026-06-18T15:01:57.434Z" }, + { url = "https://files.pythonhosted.org/packages/89/6c/1e71fd936862dc69b2e164ae917bcc3863e50cf6316068a04206964339ed/tangram_core-0.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:12e59784eff8a3f347d798d29c10050b3281992b38a66c63b19c6895e412c6a6", size = 20160394, upload-time = "2026-06-18T15:02:00.068Z" }, + { url = "https://files.pythonhosted.org/packages/ad/f5/ba7d107a54c0de2329ff627b7482422db099fef0d9320ce526969a380626/tangram_core-0.5.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:6a16986fbce22cf27fa3266531d17cbf6365e27d3000cfc4bafd7d6babddd8f5", size = 20279112, upload-time = "2026-06-18T15:02:02.474Z" }, + { url = "https://files.pythonhosted.org/packages/06/ac/399223615a8887df35c2180e3efaf7124106a25c124492b9396e27bb652b/tangram_core-0.5.0-cp311-cp311-manylinux_2_28_armv7l.whl", hash = "sha256:befb3c8bab9fd42a98ea9ba300c9878f7376e8e34357ca908502d640507eff56", size = 19938903, upload-time = "2026-06-18T15:02:04.96Z" }, + { url = "https://files.pythonhosted.org/packages/85/94/e391303c043fc6ed800685b34cfc58388a4e26b1db08ed13841be1d2f364/tangram_core-0.5.0-cp311-cp311-manylinux_2_28_i686.whl", hash = "sha256:74e794ab559c8964dafc5327ac8324b68f1fbd962c5516c88406aa02d7282cc8", size = 20118474, upload-time = "2026-06-18T15:02:07.587Z" }, + { url = "https://files.pythonhosted.org/packages/1d/44/f8465c7baef19a98230f498052a372d6fc3fee1142aef14fbe681d9a10ec/tangram_core-0.5.0-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:4f75b83db94f68d176ba841e5b185f1c90c21e6f3ca0b2cf3f193f0be3e45ac5", size = 20315820, upload-time = "2026-06-18T15:02:10.099Z" }, + { url = "https://files.pythonhosted.org/packages/ff/cc/e55f82aba0da4c7a9febaf0a8f5faf96346506447a6bb66fa09b67f29ce2/tangram_core-0.5.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:2b92e583fdfe4aa8963c95b0ed242889e8565344fa6a381ca7127ebe77e07831", size = 20410292, upload-time = "2026-06-18T15:02:12.63Z" }, + { url = "https://files.pythonhosted.org/packages/c0/40/6ad968152cd4f91e5ae0e4b7889fd9104874d231618a06b0b891b6704dd6/tangram_core-0.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:963203164233a45f9e82fb54da47e73e090ec429b1dc30879b2277856c79408c", size = 20475577, upload-time = "2026-06-18T15:02:15.447Z" }, + { url = "https://files.pythonhosted.org/packages/f3/04/1aab038404c53ec17207b07eb287a6ac4af5933df1a909490ea96def668a/tangram_core-0.5.0-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:14ea2a1a08411ba1abc5b0790b89c5dad7b1dd2d4759a0d18aec3581fa472bbc", size = 20182700, upload-time = "2026-06-18T15:02:18.062Z" }, + { url = "https://files.pythonhosted.org/packages/af/87/8cd9e710277b2c034042a8de946e45679457ae3565c10837b8d9d3a4ff9a/tangram_core-0.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b4fd573b268eac25b6fd04bc535939e7833b1aa860c3d0b2dbd8bab75ec007e1", size = 20651433, upload-time = "2026-06-18T15:02:20.402Z" }, + { url = "https://files.pythonhosted.org/packages/62/93/3df66df42bad30c0bfb008493e30de4f88b65374a9b3052e238ee077f3d5/tangram_core-0.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:d44b4b027792b227908995ca83e7baf71168690da66dffe238f5f28ed8c6cf26", size = 19801442, upload-time = "2026-06-18T15:02:23.018Z" }, + { url = "https://files.pythonhosted.org/packages/27/ee/3690de04c69d0d7fe6b0edf7d7b5d089ad80dc6c1f43b8b2685fb3a936d5/tangram_core-0.5.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:29721830780399be2ee8b8703c01c60e9b75d8502c084a070c67a28ddaa78ce4", size = 20273698, upload-time = "2026-06-18T15:02:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/15/1a/a2ac83c81597c1ca7618c796cb564e420d42512b2be06cc5fb3d123d839a/tangram_core-0.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7887d7f06ee0207b49990b2d65e0e415bd1e7e7f2f9e33297c3d65a21b96218b", size = 20153147, upload-time = "2026-06-18T15:02:27.295Z" }, + { url = "https://files.pythonhosted.org/packages/68/d4/bcda2e7c5a47e1d46fb3de888d48f2db677c88eec199d4fc8f11a3c04498/tangram_core-0.5.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:43a22ae211d1525a5fc79a96ac8fb37b62380043cd2a84f0d7d6d3c80a66bcbe", size = 20273106, upload-time = "2026-06-18T15:02:30.212Z" }, + { url = "https://files.pythonhosted.org/packages/bc/57/7990fff87f4aac0088f278d08776e450e7ffae1efe57fedd5d33fd239bbb/tangram_core-0.5.0-cp312-cp312-manylinux_2_28_armv7l.whl", hash = "sha256:80a6d25b8e5682efd5870888154c195d13c548bb971d3965bb77fff4838facef", size = 19935457, upload-time = "2026-06-18T15:02:32.505Z" }, + { url = "https://files.pythonhosted.org/packages/80/45/731f5fc8db4563a478b8bdc0b4e7580d7190766038cc553647a017a3e375/tangram_core-0.5.0-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:42f3537015c89b3a96b4d25c1c6f55d5c464129adc1f6041afa5b444b9cced51", size = 20110330, upload-time = "2026-06-18T15:02:34.984Z" }, + { url = "https://files.pythonhosted.org/packages/b6/24/c5e1de17a77d74a8da32c49312d483b755367cdb1647d5557fc0c8055aec/tangram_core-0.5.0-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:ff9ff8fc18e0a76a4f094c0f6c16d73e819878a62572c96370d9aa5f883c5206", size = 20309843, upload-time = "2026-06-18T15:02:37.481Z" }, + { url = "https://files.pythonhosted.org/packages/ed/fe/c554bb9d5a68e1f8bc55f9bea46a3c3676b963b028950b99ca170c747656/tangram_core-0.5.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:0faf50b396bfdea60a825231772d8da7c2a013bf0e359157134eb4ec4ad18bef", size = 20408415, upload-time = "2026-06-18T15:02:40.084Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e3/327ce9047fc2c7646919e10df0aeb4ee90cbdad5a9743a1009038159c480/tangram_core-0.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:283879f6e173b099147bb91e82e6f20cefcb27757afc6d9cd0e58114bc8d67a9", size = 20469964, upload-time = "2026-06-18T15:02:42.481Z" }, + { url = "https://files.pythonhosted.org/packages/a7/bc/38da7755bf0dd3af6866e2a9eada3d590c53c32670346a0d2f533f0559c6/tangram_core-0.5.0-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:1574a7fe17a4cacb7421ed8c74f94f57d94d36cec9d32b7d45ae0a27d6a7f540", size = 20179879, upload-time = "2026-06-18T15:02:45.119Z" }, + { url = "https://files.pythonhosted.org/packages/08/23/8250a0208eefce93b7d6237cc82ef9e8305e751bc2c25e2ebc13ac99f850/tangram_core-0.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:be99a53572626160ee331892de309452d9019ea8e3924fb3dcc418374be06b8d", size = 20647626, upload-time = "2026-06-18T15:02:47.602Z" }, + { url = "https://files.pythonhosted.org/packages/c1/86/ee3428c076f36f11e5fa72d3147934bda599fd8b08796f1e45f0ed45f6b2/tangram_core-0.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:679bda458db778eab07470fa7dfc8e802091aede2a6ac3e2fcb7999716fcb0cd", size = 19800748, upload-time = "2026-06-18T15:02:50.086Z" }, + { url = "https://files.pythonhosted.org/packages/6f/63/d7a06732b41b916cdb0f590e5b49fa454ae24cf27906dcb7bd0d17109432/tangram_core-0.5.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:d162910b47fb5453629deba9bab6844cec85e45ed3b791f709c974c1a36b5c46", size = 20274077, upload-time = "2026-06-18T15:02:52.393Z" }, + { url = "https://files.pythonhosted.org/packages/96/93/7f380ad1e2f12ddbc98b5961c0640bba5a38548771c0c99e5032000a62e2/tangram_core-0.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1209d38908de943128069249c971c8f2db1ef912f3e406c4e68c92a8c4f6f2bb", size = 20153642, upload-time = "2026-06-18T15:02:54.701Z" }, + { url = "https://files.pythonhosted.org/packages/91/a9/d7b4236f0681025e5a9dc34cb40733e3c7d6cc2c57b60b9174c2f53074e6/tangram_core-0.5.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:182781d128796e848b9ff3c7671be5b926835ac73e6f856eb1730464c05948ca", size = 20273100, upload-time = "2026-06-18T15:02:57.353Z" }, + { url = "https://files.pythonhosted.org/packages/6d/6e/f64113313f4f90f57518a82e3fb2675203cc2d1af2187b674fa922fc84a9/tangram_core-0.5.0-cp313-cp313-manylinux_2_28_armv7l.whl", hash = "sha256:cc86ac8cfb51b4c36417372abc1e0a9693d42e239a5114ae99fc07de36e0be5a", size = 19934884, upload-time = "2026-06-18T15:03:00.004Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e3/dc0b72507efdbad0003694a19a40b3638397104c7202c9553c5d7faf67d7/tangram_core-0.5.0-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:768d9726296bf2dd08d2851f1fbd009e75898b5c0ee03755a50c44322566c7ef", size = 20108903, upload-time = "2026-06-18T15:03:02.889Z" }, + { url = "https://files.pythonhosted.org/packages/bd/5d/92ff9d8b735e50bf95e5cf57c427e246514e6120551cef502ff8ef0cfe75/tangram_core-0.5.0-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:7a87279f9795a775be226b6f596039c04b27497486ab7bac001d7252f905733f", size = 20309505, upload-time = "2026-06-18T15:03:05.443Z" }, + { url = "https://files.pythonhosted.org/packages/81/4f/fbb6f366c413172a9b30a811aff99b1de9ee608b57d3d1f32450b1e11548/tangram_core-0.5.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1f0c5fe28b1df45d9c67ea9ce3bf8327bedcbc9e39f141f9c3607f16c1545d99", size = 20408873, upload-time = "2026-06-18T15:03:07.883Z" }, + { url = "https://files.pythonhosted.org/packages/f5/c3/c00c40af178ebee97db862da59ba80e2c6960e17aa6f7623176bd79324b0/tangram_core-0.5.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:897371d641fe05e999fb2b1389883e25802455e3270f73dbb397fe8be512a93a", size = 20469424, upload-time = "2026-06-18T15:03:10.396Z" }, + { url = "https://files.pythonhosted.org/packages/e3/ba/50ae2655f4fa906d8127b887d8cd78de97bcc42ea916e6643feae2735ed5/tangram_core-0.5.0-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:9674631ea81ace47da598258c44e2d2f9fbac08c02db904d7a294b1447f1eeff", size = 20179274, upload-time = "2026-06-18T15:03:13.412Z" }, + { url = "https://files.pythonhosted.org/packages/e6/9e/14a45493e9dcc5c89b321704d98c309d10075b7234bdfc8786d7423778c1/tangram_core-0.5.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7345354a438e415ab778c68e44fcffdb0b6341f1d94e87e7608a28cd1b17e5fe", size = 20647752, upload-time = "2026-06-18T15:03:15.838Z" }, + { url = "https://files.pythonhosted.org/packages/0e/07/986ae65f4ce95ec1824b54de4f4eefc5968fcbe89823961188853506b23c/tangram_core-0.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:781772505f8ad3c68025c17e91d89bd6a67bab7625ba535eb2b65ba95fd451d3", size = 19801146, upload-time = "2026-06-18T15:03:18.336Z" }, + { url = "https://files.pythonhosted.org/packages/04/dd/3ec967bea2458098cd3ee2c6f80d3c94d98dc3c5c72d409e29644740d36c/tangram_core-0.5.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:85a034c2bf6f706beb9b995cab16504c07c2d7303dee6ce3a79d181a04526a4f", size = 20274766, upload-time = "2026-06-18T15:03:20.606Z" }, + { url = "https://files.pythonhosted.org/packages/c2/9d/f1ccdbee01cb255dbfa9011ed37c8ea2f1d00fc98dc4ea808f64e94534ec/tangram_core-0.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3da85e7a865e0dda786d7d798a44b1480d5f58d861f43c7651cb562e5323ea0f", size = 20151182, upload-time = "2026-06-18T15:03:22.791Z" }, + { url = "https://files.pythonhosted.org/packages/3a/a4/97037d57a43cdc0af1a91d418dbcbcf79a00e7609c8ae5249e988ae53349/tangram_core-0.5.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:f3bb3dd3dcffe0bd8f277412d15465196817a610e60d120e8e328942b4c25a60", size = 20273126, upload-time = "2026-06-18T15:03:25.159Z" }, + { url = "https://files.pythonhosted.org/packages/aa/14/892aee8e935c7b30c47370059cbd083303eb47d11c57a4066caa337669d1/tangram_core-0.5.0-cp314-cp314-manylinux_2_28_armv7l.whl", hash = "sha256:ccb2e4c34d511a9b9cab2369f263f4803f9625ba3ed25f32cc9bd48effee8a8d", size = 19934855, upload-time = "2026-06-18T15:03:27.898Z" }, + { url = "https://files.pythonhosted.org/packages/0c/8d/ef072fec732dba428870ccba2ad2cef3a5f1d427682207956c22ae0ce25e/tangram_core-0.5.0-cp314-cp314-manylinux_2_28_i686.whl", hash = "sha256:edd2de1fb77b3c5a12eff25df54d6fc9c67403ad133120cf3a1fd1bde9999549", size = 20108836, upload-time = "2026-06-18T15:03:30.269Z" }, + { url = "https://files.pythonhosted.org/packages/24/07/66a1286f700f13778a79050cf9569a10513531488b7d6a43be73802f3872/tangram_core-0.5.0-cp314-cp314-manylinux_2_28_ppc64le.whl", hash = "sha256:9a94a9255bdc6347c2da260adf807c7b8733c3d523416fd68f54696e5d4a578c", size = 20309950, upload-time = "2026-06-18T15:03:32.886Z" }, + { url = "https://files.pythonhosted.org/packages/ea/7f/92475df115a717664a323240771f8186c0004b820d9cb22251f48983e54e/tangram_core-0.5.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:3b79d182340291b2dd4e1946a369bdbbadb522cd8680ed4209a6f32f37440c96", size = 20407649, upload-time = "2026-06-18T15:03:35.314Z" }, + { url = "https://files.pythonhosted.org/packages/5e/7b/ad6cb644339f939c79a14b07dd1ae5bc7992c594eb99a1a1a51aec990849/tangram_core-0.5.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:ce19ed25d612db422bfad192b7346d911cbef023e740a75faabbc26309af0bdd", size = 20469653, upload-time = "2026-06-18T15:03:37.644Z" }, + { url = "https://files.pythonhosted.org/packages/89/c6/3f76813e6312ed45a5c636a56b431f2743a94d9f20ceea9d673f104cdd36/tangram_core-0.5.0-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:de19b9bfb72d1ef1c5023ddd3a755155af27f9a317bf80c9b0b6671ea6c7d4d9", size = 20179216, upload-time = "2026-06-18T15:03:40.281Z" }, + { url = "https://files.pythonhosted.org/packages/12/b1/b8f3263c9624f344a8a828782b23e883bebee8a97b3a1c17e45b9b2a57ba/tangram_core-0.5.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:41d51cfb42f50fce5781b6664e170ef9d167e2a9de0a247263d6085d08785427", size = 20647592, upload-time = "2026-06-18T15:03:43.19Z" }, +] + +[[package]] +name = "tangram-minisky" +version = "0.1.0" +source = { editable = "example_plugins/tangram/tangram_minisky" } +dependencies = [ + { name = "tangram-core" }, +] + +[package.metadata] +requires-dist = [{ name = "tangram-core", specifier = ">=0.5.0" }] + [[package]] name = "tornado" version = "6.5.7"