feat(site): /node section for lofi-node — homepage, docs, api, llms.txt (#112)
- commit
- ed889918924e9d93c9a7af7f98b62df0ed5a2e92
- parent
- 7db5c0f5620796b649991db6a61528686ebba114
- parent
- 6aa14b3711625f09d642d5f5a9f2feb4c5c09b29
- author
- FelineStateMachine <dami.etoile@gmail.com>
@@ -11,6 +11,7 @@ on:
1111 - "package/**"1212 - "tools/api_docs.ts"1313 - "tools/site_llms.ts"14+ - "tools/node_docs.ts"1415 - "tools/entrypoints.ts"1516 - "deno.json"1617 - ".github/workflows/site.yml"@@ -18,11 +19,25 @@ on:
1819 permissions:1920 contents: read2021 22+# The /node section renders from a PINNED lofi-node checkout so site builds are23+# reproducible; content changes there ride an explicit bump of this ref.24+# Bump procedure: point LOFI_NODE_REF at the new lofi-node commit SHA in the25+# same PR that reviews the resulting /node pages.26+env:27+ LOFI_NODE_REF: 1f83f723ce3cac025800407806aa5c4f51bc8a2328+2129 jobs:2230 build:2331 runs-on: ubuntu-latest32+ env:33+ LOFI_NODE_DIR: lofi-node-checkout2434 steps:2535 - uses: actions/checkout@v436+ - uses: actions/checkout@v437+ with:38+ repository: FelineStateMachine/lofi-node39+ ref: ${{ env.LOFI_NODE_REF }}40+ path: lofi-node-checkout2641 - uses: denoland/setup-deno@v22742 with:2843 deno-version: v2.x@@ -31,10 +46,8 @@ jobs:
3146 node-version: 223247 cache: npm3348 cache-dependency-path: website/package-lock.json34- - name: Generate API reference from JSDoc35- run: deno task site:api36- - name: Generate llms.txt and llms-full.txt37- run: deno task site:llms49+ - name: Generate API references, /node docs, and llms.txt pairs50+ run: deno task site:gen3851 - name: Install site dependencies3952 run: npm ci --prefix website4053 - name: Build site (fails on broken links)@@ -64,9 +64,12 @@
6464 "test:golden": "deno run -A tools/golden.ts --source local",6565 "test:golden:registry": "deno run -A tools/golden.ts --source registry",6666 "site:api": "deno run --allow-read --allow-run --allow-write=website/api-gen tools/api_docs.ts",67+ "site:api:node": "deno run --allow-read --allow-run --allow-env=LOFI_NODE_DIR --allow-write=website/node-api-gen tools/api_docs.ts --node",68+ "site:node": "deno run --allow-read --allow-run=git --allow-env=LOFI_NODE_DIR,LOFI_NODE_REF --allow-write=website/node-docs-gen tools/node_docs.ts",6769 "site:llms": "deno run --allow-read --allow-write=website/static --allow-env=SITE_URL tools/site_llms.ts",68- "site:build": "deno task site:api && deno task site:llms && npm run build --prefix website",69- "site:dev": "deno task site:api && deno task site:llms && npm start --prefix website",70+ "site:gen": "deno task site:api && deno task site:api:node && deno task site:node && deno task site:llms",71+ "site:build": "deno task site:gen && npm run build --prefix website",72+ "site:dev": "deno task site:gen && npm start --prefix website",7073 "publish:dry": "deno publish --dry-run"7174 },7275 "fmt": {@@ -44,6 +44,8 @@ shortest product overview and command summary. AI agents can ingest these docs a
4444 - [Schema evolution](examples/schema-evolution.md)4545 - [Nested app namespaces](examples/nested-namespaces.md)4646 - [Store provisioning](store-provisioning.md)47+- [lofi-node](https://lofi.host/node) — the sync node your **users** can run to own their data's48+ location; tutorials, guides, and the node reference live in the site's `/node` section4749 - [Optional installed-app recipes](recipes/README.md)4850 - [Advanced device-auth primitive](auth.md)4951 Binary file.
@@ -159,6 +159,10 @@ Every prerendered route is included in the shell precache. While offline, a dire
159159 as `/field-notes/settings/` resolves its cached `settings/index.html`; if that route was not160160 emitted, the worker falls back to the cached application root.161161 162+The app is the only thing that needs hosting: sync is optional infrastructure the user can point at163+a managed Jazz app or at [a self-hosted sync node](/node) they run themselves — the deployment does164+not change either way.165+162166 ### Offline cache policy163167 164168 The build's precache manifest contains required shell resources only. Shell assets are fetched@@ -0,0 +1,35 @@
1+# Your data, on your node2+3+<!-- Source: FelineStateMachine/lofi-node README.md; site-voice page owned by lofi docs/node/. -->4+5+You use lofi apps; lofi-node is how you decide where their data lives. This is not something an6+app's developer sets up for you, and it is not a way to host the app itself — the app stays wherever7+it is deployed. The node is yours: [lofi-node](https://github.com/FelineStateMachine/lofi-node) is8+one daemon that embeds a real Jazz sync server, [iroh](https://iroh.computer) node-to-node9+transport, and a ticket-based access gate. Browsers keep speaking Jazz's protocol; only the server10+URL changes, and that URL is data you hand the app at runtime, not configuration its developer11+compiled in.12+13+What the node gives you:14+15+- **A real Jazz sync server** — SQLite-backed, health-checked, running exactly the pinned Jazz16+ version the apps you use run. Any lofi app you use can sync into it.17+- **Tickets, not accounts.** A `lofisync1.` app-connect ticket carries location and access in one18+ string; possession is transport access, revocation is one command. A provision-scoped ticket19+ additionally administers the store, with the admin secret never leaving the node.20+- **Node-to-node replication over iroh.** Two homes converge by pairing tickets: dialed by public21+ key, hole-punched, no static IPs, no cloud dependency.22+- **No silent degradation.** If the native transport layer can't load, the Jazz server still runs23+ LAN-only and `status()` says exactly why pairing is off.24+25+## Where to go26+27+- Never run a node before? Start with [Self-host your first sync node](self-host-a-sync-node.md).28+- Two locations that should hold the same data: [Pair two homes](pair-two-homes.md).29+- Setting up the store an app syncs into — including a second app joining a store that already holds30+ data: [Provision a store](provision-a-store.md).31+- The mental model behind all of it — slices, shared stores, and why apps can't clobber each other:32+ [Sliceable apps and shared stores](sliceable-apps-and-shared-stores.md).33+34+The app-side enrollment flow (pasting a ticket into a lofi app) is part of the framework docs:35+[Sync and recovery](/docs/sync-and-recovery).@@ -0,0 +1,60 @@
1+# Architecture and provenance2+3+<!-- Source: FelineStateMachine/lofi-node README.md, src/{gate,tunnel,node}.ts,4+ native/iroh-js/UPSTREAM.md, docs/port-iroh-js.md, CONTRIBUTING.md. -->5+6+One daemon, three layers, each with a single job:7+8+```mermaid9+flowchart LR10+ subgraph Device["User devices"]11+ App["lofi app (browser)"]12+ end13+ subgraph Node["lofi-node daemon"]14+ Gate["Access gate<br/>public port, tickets"]15+ Jazz["Jazz sync server<br/>loopback-only, SQLite"]16+ Tunnel["iroh tunnel<br/>node-to-node"]17+ end18+ subgraph Peer["Paired node"]19+ Root["Root store"]20+ end21+22+ App -->|"WS + HTTP with /t/(secret)"| Gate23+ Gate --> Jazz24+ Jazz --- Tunnel25+ Tunnel -->|"QUIC, dialed by key"| Root26+```27+28+- **The gate is both enforcement and reachability.** The embedded Jazz server binds loopback-only29+ and is verified to; the only path to it is through the gate, which validates tickets, strips and30+ injects admin headers, and re-originates WebSockets. Access control is therefore not a mode the31+ store honors — it is the shape of the network.32+- **The tunnel carries exactly what Jazz speaks.** One iroh connection per WebSocket or HTTP33+ request, opened with a HELLO frame naming its kind, path, and headers. Sync and catalogue traffic34+ both ride it, which is why deploying against a leaf lands on the root without either side35+ special-casing anything.36+- **Pairing is upstream election.** A leaf's Jazz server chains to the root; re-election at runtime37+ restarts Jazz on the same internal port while the gate keeps the public port stable.38+39+## The version invariant40+41+The data plane is Jazz 2.0 **alpha**, pinned exactly. A node must run the same alpha as the apps it42+serves — treat version bumps as coordinated, release-lockstep changes, never drive-by. This is a43+stated contract of the lofi-node repo (its contributing guide calls version pins decisions, with the44+full suite as evidence).45+46+## Native layer provenance47+48+The iroh transport is a vendored port of the upstream `n0-computer/iroh-ffi` napi crate at module49+granularity, with every local edit marked and an `UPSTREAM.md` recording provenance and the50+tag-bump-by-diff procedure. Prebuilt libraries for the supported platform matrix are committed51+in-repo, digest-pinned, and embedded into the compiled binary; at first run the right one extracts52+to a version-keyed OS cache. Contributors changing the native layer start from53+`native/iroh-js/UPSTREAM.md` and `docs/port-iroh-js.md` in the lofi-node repo.54+55+## The library surface56+57+Everything the CLI does, an embedder can do: `createSyncNode()` returns a node whose58+`url`/`ticket()`/`issueTicket()`/`pair()`/`status()`/`stop()` are the same operations, and a test59+mesh helper stands up paired topologies for suites. The generated [API reference](/node/api) covers60+the full surface.@@ -0,0 +1,52 @@
1+# Deploying beyond the LAN2+3+<!-- Source: FelineStateMachine/lofi-node README.md, docs/hosting-lofi-apps.md,4+ deno.json compile task, src/native/loader.ts. -->5+6+A node on a trusted LAN needs nothing beyond the tutorial. Leaving the LAN — a VPS, a reachable home7+server, tickets that travel — adds three concerns: the address you pin into tickets, TLS, and the8+binary you actually run.9+10+## Pin the public URL11+12+```sh13+lofi-node init --public-url https://sync.example.net14+```15+16+`publicUrl` is the base embedded in every issued ticket. It must be the address enrolled devices17+will reach — not `localhost`, not an internal IP that stops resolving off-network. It can be changed18+per-issuance (`ticket issue --url …`), but pinning it at init keeps tickets consistent.19+20+## Front the gate with TLS21+22+The gate speaks plain HTTP; beyond a trusted LAN, put TLS in front of it (a reverse proxy such as23+Caddy or nginx terminating `https://` and forwarding to the gate's port) and issue tickets with the24+`https://` public URL. Two reasons, one hard and one practical: the ticket URL is a bearer25+credential and should not transit networks unencrypted; and installed PWAs generally require a26+secure origin, so an app enrolled over plain http may work in a tab and fail as an installed app.27+28+Note what TLS does **not** change: the ticket still gates access, revocation still works the same,29+and the WebSocket upgrade still rides the same URL — the browser client derives its endpoints from30+the http(s) base.31+32+## One binary33+34+```sh35+deno task compile # → dist/lofi-node36+```37+38+The compiled binary embeds the prebuilt native transport matrix — macOS arm64 and x86_64, Linux39+x86_64 and aarch64 — and extracts the right library to a version-keyed OS cache on first run.40+Artifact digests are pinned in the source, so an extraction that doesn't match its pin fails loudly.41+42+**Windows** is a documented gap: the native layer's build path needs a `libnode.dll` import library43+the upstream toolchain doesn't ship for this configuration. On Windows the node runs LAN-only — the44+Jazz server works, `status().mesh` reports `{ state: "unavailable", reason: … }`, and45+`ticket()`/`pair()` throw typed errors rather than degrade silently.46+47+## The version invariant48+49+A node pins the **exact** Jazz alpha of the apps it serves; version bumps are coordinated changes,50+never drive-by. Before pointing the apps you rely on at your node, confirm the pins match — a51+mismatched pair fails in protocol-level ways no amount of network configuration fixes. See52+[troubleshooting](troubleshooting.md) for what the mismatches look like.@@ -0,0 +1,62 @@
1+# CLI2+3+<!-- Source: FelineStateMachine/lofi-node cli.ts (USAGE block and argument parsing). -->4+5+```6+lofi-node — self-hostable sync node for lofi apps7+8+Usage:9+ lofi-node init [--dir <dataDir>] [--app-id <id>] [--port <n>]10+ [--public-url <base>] [--open] [--storage-path <path>] [--memory]11+ lofi-node start [--dir <dataDir>]12+ lofi-node pair <node-ticket> [--dir <dataDir>]13+ lofi-node ticket issue [--label <s>] [--url <base>] [--provision] [--dir <dataDir>]14+ lofi-node ticket list [--dir <dataDir>]15+ lofi-node ticket revoke <id> [--dir <dataDir>]16+ lofi-node status [--dir <dataDir>]17+```18+19+The data directory defaults to `./lofi-node-data` everywhere.20+21+## init22+23+Creates the data directory and writes [`config.json`](configuration.md): a generated Jazz app id (or24+`--app-id` to pin one), fresh backend and admin secrets, and your choices.25+26+| Flag | Meaning |27+| ----------------------- | --------------------------------------------------------------------------------------------------------- |28+| `--port <n>` | Fixed public port for the gate; auto-allocated when omitted. |29+| `--public-url <base>` | Base URL embedded in issued tickets (use the reachable address). |30+| `--open` | Opt out of ticket gating — anyone who can reach the port can sync. CLI inits default to **ticket-gated**. |31+| `--storage-path <path>` | SQLite store on any mounted location; probed writable at boot. |32+| `--memory` | Ephemeral in-memory store. |33+34+## start35+36+Starts the daemon from an initialized directory: boots the loopback-only Jazz server, opens the37+public gate, loads the native transport when available, and prints the gate URL plus the38+node-pairing ticket when the mesh is up.39+40+## pair41+42+`lofi-node pair <node-ticket>` elects the peer as this node's upstream — the node becomes a leaf43+relaying sync and catalogue traffic over iroh. The argument is a node-pairing `endpoint…` string44+from the peer's `start` output, not an app ticket (the CLI rejects app tickets with a pointed45+message). Re-electable at runtime; the public port does not change.46+47+## ticket issue / list / revoke48+49+`issue` mints an app-connect ticket and prints it **exactly once** — the node stores only a digest.50+`--label` names it for your bookkeeping, `--url` overrides the embedded base for this ticket, and51+`--provision` mints a provision-scoped ticket that can also administer the store52+([scopes](tickets-explained.md)).53+54+`list` shows every issued ticket: id, scope, label, issued time, and revocation state. `revoke55+<id>`56+invalidates one; a running daemon picks the change up within seconds and closes the ticket's live57+connections with close code 4001 — no restart, no IPC.58+59+## status60+61+Prints configuration and live state without changing anything: access mode, storage, issued tickets,62+upstream election, and mesh state (up with connection stats, off, or unavailable with the reason).@@ -0,0 +1,65 @@
1+# Configuration2+3+<!-- Source: FelineStateMachine/lofi-node src/config.ts (NodeConfig, migration) and4+ src/appticket.ts (tickets.json). -->5+6+A node's durable state is two JSON files in its data directory, plus a small set of environment7+variables. Both files are owned by the node; edit `config.json` only with the daemon stopped.8+9+## config.json (v2)10+11+```jsonc12+{13+ "v": 2,14+ "appId": "cfe52e44-…", // Jazz app id, generated at init (UUID — required by the engine)15+ "backendSecret": "lofi_backend_…", // Jazz credential; server-side only16+ "adminSecret": "lofi_admin_…", // authorizes schema/permission administration17+ "listenPort": 4802, // optional fixed public port; auto-allocated when absent18+ "access": "ticket", // "ticket" (CLI-init default) | "open" (dev; library default)19+ "storage": { "type": "sqlite", "path": "/mnt/nas/lofi" }, // or { "type": "memory" }20+ "publicUrl": "http://192.168.1.10:4802", // base embedded in issued tickets21+ "upstream": "none", // "none" | { "url": "wss://…" } | { "peer": "endpoint…" }22+ "allowLocalFirstAuth": true23+}24+```25+26+- **`access`** — `ticket` puts the access gate in front of everything; `open` proxies27+ unauthenticated (the gate still owns the public port and the Jazz server still binds28+ loopback-only).29+- **`storage`** — the honest engine surface: a SQLite directory (optional path, probed writable at30+ boot) or memory. See [storage choices](storage-choices.md).31+- **`upstream`** — `"none"` for a root node; `{ "peer": … }` for an iroh-paired leaf32+ ([pairing](pair-two-homes.md)); `{ "url": … }` for a plain HTTP(S) upstream Jazz server.33+- **Secrets** stay in this file and never transit issued tickets; a provision-scoped ticket causes34+ the gate to inject `adminSecret` server-side rather than revealing it.35+36+**v1 migration**: v1 files load transparently with legacy defaults applied (`access: "open"`, SQLite37+storage) and are rewritten as v2 on the next save.38+39+## tickets.json40+41+```jsonc42+{43+ "v": 1,44+ "tickets": [45+ {46+ "id": "a1b2c3d4e5f6", // first 12 hex chars of the secret's SHA-25647+ "scope": "provision", // absent means "sync"48+ "label": "laptop-admin",49+ "secretHash": "…", // full SHA-256 hex — the secret itself is never stored50+ "createdAt": "2026-07-18T…",51+ "revokedAt": "2026-07-19T…" // present only once revoked52+ }53+ ]54+}55+```56+57+The CLI writes this file; a running daemon only reads it, picking up changes by modification time —58+which is why `ticket issue` and `ticket revoke` need no IPC and no restart.59+60+## Environment variables61+62+| Name | Effect |63+| ----------------- | --------------------------------------------------------------------- |64+| `LOFI_NODE_DEBUG` | `1` enables `[gate]`/`[tunnel]` debug logging to stderr. |65+| `LOFI_NODE_IROH` | Explicit path to the native transport library, overriding resolution. |@@ -0,0 +1,59 @@
1+# HTTP surface2+3+<!-- Source: FelineStateMachine/lofi-node src/gate.ts; normative ticket semantics in the4+ app-ticket contract. -->5+6+The gate is the node's public face: it owns the public port, validates tickets, and proxies to the7+loopback-only Jazz server. This page is the route-level reference for a **ticket-gated** node; in8+`open` mode the same routes exist without the `/t/<secret>` prefix or scope checks.9+10+## Routes11+12+| Route | Ticket | Behavior |13+| ---------------------------------- | ----------- | ------------------------------------------------------------------------- |14+| `GET /health` | none | Liveness; proxied to Jazz. `200`, or `502` when the store is unreachable. |15+| `GET /t/<secret>/store-status` | any scope | Metadata-only schema preflight (below). |16+| `/t/<secret>/apps/<appId>/ws` | any scope | WebSocket sync; re-originated toward Jazz with subprotocol forwarded. |17+| `/t/<secret>/apps/<appId>/…` | any scope | Catalogue reads and other app routes, prefix-stripped and proxied. |18+| `/t/<secret>/apps/<appId>/admin/…` | `provision` | Store administration; the node's admin secret is injected server-side. |19+20+The `<secret>` is a 43-character base64url path segment; the gate compares digests in constant time,21+strips the prefix, preserves the query string, and forwards.22+23+## store-status24+25+```jsonc26+// 20027+{28+ "v": 1,29+ "appId": "…",30+ "schema": {31+ "deployed": true, // false → the object carries only { "deployed": false }32+ "headHash": "ff85ac…", // newest stored schema hash (ordered by publishedAt)33+ "permissionsHead": "0195…" // current permissions head object id, or null34+ }35+}36+37+// 502 — the node is up but its store is not38+{ "error": "store_unavailable" }39+```40+41+Metadata only — never schema contents, policies, or secrets. This endpoint exists so a sync-scoped42+client can classify a store (most importantly `no_schema`, where writes would hang) without any43+admin capability.44+45+## Errors and close codes46+47+| Signal | Meaning |48+| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |49+| `401 {"error":"invalid_ticket"}` | Unknown ticket, revoked ticket, or a sync-scoped ticket on an admin route — deliberately indistinguishable. On WebSocket, the 401 **is** the upgrade response. |50+| WS close `4001` (`ticket revoked`) | The ticket was revoked mid-session; live sockets close within a couple of seconds. The app treats the sink as dead and surfaces re-enrollment. |51+| WS close `1011` | Upstream dial or pump failure inside the gate. |52+| `502 {"error":"store_unavailable"}` | Gate up, Jazz (or the tunnel to the root) unreachable. |53+54+## Header handling55+56+Inbound `X-Jazz-Admin-Secret` headers are **stripped unconditionally** in ticket mode; for57+provision-scoped requests on admin routes the gate injects its own. Hop-by-hop headers58+(`connection`, `keep-alive`, `transfer-encoding`, `upgrade`, `host`, `content-length`) are managed59+by the gate in both directions; everything else passes through.@@ -0,0 +1,54 @@
1+# Pair two homes2+3+<!-- Source: FelineStateMachine/lofi-node README.md, src/tunnel.ts, src/node.ts (pair/status). -->4+5+Two nodes in two places — a home server and a studio machine, say — can hold the same store and6+converge over [iroh](https://iroh.computer): dialed by public key, hole-punched through NATs, no7+static IPs, no port forwarding, no cloud dependency. Devices keep syncing against whichever node is8+closest; the nodes reconcile with each other.9+10+## Pairing11+12+Every node with the mesh up prints a **node-pairing ticket** on start — an iroh `endpoint…` string,13+distinct from the `lofisync1.` app tickets users enroll. On the second node:14+15+```sh16+lofi-node pair endpoint… --dir ./data17+```18+19+or at runtime, without a restart and without the public port changing:20+21+```ts22+await node.pair(otherNodeTicket);23+```24+25+Pairing is an **upstream election**: the paired node becomes a leaf that forwards to the root. Sync26+WebSockets and catalogue HTTP (schema reads, deploys) tunnel over iroh, so administering either node27+reaches the root — [provisioning through a leaf](provision-a-store.md) lands on the root's store,28+verified end to end in lofi-node's test suite.29+30+## What the tunnel carries31+32+One iroh connection carries exactly one WebSocket or one HTTP request. Each connection opens with a33+small HELLO frame naming what it carries (`ws` with path and subprotocol, or `http` with method,34+path, and headers); frames then bridge both ways until either side closes. This is why the35+browser-facing protocol never changes: the leaf re-originates exactly what Jazz expects.36+37+## Observing the mesh38+39+```ts40+node.status().mesh;41+// { state: "up", nodeId, ticket, connections: [{ direction, rtt, paths }] }42+```43+44+`connections` reports each live tunnel with its round-trip time and path counts — direct versus45+relay — so you can see whether hole-punching succeeded or traffic is riding an iroh relay. The other46+two states are honest degradations: `{ state: "off" }` when the mesh is disabled, and47+`{ state: "unavailable", reason }` when the native layer could not load — the Jazz server still runs48+LAN-only, and `ticket()`/`pair()` throw `MeshUnavailableError` rather than pretending.49+50+## Choosing a root51+52+The root holds the authoritative store; leaves relay to it. Put the root on the machine with the53+most reliable storage ([storage choices](storage-choices.md)) and pair outward from there. A leaf's54+election can be redone at runtime (`pair` again) if the topology changes.@@ -0,0 +1,89 @@
1+# Provision a store2+3+<!-- Source: lofi docs/store-provisioning.md + FelineStateMachine/lofi-node4+ tests/provisioning_test.ts (the executable version of this tutorial). -->5+6+A fresh node has no schema deployed, and an app must never sync against a store in that state — the7+engine's writes hang rather than fail. Provisioning is the explicit step you opt into that creates8+or updates the store's schema, and this tutorial walks it end to end: first app in, then a **second9+app joining the store that already holds the first app's data**. Every step here is also executable10+— lofi-node's `tests/provisioning_test.ts` runs this exact flow through the gate in CI.11+12+## 0. The opt-in: a provision ticket13+14+```sh15+lofi-node ticket issue --dir ./data --provision --label laptop-admin16+```17+18+A provision-scoped ticket is a strict superset of a sync ticket: transport plus store19+administration. For provisioning requests the gate injects the node's own admin secret and strips20+any inbound one — the secret never transits the client, and possession of the ticket **is** the21+opt-in. Issue one per provisioning context, not per device.22+23+## 1. Preflight24+25+Any valid ticket — sync scope included — may ask the node where the store stands:26+27+```28+GET <ticket-url>/store-status29+→ { "v": 1, "appId": "…", "schema": { "deployed": false } }30+```31+32+On the app side this is `readTicketStoreStatus(ticketUrl)`, and with administration in hand the33+richer classifier is `readStoreStatus(root, target)`, which reports one of `ok`, `no_schema`,34+`schema_out_of_date`, or `schema_drift`. `deployed: false` here means `no_schema`: create.35+36+## 2. First app: create37+38+With the app's nested schema and merged permissions bundle:39+40+```ts41+import { provisionStore } from "@nzip/lofi/schema/store";42+import { permissions, root } from "./schema.ts";43+44+await provisionStore({45+ app: root,46+ permissions,47+ target: { serverUrl: provisionTicketUrl, appId: ticketAppId },48+});49+// → { status: "created", headHash: "…" }50+```51+52+Note the absent `adminSecret` — the provision ticket's gate injection carries administration.53+`store-status` now reports `deployed: true` with the head hash, and enrolled devices sync normally.54+55+## 3. Second app: merge into an occupied store56+57+Time passes; the store holds `taskapp` data. Now a notes app — its own namespaces, its own58+deployment — enrolls against the same store. Its preflight reports `schema_out_of_date` with exactly59+its missing tables, and its opt-in provisioning **merges its slice** rather than replacing anything:60+61+- The stored head schema is fetched verbatim and only extended — the first app's tables keep their62+ exact serialization.63+- The union deploys as an ordinary migration advancing the store's one permissions head, with the64+ first app's policies preserved unchanged and the second app's swapped in for its own tables only.65+- The joining app's own compiled schema is registered and connected to the head, so its clients are66+ never disconnected.67+68+```ts69+await provisionStore({ app: notesRoot, permissions: notesPermissions, target });70+// → { status: "updated", headHash: "…" }71+```72+73+The first app's devices keep working untouched across the merge — their schema stays connected74+through the migration chain — and its access rules still hold: a user who couldn't read a gated75+`taskapp` row before the merge still can't after it. Why apps can't step on each other here is the76+subject of [Sliceable apps and shared stores](sliceable-apps-and-shared-stores.md).77+78+## 4. What provisioning refuses79+80+- **Drift is never auto-repaired.** If the store's copy of an app's own namespaces differs from what81+ the app declares, provisioning throws with the differing tables. A human decides.82+- **Out-of-namespace changes are hard errors** before any request is made.83+- **Sync-scoped tickets cannot provision**: admin routes answer the same84+ `401 {"error":"invalid_ticket"}` as an unknown ticket — nothing to enumerate.85+86+## Through a paired leaf87+88+Provisioning against a [paired](pair-two-homes.md) leaf lands on the root: catalogue writes ride the89+iroh tunnel's HTTP path. Deploy wherever is convenient; the root's store is what advances.@@ -0,0 +1,92 @@
1+# Self-host your first sync node2+3+<!-- Source: FelineStateMachine/lofi-node docs/hosting-lofi-apps.md and cli.ts; validated flow. -->4+5+By the end of this tutorial you will have a sync node running on your own machine, an app-connect6+ticket in hand, and a lofi app you use syncing against it. You are the app's user here, not its7+developer: the app stays exactly as it was deployed, and pointing it at your node is entirely your8+call. Nothing below requires a cloud account, a static IP, or TLS on a trusted LAN — one binary and9+one paste.10+11+## 1. Install lofi-node12+13+Install the tool from [JSR](https://jsr.io/@nzip/lofi-node), which puts `lofi-node` on your PATH:14+15+```sh16+deno install -g -A -n lofi-node jsr:@nzip/lofi-node/cli17+```18+19+For a single self-contained binary instead, compile from a checkout of20+[lofi-node](https://github.com/FelineStateMachine/lofi-node):21+22+```sh23+deno task compile # → dist/lofi-node24+```25+26+Either way, the native transport layer covers macOS arm64/x86_64 and Linux x86_64/aarch64; on27+Windows the node runs LAN-only ([why](troubleshooting.md)).28+29+## 2. Initialize and start30+31+```sh32+lofi-node init --dir ./data --port 4802 --public-url http://192.168.1.10:480233+lofi-node start --dir ./data34+```35+36+`init` writes `data/config.json`: a generated Jazz app id, the node's secrets, your access mode, and37+storage choice. New inits are **ticket-gated** — only requests carrying an issued app-ticket secret38+reach the store; `--open` opts out for dev setups. The embedded Jazz server binds loopback-only; the39+node's access gate owns the public port, so the gate is both enforcement and reachability.40+41+`--public-url` is the base address that will be embedded in the tickets you issue — use the address42+other devices on your network can reach. `--storage-path /mnt/nas/lofi` puts the store on any43+mounted location; `--memory` keeps everything ephemeral ([storage choices](storage-choices.md)).44+45+`start` prints the gate URL and, when the mesh is up, the node-pairing ticket for46+[pairing a second node](pair-two-homes.md).47+48+## 3. Issue an app ticket49+50+```sh51+lofi-node ticket issue --dir ./data --label phone52+# → lofisync1.eyJ2IjoxLCJhcHBJZCI6… (shown once; the secret is never stored)53+```54+55+The ticket is one string carrying the store's app id and the gate URL with an access secret embedded56+in its path. It is displayed exactly once — the node keeps only a digest. Issue one per device or57+context (`--label` is for your own bookkeeping), so revocation stays scoped:58+59+```sh60+lofi-node ticket list --dir ./data61+lofi-node ticket revoke <id> --dir ./data # live connections close within seconds62+```63+64+Scopes matter later: a plain ticket is transport-only; `ticket issue --provision` mints one that can65+also [set up the store's schema](provision-a-store.md). What the string actually contains and why66+the secret lives in the URL path: [Tickets explained](tickets-explained.md).67+68+## 4. Enroll the ticket in a lofi app69+70+Paste the ticket into the app — a lofi app with sync exposes enrollment in its account UI. That71+declares your node as the device's sync location and elects sync in one step; the data you already72+made in the app pushes up under the same account identity. Nothing about the app changes or73+redeploys — pointing it at your node is your call, not the app developer's.74+75+(If you are the app developer: the framework side is one call, `enrollSyncTicket`, documented in76+[Sync and recovery](/docs/sync-and-recovery).)77+78+## 5. Verify79+80+- `GET <gate-url>/health` answers without a ticket — safe for liveness probes.81+- `GET <ticket-url>/store-status` answers for any valid ticket with metadata about the store's82+ schema. A fresh node reports `{ "schema": { "deployed": false } }` — that is expected, and it is83+ exactly the state the [store provisioning tutorial](provision-a-store.md) resolves. An app should84+ not sync against a store with no schema; its writes would hang rather than fail.85+- `lofi-node status --dir ./data` shows configuration, tickets, upstream, and mesh state without86+ starting anything.87+88+## Where you are89+90+You have a node, a ticket, and an enrolled app — but the store may not have a schema yet. Continue91+with [Provision a store](provision-a-store.md), or connect a second node with92+[Pair two homes](pair-two-homes.md).@@ -0,0 +1,73 @@
1+# Sliceable apps and shared stores2+3+<!-- Source: lofi #109 (design + conformance), lofi docs/store-provisioning.md and4+ docs/examples/nested-namespaces.md, lofi-node #2; jazz-tools typed-app surface. -->5+6+Your store is shared infrastructure. You bring the store; the apps you use are tenants in it. This7+guide is the mental model that makes that safe: what a slice is, how one store's schema grows app by8+app, and why an app cannot damage a neighbor even though the store trusts its administrator.9+10+## A slice is a naming discipline, not a partition11+12+Jazz's pinned DSL compiles **one schema per store** — one hash, one migration lineage, one query13+planner. `defineSliceableApp` derives typed sub-app views over that single schema, and lofi's14+`s.defineNestedApp` builds the naming layer on top: an app declares namespaces, its tables flatten15+to global names with a reserved separator (`taskapp` + `tasks` → `taskapp__tasks`), and app code16+uses unprefixed typed handles as if the namespace were its whole world.17+18+Nothing at the storage layer partitions those tables — every table is a sibling of every other in19+the one store. The **slice** is the set of tables under an app's declared namespaces, and the20+namespace prefix is what makes ownership checkable.21+22+## The store's schema grows slice by slice23+24+The store enforces exactly one schema at a time — the head of a migration lineage. When a second app25+joins an occupied store, its provisioning _merges_: the stored schema is fetched verbatim, the app's26+missing tables are appended, and the union deploys as an ordinary migration advancing the head.27+Devices running the previous schema keep working, because their schema stays connected to the head28+through the migration chain.29+30+Four states describe any store relative to any app — the same taxonomy the tooling reports:31+32+| State | Meaning |33+| -------------------- | ------------------------------------------------------------------- |34+| `ok` | The enforced schema carries this app's slice exactly. |35+| `no_schema` | Nothing is deployed. Writes would hang; never sync in this state. |36+| `schema_out_of_date` | The store lacks some of this app's tables. Remedy: opt-in update. |37+| `schema_drift` | The store's copy of the app's own namespaces differs unexplainably. |38+39+`no_schema` deserves its emphasis: against an empty store the engine's writes hang rather than fail.40+That is why the node exposes a metadata-only `store-status` preflight any valid ticket can call, and41+why lofi classifies before attaching sync instead of writing and hoping. `schema_drift` is the42+opposite discipline — it is surfaced with the differing tables and **never** auto-repaired; a store43+whose copy of your namespace changed under you needs a human, not a merge.44+45+## The honesty invariant46+47+An app's provisioning may create tables **only under its own declared namespaces**. Everything else48+in the store — sibling apps' tables _and their access policies_ — carries through a merge49+byte-for-byte. Any generated change naming a table outside the app's namespaces is a hard error50+before a single request is made. A flat (non-nested) app declares no namespace and therefore gets no51+tenant rights: it may only provision a store it wholly owns.52+53+Two properties fall out, both verified against a real server in lofi's conformance suite:54+55+- **Continuity.** The first app's devices write and read across a second app's merge untouched.56+- **Policy preservation.** A user who couldn't read a gated row of the first app before the merge57+ still can't after it — the joining app re-publishes the union permissions with the sibling's58+ policies recovered from the store, unchanged.59+60+Stated honestly: this is a _framework_ boundary, not cryptography. Store administration (the admin61+secret, or a provision-scoped ticket) can technically do anything; the invariant keeps apps built on62+lofi honest through the surface they actually use. The node-side complement is that a provision63+ticket never exposes the admin secret itself — the gate injects it — so holding provisioning power64+never means holding a credential you could leak.65+66+## Where each piece lives67+68+- Declaring namespaces and per-namespace permissions:69+ [nested app namespaces](/docs/examples/nested-namespaces) in the framework docs.70+- The classifier and merge flow an app runs: [store provisioning](/docs/store-provisioning).71+- Doing it against a node with a provision ticket: [Provision a store](provision-a-store.md).72+- The ticket semantics underneath: [Tickets explained](tickets-explained.md) and the73+ [app-ticket contract](/node/docs/app-ticket).@@ -0,0 +1,45 @@
1+# Storage choices2+3+<!-- Source: FelineStateMachine/lofi-node docs/hosting-lofi-apps.md ("Storage choice") and4+ src/config.ts (StorageConfig). -->5+6+The node's store is where your synced data durably lives, and the choice is deliberately narrow7+because the underlying engine's honest surface is narrow: jazz-napi supports exactly a SQLite8+directory or memory today. The `storage.type` discriminator in `config.json` is the seam future9+providers slot into.10+11+## SQLite on a path you choose12+13+```sh14+lofi-node init --storage-path /mnt/nas/lofi15+```16+17+```jsonc18+// config.json19+"storage": { "type": "sqlite", "path": "/mnt/nas/lofi" }20+```21+22+The path can be any mounted location — a NAS volume, a synced directory, an external disk. It is23+probed writable at boot; an unwritable location fails fast by name instead of silently degrading.24+Omitting the path keeps the store inside the data directory (`<dataDir>/jazz`).25+26+## Memory27+28+```sh29+lofi-node init --memory30+```31+32+Everything is ephemeral — useful for tests, demos, and throwaway topologies. A restart is a fresh,33+schema-less store: enrolled apps will classify it `no_schema` again and prompt for34+[provisioning](provision-a-store.md).35+36+## Off-site durability37+38+For cloud-grade durability without a cloud sync service, replicate the SQLite file: a39+Litestream-style continuous replica to S3-compatible storage, or snapshots of the volume the path40+lives on. [Pairing a second node](pair-two-homes.md) is the other half of the story — replication at41+the Jazz layer rather than the file layer — and the two compose.42+43+One honest caveat: back up the **data directory** alongside the store. `config.json` holds the44+node's secrets and app id, and `tickets.json` holds the digests that make issued tickets valid; a45+restored store without them is reachable by nobody.@@ -0,0 +1,65 @@
1+# Tickets explained2+3+<!-- Source: FelineStateMachine/lofi-node docs/app-ticket.md (the normative contract) and4+ src/ticket.ts; lofi package/runtime/data-sink.ts for the app side. -->5+6+lofi-node uses two kinds of ticket, and confusing them is the most common early stumble. This guide7+is the conceptual tour; the [app-ticket contract](/node/docs/app-ticket) is the normative spec the8+lofi side implements against.9+10+## Two ticket kinds11+12+- A **node-pairing ticket** (`endpoint…`) is an iroh endpoint address. It names a _node_ so another13+ node can dial it. It is used by `lofi-node pair` and never by browsers or apps.14+- An **app-connect ticket** (`lofisync1.…`) is the credential _you_ hand to a lofi app so it syncs15+ against your node. It carries the store's app id and the gate URL with a 256-bit secret embedded16+ in its path.17+18+## Why the secret lives in the URL path19+20+Jazz clients preserve a base path in their server URL and reject query parameters. By making the21+ticket's URL `http(s)://host:port/t/<secret>`, every request the client ever makes — sync WebSocket22+connects, catalogue reads, admin calls — carries the secret with **zero client changes**. The gate23+validates the secret (timing-safe, digest against digest), strips the prefix, and proxies to the24+loopback-only Jazz server. The lofi app uses the ticket URL verbatim as its sync server; that is the25+whole integration.26+27+## Scopes28+29+- **`sync`** (the default, and what an absent scope means — every pre-scope ticket keeps meaning30+ transport-only): sync, catalogue reads that transport needs, and the metadata-only `store-status`31+ preflight. Admin routes answer the same `401 {"error":"invalid_ticket"}` as an unknown secret, so32+ a probe learns nothing.33+- **`provision`**: a strict superset — everything above plus store administration. The gate injects34+ the node's own `X-Jazz-Admin-Secret` for provisioning requests and strips any inbound one, so the35+ admin secret never transits the client. Possession of a provision ticket **is** the36+ store-administration opt-in; issue one per provisioning context, not per device.37+38+A ticket with an unrecognized scope is rejected outright by the lofi parser — never silently granted39+less than it claims.40+41+## Revocation42+43+- The node stores only SHA-256 digests; the ticket string is displayable once, at issuance.44+- Unknown and revoked secrets are indistinguishable to probers: both get45+ `401 {"error":"invalid_ticket"}`, on HTTP and on the WebSocket upgrade.46+- Revoking a ticket mid-session closes its live WebSockets with close code **4001** within a couple47+ of seconds. The app treats the stored sink as dead and surfaces re-enrollment — it never silently48+ retries forever.49+50+## The security posture, honestly51+52+An app ticket is a **bearer credential** with 256-bit entropy. Anyone holding it can sync as an53+authorized transport peer; identity and row-level permissions remain Jazz's local-first layer on54+top, enforced by the deployed policy, not by the ticket. Plain http is acceptable on a trusted LAN;55+beyond one, front the gate with TLS — installed PWAs generally require a secure origin anyway. Issue56+tickets per device or context so revocation is scoped.57+58+## What the app does with it59+60+You paste; the app does the rest. The ticket is validated (against the same rules this node61+enforces; the two repos share machine-readable conformance fixtures), declared as the device's sync62+location, and sync is elected in one step. The app keeps the ticket URL in a device-local record and63+never surfaces it through its session snapshot — only the host and your label show. For app64+developers, the framework call behind that paste is `enrollSyncTicket`, documented in65+[Sync and recovery](/docs/sync-and-recovery).@@ -0,0 +1,60 @@
1+# Troubleshooting and compatibility2+3+<!-- Source: FelineStateMachine/lofi-node README.md, docs/hosting-lofi-apps.md4+ ("Validation status"), src/errors.ts, src/native/loader.ts. -->5+6+The node's failure modes are designed to be nameable. Work from the signal you see.7+8+## Mesh: `unavailable`9+10+`status().mesh` reports `{ state: "unavailable", reason: … }` when the native transport could not11+load; the Jazz server still runs LAN-only and `ticket()`/`pair()` throw `MeshUnavailableError`12+rather than degrade silently. Causes, in resolution order:13+14+1. An explicit `LOFI_NODE_IROH` path that doesn't exist.15+2. Running from source without building the native layer (`cargo build --release` in16+ `native/iroh-js`).17+3. An unsupported platform — most notably **Windows**, a documented gap (the `*-gnu` build path18+ needs a `libnode.dll` import library the upstream toolchain ships only for msvc). Windows nodes19+ run LAN-only until that resolves.20+21+The compiled binary embeds digest-pinned prebuilts for macOS arm64/x86_64 and Linux x86_64/aarch64;22+a cache extraction that doesn't match its pin fails loudly rather than loading.23+24+## 401 `invalid_ticket`25+26+Unknown ticket, revoked ticket, or a sync-scoped ticket on an admin route — deliberately one answer27+for all three. If a previously working app starts seeing it, the ticket was revoked: re-enroll with28+a fresh one. If provisioning sees it, the ticket lacks `provision` scope.29+30+## WebSocket closes with 400131+32+The ticket was revoked mid-session. This is the live-revocation path working as designed; the app33+should treat its stored sync location as dead and surface re-enrollment, not retry.34+35+## 502 `store_unavailable`36+37+The gate is up but its store is not — the loopback Jazz process, or on a leaf, the tunnel to the38+root. Check `lofi-node status` and, for a leaf, `status().mesh.connections`.39+40+## Writes hang; nothing errors41+42+Almost always a store with **no deployed schema** — the engine hangs rather than fails in this43+state. `GET <ticket-url>/store-status` reporting `{ "deployed": false }` confirms it; the remedy is44+[provisioning](provision-a-store.md). lofi apps classify before attaching sync for exactly this45+reason.46+47+## Version mismatch48+49+A node must run the **same Jazz alpha** as the apps it serves. Mismatches surface as protocol-level50+failures (schema hash and catalogue errors) that no network configuration fixes. Confirm both pins51+before debugging anything else.52+53+## Compatibility status, honestly54+55+The whole flow is validated end to end against lofi's reference app with real browser clients:56+build, boot, offline edits, reconvergence, and the full provisioning lifecycle through the gate and57+through an iroh-paired leaf. One known upstream finding: lofi's opt-in browser convergence gate58+fails at its final stage with a catalogue error **identically against lofi-node and against lofi's59+own first-party dev server** — a controlled experiment placing the issue in the pinned Jazz alpha,60+not the node. lofi-node's contract docs carry the current validation table.@@ -5,8 +5,10 @@ makes sync available; it does not automatically upload every user's local data.
55 back up and sync from the generated `AccountGate` UI.66 77 There are two ways a device gets a sync location: the deployment can compile one in (a managed Jazz8-app — the _default sink_), or the user can declare one at runtime by enrolling a ticket from a9-self-hosted node. Both feed the same election flow.8+app — the _default sink_), or the user can declare one at runtime by enrolling a ticket from a node9+**they** run. That second path is an end-user capability, not app hosting — your deployment does not10+change. What the user runs on their side (one binary, tickets, optional node-to-node replication) is11+covered in the [lofi-node section](/node).1012 1113 ## Provision a managed Jazz app1214 @@ -3,9 +3,34 @@
33 * into `website/api-gen/`. One page per entry in { entrypoints}, with the44 * six CLI command wrappers sharing a single page. Run via `deno task site:api`.55 */6-import { entrypoints } from "./entrypoints.ts";7-8-const OUT_DIR = "website/api-gen";6+import { type Entrypoint, entrypoints } from "./entrypoints.ts";7+8+// `--node` renders the lofi-node API instead: same renderer, sourced from the9+// pinned checkout of FelineStateMachine/lofi-node (LOFI_NODE_DIR, default10+// ../lofi-node) so pages match the ref the /node docs render from; the11+// package itself is published as jsr:@nzip/lofi-node.12+const NODE_MODE = Deno.args.includes("--node");13+const NODE_DIR = NODE_MODE ? (Deno.env.get("LOFI_NODE_DIR") ?? "../lofi-node") : "";14+15+const nodeEntrypoints: readonly Entrypoint[] = [16+ {17+ jsrName: ".",18+ file: `${NODE_DIR}/mod.ts`,19+ page: "node",20+ title: "Sync node",21+ },22+ {23+ jsrName: "./testing",24+ file: `${NODE_DIR}/testing/mod.ts`,25+ page: "testing",26+ title: "Testing",27+ },28+];29+30+const activeEntrypoints = NODE_MODE ? nodeEntrypoints : entrypoints;31+const packageName = NODE_MODE ? "@nzip/lofi-node" : "@nzip/lofi";32+33+const OUT_DIR = NODE_MODE ? "website/node-api-gen" : "website/api-gen";934 1035 type JsDocTag = {1136 kind: string;@@ -22,8 +47,14 @@ type TsType = {
2247 // deno-lint-ignore no-explicit-any2348 [key: string]: any;2449 };25-// deno-lint-ignore no-explicit-any26-type Param = { kind: string; name?: string; optional?: boolean; tsType?: TsType; [k: string]: any };50+type Param = {51+ kind: string;52+ name?: string;53+ optional?: boolean;54+ tsType?: TsType;55+ // deno-lint-ignore no-explicit-any56+ [k: string]: any;57+};2758 type TypeParam = { name: string; constraint?: TsType; default?: TsType };2859 type Declaration = {2960 kind: string;@@ -89,7 +120,9 @@ function printType(t: TsType | undefined | null): string {
89120 const v = t.value ?? {};90121 const parts: string[] = [];91122 for (const sig of v.indexSignatures ?? []) {92- parts.push(`[${(sig.params ?? []).map(printParam).join(", ")}]: ${printType(sig.tsType)}`);123+ parts.push(124+ `[${(sig.params ?? []).map(printParam).join(", ")}]: ${printType(sig.tsType)}`,125+ );93126 }94127 for (const p of v.properties ?? []) {95128 parts.push(@@ -188,10 +221,14 @@ function classSignature(name: string, def: any): string {
188221 const lines: string[] = [];189222 let head = `class ${name}${printTypeParams(def?.typeParams ?? [])}`;190223 if (def?.extends) head += ` extends ${def.extends}`;191- if (def?.implements?.length) head += ` implements ${def.implements.map(printType).join(", ")}`;224+ if (def?.implements?.length) {225+ head += ` implements ${def.implements.map(printType).join(", ")}`;226+ }192227 lines.push(`${head} {`);193228 for (const ctor of def?.constructors ?? []) {194- lines.push(` constructor(${(ctor.params ?? []).map(printParam).join(", ")});`);229+ lines.push(230+ ` constructor(${(ctor.params ?? []).map(printParam).join(", ")});`,231+ );195232 }196233 for (const prop of def?.properties ?? []) {197234 const mods = [@@ -222,7 +259,9 @@ function classSignature(name: string, def: any): string {
222259 function interfaceSignature(name: string, def: any): string {223260 const lines: string[] = [];224261 let head = `interface ${name}${printTypeParams(def?.typeParams ?? [])}`;225- if (def?.extends?.length) head += ` extends ${def.extends.map(printType).join(", ")}`;262+ if (def?.extends?.length) {263+ head += ` extends ${def.extends.map(printType).join(", ")}`;264+ }226265 lines.push(`${head} {`);227266 for (const prop of def?.properties ?? []) {228267 lines.push(@@ -332,7 +371,9 @@ function renderJsDoc(jsDoc: JsDoc | undefined): string {
332371 out.push("| Parameter | Description |");333372 out.push("| --- | --- |");334373 for (const tag of params) {335- out.push(`| \`${tag.name}\` | ${renderJsDocText(tag.doc ?? "").replaceAll("\n", " ")} |`);374+ out.push(375+ `| \`${tag.name}\` | ${renderJsDocText(tag.doc ?? "").replaceAll("\n", " ")} |`,376+ );336377 }337378 }338379 for (const tag of tags) {@@ -364,14 +405,22 @@ async function docModule(file: string): Promise<ModuleNodes> {
364405 });365406 const result = await command.output();366407 if (!result.success) {367- throw new Error(`deno doc --json ${file} failed:\n${new TextDecoder().decode(result.stderr)}`);408+ throw new Error(409+ `deno doc --json ${file} failed:\n${new TextDecoder().decode(result.stderr)}`,410+ );368411 }369- const parsed = JSON.parse(new TextDecoder().decode(result.stdout)) as DocOutput;412+ const parsed = JSON.parse(413+ new TextDecoder().decode(result.stdout),414+ ) as DocOutput;370415 if (parsed.version !== 2) {371- throw new Error(`deno doc --json for ${file} returned schema v${parsed.version}; expected v2.`);416+ throw new Error(417+ `deno doc --json for ${file} returned schema v${parsed.version}; expected v2.`,418+ );372419 }373420 const modules = Object.values(parsed.nodes);374- if (modules.length === 0) throw new Error(`deno doc --json ${file} returned no modules.`);421+ if (modules.length === 0) {422+ throw new Error(`deno doc --json ${file} returned no modules.`);423+ }375424 return modules[0];376425 }377426 @@ -387,7 +436,11 @@ const KIND_LABEL: Record<string, string> = {
387436 388437 function renderSymbol(symbol: SymbolNode): string {389438 const out: string[] = [];390- const kinds = [...new Set(symbol.declarations.map((dec) => KIND_LABEL[dec.kind] ?? dec.kind))];439+ const kinds = [440+ ...new Set(441+ symbol.declarations.map((dec) => KIND_LABEL[dec.kind] ?? dec.kind),442+ ),443+ ];391444 // h2 keeps the heading hierarchy sequential under the page h1392445 out.push(`## ${symbol.name}`);393446 out.push(`<sup>${kinds.join(", ")}</sup>`);@@ -406,7 +459,8 @@ function renderSymbol(symbol: SymbolNode): string {
406459 function symbolSortKey(symbol: SymbolNode): [number, string] {407460 const kindRank =408461 symbol.declarations.some((dec) =>409- dec.kind === "function" || dec.kind === "class" || dec.kind === "variable"462+ dec.kind === "function" || dec.kind === "class" ||463+ dec.kind === "variable"410464 )411465 ? 0412466 : 1;@@ -415,7 +469,8 @@ function symbolSortKey(symbol: SymbolNode): [number, string] {
415469 416470 function moduleSummary(moduleDoc: JsDoc | undefined): string {417471 const doc = moduleDoc?.doc ?? "";418- const firstSentence = doc.split(/\n\s*\n/)[0]?.replaceAll("\n", " ").trim() ?? "";472+ const firstSentence = doc.split(/\n\s*\n/)[0]?.replaceAll("\n", " ").trim() ??473+ "";419474 return renderJsDocText(firstSentence);420475 }421476 @@ -425,16 +480,17 @@ type PageGroup = {
425480 entries: typeof entrypoints[number][];426481 };427482 428-const pageOrder = ["runtime", "astro", "access", "preact", "testing", "cli"];483+const pageOrder = NODE_MODE484+ ? ["node", "testing"]485+ : ["runtime", "astro", "access", "preact", "testing", "cli"];429486 const groups = new Map<string, PageGroup>();430-for (const entry of entrypoints) {487+for (const entry of activeEntrypoints) {431488 const group = groups.get(entry.page) ?? {432489 page: entry.page,433490 position: entry.page.startsWith("recipes/")434491 ? 1 +435- [...entrypoints.filter((e) => e.page.startsWith("recipes/"))].findIndex((e) =>436- e.page === entry.page437- )492+ [...activeEntrypoints.filter((e) => e.page.startsWith("recipes/"))]493+ .findIndex((e) => e.page === entry.page)438494 : 1 + pageOrder.indexOf(entry.page),439495 entries: [],440496 };@@ -442,12 +498,16 @@ for (const entry of entrypoints) {
442498 groups.set(entry.page, group);443499 }444500 445-const denoJson = JSON.parse(await Deno.readTextFile("deno.json")) as {501+const denoJson = JSON.parse(502+ await Deno.readTextFile(NODE_MODE ? `${NODE_DIR}/deno.json` : "deno.json"),503+) as {446504 version: string;447505 description: string;448506 };449507 450-await Deno.mkdir(`${OUT_DIR}/recipes`, { recursive: true });508+await Deno.mkdir(NODE_MODE ? OUT_DIR : `${OUT_DIR}/recipes`, {509+ recursive: true,510+});451511 452512 const indexRows: string[] = [];453513 let totalSymbols = 0;@@ -476,7 +536,9 @@ for (const group of groups.values()) {
476536 });477537 const isCommand = group.page === "cli";478538 if (!isCommand && symbols.length === 0) {479- throw new Error(`No exported symbols found for ${entry.file}; refusing to emit empty page.`);539+ throw new Error(540+ `No exported symbols found for ${entry.file}; refusing to emit empty page.`,541+ );480542 }481543 if (!module.module_doc?.doc && symbols.length === 0) {482544 throw new Error(`No module doc or symbols for ${entry.file}.`);@@ -484,15 +546,19 @@ for (const group of groups.values()) {
484546 totalSymbols += symbols.length;485547 486548 const jsrSpecifier = entry.jsrName === "."487- ? "@nzip/lofi"488- : `@nzip/lofi${entry.jsrName.slice(1)}`;549+ ? packageName550+ : `${packageName}${entry.jsrName.slice(1)}`;489551 if (isCommand) {490552 sections.push(`## ${entry.title}`);491553 sections.push(`\`\`\`sh\ndeno run -A jsr:${jsrSpecifier}\n\`\`\``);492- if (module.module_doc?.doc) sections.push(renderJsDocText(module.module_doc.doc.trim()));554+ if (module.module_doc?.doc) {555+ sections.push(renderJsDocText(module.module_doc.doc.trim()));556+ }493557 } else {494558 sections.push(`\`\`\`ts\nimport * as mod from "jsr:${jsrSpecifier}";\n\`\`\``);495- if (module.module_doc?.doc) sections.push(renderJsDocText(module.module_doc.doc.trim()));559+ if (module.module_doc?.doc) {560+ sections.push(renderJsDocText(module.module_doc.doc.trim()));561+ }496562 sections.push(...symbols.map(renderSymbol));497563 }498564 indexRows.push(@@ -512,16 +578,21 @@ for (const group of groups.values()) {
512578 await Deno.writeTextFile(`${OUT_DIR}/${group.page}.md`, body);513579 }514580 581+const indexIntro = NODE_MODE582+ ? `Generated from the JSDoc of [\`@nzip/lofi-node\`](https://jsr.io/@nzip/lofi-node) v${denoJson.version}.583+${denoJson.description}`584+ : `Generated from the JSDoc of [\`@nzip/lofi\`](https://jsr.io/@nzip/lofi) v${denoJson.version}.585+${denoJson.description}`;586+515587 const index = `---516588 title: "API reference"517589 sidebar_position: 0518590 slug: /519591 ---520592 521-# API reference593+# ${NODE_MODE ? "lofi-node API reference" : "API reference"}522594 523-Generated from the JSDoc of [\`@nzip/lofi\`](https://jsr.io/@nzip/lofi) v${denoJson.version}.524-${denoJson.description}595+${indexIntro}525596 526597 | Export | Summary |527598 | --- | --- |@@ -529,16 +600,18 @@ ${indexRows.join("\n")}
529600 `;530601 await Deno.writeTextFile(`${OUT_DIR}/index.md`, index);531602 532-await Deno.writeTextFile(533- `${OUT_DIR}/recipes/_category_.json`,534- `${535- JSON.stringify(536- { label: "Recipes", position: 8, collapsed: true },537- null,538- 2,539- )540- }\n`,541-);603+if (!NODE_MODE) {604+ await Deno.writeTextFile(605+ `${OUT_DIR}/recipes/_category_.json`,606+ `${607+ JSON.stringify(608+ { label: "Recipes", position: 8, collapsed: true },609+ null,610+ 2,611+ )612+ }\n`,613+ );614+}542615 543616 console.log(544617 `Generated ${groups.size + 1} API pages (${totalSymbols} exported symbols) into ${OUT_DIR}/.`,@@ -12,7 +12,9 @@ async function collectMarkdown(directory: string): Promise<void> {
1212 for await (const entry of Deno.readDir(directory)) {1313 const path = resolve(directory, entry.name);1414 if (entry.isDirectory) await collectMarkdown(path);15- else if (entry.isFile && entry.name.endsWith(".md")) markdownFiles.push(path);15+ else if (entry.isFile && entry.name.endsWith(".md")) {16+ markdownFiles.push(path);17+ }1618 }1719 }1820 @@ -25,6 +27,10 @@ for (const markdown of markdownFiles) {
2527 for (const match of source.matchAll(linkPattern)) {2628 const raw = match[1].replace(/^<|>$/g, "");2729 if (raw.startsWith("#") || /^[a-z][a-z0-9+.-]*:/i.test(raw)) continue;30+ // Site-absolute routes (e.g. /node/docs/app-ticket) point at deployed31+ // pages, some assembled at build time; the Docusaurus build validates32+ // them via onBrokenLinks, not the repository tree.33+ if (raw.startsWith("/")) continue;2834 const target = decodeURIComponent(raw.split("#", 1)[0]);2935 if (!target) continue;3036 try {@@ -37,7 +43,9 @@ for (const markdown of markdownFiles) {
3743 }3844 3945 if (broken.length > 0) {40- console.error(`Broken local Markdown links:\n${broken.map((link) => `- ${link}`).join("\n")}`);46+ console.error(47+ `Broken local Markdown links:\n${broken.map((link) => `- ${link}`).join("\n")}`,48+ );4149 Deno.exit(1);4250 }4351 @@ -0,0 +1,115 @@
1+/**2+ * Assembles the `/node/docs` section into `website/node-docs-gen/` from two3+ * sources, per `website/node-docs-manifest.json`:4+ *5+ * - `docs/node/*.md` in this repo — site-voice tutorials, guides, and6+ * reference pages (each names its lofi-node source material inline).7+ * - Contract pages from a lofi-node checkout (`source` values prefixed8+ * `lofi-node:`) — rendered verbatim with a provenance header and relative9+ * links rewritten to the pinned checkout on GitHub, so the source of truth10+ * stays in the lofi-node repo where its CI gates it.11+ *12+ * The checkout location comes from `LOFI_NODE_DIR` (default `../lofi-node`);13+ * the ref used in provenance links comes from `LOFI_NODE_REF` (default: the14+ * checkout's current commit). Run via `deno task site:node`.15+ */16+17+const OUT_DIR = "website/node-docs-gen";18+19+type ManifestItem = { id: string; label: string; source: string };20+type ManifestSection = { label: string | null; items: ManifestItem[] };21+type Manifest = { checkoutRepo: string; sections: ManifestSection[] };22+23+const manifest = JSON.parse(24+ await Deno.readTextFile("website/node-docs-manifest.json"),25+) as Manifest;26+27+const checkoutDir = Deno.env.get("LOFI_NODE_DIR") ?? "../lofi-node";28+29+async function checkoutRef(): Promise<string> {30+ const configured = Deno.env.get("LOFI_NODE_REF");31+ if (configured) return configured;32+ try {33+ const result = await new Deno.Command("git", {34+ args: ["-C", checkoutDir, "rev-parse", "HEAD"],35+ stdout: "piped",36+ stderr: "null",37+ }).output();38+ const ref = new TextDecoder().decode(result.stdout).trim();39+ if (ref) return ref;40+ } catch {41+ // Fall through to the branch default below.42+ }43+ return "main";44+}45+46+const ref = await checkoutRef();47+48+/** Rewrites a contract page's relative links to the pinned checkout on GitHub. */49+function rewriteContractLinks(markdown: string, sourceDir: string): string {50+ return markdown.replace(51+ /\]\((?!https?:|\/|#)([^)]+)\)/g,52+ (_match, target: string) =>53+ `](https://github.com/${manifest.checkoutRepo}/blob/${ref}/${sourceDir}/${target})`,54+ );55+}56+57+function provenanceHeader(sourcePath: string): string {58+ const url = `https://github.com/${manifest.checkoutRepo}/blob/${ref}/${sourcePath}`;59+ return `:::info[Contract page]\nRendered from [\`${sourcePath}\`](${url}) in the ` +60+ `\`${manifest.checkoutRepo}\` repository at the site's pinned ref — that file is the ` +61+ `source of truth, gated by lofi-node's own CI.\n:::\n\n`;62+}63+64+// Docusaurus derives ids from file paths; the manifest is authoritative, so a65+// page that exists on disk but not in the manifest (or vice versa) is drift.66+const manifestItems = manifest.sections.flatMap((section) => section.items);67+68+await Deno.remove(OUT_DIR, { recursive: true }).catch(() => undefined);69+await Deno.mkdir(OUT_DIR, { recursive: true });70+71+let assembled = 0;72+for (const item of manifestItems) {73+ let markdown: string;74+ if (item.source.startsWith("lofi-node:")) {75+ const relative = item.source.slice("lofi-node:".length);76+ const path = `${checkoutDir}/${relative}`;77+ try {78+ markdown = await Deno.readTextFile(path);79+ } catch {80+ console.error(81+ `Missing lofi-node checkout file ${path}. Set LOFI_NODE_DIR to a checkout of ` +82+ `${manifest.checkoutRepo} (default ../lofi-node).`,83+ );84+ Deno.exit(1);85+ }86+ const sourceDir = relative.split("/").slice(0, -1).join("/");87+ markdown = provenanceHeader(relative) + rewriteContractLinks(markdown, sourceDir);88+ } else {89+ try {90+ markdown = await Deno.readTextFile(item.source);91+ } catch {92+ console.error(`Manifest names missing page source ${item.source} (id "${item.id}").`);93+ Deno.exit(1);94+ }95+ }96+ await Deno.writeTextFile(`${OUT_DIR}/${item.id}.md`, markdown);97+ assembled++;98+}99+100+// Site-voice pages not named by the manifest would silently miss the sidebar101+// and the llms corpus — treat them as drift.102+for await (const entry of Deno.readDir("docs/node")) {103+ if (!entry.isFile || !entry.name.endsWith(".md")) continue;104+ const source = `docs/node/${entry.name}`;105+ if (!manifestItems.some((item) => item.source === source)) {106+ console.error(`docs/node/${entry.name} is not listed in website/node-docs-manifest.json.`);107+ Deno.exit(1);108+ }109+}110+111+console.log(112+ `Assembled ${assembled} /node/docs pages into ${OUT_DIR}/ (lofi-node checkout: ${checkoutDir} @ ${113+ ref.slice(0, 12)114+ }).`,115+);@@ -66,7 +66,12 @@ for (const section of manifest.sections) {
6666 6767 // API pages in sidebar order: index first, then top-level pages by6868 // sidebar_position, then recipes.69-type ApiEntry = { title: string; url: string; sourcePath: string; position: number };69+type ApiEntry = {70+ title: string;71+ url: string;72+ sourcePath: string;73+ position: number;74+};7075 const apiEntries: ApiEntry[] = [];7176 async function collectApiDir(dir: string, urlPrefix: string): Promise<void> {7277 for await (const entry of Deno.readDir(dir)) {@@ -78,8 +83,10 @@ async function collectApiDir(dir: string, urlPrefix: string): Promise<void> {
7883 const sourcePath = `${dir}/${entry.name}`;7984 const markdown = await Deno.readTextFile(sourcePath);8085 const stem = entry.name.slice(0, -".md".length);81- const position = Number(markdown.match(/^sidebar_position:\s*(\d+)$/m)?.[1] ?? 99);82- const url = stem === "index" ? `${siteUrl}/api` : `${urlPrefix}/${stem}`;86+ const position = Number(87+ markdown.match(/^sidebar_position:\s*(\d+)$/m)?.[1] ?? 99,88+ );89+ const url = stem === "index" ? urlPrefix : `${urlPrefix}/${stem}`;8390 apiEntries.push({8491 title: firstHeading(markdown) ?? stem,8592 url,@@ -96,7 +103,9 @@ try {
96103 Deno.exit(1);97104 }98105 if (apiEntries.length === 0) {99- console.error(`No API pages found in ${API_DIR}; run \`deno task site:api\` first.`);106+ console.error(107+ `No API pages found in ${API_DIR}; run \`deno task site:api\` first.`,108+ );100109 Deno.exit(1);101110 }102111 apiEntries.sort((a, b) => a.position - b.position || a.url.localeCompare(b.url));@@ -110,6 +119,8 @@ const shortLines: string[] = [
110119 `lofi is published on JSR as \`@nzip/lofi\` (v${denoJson.version}).`,111120 `This file indexes the documentation deployed at ${siteUrl} for this exact version.`,112121 `The full documentation corpus is available at ${siteUrl}/llms-full.txt.`,122+ `lofi-node (user-owned sync) is indexed separately at ${siteUrl}/node/llms.txt` +123+ ` (full corpus: ${siteUrl}/node/llms-full.txt).`,113124 "",114125 ];115126 for (const section of sections) {@@ -139,14 +150,118 @@ const allDocs: DocEntry[] = [
139150 ...apiEntries,140151 ];141152 for (const doc of allDocs) {142- const markdown = stripFrontmatter(await Deno.readTextFile(doc.sourcePath)).trim();153+ const markdown = stripFrontmatter(await Deno.readTextFile(doc.sourcePath))154+ .trim();143155 fullParts.push("---", "", `<!-- Source: ${doc.url} -->`, "", markdown, "");144156 }145157 146158 await Deno.writeTextFile(`${OUT_DIR}/llms.txt`, shortLines.join("\n"));147159 await Deno.writeTextFile(`${OUT_DIR}/llms-full.txt`, fullParts.join("\n"));160+const mainApiCount = apiEntries.length;161+162+// The node-scoped pair: agents enrolling an app against a self-hosted node163+// should not have to ingest the full framework corpus, and vice versa. Pages164+// come from the assembled /node/docs set (tools/node_docs.ts) so contract165+// pages rendered from the lofi-node checkout are included verbatim.166+const NODE_DOCS_DIR = "website/node-docs-gen";167+const NODE_API_DIR = "website/node-api-gen";168+169+const nodeManifest = JSON.parse(170+ await Deno.readTextFile("website/node-docs-manifest.json"),171+) as Manifest;172+173+function nodeDocUrl(id: string): string {174+ return id === "README" ? `${siteUrl}/node/docs` : `${siteUrl}/node/docs/${id}`;175+}176+177+const nodeSections: Section[] = [];178+for (const section of nodeManifest.sections) {179+ const docs: DocEntry[] = [];180+ for (const item of section.items) {181+ const sourcePath = `${NODE_DOCS_DIR}/${item.id}.md`;182+ let markdown: string;183+ try {184+ markdown = await Deno.readTextFile(sourcePath);185+ } catch {186+ console.error(187+ `Missing ${sourcePath}; run \`deno task site:node\` first.`,188+ );189+ Deno.exit(1);190+ }191+ docs.push({192+ title: firstHeading(markdown) ?? item.label,193+ url: nodeDocUrl(item.id),194+ sourcePath,195+ });196+ }197+ nodeSections.push({ label: section.label ?? "Documentation", docs });198+}199+200+apiEntries.length = 0;201+try {202+ await collectApiDir(NODE_API_DIR, `${siteUrl}/node/api`);203+} catch (error) {204+ if (!(error instanceof Deno.errors.NotFound)) throw error;205+ console.error(206+ `Missing ${NODE_API_DIR}; run \`deno task site:api:node\` first.`,207+ );208+ Deno.exit(1);209+}210+const nodeApiEntries = [...apiEntries];211+nodeApiEntries.sort((a, b) => a.position - b.position || a.url.localeCompare(b.url));148212 149-const docCount = sections.reduce((sum, section) => sum + section.docs.length, 0);213+const nodeShort: string[] = [214+ "# lofi-node",215+ "",216+ "> The node a lofi-app user runs to own where their data syncs: one daemon embedding a Jazz sync server, iroh node-to-node transport, and ticket-gated access.",217+ "",218+ `This file indexes the lofi-node documentation deployed at ${siteUrl}/node.`,219+ `The full corpus is available at ${siteUrl}/node/llms-full.txt; the lofi framework itself is indexed at ${siteUrl}/llms.txt.`,220+ "",221+];222+for (const section of nodeSections) {223+ nodeShort.push(`## ${section.label}`, "");224+ for (const doc of section.docs) {225+ nodeShort.push(`- [${doc.title}](${doc.url})`);226+ }227+ nodeShort.push("");228+}229+nodeShort.push("## API reference", "");230+for (const entry of nodeApiEntries) {231+ nodeShort.push(`- [${entry.title}](${entry.url})`);232+}233+nodeShort.push("");234+235+const nodeFull: string[] = [236+ "# lofi-node documentation",237+ "",238+ `> Deployed at ${siteUrl}/node. Source: https://github.com/FelineStateMachine/lofi-node`,239+ "",240+];241+for (242+ const doc of [243+ ...nodeSections.flatMap((section) => section.docs),244+ ...nodeApiEntries,245+ ]246+) {247+ const markdown = stripFrontmatter(await Deno.readTextFile(doc.sourcePath))248+ .trim();249+ nodeFull.push("---", "", `<!-- Source: ${doc.url} -->`, "", markdown, "");250+}251+252+await Deno.mkdir(`${OUT_DIR}/node`, { recursive: true });253+await Deno.writeTextFile(`${OUT_DIR}/node/llms.txt`, nodeShort.join("\n"));254+await Deno.writeTextFile(`${OUT_DIR}/node/llms-full.txt`, nodeFull.join("\n"));255+256+const docCount = sections.reduce(257+ (sum, section) => sum + section.docs.length,258+ 0,259+);260+const nodeDocCount = nodeSections.reduce(261+ (sum, section) => sum + section.docs.length,262+ 0,263+);150264 console.log(151- `Generated llms.txt (${docCount} docs + ${apiEntries.length} API pages) and llms-full.txt into ${OUT_DIR}/.`,265+ `Generated llms.txt (${docCount} docs + ${mainApiCount} API pages), node/llms.txt ` +266+ `(${nodeDocCount} docs + ${nodeApiEntries.length} API pages), and the -full pair into ${OUT_DIR}/.`,152267 );@@ -1,4 +1,5 @@
11 import { readFileSync } from "node:fs";2+import { fileURLToPath } from "node:url";23 import { themes as prismThemes } from "prism-react-renderer";34 import type { Config } from "@docusaurus/types";45 import type * as Preset from "@docusaurus/preset-classic";@@ -9,6 +10,32 @@ const denoJson = JSON.parse(
910 readFileSync(new URL("../deno.json", import.meta.url), "utf8"),1011 ) as { version: string; description: string };1112 13+// The /node landing shows the lofi-node version from the same pinned checkout14+// the docs render from (LOFI_NODE_DIR, default ../lofi-node); "alpha" when the15+// checkout is absent so a docs-only environment still builds.16+let lofiNodeVersion = "alpha";17+try {18+ const nodeDir = process.env.LOFI_NODE_DIR ?? "../lofi-node";19+ lofiNodeVersion = (JSON.parse(20+ readFileSync(new URL(`../${nodeDir}/deno.json`, import.meta.url), "utf8"),21+ ) as { version: string }).version;22+} catch {23+ // Keep the fallback label.24+}25+26+// Fonts must ship as files, not base64 in the render-blocking stylesheet —27+// webpack's default inline threshold would bloat styles.css by hundreds of KB.28+function noInlineFontsPlugin() {29+ return {30+ name: "lofi-no-inline-fonts",31+ configureWebpack: () => ({32+ module: {33+ rules: [{ test: /\.(?:woff2?|eot|ttf|otf)$/i, type: "asset/resource" }],34+ },35+ }),36+ };37+}38+1239 const config: Config = {1340 title: "lofi",1441 tagline: "Put down roots. Skip the spinner.",@@ -28,6 +55,7 @@ const config: Config = {
2855 customFields: {2956 lofiVersion: denoJson.version,3057 lofiDescription: denoJson.description,58+ lofiNodeVersion,3159 },3260 3361 markdown: {@@ -49,8 +77,8 @@ const config: Config = {
4977 {5078 hashed: true,5179 indexBlog: false,52- docsRouteBasePath: ["docs", "api"],53- docsDir: ["../docs", "api-gen"],80+ docsRouteBasePath: ["docs", "api", "node/docs", "node/api"],81+ docsDir: ["../docs", "api-gen", "node-docs-gen", "node-api-gen"],5482 },5583 ],5684 ],@@ -60,25 +88,46 @@ const config: Config = {
6088 locales: ["en"],6189 },6290 63- headTags: [64- {65- tagName: "link",66- attributes: { rel: "preconnect", href: "https://fonts.googleapis.com" },67- },68- {69- tagName: "link",70- attributes: {71- rel: "preconnect",72- href: "https://fonts.gstatic.com",73- crossorigin: "anonymous",74- },75- },76- ],77- stylesheets: [78- "https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,400..700;1,9..144,400..700&family=Nunito:wght@800;900&family=Karla:wght@400;500;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap",79- ],91+ // Fonts are self-hosted via Fontsource imports in src/css/custom.css, so92+ // the docs work offline with their real faces and no third-party requests.8093 8194 plugins: [95+ noInlineFontsPlugin,96+ [97+ // The docs for the offline-first framework install and read offline.98+ // Precaching is opt-in (installed PWA / standalone / ?offlineMode) so99+ // drive-by visitors are not handed the whole corpus.100+ "@docusaurus/plugin-pwa",101+ {102+ // trailingSlash:false emits routes as <path>.html, which the default103+ // worker never checks; see src/sw-custom.js.104+ swCustom: fileURLToPath(new URL("./src/sw-custom.js", import.meta.url)),105+ offlineModeActivationStrategies: [106+ "appInstalled",107+ "standalone",108+ "queryString",109+ ],110+ pwaHead: [111+ { tagName: "link", rel: "manifest", href: "/manifest.json" },112+ { tagName: "meta", name: "theme-color", content: "#18231f" },113+ {114+ tagName: "meta",115+ name: "apple-mobile-web-app-capable",116+ content: "yes",117+ },118+ {119+ tagName: "meta",120+ name: "apple-mobile-web-app-status-bar-style",121+ content: "black-translucent",122+ },123+ {124+ tagName: "link",125+ rel: "apple-touch-icon",126+ href: "/img/pwa/icon-192.png",127+ },128+ ],129+ },130+ ],82131 [83132 "@docusaurus/plugin-content-docs",84133 {@@ -89,6 +138,29 @@ const config: Config = {
89138 editUrl: undefined,90139 },91140 ],141+ [142+ "@docusaurus/plugin-content-docs",143+ {144+ // Assembled by tools/node_docs.ts from docs/node/ plus contract pages145+ // rendered out of the pinned lofi-node checkout; no edit URL because146+ // pages have two possible homes — each page names its source.147+ id: "node",148+ path: "node-docs-gen",149+ routeBasePath: "node/docs",150+ sidebarPath: "./sidebars-node.ts",151+ editUrl: undefined,152+ },153+ ],154+ [155+ "@docusaurus/plugin-content-docs",156+ {157+ id: "node-api",158+ path: "node-api-gen",159+ routeBasePath: "node/api",160+ sidebarPath: "./sidebars-node-api.ts",161+ editUrl: undefined,162+ },163+ ],92164 ],93165 94166 presets: [@@ -106,6 +178,8 @@ const config: Config = {
106178 "devx-contract.md",107179 "seed.md",108180 "assets/**",181+ // Served under /node/docs by the "node" plugin instance instead.182+ "node/**",109183 "**/_*.{js,jsx,ts,tsx,md,mdx}",110184 "**/_*/**",111185 ],@@ -169,50 +243,65 @@ const config: Config = {
169243 respectPrefersColorScheme: true,170244 },171245 navbar: {246+ // Two trees, no wordmark: the site now fronts two products, and each247+ // product's own dropdown carries its links (so no top-level GitHub/JSR).172248 logo: {173249 alt: "lofi",174- src: "img/lofi-wordmark.svg",175- // intrinsic dimensions (800x360 viewBox at the navbar's 1.7rem height)176- // so the image never contributes layout shift177- width: 60,178- height: 27,250+ src: "img/two-trees.svg",251+ // square intrinsic dimensions at the navbar's 1.7rem height so the252+ // image never contributes layout shift253+ width: 30,254+ height: 30,179255 },180256 items: [181- { type: "docSidebar", sidebarId: "docs", position: "left", label: "Docs" },182- { to: "/api", label: "API", position: "left" },183- { href: "https://jsr.io/@nzip/lofi", label: "JSR", position: "right" },184257 {185- href: "https://github.com/FelineStateMachine/lofi",186- label: "GitHub",187- position: "right",258+ type: "dropdown",259+ label: "lofi",260+ position: "left",261+ to: "/",262+ items: [263+ { label: "docs", to: "/docs" },264+ { label: "api", to: "/api" },265+ { label: "github", href: "https://github.com/FelineStateMachine/lofi" },266+ { label: "jsr", href: "https://jsr.io/@nzip/lofi" },267+ ],268+ },269+ {270+ type: "dropdown",271+ label: "lofi-node",272+ position: "left",273+ to: "/node",274+ items: [275+ { label: "docs", to: "/node/docs" },276+ { label: "api", to: "/node/api" },277+ { label: "github", href: "https://github.com/FelineStateMachine/lofi-node" },278+ { label: "jsr", href: "https://jsr.io/@nzip/lofi-node" },279+ ],188280 },189281 ],190282 },191283 footer: {192284 style: "dark",285+ // One column per product, parallel structure, lowercase like the nav.193286 links: [194287 {195- title: "Start",288+ title: "lofi",196289 items: [197- { label: "Getting started", to: "/docs/getting-started" },198- { label: "Data and UI", to: "/docs/data-and-ui" },199- { label: "Testing", to: "/docs/testing" },200- ],201- },202- {203- title: "Deeper",204- items: [205- { label: "Permissions", to: "/docs/permissions" },206- { label: "Sync and recovery", to: "/docs/sync-and-recovery" },207- { label: "API reference", to: "/api" },290+ { label: "docs", to: "/docs" },291+ { label: "api", to: "/api" },292+ { label: "github", href: "https://github.com/FelineStateMachine/lofi" },293+ { label: "jsr", href: "https://jsr.io/@nzip/lofi" },294+ { label: "llms.txt", to: "/llms.txt", target: "_blank" },208295 ],209296 },210297 {211- title: "Repo",298+ title: "lofi-node",212299 items: [213- { label: "GitHub", href: "https://github.com/FelineStateMachine/lofi" },214- { label: "JSR", href: "https://jsr.io/@nzip/lofi" },215- { label: "llms.txt", to: "/llms.txt", target: "_blank" },300+ { label: "docs", to: "/node/docs" },301+ { label: "api", to: "/node/api" },302+ { label: "github", href: "https://github.com/FelineStateMachine/lofi-node" },303+ { label: "jsr", href: "https://jsr.io/@nzip/lofi-node" },304+ { label: "llms.txt", to: "/node/llms.txt", target: "_blank" },216305 ],217306 },218307 ],@@ -0,0 +1,90 @@
1+{2+ "siteUrl": "https://lofi.host",3+ "checkoutRepo": "FelineStateMachine/lofi-node",4+ "sections": [5+ {6+ "label": null,7+ "items": [8+ { "id": "README", "label": "Overview", "source": "docs/node/README.md" }9+ ]10+ },11+ {12+ "label": "Tutorials",13+ "collapsed": false,14+ "items": [15+ {16+ "id": "self-host-a-sync-node",17+ "label": "Self-host your first sync node",18+ "source": "docs/node/self-host-a-sync-node.md"19+ },20+ {21+ "id": "pair-two-homes",22+ "label": "Pair two homes",23+ "source": "docs/node/pair-two-homes.md"24+ },25+ {26+ "id": "provision-a-store",27+ "label": "Provision a store",28+ "source": "docs/node/provision-a-store.md"29+ }30+ ]31+ },32+ {33+ "label": "Guides",34+ "collapsed": false,35+ "items": [36+ {37+ "id": "sliceable-apps-and-shared-stores",38+ "label": "Sliceable apps and shared stores",39+ "source": "docs/node/sliceable-apps-and-shared-stores.md"40+ },41+ {42+ "id": "tickets-explained",43+ "label": "Tickets explained",44+ "source": "docs/node/tickets-explained.md"45+ },46+ {47+ "id": "storage-choices",48+ "label": "Storage choices",49+ "source": "docs/node/storage-choices.md"50+ },51+ {52+ "id": "beyond-the-lan",53+ "label": "Deploying beyond the LAN",54+ "source": "docs/node/beyond-the-lan.md"55+ }56+ ]57+ },58+ {59+ "label": "Reference",60+ "items": [61+ { "id": "cli", "label": "CLI", "source": "docs/node/cli.md" },62+ {63+ "id": "configuration",64+ "label": "Configuration",65+ "source": "docs/node/configuration.md"66+ },67+ {68+ "id": "app-ticket",69+ "label": "App-ticket contract",70+ "source": "lofi-node:docs/app-ticket.md"71+ },72+ {73+ "id": "http-surface",74+ "label": "HTTP surface",75+ "source": "docs/node/http-surface.md"76+ },77+ {78+ "id": "architecture",79+ "label": "Architecture and provenance",80+ "source": "docs/node/architecture.md"81+ },82+ {83+ "id": "troubleshooting",84+ "label": "Troubleshooting and compatibility",85+ "source": "docs/node/troubleshooting.md"86+ }87+ ]88+ }89+ ]90+}@@ -10,9 +10,18 @@
1010 "dependencies": {1111 "@docusaurus/core": "3.10.2",1212 "@docusaurus/faster": "3.10.2",13+ "@docusaurus/plugin-pwa": "^3.10.2",1314 "@docusaurus/preset-classic": "3.10.2",1415 "@docusaurus/theme-mermaid": "^3.10.2",1516 "@easyops-cn/docusaurus-search-local": "^0.55.2",17+ "@fontsource/alegreya-sans": "^5.2.8",18+ "@fontsource/comfortaa": "^5.2.8",19+ "@fontsource/grenze-gotisch": "^5.2.8",20+ "@fontsource/ibm-plex-mono": "^5.2.7",21+ "@fontsource/karla": "^5.2.8",22+ "@fontsource/nunito": "^5.2.7",23+ "@fontsource/spline-sans-mono": "^5.2.8",24+ "@fontsource/young-serif": "^5.2.7",1625 "@mdx-js/react": "^3.0.0",1726 "clsx": "^2.0.0",1827 "prism-react-renderer": "^2.3.0",@@ -291,6 +300,22 @@
291300 "url": "https://github.com/sponsors/antfu"292301 }293302 },303+ "node_modules/@apideck/better-ajv-errors": {304+ "version": "0.3.7",305+ "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.7.tgz",306+ "integrity": "sha512-TajUJwGWbDwkCx/CZi7tRE8PVB7simCvKJfHUsSdvps+aTM/PDPP4gkLmKnc+x3CE//y9i/nj74GqdL/hwk7Iw==",307+ "license": "MIT",308+ "dependencies": {309+ "jsonpointer": "^5.0.1",310+ "leven": "^3.1.0"311+ },312+ "engines": {313+ "node": ">=10"314+ },315+ "peerDependencies": {316+ "ajv": ">=8"317+ }318+ },294319 "node_modules/@babel/code-frame": {295320 "version": "7.29.7",296321 "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",@@ -3877,6 +3902,54 @@
38773902 "react-dom": "^18.0.0 || ^19.0.0"38783903 }38793904 },3905+ "node_modules/@docusaurus/plugin-pwa": {3906+ "version": "3.10.2",3907+ "resolved": "https://registry.npmjs.org/@docusaurus/plugin-pwa/-/plugin-pwa-3.10.2.tgz",3908+ "integrity": "sha512-pJUaEZnIIzJdIvDCVENLS4ER5DnT+VJYXU6TYCsshUUg9FHAYrkAWbsldsv4u769FtHRUjO7TWjWsS0B8+RO9g==",3909+ "license": "MIT",3910+ "dependencies": {3911+ "@babel/core": "^7.25.9",3912+ "@babel/preset-env": "^7.25.9",3913+ "@docusaurus/bundler": "3.10.2",3914+ "@docusaurus/core": "3.10.2",3915+ "@docusaurus/logger": "3.10.2",3916+ "@docusaurus/theme-common": "3.10.2",3917+ "@docusaurus/theme-translations": "3.10.2",3918+ "@docusaurus/types": "3.10.2",3919+ "@docusaurus/utils": "3.10.2",3920+ "@docusaurus/utils-validation": "3.10.2",3921+ "babel-loader": "^9.2.1",3922+ "clsx": "^2.0.0",3923+ "core-js": "^3.31.1",3924+ "tslib": "^2.6.0",3925+ "webpack": "^5.95.0",3926+ "webpack-merge": "^5.9.0",3927+ "workbox-build": "^7.0.0",3928+ "workbox-precaching": "^7.0.0",3929+ "workbox-window": "^7.0.0"3930+ },3931+ "engines": {3932+ "node": ">=20.0"3933+ },3934+ "peerDependencies": {3935+ "react": "^18.0.0 || ^19.0.0",3936+ "react-dom": "^18.0.0 || ^19.0.0"3937+ }3938+ },3939+ "node_modules/@docusaurus/plugin-pwa/node_modules/webpack-merge": {3940+ "version": "5.10.0",3941+ "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz",3942+ "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==",3943+ "license": "MIT",3944+ "dependencies": {3945+ "clone-deep": "^4.0.1",3946+ "flat": "^5.0.2",3947+ "wildcard": "^2.0.0"3948+ },3949+ "engines": {3950+ "node": ">=10.0.0"3951+ }3952+ },38803953 "node_modules/@docusaurus/plugin-sitemap": {38813954 "version": "3.10.2",38823955 "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.10.2.tgz",@@ -4353,6 +4426,78 @@
43534426 "tslib": "^2.4.0"43544427 }43554428 },4429+ "node_modules/@fontsource/alegreya-sans": {4430+ "version": "5.2.8",4431+ "resolved": "https://registry.npmjs.org/@fontsource/alegreya-sans/-/alegreya-sans-5.2.8.tgz",4432+ "integrity": "sha512-kXUTR1S6dsWa7rBxaDJ3P05HodmYmxfY20aQtQ4SYVmrEYrWtaw5A1n+h+wcCF0nAMwf+CQ4M435dNuWD1YfMg==",4433+ "license": "OFL-1.1",4434+ "funding": {4435+ "url": "https://github.com/sponsors/ayuhito"4436+ }4437+ },4438+ "node_modules/@fontsource/comfortaa": {4439+ "version": "5.2.8",4440+ "resolved": "https://registry.npmjs.org/@fontsource/comfortaa/-/comfortaa-5.2.8.tgz",4441+ "integrity": "sha512-S5oyMR0QzBPIJvS52W8Xgv9IcJkScIHgHZ7R2lqwW9nVD10mlIzunNPrIrAuU6kES+A35tc52UdQ9tunL1CWeg==",4442+ "license": "OFL-1.1",4443+ "funding": {4444+ "url": "https://github.com/sponsors/ayuhito"4445+ }4446+ },4447+ "node_modules/@fontsource/grenze-gotisch": {4448+ "version": "5.2.8",4449+ "resolved": "https://registry.npmjs.org/@fontsource/grenze-gotisch/-/grenze-gotisch-5.2.8.tgz",4450+ "integrity": "sha512-tfcZ3OBCAQ+c70Sw76wftJQYNrD380l3lhwNAexfvTGE0MA//Asg5fBn3KakIO5gaSOs3wpYiZYmgH5oU3eZjg==",4451+ "license": "OFL-1.1",4452+ "funding": {4453+ "url": "https://github.com/sponsors/ayuhito"4454+ }4455+ },4456+ "node_modules/@fontsource/ibm-plex-mono": {4457+ "version": "5.2.7",4458+ "resolved": "https://registry.npmjs.org/@fontsource/ibm-plex-mono/-/ibm-plex-mono-5.2.7.tgz",4459+ "integrity": "sha512-MKAb8qV+CaiMQn2B0dIi1OV3565NYzp3WN5b4oT6LTkk+F0jR6j0ZN+5BKJiIhffDC3rtBULsYZE65+0018z9w==",4460+ "license": "OFL-1.1",4461+ "funding": {4462+ "url": "https://github.com/sponsors/ayuhito"4463+ }4464+ },4465+ "node_modules/@fontsource/karla": {4466+ "version": "5.2.8",4467+ "resolved": "https://registry.npmjs.org/@fontsource/karla/-/karla-5.2.8.tgz",4468+ "integrity": "sha512-uZMA4ugNcHFYMpjr/9FIHQC3ldtILlqPdK3f4P9XInDgRL0NxdIyRwH9MuGZUT7sdup0kshk9zsGXitq1xpFAw==",4469+ "license": "OFL-1.1",4470+ "funding": {4471+ "url": "https://github.com/sponsors/ayuhito"4472+ }4473+ },4474+ "node_modules/@fontsource/nunito": {4475+ "version": "5.2.7",4476+ "resolved": "https://registry.npmjs.org/@fontsource/nunito/-/nunito-5.2.7.tgz",4477+ "integrity": "sha512-pmtBq0H9ex9nk+RtJYEJOD9pag393iHETnl/PVKleF4i06cd0ttngK5ZCTgYb5eOqR3Xdlrjtev8m7bmgYprew==",4478+ "license": "OFL-1.1",4479+ "funding": {4480+ "url": "https://github.com/sponsors/ayuhito"4481+ }4482+ },4483+ "node_modules/@fontsource/spline-sans-mono": {4484+ "version": "5.2.8",4485+ "resolved": "https://registry.npmjs.org/@fontsource/spline-sans-mono/-/spline-sans-mono-5.2.8.tgz",4486+ "integrity": "sha512-lxHnpGHjPGPXpv+H0SwDZxUkbYrpqSlUBfe1f/ScERQcKhLbQxDMzd7TpRPqIVWC/Ze2WLu0vcOBuL1I8BD5tQ==",4487+ "license": "OFL-1.1",4488+ "funding": {4489+ "url": "https://github.com/sponsors/ayuhito"4490+ }4491+ },4492+ "node_modules/@fontsource/young-serif": {4493+ "version": "5.2.7",4494+ "resolved": "https://registry.npmjs.org/@fontsource/young-serif/-/young-serif-5.2.7.tgz",4495+ "integrity": "sha512-BltTmAlXLWQpu9XCQa+dfDJHQXt0EHUA4h9Q53VToDW3Onq0dkWzCPM1EyhinqfuwD9tb/5cEcGoRcRwiyiUUA==",4496+ "license": "OFL-1.1",4497+ "funding": {4498+ "url": "https://github.com/sponsors/ayuhito"4499+ }4500+ },43564501 "node_modules/@hapi/hoek": {43574502 "version": "9.3.0",43584503 "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",@@ -4385,6 +4530,15 @@
43854530 "import-meta-resolve": "^4.2.0"43864531 }43874532 },4533+ "node_modules/@isaacs/cliui": {4534+ "version": "9.0.0",4535+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz",4536+ "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==",4537+ "license": "BlueOak-1.0.0",4538+ "engines": {4539+ "node": ">=18"4540+ }4541+ },43884542 "node_modules/@jest/schemas": {43894543 "version": "29.6.3",43904544 "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",@@ -5541,28 +5695,178 @@
55415695 "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==",55425696 "license": "MIT"55435697 },5544- "node_modules/@rspack/binding": {5545- "version": "1.7.12",5546- "resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-1.7.12.tgz",5547- "integrity": "sha512-f4HHuLbvuld8Ba4iB/4ibse5XrKxFrgmM3S4P2AOKnPlekAFlBjmltCuaTL/W2ggYvILaVY+YcFXrEH1rrKeQA==",5698+ "node_modules/@rollup/plugin-babel": {5699+ "version": "6.1.0",5700+ "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.1.0.tgz",5701+ "integrity": "sha512-dFZNuFD2YRcoomP4oYf+DvQNSUA9ih+A3vUqopQx5EdtPGo3WBnQcI/S8pwpz91UsGfL0HsMSOlaMld8HrbubA==",55485702 "license": "MIT",5549- "optionalDependencies": {5550- "@rspack/binding-darwin-arm64": "1.7.12",5551- "@rspack/binding-darwin-x64": "1.7.12",5552- "@rspack/binding-linux-arm64-gnu": "1.7.12",5553- "@rspack/binding-linux-arm64-musl": "1.7.12",5554- "@rspack/binding-linux-x64-gnu": "1.7.12",5555- "@rspack/binding-linux-x64-musl": "1.7.12",5556- "@rspack/binding-wasm32-wasi": "1.7.12",5557- "@rspack/binding-win32-arm64-msvc": "1.7.12",5558- "@rspack/binding-win32-ia32-msvc": "1.7.12",5559- "@rspack/binding-win32-x64-msvc": "1.7.12"5703+ "dependencies": {5704+ "@babel/helper-module-imports": "^7.18.6",5705+ "@rollup/pluginutils": "^5.0.1"5706+ },5707+ "engines": {5708+ "node": ">=14.0.0"5709+ },5710+ "peerDependencies": {5711+ "@babel/core": "^7.0.0",5712+ "@types/babel__core": "^7.1.9",5713+ "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"5714+ },5715+ "peerDependenciesMeta": {5716+ "@types/babel__core": {5717+ "optional": true5718+ },5719+ "rollup": {5720+ "optional": true5721+ }55605722 }55615723 },5562- "node_modules/@rspack/binding-darwin-arm64": {5563- "version": "1.7.12",5564- "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.7.12.tgz",5565- "integrity": "sha512-rbFprJaJiqrmfy8SHth8EsoRS0wg4bXcucwj9NiMzpGFq14Opw8c04iQ6H9BECYzgmN0PKZ9rh41LdVvhdZe4A==",5724+ "node_modules/@rollup/plugin-node-resolve": {5725+ "version": "16.0.3",5726+ "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.3.tgz",5727+ "integrity": "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==",5728+ "license": "MIT",5729+ "dependencies": {5730+ "@rollup/pluginutils": "^5.0.1",5731+ "@types/resolve": "1.20.2",5732+ "deepmerge": "^4.2.2",5733+ "is-module": "^1.0.0",5734+ "resolve": "^1.22.1"5735+ },5736+ "engines": {5737+ "node": ">=14.0.0"5738+ },5739+ "peerDependencies": {5740+ "rollup": "^2.78.0||^3.0.0||^4.0.0"5741+ },5742+ "peerDependenciesMeta": {5743+ "rollup": {5744+ "optional": true5745+ }5746+ }5747+ },5748+ "node_modules/@rollup/plugin-replace": {5749+ "version": "6.0.3",5750+ "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.3.tgz",5751+ "integrity": "sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==",5752+ "license": "MIT",5753+ "dependencies": {5754+ "@rollup/pluginutils": "^5.0.1",5755+ "magic-string": "^0.30.3"5756+ },5757+ "engines": {5758+ "node": ">=14.0.0"5759+ },5760+ "peerDependencies": {5761+ "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"5762+ },5763+ "peerDependenciesMeta": {5764+ "rollup": {5765+ "optional": true5766+ }5767+ }5768+ },5769+ "node_modules/@rollup/plugin-terser": {5770+ "version": "1.0.0",5771+ "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-1.0.0.tgz",5772+ "integrity": "sha512-FnCxhTBx6bMOYQrar6C8h3scPt8/JwIzw3+AJ2K++6guogH5fYaIFia+zZuhqv0eo1RN7W1Pz630SyvLbDjhtQ==",5773+ "license": "MIT",5774+ "dependencies": {5775+ "serialize-javascript": "^7.0.3",5776+ "smob": "^1.0.0",5777+ "terser": "^5.17.4"5778+ },5779+ "engines": {5780+ "node": ">=20.0.0"5781+ },5782+ "peerDependencies": {5783+ "rollup": "^2.0.0||^3.0.0||^4.0.0"5784+ },5785+ "peerDependenciesMeta": {5786+ "rollup": {5787+ "optional": true5788+ }5789+ }5790+ },5791+ "node_modules/@rollup/plugin-terser/node_modules/serialize-javascript": {5792+ "version": "7.0.7",5793+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.7.tgz",5794+ "integrity": "sha512-YAy8Od6KV+uuwUuU50np8fGB/Aues6Y0nAhA9y/hId74PlKUcme4pXcBD46NWKr1Q4osN/iseZ17YqO1XfmI8g==",5795+ "license": "BSD-3-Clause",5796+ "engines": {5797+ "node": ">=20.0.0"5798+ }5799+ },5800+ "node_modules/@rollup/pluginutils": {5801+ "version": "5.4.0",5802+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz",5803+ "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==",5804+ "license": "MIT",5805+ "dependencies": {5806+ "@types/estree": "^1.0.0",5807+ "estree-walker": "^2.0.2",5808+ "picomatch": "^4.0.2"5809+ },5810+ "engines": {5811+ "node": ">=14.0.0"5812+ },5813+ "peerDependencies": {5814+ "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"5815+ },5816+ "peerDependenciesMeta": {5817+ "rollup": {5818+ "optional": true5819+ }5820+ }5821+ },5822+ "node_modules/@rollup/pluginutils/node_modules/estree-walker": {5823+ "version": "2.0.2",5824+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",5825+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",5826+ "license": "MIT"5827+ },5828+ "node_modules/@rollup/pluginutils/node_modules/picomatch": {5829+ "version": "4.0.5",5830+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",5831+ "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",5832+ "license": "MIT",5833+ "engines": {5834+ "node": ">=12"5835+ },5836+ "funding": {5837+ "url": "https://github.com/sponsors/jonschlinkert"5838+ }5839+ },5840+ "node_modules/@rollup/rollup-android-arm-eabi": {5841+ "version": "4.62.2",5842+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz",5843+ "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==",5844+ "cpu": [5845+ "arm"5846+ ],5847+ "license": "MIT",5848+ "optional": true,5849+ "os": [5850+ "android"5851+ ]5852+ },5853+ "node_modules/@rollup/rollup-android-arm64": {5854+ "version": "4.62.2",5855+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz",5856+ "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==",5857+ "cpu": [5858+ "arm64"5859+ ],5860+ "license": "MIT",5861+ "optional": true,5862+ "os": [5863+ "android"5864+ ]5865+ },5866+ "node_modules/@rollup/rollup-darwin-arm64": {5867+ "version": "4.62.2",5868+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz",5869+ "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==",55665870 "cpu": [55675871 "arm64"55685872 ],@@ -5572,10 +5876,10 @@
55725876 "darwin"55735877 ]55745878 },5575- "node_modules/@rspack/binding-darwin-x64": {5576- "version": "1.7.12",5577- "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-1.7.12.tgz",5578- "integrity": "sha512-jnOp+/UXOJa9xqUb8KXH03sysoO2e4Ij6tw6MqDdmdj8n/A8PQENRPUbW9AwXpPtVDJPus9r4fi7b3+6e4B8Hg==",5879+ "node_modules/@rollup/rollup-darwin-x64": {5880+ "version": "4.62.2",5881+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz",5882+ "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==",55795883 "cpu": [55805884 "x64"55815885 ],@@ -5585,13 +5889,39 @@
55855889 "darwin"55865890 ]55875891 },5588- "node_modules/@rspack/binding-linux-arm64-gnu": {5589- "version": "1.7.12",5590- "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.7.12.tgz",5591- "integrity": "sha512-C8owWG+yvo7X0oVLIXetkoJhIFBP1LYNcAQqtgLmJnQLQDklGuP83dKC+zISGQWpjawHfZ1ER96vLgoTrxKZdw==",5892+ "node_modules/@rollup/rollup-freebsd-arm64": {5893+ "version": "4.62.2",5894+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz",5895+ "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==",55925896 "cpu": [55935897 "arm64"55945898 ],5899+ "license": "MIT",5900+ "optional": true,5901+ "os": [5902+ "freebsd"5903+ ]5904+ },5905+ "node_modules/@rollup/rollup-freebsd-x64": {5906+ "version": "4.62.2",5907+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz",5908+ "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==",5909+ "cpu": [5910+ "x64"5911+ ],5912+ "license": "MIT",5913+ "optional": true,5914+ "os": [5915+ "freebsd"5916+ ]5917+ },5918+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {5919+ "version": "4.62.2",5920+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz",5921+ "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==",5922+ "cpu": [5923+ "arm"5924+ ],55955925 "libc": [55965926 "glibc"55975927 ],@@ -5601,12 +5931,12 @@
56015931 "linux"56025932 ]56035933 },5604- "node_modules/@rspack/binding-linux-arm64-musl": {5605- "version": "1.7.12",5606- "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.7.12.tgz",5607- "integrity": "sha512-i51WWI64aRpsfSki6rN0aepPqXkVfS+vZM7+4bWDcmnhUmdMvhIPcYg0QRk3DtyJnu33jqNLM0WHY78k00NyfA==",5934+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {5935+ "version": "4.62.2",5936+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz",5937+ "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==",56085938 "cpu": [5609- "arm64"5939+ "arm"56105940 ],56115941 "libc": [56125942 "musl"@@ -5617,12 +5947,12 @@
56175947 "linux"56185948 ]56195949 },5620- "node_modules/@rspack/binding-linux-x64-gnu": {5621- "version": "1.7.12",5622- "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.7.12.tgz",5623- "integrity": "sha512-MSos0FuPEefqo9V92ULd5hggKG29EkSNg1zDcypy0OkpsKh5pfjVxTLYFXgTcVyFoUQQbdG8zFBzYbwmJ8V4ew==",5950+ "node_modules/@rollup/rollup-linux-arm64-gnu": {5951+ "version": "4.62.2",5952+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz",5953+ "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==",56245954 "cpu": [5625- "x64"5955+ "arm64"56265956 ],56275957 "libc": [56285958 "glibc"@@ -5633,12 +5963,12 @@
56335963 "linux"56345964 ]56355965 },5636- "node_modules/@rspack/binding-linux-x64-musl": {5637- "version": "1.7.12",5638- "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-1.7.12.tgz",5639- "integrity": "sha512-JcAMVKXOnjfpC3coWjCFPWD3Yl8RBw6a+IXQQ8mfRlHaHMIiOv8IfZqx15XRxMUn49CtP7Z0Na8iiAg2aKrcfw==",5966+ "node_modules/@rollup/rollup-linux-arm64-musl": {5967+ "version": "4.62.2",5968+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz",5969+ "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==",56405970 "cpu": [5641- "x64"5971+ "arm64"56425972 ],56435973 "libc": [56445974 "musl"@@ -5649,20 +5979,350 @@
56495979 "linux"56505980 ]56515981 },5652- "node_modules/@rspack/binding-wasm32-wasi": {5653- "version": "1.7.12",5654- "resolved": "https://registry.npmjs.org/@rspack/binding-wasm32-wasi/-/binding-wasm32-wasi-1.7.12.tgz",5655- "integrity": "sha512-n+ZqP6ZMc0nhOgvadg5VhEs9ojtbES80AcWeFnmGkbzIszvGSO63GKNiRkXtjJ9KFuRzytbbmsCqkUVH+Tywxg==",5982+ "node_modules/@rollup/rollup-linux-loong64-gnu": {5983+ "version": "4.62.2",5984+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz",5985+ "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==",56565986 "cpu": [5657- "wasm32"5987+ "loong64"5988+ ],5989+ "libc": [5990+ "glibc"56585991 ],56595992 "license": "MIT",56605993 "optional": true,5661- "dependencies": {5662- "@napi-rs/wasm-runtime": "1.0.7"5663- }5664- },5665- "node_modules/@rspack/binding-win32-arm64-msvc": {5994+ "os": [5995+ "linux"5996+ ]5997+ },5998+ "node_modules/@rollup/rollup-linux-loong64-musl": {5999+ "version": "4.62.2",6000+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz",6001+ "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==",6002+ "cpu": [6003+ "loong64"6004+ ],6005+ "libc": [6006+ "musl"6007+ ],6008+ "license": "MIT",6009+ "optional": true,6010+ "os": [6011+ "linux"6012+ ]6013+ },6014+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {6015+ "version": "4.62.2",6016+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz",6017+ "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==",6018+ "cpu": [6019+ "ppc64"6020+ ],6021+ "libc": [6022+ "glibc"6023+ ],6024+ "license": "MIT",6025+ "optional": true,6026+ "os": [6027+ "linux"6028+ ]6029+ },6030+ "node_modules/@rollup/rollup-linux-ppc64-musl": {6031+ "version": "4.62.2",6032+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz",6033+ "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==",6034+ "cpu": [6035+ "ppc64"6036+ ],6037+ "libc": [6038+ "musl"6039+ ],6040+ "license": "MIT",6041+ "optional": true,6042+ "os": [6043+ "linux"6044+ ]6045+ },6046+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {6047+ "version": "4.62.2",6048+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz",6049+ "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==",6050+ "cpu": [6051+ "riscv64"6052+ ],6053+ "libc": [6054+ "glibc"6055+ ],6056+ "license": "MIT",6057+ "optional": true,6058+ "os": [6059+ "linux"6060+ ]6061+ },6062+ "node_modules/@rollup/rollup-linux-riscv64-musl": {6063+ "version": "4.62.2",6064+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz",6065+ "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==",6066+ "cpu": [6067+ "riscv64"6068+ ],6069+ "libc": [6070+ "musl"6071+ ],6072+ "license": "MIT",6073+ "optional": true,6074+ "os": [6075+ "linux"6076+ ]6077+ },6078+ "node_modules/@rollup/rollup-linux-s390x-gnu": {6079+ "version": "4.62.2",6080+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz",6081+ "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==",6082+ "cpu": [6083+ "s390x"6084+ ],6085+ "libc": [6086+ "glibc"6087+ ],6088+ "license": "MIT",6089+ "optional": true,6090+ "os": [6091+ "linux"6092+ ]6093+ },6094+ "node_modules/@rollup/rollup-linux-x64-gnu": {6095+ "version": "4.62.2",6096+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz",6097+ "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==",6098+ "cpu": [6099+ "x64"6100+ ],6101+ "libc": [6102+ "glibc"6103+ ],6104+ "license": "MIT",6105+ "optional": true,6106+ "os": [6107+ "linux"6108+ ]6109+ },6110+ "node_modules/@rollup/rollup-linux-x64-musl": {6111+ "version": "4.62.2",6112+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz",6113+ "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==",6114+ "cpu": [6115+ "x64"6116+ ],6117+ "libc": [6118+ "musl"6119+ ],6120+ "license": "MIT",6121+ "optional": true,6122+ "os": [6123+ "linux"6124+ ]6125+ },6126+ "node_modules/@rollup/rollup-openbsd-x64": {6127+ "version": "4.62.2",6128+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz",6129+ "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==",6130+ "cpu": [6131+ "x64"6132+ ],6133+ "license": "MIT",6134+ "optional": true,6135+ "os": [6136+ "openbsd"6137+ ]6138+ },6139+ "node_modules/@rollup/rollup-openharmony-arm64": {6140+ "version": "4.62.2",6141+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz",6142+ "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==",6143+ "cpu": [6144+ "arm64"6145+ ],6146+ "license": "MIT",6147+ "optional": true,6148+ "os": [6149+ "openharmony"6150+ ]6151+ },6152+ "node_modules/@rollup/rollup-win32-arm64-msvc": {6153+ "version": "4.62.2",6154+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz",6155+ "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==",6156+ "cpu": [6157+ "arm64"6158+ ],6159+ "license": "MIT",6160+ "optional": true,6161+ "os": [6162+ "win32"6163+ ]6164+ },6165+ "node_modules/@rollup/rollup-win32-ia32-msvc": {6166+ "version": "4.62.2",6167+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz",6168+ "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==",6169+ "cpu": [6170+ "ia32"6171+ ],6172+ "license": "MIT",6173+ "optional": true,6174+ "os": [6175+ "win32"6176+ ]6177+ },6178+ "node_modules/@rollup/rollup-win32-x64-gnu": {6179+ "version": "4.62.2",6180+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz",6181+ "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==",6182+ "cpu": [6183+ "x64"6184+ ],6185+ "license": "MIT",6186+ "optional": true,6187+ "os": [6188+ "win32"6189+ ]6190+ },6191+ "node_modules/@rollup/rollup-win32-x64-msvc": {6192+ "version": "4.62.2",6193+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz",6194+ "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==",6195+ "cpu": [6196+ "x64"6197+ ],6198+ "license": "MIT",6199+ "optional": true,6200+ "os": [6201+ "win32"6202+ ]6203+ },6204+ "node_modules/@rspack/binding": {6205+ "version": "1.7.12",6206+ "resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-1.7.12.tgz",6207+ "integrity": "sha512-f4HHuLbvuld8Ba4iB/4ibse5XrKxFrgmM3S4P2AOKnPlekAFlBjmltCuaTL/W2ggYvILaVY+YcFXrEH1rrKeQA==",6208+ "license": "MIT",6209+ "optionalDependencies": {6210+ "@rspack/binding-darwin-arm64": "1.7.12",6211+ "@rspack/binding-darwin-x64": "1.7.12",6212+ "@rspack/binding-linux-arm64-gnu": "1.7.12",6213+ "@rspack/binding-linux-arm64-musl": "1.7.12",6214+ "@rspack/binding-linux-x64-gnu": "1.7.12",6215+ "@rspack/binding-linux-x64-musl": "1.7.12",6216+ "@rspack/binding-wasm32-wasi": "1.7.12",6217+ "@rspack/binding-win32-arm64-msvc": "1.7.12",6218+ "@rspack/binding-win32-ia32-msvc": "1.7.12",6219+ "@rspack/binding-win32-x64-msvc": "1.7.12"6220+ }6221+ },6222+ "node_modules/@rspack/binding-darwin-arm64": {6223+ "version": "1.7.12",6224+ "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.7.12.tgz",6225+ "integrity": "sha512-rbFprJaJiqrmfy8SHth8EsoRS0wg4bXcucwj9NiMzpGFq14Opw8c04iQ6H9BECYzgmN0PKZ9rh41LdVvhdZe4A==",6226+ "cpu": [6227+ "arm64"6228+ ],6229+ "license": "MIT",6230+ "optional": true,6231+ "os": [6232+ "darwin"6233+ ]6234+ },6235+ "node_modules/@rspack/binding-darwin-x64": {6236+ "version": "1.7.12",6237+ "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-1.7.12.tgz",6238+ "integrity": "sha512-jnOp+/UXOJa9xqUb8KXH03sysoO2e4Ij6tw6MqDdmdj8n/A8PQENRPUbW9AwXpPtVDJPus9r4fi7b3+6e4B8Hg==",6239+ "cpu": [6240+ "x64"6241+ ],6242+ "license": "MIT",6243+ "optional": true,6244+ "os": [6245+ "darwin"6246+ ]6247+ },6248+ "node_modules/@rspack/binding-linux-arm64-gnu": {6249+ "version": "1.7.12",6250+ "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.7.12.tgz",6251+ "integrity": "sha512-C8owWG+yvo7X0oVLIXetkoJhIFBP1LYNcAQqtgLmJnQLQDklGuP83dKC+zISGQWpjawHfZ1ER96vLgoTrxKZdw==",6252+ "cpu": [6253+ "arm64"6254+ ],6255+ "libc": [6256+ "glibc"6257+ ],6258+ "license": "MIT",6259+ "optional": true,6260+ "os": [6261+ "linux"6262+ ]6263+ },6264+ "node_modules/@rspack/binding-linux-arm64-musl": {6265+ "version": "1.7.12",6266+ "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.7.12.tgz",6267+ "integrity": "sha512-i51WWI64aRpsfSki6rN0aepPqXkVfS+vZM7+4bWDcmnhUmdMvhIPcYg0QRk3DtyJnu33jqNLM0WHY78k00NyfA==",6268+ "cpu": [6269+ "arm64"6270+ ],6271+ "libc": [6272+ "musl"6273+ ],6274+ "license": "MIT",6275+ "optional": true,6276+ "os": [6277+ "linux"6278+ ]6279+ },6280+ "node_modules/@rspack/binding-linux-x64-gnu": {6281+ "version": "1.7.12",6282+ "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.7.12.tgz",6283+ "integrity": "sha512-MSos0FuPEefqo9V92ULd5hggKG29EkSNg1zDcypy0OkpsKh5pfjVxTLYFXgTcVyFoUQQbdG8zFBzYbwmJ8V4ew==",6284+ "cpu": [6285+ "x64"6286+ ],6287+ "libc": [6288+ "glibc"6289+ ],6290+ "license": "MIT",6291+ "optional": true,6292+ "os": [6293+ "linux"6294+ ]6295+ },6296+ "node_modules/@rspack/binding-linux-x64-musl": {6297+ "version": "1.7.12",6298+ "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-1.7.12.tgz",6299+ "integrity": "sha512-JcAMVKXOnjfpC3coWjCFPWD3Yl8RBw6a+IXQQ8mfRlHaHMIiOv8IfZqx15XRxMUn49CtP7Z0Na8iiAg2aKrcfw==",6300+ "cpu": [6301+ "x64"6302+ ],6303+ "libc": [6304+ "musl"6305+ ],6306+ "license": "MIT",6307+ "optional": true,6308+ "os": [6309+ "linux"6310+ ]6311+ },6312+ "node_modules/@rspack/binding-wasm32-wasi": {6313+ "version": "1.7.12",6314+ "resolved": "https://registry.npmjs.org/@rspack/binding-wasm32-wasi/-/binding-wasm32-wasi-1.7.12.tgz",6315+ "integrity": "sha512-n+ZqP6ZMc0nhOgvadg5VhEs9ojtbES80AcWeFnmGkbzIszvGSO63GKNiRkXtjJ9KFuRzytbbmsCqkUVH+Tywxg==",6316+ "cpu": [6317+ "wasm32"6318+ ],6319+ "license": "MIT",6320+ "optional": true,6321+ "dependencies": {6322+ "@napi-rs/wasm-runtime": "1.0.7"6323+ }6324+ },6325+ "node_modules/@rspack/binding-win32-arm64-msvc": {56666326 "version": "1.7.12",56676327 "resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.7.12.tgz",56686328 "integrity": "sha512-8+h5fYDXYdmugbdfZ+D1y8IQ3rv2EhSfyGP7vBe+bjNyaMa4jWrpucmZbtxojUL1AzaeuHbvMdj9UO/gelk/+g==",@@ -6549,6 +7209,21 @@
65497209 "node": ">=14.16"65507210 }65517211 },7212+ "node_modules/@trickfilm400/rollup-plugin-off-main-thread": {7213+ "version": "3.0.0-pre1",7214+ "resolved": "https://registry.npmjs.org/@trickfilm400/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-3.0.0-pre1.tgz",7215+ "integrity": "sha512-/67zpWDBLV+oYAEL682s1ktXL0HgqX76f6gaVGkGnVZlBbm1zd0v4Bz8MFF2GGhoX9rvfq3KSQHubFHwa6w6/Q==",7216+ "license": "Apache-2.0",7217+ "dependencies": {7218+ "ejs": "^3.1.10",7219+ "json5": "^2.2.3",7220+ "magic-string": "^0.30.21",7221+ "string.prototype.matchall": "^4.0.12"7222+ },7223+ "engines": {7224+ "node": ">=12"7225+ }7226+ },65527227 "node_modules/@tybys/wasm-util": {65537228 "version": "0.10.3",65547229 "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz",@@ -7071,6 +7746,12 @@
70717746 "@types/react-router": "*"70727747 }70737748 },7749+ "node_modules/@types/resolve": {7750+ "version": "1.20.2",7751+ "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",7752+ "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",7753+ "license": "MIT"7754+ },70747755 "node_modules/@types/retry": {70757756 "version": "0.12.2",70767757 "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz",@@ -7138,8 +7819,7 @@
71387819 "version": "2.0.7",71397820 "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",71407821 "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",7141- "license": "MIT",7142- "optional": true7822+ "license": "MIT"71437823 },71447824 "node_modules/@types/unist": {71457825 "version": "3.0.3",@@ -7636,6 +8316,22 @@
76368316 "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",76378317 "license": "Python-2.0"76388318 },8319+ "node_modules/array-buffer-byte-length": {8320+ "version": "1.0.2",8321+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",8322+ "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",8323+ "license": "MIT",8324+ "dependencies": {8325+ "call-bound": "^1.0.3",8326+ "is-array-buffer": "^3.0.5"8327+ },8328+ "engines": {8329+ "node": ">= 0.4"8330+ },8331+ "funding": {8332+ "url": "https://github.com/sponsors/ljharb"8333+ }8334+ },76398335 "node_modules/array-flatten": {76408336 "version": "1.1.1",76418337 "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",@@ -7651,6 +8347,27 @@
76518347 "node": ">=8"76528348 }76538349 },8350+ "node_modules/arraybuffer.prototype.slice": {8351+ "version": "1.0.4",8352+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz",8353+ "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==",8354+ "license": "MIT",8355+ "dependencies": {8356+ "array-buffer-byte-length": "^1.0.1",8357+ "call-bind": "^1.0.8",8358+ "define-properties": "^1.2.1",8359+ "es-abstract": "^1.23.5",8360+ "es-errors": "^1.3.0",8361+ "get-intrinsic": "^1.2.6",8362+ "is-array-buffer": "^3.0.4"8363+ },8364+ "engines": {8365+ "node": ">= 0.4"8366+ },8367+ "funding": {8368+ "url": "https://github.com/sponsors/ljharb"8369+ }8370+ },76548371 "node_modules/asn1js": {76558372 "version": "3.0.10",76568373 "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.10.tgz",@@ -7674,6 +8391,30 @@
76748391 "astring": "bin/astring"76758392 }76768393 },8394+ "node_modules/async": {8395+ "version": "3.2.6",8396+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",8397+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",8398+ "license": "MIT"8399+ },8400+ "node_modules/async-function": {8401+ "version": "1.0.0",8402+ "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",8403+ "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",8404+ "license": "MIT",8405+ "engines": {8406+ "node": ">= 0.4"8407+ }8408+ },8409+ "node_modules/at-least-node": {8410+ "version": "1.0.0",8411+ "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",8412+ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",8413+ "license": "ISC",8414+ "engines": {8415+ "node": ">= 4.0.0"8416+ }8417+ },76778418 "node_modules/autoprefixer": {76788419 "version": "10.5.4",76798420 "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.4.tgz",@@ -7710,6 +8451,21 @@
77108451 "postcss": "^8.1.0"77118452 }77128453 },8454+ "node_modules/available-typed-arrays": {8455+ "version": "1.0.7",8456+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",8457+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",8458+ "license": "MIT",8459+ "dependencies": {8460+ "possible-typed-array-names": "^1.0.0"8461+ },8462+ "engines": {8463+ "node": ">= 0.4"8464+ },8465+ "funding": {8466+ "url": "https://github.com/sponsors/ljharb"8467+ }8468+ },77138469 "node_modules/babel-loader": {77148470 "version": "9.2.1",77158471 "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz",@@ -8509,6 +9265,15 @@
85099265 "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==",85109266 "license": "ISC"85119267 },9268+ "node_modules/common-tags": {9269+ "version": "1.8.2",9270+ "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz",9271+ "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==",9272+ "license": "MIT",9273+ "engines": {9274+ "node": ">=4.0.0"9275+ }9276+ },85129277 "node_modules/compressible": {85139278 "version": "2.0.18",85149279 "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",@@ -9781,6 +10546,57 @@
978110546 "lodash-es": "^4.17.21"978210547 }978310548 },10549+ "node_modules/data-view-buffer": {10550+ "version": "1.0.2",10551+ "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",10552+ "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",10553+ "license": "MIT",10554+ "dependencies": {10555+ "call-bound": "^1.0.3",10556+ "es-errors": "^1.3.0",10557+ "is-data-view": "^1.0.2"10558+ },10559+ "engines": {10560+ "node": ">= 0.4"10561+ },10562+ "funding": {10563+ "url": "https://github.com/sponsors/ljharb"10564+ }10565+ },10566+ "node_modules/data-view-byte-length": {10567+ "version": "1.0.2",10568+ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz",10569+ "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==",10570+ "license": "MIT",10571+ "dependencies": {10572+ "call-bound": "^1.0.3",10573+ "es-errors": "^1.3.0",10574+ "is-data-view": "^1.0.2"10575+ },10576+ "engines": {10577+ "node": ">= 0.4"10578+ },10579+ "funding": {10580+ "url": "https://github.com/sponsors/inspect-js"10581+ }10582+ },10583+ "node_modules/data-view-byte-offset": {10584+ "version": "1.0.1",10585+ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",10586+ "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",10587+ "license": "MIT",10588+ "dependencies": {10589+ "call-bound": "^1.0.2",10590+ "es-errors": "^1.3.0",10591+ "is-data-view": "^1.0.1"10592+ },10593+ "engines": {10594+ "node": ">= 0.4"10595+ },10596+ "funding": {10597+ "url": "https://github.com/sponsors/ljharb"10598+ }10599+ },978410600 "node_modules/dayjs": {978510601 "version": "1.11.21",978610602 "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz",@@ -10192,6 +11008,21 @@
1019211008 "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",1019311009 "license": "MIT"1019411010 },11011+ "node_modules/ejs": {11012+ "version": "3.1.10",11013+ "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",11014+ "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",11015+ "license": "Apache-2.0",11016+ "dependencies": {11017+ "jake": "^10.8.5"11018+ },11019+ "bin": {11020+ "ejs": "bin/cli.js"11021+ },11022+ "engines": {11023+ "node": ">=0.10.0"11024+ }11025+ },1019511026 "node_modules/electron-to-chromium": {1019611027 "version": "1.5.393",1019711028 "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.393.tgz",@@ -10285,6 +11116,92 @@
1028511116 "is-arrayish": "^0.2.1"1028611117 }1028711118 },11119+ "node_modules/es-abstract": {11120+ "version": "1.24.2",11121+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz",11122+ "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==",11123+ "license": "MIT",11124+ "dependencies": {11125+ "array-buffer-byte-length": "^1.0.2",11126+ "arraybuffer.prototype.slice": "^1.0.4",11127+ "available-typed-arrays": "^1.0.7",11128+ "call-bind": "^1.0.8",11129+ "call-bound": "^1.0.4",11130+ "data-view-buffer": "^1.0.2",11131+ "data-view-byte-length": "^1.0.2",11132+ "data-view-byte-offset": "^1.0.1",11133+ "es-define-property": "^1.0.1",11134+ "es-errors": "^1.3.0",11135+ "es-object-atoms": "^1.1.1",11136+ "es-set-tostringtag": "^2.1.0",11137+ "es-to-primitive": "^1.3.0",11138+ "function.prototype.name": "^1.1.8",11139+ "get-intrinsic": "^1.3.0",11140+ "get-proto": "^1.0.1",11141+ "get-symbol-description": "^1.1.0",11142+ "globalthis": "^1.0.4",11143+ "gopd": "^1.2.0",11144+ "has-property-descriptors": "^1.0.2",11145+ "has-proto": "^1.2.0",11146+ "has-symbols": "^1.1.0",11147+ "hasown": "^2.0.2",11148+ "internal-slot": "^1.1.0",11149+ "is-array-buffer": "^3.0.5",11150+ "is-callable": "^1.2.7",11151+ "is-data-view": "^1.0.2",11152+ "is-negative-zero": "^2.0.3",11153+ "is-regex": "^1.2.1",11154+ "is-set": "^2.0.3",11155+ "is-shared-array-buffer": "^1.0.4",11156+ "is-string": "^1.1.1",11157+ "is-typed-array": "^1.1.15",11158+ "is-weakref": "^1.1.1",11159+ "math-intrinsics": "^1.1.0",11160+ "object-inspect": "^1.13.4",11161+ "object-keys": "^1.1.1",11162+ "object.assign": "^4.1.7",11163+ "own-keys": "^1.0.1",11164+ "regexp.prototype.flags": "^1.5.4",11165+ "safe-array-concat": "^1.1.3",11166+ "safe-push-apply": "^1.0.0",11167+ "safe-regex-test": "^1.1.0",11168+ "set-proto": "^1.0.0",11169+ "stop-iteration-iterator": "^1.1.0",11170+ "string.prototype.trim": "^1.2.10",11171+ "string.prototype.trimend": "^1.0.9",11172+ "string.prototype.trimstart": "^1.0.8",11173+ "typed-array-buffer": "^1.0.3",11174+ "typed-array-byte-length": "^1.0.3",11175+ "typed-array-byte-offset": "^1.0.4",11176+ "typed-array-length": "^1.0.7",11177+ "unbox-primitive": "^1.1.0",11178+ "which-typed-array": "^1.1.19"11179+ },11180+ "engines": {11181+ "node": ">= 0.4"11182+ },11183+ "funding": {11184+ "url": "https://github.com/sponsors/ljharb"11185+ }11186+ },11187+ "node_modules/es-abstract-get": {11188+ "version": "1.0.0",11189+ "resolved": "https://registry.npmjs.org/es-abstract-get/-/es-abstract-get-1.0.0.tgz",11190+ "integrity": "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==",11191+ "license": "MIT",11192+ "dependencies": {11193+ "es-errors": "^1.3.0",11194+ "es-object-atoms": "^1.1.2",11195+ "is-callable": "^1.2.7",11196+ "object-inspect": "^1.13.4"11197+ },11198+ "engines": {11199+ "node": ">= 0.4"11200+ },11201+ "funding": {11202+ "url": "https://github.com/sponsors/ljharb"11203+ }11204+ },1028811205 "node_modules/es-define-property": {1028911206 "version": "1.0.1",1029011207 "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",@@ -10321,6 +11238,41 @@
1032111238 "node": ">= 0.4"1032211239 }1032311240 },11241+ "node_modules/es-set-tostringtag": {11242+ "version": "2.1.0",11243+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",11244+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",11245+ "license": "MIT",11246+ "dependencies": {11247+ "es-errors": "^1.3.0",11248+ "get-intrinsic": "^1.2.6",11249+ "has-tostringtag": "^1.0.2",11250+ "hasown": "^2.0.2"11251+ },11252+ "engines": {11253+ "node": ">= 0.4"11254+ }11255+ },11256+ "node_modules/es-to-primitive": {11257+ "version": "1.3.4",11258+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.4.tgz",11259+ "integrity": "sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==",11260+ "license": "MIT",11261+ "dependencies": {11262+ "es-abstract-get": "^1.0.0",11263+ "es-define-property": "^1.0.1",11264+ "es-errors": "^1.3.0",11265+ "is-callable": "^1.2.7",11266+ "is-date-object": "^1.1.0",11267+ "is-symbol": "^1.1.1"11268+ },11269+ "engines": {11270+ "node": ">= 0.4"11271+ },11272+ "funding": {11273+ "url": "https://github.com/sponsors/ljharb"11274+ }11275+ },1032411276 "node_modules/es-toolkit": {1032511277 "version": "1.49.0",1032611278 "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.49.0.tgz",@@ -10893,6 +11845,36 @@
1089311845 "url": "https://opencollective.com/webpack"1089411846 }1089511847 },11848+ "node_modules/filelist": {11849+ "version": "1.0.6",11850+ "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz",11851+ "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==",11852+ "license": "Apache-2.0",11853+ "dependencies": {11854+ "minimatch": "^5.0.1"11855+ }11856+ },11857+ "node_modules/filelist/node_modules/brace-expansion": {11858+ "version": "2.1.2",11859+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz",11860+ "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==",11861+ "license": "MIT",11862+ "dependencies": {11863+ "balanced-match": "^1.0.0"11864+ }11865+ },11866+ "node_modules/filelist/node_modules/minimatch": {11867+ "version": "5.1.9",11868+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz",11869+ "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",11870+ "license": "ISC",11871+ "dependencies": {11872+ "brace-expansion": "^2.0.1"11873+ },11874+ "engines": {11875+ "node": ">=10"11876+ }11877+ },1089611878 "node_modules/fill-range": {1089711879 "version": "7.1.1",1089811880 "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",@@ -10999,6 +11981,49 @@
1099911981 }1100011982 }1100111983 },11984+ "node_modules/for-each": {11985+ "version": "0.3.5",11986+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",11987+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",11988+ "license": "MIT",11989+ "dependencies": {11990+ "is-callable": "^1.2.7"11991+ },11992+ "engines": {11993+ "node": ">= 0.4"11994+ },11995+ "funding": {11996+ "url": "https://github.com/sponsors/ljharb"11997+ }11998+ },11999+ "node_modules/foreground-child": {12000+ "version": "3.3.1",12001+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",12002+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",12003+ "license": "ISC",12004+ "dependencies": {12005+ "cross-spawn": "^7.0.6",12006+ "signal-exit": "^4.0.1"12007+ },12008+ "engines": {12009+ "node": ">=14"12010+ },12011+ "funding": {12012+ "url": "https://github.com/sponsors/isaacs"12013+ }12014+ },12015+ "node_modules/foreground-child/node_modules/signal-exit": {12016+ "version": "4.1.0",12017+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",12018+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",12019+ "license": "ISC",12020+ "engines": {12021+ "node": ">=14"12022+ },12023+ "funding": {12024+ "url": "https://github.com/sponsors/isaacs"12025+ }12026+ },1100212027 "node_modules/form-data-encoder": {1100312028 "version": "2.1.4",1100412029 "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz",@@ -11084,6 +12109,47 @@
1108412109 "url": "https://github.com/sponsors/ljharb"1108512110 }1108612111 },12112+ "node_modules/function.prototype.name": {12113+ "version": "1.2.0",12114+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.2.0.tgz",12115+ "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==",12116+ "license": "MIT",12117+ "dependencies": {12118+ "call-bind": "^1.0.9",12119+ "call-bound": "^1.0.4",12120+ "es-define-property": "^1.0.1",12121+ "es-errors": "^1.3.0",12122+ "functions-have-names": "^1.2.3",12123+ "has-property-descriptors": "^1.0.2",12124+ "hasown": "^2.0.4",12125+ "is-callable": "^1.2.7",12126+ "is-document.all": "^1.0.0"12127+ },12128+ "engines": {12129+ "node": ">= 0.4"12130+ },12131+ "funding": {12132+ "url": "https://github.com/sponsors/ljharb"12133+ }12134+ },12135+ "node_modules/functions-have-names": {12136+ "version": "1.2.3",12137+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",12138+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",12139+ "license": "MIT",12140+ "funding": {12141+ "url": "https://github.com/sponsors/ljharb"12142+ }12143+ },12144+ "node_modules/generator-function": {12145+ "version": "2.0.1",12146+ "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz",12147+ "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",12148+ "license": "MIT",12149+ "engines": {12150+ "node": ">= 0.4"12151+ }12152+ },1108712153 "node_modules/gensync": {1108812154 "version": "1.0.0-beta.2",1108912155 "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",@@ -11148,12 +12214,53 @@
1114812214 "url": "https://github.com/sponsors/sindresorhus"1114912215 }1115012216 },12217+ "node_modules/get-symbol-description": {12218+ "version": "1.1.0",12219+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",12220+ "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",12221+ "license": "MIT",12222+ "dependencies": {12223+ "call-bound": "^1.0.3",12224+ "es-errors": "^1.3.0",12225+ "get-intrinsic": "^1.2.6"12226+ },12227+ "engines": {12228+ "node": ">= 0.4"12229+ },12230+ "funding": {12231+ "url": "https://github.com/sponsors/ljharb"12232+ }12233+ },1115112234 "node_modules/github-slugger": {1115212235 "version": "1.5.0",1115312236 "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz",1115412237 "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==",1115512238 "license": "ISC"1115612239 },12240+ "node_modules/glob": {12241+ "version": "11.1.0",12242+ "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz",12243+ "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==",12244+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",12245+ "license": "BlueOak-1.0.0",12246+ "dependencies": {12247+ "foreground-child": "^3.3.1",12248+ "jackspeak": "^4.1.1",12249+ "minimatch": "^10.1.1",12250+ "minipass": "^7.1.2",12251+ "package-json-from-dist": "^1.0.0",12252+ "path-scurry": "^2.0.0"12253+ },12254+ "bin": {12255+ "glob": "dist/esm/bin.mjs"12256+ },12257+ "engines": {12258+ "node": "20 || >=22"12259+ },12260+ "funding": {12261+ "url": "https://github.com/sponsors/isaacs"12262+ }12263+ },1115712264 "node_modules/glob-parent": {1115812265 "version": "5.1.2",1115912266 "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",@@ -11182,6 +12289,42 @@
1118212289 "tslib": "2"1118312290 }1118412291 },12292+ "node_modules/glob/node_modules/balanced-match": {12293+ "version": "4.0.4",12294+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",12295+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",12296+ "license": "MIT",12297+ "engines": {12298+ "node": "18 || 20 || >=22"12299+ }12300+ },12301+ "node_modules/glob/node_modules/brace-expansion": {12302+ "version": "5.0.7",12303+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",12304+ "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",12305+ "license": "MIT",12306+ "dependencies": {12307+ "balanced-match": "^4.0.2"12308+ },12309+ "engines": {12310+ "node": "18 || 20 || >=22"12311+ }12312+ },12313+ "node_modules/glob/node_modules/minimatch": {12314+ "version": "10.2.5",12315+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",12316+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",12317+ "license": "BlueOak-1.0.0",12318+ "dependencies": {12319+ "brace-expansion": "^5.0.5"12320+ },12321+ "engines": {12322+ "node": "18 || 20 || >=22"12323+ },12324+ "funding": {12325+ "url": "https://github.com/sponsors/isaacs"12326+ }12327+ },1118512328 "node_modules/global-dirs": {1118612329 "version": "3.0.1",1118712330 "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz",@@ -11197,6 +12340,22 @@
1119712340 "url": "https://github.com/sponsors/sindresorhus"1119812341 }1119912342 },12343+ "node_modules/globalthis": {12344+ "version": "1.0.4",12345+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",12346+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",12347+ "license": "MIT",12348+ "dependencies": {12349+ "define-properties": "^1.2.1",12350+ "gopd": "^1.0.1"12351+ },12352+ "engines": {12353+ "node": ">= 0.4"12354+ },12355+ "funding": {12356+ "url": "https://github.com/sponsors/ljharb"12357+ }12358+ },1120012359 "node_modules/globby": {1120112360 "version": "11.1.0",1120212361 "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",@@ -11299,6 +12458,18 @@
1129912458 "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==",1130012459 "license": "MIT"1130112460 },12461+ "node_modules/has-bigints": {12462+ "version": "1.1.0",12463+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",12464+ "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",12465+ "license": "MIT",12466+ "engines": {12467+ "node": ">= 0.4"12468+ },12469+ "funding": {12470+ "url": "https://github.com/sponsors/ljharb"12471+ }12472+ },1130212473 "node_modules/has-flag": {1130312474 "version": "4.0.0",1130412475 "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",@@ -11320,6 +12491,21 @@
1132012491 "url": "https://github.com/sponsors/ljharb"1132112492 }1132212493 },12494+ "node_modules/has-proto": {12495+ "version": "1.2.0",12496+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz",12497+ "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",12498+ "license": "MIT",12499+ "dependencies": {12500+ "dunder-proto": "^1.0.0"12501+ },12502+ "engines": {12503+ "node": ">= 0.4"12504+ },12505+ "funding": {12506+ "url": "https://github.com/sponsors/ljharb"12507+ }12508+ },1132312509 "node_modules/has-symbols": {1132412510 "version": "1.1.0",1132512511 "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",@@ -11332,6 +12518,21 @@
1133212518 "url": "https://github.com/sponsors/ljharb"1133312519 }1133412520 },12521+ "node_modules/has-tostringtag": {12522+ "version": "1.0.2",12523+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",12524+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",12525+ "license": "MIT",12526+ "dependencies": {12527+ "has-symbols": "^1.0.3"12528+ },12529+ "engines": {12530+ "node": ">= 0.4"12531+ },12532+ "funding": {12533+ "url": "https://github.com/sponsors/ljharb"12534+ }12535+ },1133512536 "node_modules/has-yarn": {1133612537 "version": "3.0.0",1133712538 "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-3.0.0.tgz",@@ -11880,6 +13081,12 @@
1188013081 "postcss": "^8.1.0"1188113082 }1188213083 },13084+ "node_modules/idb": {13085+ "version": "7.1.1",13086+ "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz",13087+ "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==",13088+ "license": "ISC"13089+ },1188313090 "node_modules/ignore": {1188413091 "version": "5.3.2",1188513092 "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",@@ -11990,6 +13197,20 @@
1199013197 "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==",1199113198 "license": "MIT"1199213199 },13200+ "node_modules/internal-slot": {13201+ "version": "1.1.0",13202+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",13203+ "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",13204+ "license": "MIT",13205+ "dependencies": {13206+ "es-errors": "^1.3.0",13207+ "hasown": "^2.0.2",13208+ "side-channel": "^1.1.0"13209+ },13210+ "engines": {13211+ "node": ">= 0.4"13212+ }13213+ },1199313214 "node_modules/internmap": {1199413215 "version": "2.0.3",1199513216 "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",@@ -12041,12 +13262,63 @@
1204113262 "url": "https://github.com/sponsors/wooorm"1204213263 }1204313264 },13265+ "node_modules/is-array-buffer": {13266+ "version": "3.0.5",13267+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",13268+ "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==",13269+ "license": "MIT",13270+ "dependencies": {13271+ "call-bind": "^1.0.8",13272+ "call-bound": "^1.0.3",13273+ "get-intrinsic": "^1.2.6"13274+ },13275+ "engines": {13276+ "node": ">= 0.4"13277+ },13278+ "funding": {13279+ "url": "https://github.com/sponsors/ljharb"13280+ }13281+ },1204413282 "node_modules/is-arrayish": {1204513283 "version": "0.2.1",1204613284 "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",1204713285 "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",1204813286 "license": "MIT"1204913287 },13288+ "node_modules/is-async-function": {13289+ "version": "2.1.1",13290+ "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",13291+ "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",13292+ "license": "MIT",13293+ "dependencies": {13294+ "async-function": "^1.0.0",13295+ "call-bound": "^1.0.3",13296+ "get-proto": "^1.0.1",13297+ "has-tostringtag": "^1.0.2",13298+ "safe-regex-test": "^1.1.0"13299+ },13300+ "engines": {13301+ "node": ">= 0.4"13302+ },13303+ "funding": {13304+ "url": "https://github.com/sponsors/ljharb"13305+ }13306+ },13307+ "node_modules/is-bigint": {13308+ "version": "1.1.0",13309+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz",13310+ "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",13311+ "license": "MIT",13312+ "dependencies": {13313+ "has-bigints": "^1.0.2"13314+ },13315+ "engines": {13316+ "node": ">= 0.4"13317+ },13318+ "funding": {13319+ "url": "https://github.com/sponsors/ljharb"13320+ }13321+ },1205013322 "node_modules/is-binary-path": {1205113323 "version": "2.1.0",1205213324 "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",@@ -12059,6 +13331,34 @@
1205913331 "node": ">=8"1206013332 }1206113333 },13334+ "node_modules/is-boolean-object": {13335+ "version": "1.2.2",13336+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",13337+ "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",13338+ "license": "MIT",13339+ "dependencies": {13340+ "call-bound": "^1.0.3",13341+ "has-tostringtag": "^1.0.2"13342+ },13343+ "engines": {13344+ "node": ">= 0.4"13345+ },13346+ "funding": {13347+ "url": "https://github.com/sponsors/ljharb"13348+ }13349+ },13350+ "node_modules/is-callable": {13351+ "version": "1.2.7",13352+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",13353+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",13354+ "license": "MIT",13355+ "engines": {13356+ "node": ">= 0.4"13357+ },13358+ "funding": {13359+ "url": "https://github.com/sponsors/ljharb"13360+ }13361+ },1206213362 "node_modules/is-ci": {1206313363 "version": "3.0.1",1206413364 "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz",@@ -12086,6 +13386,39 @@
1208613386 "url": "https://github.com/sponsors/ljharb"1208713387 }1208813388 },13389+ "node_modules/is-data-view": {13390+ "version": "1.0.2",13391+ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz",13392+ "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==",13393+ "license": "MIT",13394+ "dependencies": {13395+ "call-bound": "^1.0.2",13396+ "get-intrinsic": "^1.2.6",13397+ "is-typed-array": "^1.1.13"13398+ },13399+ "engines": {13400+ "node": ">= 0.4"13401+ },13402+ "funding": {13403+ "url": "https://github.com/sponsors/ljharb"13404+ }13405+ },13406+ "node_modules/is-date-object": {13407+ "version": "1.1.0",13408+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",13409+ "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",13410+ "license": "MIT",13411+ "dependencies": {13412+ "call-bound": "^1.0.2",13413+ "has-tostringtag": "^1.0.2"13414+ },13415+ "engines": {13416+ "node": ">= 0.4"13417+ },13418+ "funding": {13419+ "url": "https://github.com/sponsors/ljharb"13420+ }13421+ },1208913422 "node_modules/is-decimal": {1209013423 "version": "2.0.1",1209113424 "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",@@ -12111,6 +13444,21 @@
1211113444 "url": "https://github.com/sponsors/sindresorhus"1211213445 }1211313446 },13447+ "node_modules/is-document.all": {13448+ "version": "1.0.0",13449+ "resolved": "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz",13450+ "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==",13451+ "license": "MIT",13452+ "dependencies": {13453+ "call-bound": "^1.0.4"13454+ },13455+ "engines": {13456+ "node": ">= 0.4"13457+ },13458+ "funding": {13459+ "url": "https://github.com/sponsors/ljharb"13460+ }13461+ },1211413462 "node_modules/is-extendable": {1211513463 "version": "0.1.1",1211613464 "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",@@ -12129,6 +13477,21 @@
1212913477 "node": ">=0.10.0"1213013478 }1213113479 },13480+ "node_modules/is-finalizationregistry": {13481+ "version": "1.1.1",13482+ "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",13483+ "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",13484+ "license": "MIT",13485+ "dependencies": {13486+ "call-bound": "^1.0.3"13487+ },13488+ "engines": {13489+ "node": ">= 0.4"13490+ },13491+ "funding": {13492+ "url": "https://github.com/sponsors/ljharb"13493+ }13494+ },1213213495 "node_modules/is-fullwidth-code-point": {1213313496 "version": "3.0.0",1213413497 "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",@@ -12138,6 +13501,25 @@
1213813501 "node": ">=8"1213913502 }1214013503 },13504+ "node_modules/is-generator-function": {13505+ "version": "1.1.2",13506+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",13507+ "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",13508+ "license": "MIT",13509+ "dependencies": {13510+ "call-bound": "^1.0.4",13511+ "generator-function": "^2.0.0",13512+ "get-proto": "^1.0.1",13513+ "has-tostringtag": "^1.0.2",13514+ "safe-regex-test": "^1.1.0"13515+ },13516+ "engines": {13517+ "node": ">= 0.4"13518+ },13519+ "funding": {13520+ "url": "https://github.com/sponsors/ljharb"13521+ }13522+ },1214113523 "node_modules/is-glob": {1214213524 "version": "4.0.3",1214313525 "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",@@ -12209,6 +13591,36 @@
1220913591 "url": "https://github.com/sponsors/sindresorhus"1221013592 }1221113593 },13594+ "node_modules/is-map": {13595+ "version": "2.0.3",13596+ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",13597+ "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",13598+ "license": "MIT",13599+ "engines": {13600+ "node": ">= 0.4"13601+ },13602+ "funding": {13603+ "url": "https://github.com/sponsors/ljharb"13604+ }13605+ },13606+ "node_modules/is-module": {13607+ "version": "1.0.0",13608+ "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",13609+ "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",13610+ "license": "MIT"13611+ },13612+ "node_modules/is-negative-zero": {13613+ "version": "2.0.3",13614+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",13615+ "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",13616+ "license": "MIT",13617+ "engines": {13618+ "node": ">= 0.4"13619+ },13620+ "funding": {13621+ "url": "https://github.com/sponsors/ljharb"13622+ }13623+ },1221213624 "node_modules/is-network-error": {1221313625 "version": "1.3.2",1221413626 "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.2.tgz",@@ -12242,6 +13654,22 @@
1224213654 "node": ">=0.12.0"1224313655 }1224413656 },13657+ "node_modules/is-number-object": {13658+ "version": "1.1.1",13659+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz",13660+ "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==",13661+ "license": "MIT",13662+ "dependencies": {13663+ "call-bound": "^1.0.3",13664+ "has-tostringtag": "^1.0.2"13665+ },13666+ "engines": {13667+ "node": ">= 0.4"13668+ },13669+ "funding": {13670+ "url": "https://github.com/sponsors/ljharb"13671+ }13672+ },1224513673 "node_modules/is-obj": {1224613674 "version": "1.0.1",1224713675 "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",@@ -12284,6 +13712,24 @@
1228413712 "node": ">=0.10.0"1228513713 }1228613714 },13715+ "node_modules/is-regex": {13716+ "version": "1.2.1",13717+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",13718+ "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",13719+ "license": "MIT",13720+ "dependencies": {13721+ "call-bound": "^1.0.2",13722+ "gopd": "^1.2.0",13723+ "has-tostringtag": "^1.0.2",13724+ "hasown": "^2.0.2"13725+ },13726+ "engines": {13727+ "node": ">= 0.4"13728+ },13729+ "funding": {13730+ "url": "https://github.com/sponsors/ljharb"13731+ }13732+ },1228713733 "node_modules/is-regexp": {1228813734 "version": "1.0.0",1228913735 "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz",@@ -12293,6 +13739,33 @@
1229313739 "node": ">=0.10.0"1229413740 }1229513741 },13742+ "node_modules/is-set": {13743+ "version": "2.0.3",13744+ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",13745+ "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",13746+ "license": "MIT",13747+ "engines": {13748+ "node": ">= 0.4"13749+ },13750+ "funding": {13751+ "url": "https://github.com/sponsors/ljharb"13752+ }13753+ },13754+ "node_modules/is-shared-array-buffer": {13755+ "version": "1.0.4",13756+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz",13757+ "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==",13758+ "license": "MIT",13759+ "dependencies": {13760+ "call-bound": "^1.0.3"13761+ },13762+ "engines": {13763+ "node": ">= 0.4"13764+ },13765+ "funding": {13766+ "url": "https://github.com/sponsors/ljharb"13767+ }13768+ },1229613769 "node_modules/is-stream": {1229713770 "version": "2.0.1",1229813771 "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",@@ -12305,12 +13778,103 @@
1230513778 "url": "https://github.com/sponsors/sindresorhus"1230613779 }1230713780 },13781+ "node_modules/is-string": {13782+ "version": "1.1.1",13783+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",13784+ "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==",13785+ "license": "MIT",13786+ "dependencies": {13787+ "call-bound": "^1.0.3",13788+ "has-tostringtag": "^1.0.2"13789+ },13790+ "engines": {13791+ "node": ">= 0.4"13792+ },13793+ "funding": {13794+ "url": "https://github.com/sponsors/ljharb"13795+ }13796+ },13797+ "node_modules/is-symbol": {13798+ "version": "1.1.1",13799+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz",13800+ "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",13801+ "license": "MIT",13802+ "dependencies": {13803+ "call-bound": "^1.0.2",13804+ "has-symbols": "^1.1.0",13805+ "safe-regex-test": "^1.1.0"13806+ },13807+ "engines": {13808+ "node": ">= 0.4"13809+ },13810+ "funding": {13811+ "url": "https://github.com/sponsors/ljharb"13812+ }13813+ },13814+ "node_modules/is-typed-array": {13815+ "version": "1.1.15",13816+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",13817+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",13818+ "license": "MIT",13819+ "dependencies": {13820+ "which-typed-array": "^1.1.16"13821+ },13822+ "engines": {13823+ "node": ">= 0.4"13824+ },13825+ "funding": {13826+ "url": "https://github.com/sponsors/ljharb"13827+ }13828+ },1230813829 "node_modules/is-typedarray": {1230913830 "version": "1.0.0",1231013831 "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",1231113832 "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",1231213833 "license": "MIT"1231313834 },13835+ "node_modules/is-weakmap": {13836+ "version": "2.0.2",13837+ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",13838+ "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",13839+ "license": "MIT",13840+ "engines": {13841+ "node": ">= 0.4"13842+ },13843+ "funding": {13844+ "url": "https://github.com/sponsors/ljharb"13845+ }13846+ },13847+ "node_modules/is-weakref": {13848+ "version": "1.1.1",13849+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",13850+ "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",13851+ "license": "MIT",13852+ "dependencies": {13853+ "call-bound": "^1.0.3"13854+ },13855+ "engines": {13856+ "node": ">= 0.4"13857+ },13858+ "funding": {13859+ "url": "https://github.com/sponsors/ljharb"13860+ }13861+ },13862+ "node_modules/is-weakset": {13863+ "version": "2.0.4",13864+ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz",13865+ "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",13866+ "license": "MIT",13867+ "dependencies": {13868+ "call-bound": "^1.0.3",13869+ "get-intrinsic": "^1.2.6"13870+ },13871+ "engines": {13872+ "node": ">= 0.4"13873+ },13874+ "funding": {13875+ "url": "https://github.com/sponsors/ljharb"13876+ }13877+ },1231413878 "node_modules/is-wsl": {1231513879 "version": "2.2.0",1231613880 "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",@@ -12353,6 +13917,38 @@
1235313917 "node": ">=0.10.0"1235413918 }1235513919 },13920+ "node_modules/jackspeak": {13921+ "version": "4.2.3",13922+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz",13923+ "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==",13924+ "license": "BlueOak-1.0.0",13925+ "dependencies": {13926+ "@isaacs/cliui": "^9.0.0"13927+ },13928+ "engines": {13929+ "node": "20 || >=22"13930+ },13931+ "funding": {13932+ "url": "https://github.com/sponsors/isaacs"13933+ }13934+ },13935+ "node_modules/jake": {13936+ "version": "10.9.4",13937+ "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz",13938+ "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",13939+ "license": "Apache-2.0",13940+ "dependencies": {13941+ "async": "^3.2.6",13942+ "filelist": "^1.0.4",13943+ "picocolors": "^1.1.1"13944+ },13945+ "bin": {13946+ "jake": "bin/cli.js"13947+ },13948+ "engines": {13949+ "node": ">=10"13950+ }13951+ },1235613952 "node_modules/jest-util": {1235713953 "version": "29.7.0",1235813954 "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",@@ -12504,6 +14100,15 @@
1250414100 "graceful-fs": "^4.1.6"1250514101 }1250614102 },14103+ "node_modules/jsonpointer": {14104+ "version": "5.0.1",14105+ "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz",14106+ "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==",14107+ "license": "MIT",14108+ "engines": {14109+ "node": ">=0.10.0"14110+ }14111+ },1250714112 "node_modules/katex": {1250814113 "version": "0.16.47",1250914114 "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz",@@ -12955,6 +14560,12 @@
1295514560 "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",1295614561 "license": "MIT"1295714562 },14563+ "node_modules/lodash.sortby": {14564+ "version": "4.7.0",14565+ "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",14566+ "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==",14567+ "license": "MIT"14568+ },1295814569 "node_modules/lodash.uniq": {1295914570 "version": "4.5.0",1296014571 "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",@@ -13025,6 +14636,15 @@
1302514636 "integrity": "sha512-3LVgE7ekWXt04NBci/hjm+NXJxXZeRXuyClL0kA0HONyBOjxhP3ZQkuWIM4Ok3pbeptUW/rj3XcJcJuJVPwPYA==",1302614637 "license": "MPL-1.1"1302714638 },14639+ "node_modules/magic-string": {14640+ "version": "0.30.21",14641+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",14642+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",14643+ "license": "MIT",14644+ "dependencies": {14645+ "@jridgewell/sourcemap-codec": "^1.5.5"14646+ }14647+ },1302814648 "node_modules/mark.js": {1302914649 "version": "8.11.1",1303014650 "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz",@@ -15569,6 +17189,15 @@
1556917189 "url": "https://github.com/chalk/supports-color?sponsor=1"1557017190 }1557117191 },17192+ "node_modules/minipass": {17193+ "version": "7.1.3",17194+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",17195+ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",17196+ "license": "BlueOak-1.0.0",17197+ "engines": {17198+ "node": ">=16 || 14 >=14.17"17199+ }17200+ },1557217201 "node_modules/mrmime": {1557317202 "version": "2.0.1",1557417203 "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",@@ -15902,6 +17531,23 @@
1590217531 "opener": "bin/opener-bin.js"1590317532 }1590417533 },17534+ "node_modules/own-keys": {17535+ "version": "1.0.1",17536+ "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",17537+ "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",17538+ "license": "MIT",17539+ "dependencies": {17540+ "get-intrinsic": "^1.2.6",17541+ "object-keys": "^1.1.1",17542+ "safe-push-apply": "^1.0.0"17543+ },17544+ "engines": {17545+ "node": ">= 0.4"17546+ },17547+ "funding": {17548+ "url": "https://github.com/sponsors/ljharb"17549+ }17550+ },1590517551 "node_modules/p-cancelable": {1590617552 "version": "3.0.0",1590717553 "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz",@@ -16028,6 +17674,12 @@
1602817674 "url": "https://github.com/sponsors/sindresorhus"1602917675 }1603017676 },17677+ "node_modules/package-json-from-dist": {17678+ "version": "1.0.1",17679+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",17680+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",17681+ "license": "BlueOak-1.0.0"17682+ },1603117683 "node_modules/package-manager-detector": {1603217684 "version": "1.7.0",1603317685 "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.7.0.tgz",@@ -16203,11 +17855,36 @@
1620317855 "node": ">=8"1620417856 }1620517857 },16206- "node_modules/path-parse": {16207- "version": "1.0.7",16208- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",16209- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",16210- "license": "MIT"17858+ "node_modules/path-parse": {17859+ "version": "1.0.7",17860+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",17861+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",17862+ "license": "MIT"17863+ },17864+ "node_modules/path-scurry": {17865+ "version": "2.0.2",17866+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",17867+ "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",17868+ "license": "BlueOak-1.0.0",17869+ "dependencies": {17870+ "lru-cache": "^11.0.0",17871+ "minipass": "^7.1.2"17872+ },17873+ "engines": {17874+ "node": "18 || 20 || >=22"17875+ },17876+ "funding": {17877+ "url": "https://github.com/sponsors/isaacs"17878+ }17879+ },17880+ "node_modules/path-scurry/node_modules/lru-cache": {17881+ "version": "11.5.2",17882+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz",17883+ "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==",17884+ "license": "BlueOak-1.0.0",17885+ "engines": {17886+ "node": "20 || >=22"17887+ }1621117888 },1621217889 "node_modules/path-to-regexp": {1621317890 "version": "1.9.0",@@ -16293,6 +17970,15 @@
1629317970 "points-on-curve": "0.2.0"1629417971 }1629517972 },17973+ "node_modules/possible-typed-array-names": {17974+ "version": "1.1.0",17975+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",17976+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",17977+ "license": "MIT",17978+ "engines": {17979+ "node": ">= 0.4"17980+ }17981+ },1629617982 "node_modules/postcss": {1629717983 "version": "8.5.19",1629817984 "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.19.tgz",@@ -17756,6 +19442,18 @@
1775619442 "postcss": "^8.4.31"1775719443 }1775819444 },19445+ "node_modules/pretty-bytes": {19446+ "version": "5.6.0",19447+ "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",19448+ "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==",19449+ "license": "MIT",19450+ "engines": {19451+ "node": ">=6"19452+ },19453+ "funding": {19454+ "url": "https://github.com/sponsors/sindresorhus"19455+ }19456+ },1775919457 "node_modules/pretty-error": {1776019458 "version": "4.0.0",1776119459 "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",@@ -18281,6 +19979,28 @@
1828119979 "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",1828219980 "license": "Apache-2.0"1828319981 },19982+ "node_modules/reflect.getprototypeof": {19983+ "version": "1.0.10",19984+ "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",19985+ "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",19986+ "license": "MIT",19987+ "dependencies": {19988+ "call-bind": "^1.0.8",19989+ "define-properties": "^1.2.1",19990+ "es-abstract": "^1.23.9",19991+ "es-errors": "^1.3.0",19992+ "es-object-atoms": "^1.0.0",19993+ "get-intrinsic": "^1.2.7",19994+ "get-proto": "^1.0.1",19995+ "which-builtin-type": "^1.2.1"19996+ },19997+ "engines": {19998+ "node": ">= 0.4"19999+ },20000+ "funding": {20001+ "url": "https://github.com/sponsors/ljharb"20002+ }20003+ },1828420004 "node_modules/regenerate": {1828520005 "version": "1.4.2",1828620006 "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",@@ -18299,6 +20019,26 @@
1829920019 "node": ">=4"1830020020 }1830120021 },20022+ "node_modules/regexp.prototype.flags": {20023+ "version": "1.5.4",20024+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",20025+ "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",20026+ "license": "MIT",20027+ "dependencies": {20028+ "call-bind": "^1.0.8",20029+ "define-properties": "^1.2.1",20030+ "es-errors": "^1.3.0",20031+ "get-proto": "^1.0.1",20032+ "gopd": "^1.2.0",20033+ "set-function-name": "^2.0.2"20034+ },20035+ "engines": {20036+ "node": ">= 0.4"20037+ },20038+ "funding": {20039+ "url": "https://github.com/sponsors/ljharb"20040+ }20041+ },1830220042 "node_modules/regexpu-core": {1830320043 "version": "6.4.0",1830420044 "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz",@@ -18733,6 +20473,50 @@
1873320473 "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==",1873420474 "license": "Unlicense"1873520475 },20476+ "node_modules/rollup": {20477+ "version": "4.62.2",20478+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz",20479+ "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==",20480+ "license": "MIT",20481+ "dependencies": {20482+ "@types/estree": "1.0.9"20483+ },20484+ "bin": {20485+ "rollup": "dist/bin/rollup"20486+ },20487+ "engines": {20488+ "node": ">=18.0.0",20489+ "npm": ">=8.0.0"20490+ },20491+ "optionalDependencies": {20492+ "@rollup/rollup-android-arm-eabi": "4.62.2",20493+ "@rollup/rollup-android-arm64": "4.62.2",20494+ "@rollup/rollup-darwin-arm64": "4.62.2",20495+ "@rollup/rollup-darwin-x64": "4.62.2",20496+ "@rollup/rollup-freebsd-arm64": "4.62.2",20497+ "@rollup/rollup-freebsd-x64": "4.62.2",20498+ "@rollup/rollup-linux-arm-gnueabihf": "4.62.2",20499+ "@rollup/rollup-linux-arm-musleabihf": "4.62.2",20500+ "@rollup/rollup-linux-arm64-gnu": "4.62.2",20501+ "@rollup/rollup-linux-arm64-musl": "4.62.2",20502+ "@rollup/rollup-linux-loong64-gnu": "4.62.2",20503+ "@rollup/rollup-linux-loong64-musl": "4.62.2",20504+ "@rollup/rollup-linux-ppc64-gnu": "4.62.2",20505+ "@rollup/rollup-linux-ppc64-musl": "4.62.2",20506+ "@rollup/rollup-linux-riscv64-gnu": "4.62.2",20507+ "@rollup/rollup-linux-riscv64-musl": "4.62.2",20508+ "@rollup/rollup-linux-s390x-gnu": "4.62.2",20509+ "@rollup/rollup-linux-x64-gnu": "4.62.2",20510+ "@rollup/rollup-linux-x64-musl": "4.62.2",20511+ "@rollup/rollup-openbsd-x64": "4.62.2",20512+ "@rollup/rollup-openharmony-arm64": "4.62.2",20513+ "@rollup/rollup-win32-arm64-msvc": "4.62.2",20514+ "@rollup/rollup-win32-ia32-msvc": "4.62.2",20515+ "@rollup/rollup-win32-x64-gnu": "4.62.2",20516+ "@rollup/rollup-win32-x64-msvc": "4.62.2",20517+ "fsevents": "~2.3.2"20518+ }20519+ },1873620520 "node_modules/roughjs": {1873720521 "version": "4.6.6",1873820522 "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz",@@ -18804,6 +20588,31 @@
1880420588 "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",1880520589 "license": "BSD-3-Clause"1880620590 },20591+ "node_modules/safe-array-concat": {20592+ "version": "1.1.4",20593+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz",20594+ "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==",20595+ "license": "MIT",20596+ "dependencies": {20597+ "call-bind": "^1.0.9",20598+ "call-bound": "^1.0.4",20599+ "get-intrinsic": "^1.3.0",20600+ "has-symbols": "^1.1.0",20601+ "isarray": "^2.0.5"20602+ },20603+ "engines": {20604+ "node": ">=0.4"20605+ },20606+ "funding": {20607+ "url": "https://github.com/sponsors/ljharb"20608+ }20609+ },20610+ "node_modules/safe-array-concat/node_modules/isarray": {20611+ "version": "2.0.5",20612+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",20613+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",20614+ "license": "MIT"20615+ },1880720616 "node_modules/safe-buffer": {1880820617 "version": "5.2.1",1880920618 "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",@@ -18824,6 +20633,45 @@
1882420633 ],1882520634 "license": "MIT"1882620635 },20636+ "node_modules/safe-push-apply": {20637+ "version": "1.0.0",20638+ "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",20639+ "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",20640+ "license": "MIT",20641+ "dependencies": {20642+ "es-errors": "^1.3.0",20643+ "isarray": "^2.0.5"20644+ },20645+ "engines": {20646+ "node": ">= 0.4"20647+ },20648+ "funding": {20649+ "url": "https://github.com/sponsors/ljharb"20650+ }20651+ },20652+ "node_modules/safe-push-apply/node_modules/isarray": {20653+ "version": "2.0.5",20654+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",20655+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",20656+ "license": "MIT"20657+ },20658+ "node_modules/safe-regex-test": {20659+ "version": "1.1.0",20660+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",20661+ "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",20662+ "license": "MIT",20663+ "dependencies": {20664+ "call-bound": "^1.0.2",20665+ "es-errors": "^1.3.0",20666+ "is-regex": "^1.2.1"20667+ },20668+ "engines": {20669+ "node": ">= 0.4"20670+ },20671+ "funding": {20672+ "url": "https://github.com/sponsors/ljharb"20673+ }20674+ },1882720675 "node_modules/safer-buffer": {1882820676 "version": "2.1.2",1882920677 "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",@@ -19138,6 +20986,35 @@
1913820986 "node": ">= 0.4"1913920987 }1914020988 },20989+ "node_modules/set-function-name": {20990+ "version": "2.0.2",20991+ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",20992+ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",20993+ "license": "MIT",20994+ "dependencies": {20995+ "define-data-property": "^1.1.4",20996+ "es-errors": "^1.3.0",20997+ "functions-have-names": "^1.2.3",20998+ "has-property-descriptors": "^1.0.2"20999+ },21000+ "engines": {21001+ "node": ">= 0.4"21002+ }21003+ },21004+ "node_modules/set-proto": {21005+ "version": "1.0.0",21006+ "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",21007+ "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",21008+ "license": "MIT",21009+ "dependencies": {21010+ "dunder-proto": "^1.0.1",21011+ "es-errors": "^1.3.0",21012+ "es-object-atoms": "^1.0.0"21013+ },21014+ "engines": {21015+ "node": ">= 0.4"21016+ }21017+ },1914121018 "node_modules/setprototypeof": {1914221019 "version": "1.2.0",1914321020 "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",@@ -19339,6 +21216,15 @@
1933921216 "node": ">=8"1934021217 }1934121218 },21219+ "node_modules/smob": {21220+ "version": "1.6.2",21221+ "resolved": "https://registry.npmjs.org/smob/-/smob-1.6.2.tgz",21222+ "integrity": "sha512-RQsvleCbF8cVHEv+xuDGaA4pOizFqJ0GgjtMSRo6oP8pnN7WsigHgVGey6aILRBKv4W2YOMHLqbKdnB6hpB9fw==",21223+ "license": "MIT",21224+ "engines": {21225+ "node": ">=20.0.0"21226+ }21227+ },1934221228 "node_modules/snake-case": {1934321229 "version": "3.0.4",1934421230 "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz",@@ -19483,6 +21369,19 @@
1948321369 "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",1948421370 "license": "MIT"1948521371 },21372+ "node_modules/stop-iteration-iterator": {21373+ "version": "1.1.0",21374+ "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",21375+ "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",21376+ "license": "MIT",21377+ "dependencies": {21378+ "es-errors": "^1.3.0",21379+ "internal-slot": "^1.1.0"21380+ },21381+ "engines": {21382+ "node": ">= 0.4"21383+ }21384+ },1948621385 "node_modules/string_decoder": {1948721386 "version": "1.3.0",1948821387 "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",@@ -19536,6 +21435,90 @@
1953621435 "url": "https://github.com/chalk/strip-ansi?sponsor=1"1953721436 }1953821437 },21438+ "node_modules/string.prototype.matchall": {21439+ "version": "4.0.12",21440+ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",21441+ "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==",21442+ "license": "MIT",21443+ "dependencies": {21444+ "call-bind": "^1.0.8",21445+ "call-bound": "^1.0.3",21446+ "define-properties": "^1.2.1",21447+ "es-abstract": "^1.23.6",21448+ "es-errors": "^1.3.0",21449+ "es-object-atoms": "^1.0.0",21450+ "get-intrinsic": "^1.2.6",21451+ "gopd": "^1.2.0",21452+ "has-symbols": "^1.1.0",21453+ "internal-slot": "^1.1.0",21454+ "regexp.prototype.flags": "^1.5.3",21455+ "set-function-name": "^2.0.2",21456+ "side-channel": "^1.1.0"21457+ },21458+ "engines": {21459+ "node": ">= 0.4"21460+ },21461+ "funding": {21462+ "url": "https://github.com/sponsors/ljharb"21463+ }21464+ },21465+ "node_modules/string.prototype.trim": {21466+ "version": "1.2.11",21467+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz",21468+ "integrity": "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==",21469+ "license": "MIT",21470+ "dependencies": {21471+ "call-bind": "^1.0.9",21472+ "call-bound": "^1.0.4",21473+ "define-data-property": "^1.1.4",21474+ "define-properties": "^1.2.1",21475+ "es-abstract": "^1.24.2",21476+ "es-object-atoms": "^1.1.2",21477+ "has-property-descriptors": "^1.0.2",21478+ "safe-regex-test": "^1.1.0"21479+ },21480+ "engines": {21481+ "node": ">= 0.4"21482+ },21483+ "funding": {21484+ "url": "https://github.com/sponsors/ljharb"21485+ }21486+ },21487+ "node_modules/string.prototype.trimend": {21488+ "version": "1.0.10",21489+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz",21490+ "integrity": "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==",21491+ "license": "MIT",21492+ "dependencies": {21493+ "call-bind": "^1.0.9",21494+ "call-bound": "^1.0.4",21495+ "define-properties": "^1.2.1",21496+ "es-object-atoms": "^1.1.2"21497+ },21498+ "engines": {21499+ "node": ">= 0.4"21500+ },21501+ "funding": {21502+ "url": "https://github.com/sponsors/ljharb"21503+ }21504+ },21505+ "node_modules/string.prototype.trimstart": {21506+ "version": "1.0.8",21507+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",21508+ "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",21509+ "license": "MIT",21510+ "dependencies": {21511+ "call-bind": "^1.0.7",21512+ "define-properties": "^1.2.1",21513+ "es-object-atoms": "^1.0.0"21514+ },21515+ "engines": {21516+ "node": ">= 0.4"21517+ },21518+ "funding": {21519+ "url": "https://github.com/sponsors/ljharb"21520+ }21521+ },1953921522 "node_modules/stringify-entities": {1954021523 "version": "4.0.4",1954121524 "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",@@ -19585,6 +21568,15 @@
1958521568 "node": ">=0.10.0"1958621569 }1958721570 },21571+ "node_modules/strip-comments": {21572+ "version": "2.0.1",21573+ "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz",21574+ "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==",21575+ "license": "MIT",21576+ "engines": {21577+ "node": ">=10"21578+ }21579+ },1958821580 "node_modules/strip-final-newline": {1958921581 "version": "2.0.0",1959021582 "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",@@ -19697,43 +21689,103 @@
1969721689 "node": ">=14.0.0"1969821690 },1969921691 "funding": {19700- "type": "opencollective",19701- "url": "https://opencollective.com/svgo"21692+ "type": "opencollective",21693+ "url": "https://opencollective.com/svgo"21694+ }21695+ },21696+ "node_modules/svgo/node_modules/commander": {21697+ "version": "7.2.0",21698+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",21699+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",21700+ "license": "MIT",21701+ "engines": {21702+ "node": ">= 10"21703+ }21704+ },21705+ "node_modules/swc-loader": {21706+ "version": "0.2.7",21707+ "resolved": "https://registry.npmjs.org/swc-loader/-/swc-loader-0.2.7.tgz",21708+ "integrity": "sha512-nwYWw3Fh9ame3Rtm7StS9SBLpHRRnYcK7bnpF3UKZmesAK0gw2/ADvlURFAINmPvKtDLzp+GBiP9yLoEjg6S9w==",21709+ "license": "MIT",21710+ "dependencies": {21711+ "@swc/counter": "^0.1.3"21712+ },21713+ "peerDependencies": {21714+ "@swc/core": "^1.2.147",21715+ "webpack": ">=2"21716+ }21717+ },21718+ "node_modules/tapable": {21719+ "version": "2.3.3",21720+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz",21721+ "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==",21722+ "license": "MIT",21723+ "engines": {21724+ "node": ">=6"21725+ },21726+ "funding": {21727+ "type": "opencollective",21728+ "url": "https://opencollective.com/webpack"21729+ }21730+ },21731+ "node_modules/temp-dir": {21732+ "version": "2.0.0",21733+ "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz",21734+ "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==",21735+ "license": "MIT",21736+ "engines": {21737+ "node": ">=8"21738+ }21739+ },21740+ "node_modules/tempy": {21741+ "version": "0.6.0",21742+ "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz",21743+ "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==",21744+ "license": "MIT",21745+ "dependencies": {21746+ "is-stream": "^2.0.0",21747+ "temp-dir": "^2.0.0",21748+ "type-fest": "^0.16.0",21749+ "unique-string": "^2.0.0"21750+ },21751+ "engines": {21752+ "node": ">=10"21753+ },21754+ "funding": {21755+ "url": "https://github.com/sponsors/sindresorhus"1970221756 }1970321757 },19704- "node_modules/svgo/node_modules/commander": {19705- "version": "7.2.0",19706- "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",19707- "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",21758+ "node_modules/tempy/node_modules/crypto-random-string": {21759+ "version": "2.0.0",21760+ "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",21761+ "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",1970821762 "license": "MIT",1970921763 "engines": {19710- "node": ">= 10"21764+ "node": ">=8"1971121765 }1971221766 },19713- "node_modules/swc-loader": {19714- "version": "0.2.7",19715- "resolved": "https://registry.npmjs.org/swc-loader/-/swc-loader-0.2.7.tgz",19716- "integrity": "sha512-nwYWw3Fh9ame3Rtm7StS9SBLpHRRnYcK7bnpF3UKZmesAK0gw2/ADvlURFAINmPvKtDLzp+GBiP9yLoEjg6S9w==",19717- "license": "MIT",19718- "dependencies": {19719- "@swc/counter": "^0.1.3"21767+ "node_modules/tempy/node_modules/type-fest": {21768+ "version": "0.16.0",21769+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz",21770+ "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==",21771+ "license": "(MIT OR CC0-1.0)",21772+ "engines": {21773+ "node": ">=10"1972021774 },19721- "peerDependencies": {19722- "@swc/core": "^1.2.147",19723- "webpack": ">=2"21775+ "funding": {21776+ "url": "https://github.com/sponsors/sindresorhus"1972421777 }1972521778 },19726- "node_modules/tapable": {19727- "version": "2.3.3",19728- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz",19729- "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==",21779+ "node_modules/tempy/node_modules/unique-string": {21780+ "version": "2.0.0",21781+ "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",21782+ "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==",1973021783 "license": "MIT",19731- "engines": {19732- "node": ">=6"21784+ "dependencies": {21785+ "crypto-random-string": "^2.0.0"1973321786 },19734- "funding": {19735- "type": "opencollective",19736- "url": "https://opencollective.com/webpack"21787+ "engines": {21788+ "node": ">=8"1973721789 }1973821790 },1973921791 "node_modules/terser": {@@ -19931,6 +21983,15 @@
1993121983 "node": ">=6"1993221984 }1993321985 },21986+ "node_modules/tr46": {21987+ "version": "1.0.1",21988+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",21989+ "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==",21990+ "license": "MIT",21991+ "dependencies": {21992+ "punycode": "^2.1.0"21993+ }21994+ },1993421995 "node_modules/tree-dump": {1993521996 "version": "1.1.0",1993621997 "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.1.0.tgz",@@ -20046,6 +22107,80 @@
2004622107 "node": ">= 0.6"2004722108 }2004822109 },22110+ "node_modules/typed-array-buffer": {22111+ "version": "1.0.3",22112+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",22113+ "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",22114+ "license": "MIT",22115+ "dependencies": {22116+ "call-bound": "^1.0.3",22117+ "es-errors": "^1.3.0",22118+ "is-typed-array": "^1.1.14"22119+ },22120+ "engines": {22121+ "node": ">= 0.4"22122+ }22123+ },22124+ "node_modules/typed-array-byte-length": {22125+ "version": "1.0.3",22126+ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz",22127+ "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==",22128+ "license": "MIT",22129+ "dependencies": {22130+ "call-bind": "^1.0.8",22131+ "for-each": "^0.3.3",22132+ "gopd": "^1.2.0",22133+ "has-proto": "^1.2.0",22134+ "is-typed-array": "^1.1.14"22135+ },22136+ "engines": {22137+ "node": ">= 0.4"22138+ },22139+ "funding": {22140+ "url": "https://github.com/sponsors/ljharb"22141+ }22142+ },22143+ "node_modules/typed-array-byte-offset": {22144+ "version": "1.0.4",22145+ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",22146+ "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",22147+ "license": "MIT",22148+ "dependencies": {22149+ "available-typed-arrays": "^1.0.7",22150+ "call-bind": "^1.0.8",22151+ "for-each": "^0.3.3",22152+ "gopd": "^1.2.0",22153+ "has-proto": "^1.2.0",22154+ "is-typed-array": "^1.1.15",22155+ "reflect.getprototypeof": "^1.0.9"22156+ },22157+ "engines": {22158+ "node": ">= 0.4"22159+ },22160+ "funding": {22161+ "url": "https://github.com/sponsors/ljharb"22162+ }22163+ },22164+ "node_modules/typed-array-length": {22165+ "version": "1.0.8",22166+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz",22167+ "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==",22168+ "license": "MIT",22169+ "dependencies": {22170+ "call-bind": "^1.0.9",22171+ "for-each": "^0.3.5",22172+ "gopd": "^1.2.0",22173+ "is-typed-array": "^1.1.15",22174+ "possible-typed-array-names": "^1.1.0",22175+ "reflect.getprototypeof": "^1.0.10"22176+ },22177+ "engines": {22178+ "node": ">= 0.4"22179+ },22180+ "funding": {22181+ "url": "https://github.com/sponsors/ljharb"22182+ }22183+ },2004922184 "node_modules/typedarray-to-buffer": {2005022185 "version": "3.1.5",2005122186 "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",@@ -20069,6 +22204,24 @@
2006922204 "node": ">=14.17"2007022205 }2007122206 },22207+ "node_modules/unbox-primitive": {22208+ "version": "1.1.0",22209+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",22210+ "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",22211+ "license": "MIT",22212+ "dependencies": {22213+ "call-bound": "^1.0.3",22214+ "has-bigints": "^1.0.2",22215+ "has-symbols": "^1.1.0",22216+ "which-boxed-primitive": "^1.1.1"22217+ },22218+ "engines": {22219+ "node": ">= 0.4"22220+ },22221+ "funding": {22222+ "url": "https://github.com/sponsors/ljharb"22223+ }22224+ },2007222225 "node_modules/undici": {2007322226 "version": "7.28.0",2007422227 "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz",@@ -20266,6 +22419,16 @@
2026622419 "node": ">= 0.8"2026722420 }2026822421 },22422+ "node_modules/upath": {22423+ "version": "1.2.0",22424+ "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",22425+ "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",22426+ "license": "MIT",22427+ "engines": {22428+ "node": ">=4",22429+ "yarn": "*"22430+ }22431+ },2026922432 "node_modules/update-browserslist-db": {2027022433 "version": "1.2.3",2027122434 "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",@@ -20607,6 +22770,12 @@
2060722770 "url": "https://github.com/sponsors/wooorm"2060822771 }2060922772 },22773+ "node_modules/webidl-conversions": {22774+ "version": "4.0.2",22775+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",22776+ "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",22777+ "license": "BSD-2-Clause"22778+ },2061022779 "node_modules/webpack": {2061122780 "version": "5.108.4",2061222781 "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.108.4.tgz",@@ -20966,6 +23135,17 @@
2096623135 "node": ">=18"2096723136 }2096823137 },23138+ "node_modules/whatwg-url": {23139+ "version": "7.1.0",23140+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",23141+ "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",23142+ "license": "MIT",23143+ "dependencies": {23144+ "lodash.sortby": "^4.7.0",23145+ "tr46": "^1.0.1",23146+ "webidl-conversions": "^4.0.2"23147+ }23148+ },2096923149 "node_modules/which": {2097023150 "version": "2.0.2",2097123151 "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",@@ -20981,6 +23161,97 @@
2098123161 "node": ">= 8"2098223162 }2098323163 },23164+ "node_modules/which-boxed-primitive": {23165+ "version": "1.1.1",23166+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",23167+ "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==",23168+ "license": "MIT",23169+ "dependencies": {23170+ "is-bigint": "^1.1.0",23171+ "is-boolean-object": "^1.2.1",23172+ "is-number-object": "^1.1.1",23173+ "is-string": "^1.1.1",23174+ "is-symbol": "^1.1.1"23175+ },23176+ "engines": {23177+ "node": ">= 0.4"23178+ },23179+ "funding": {23180+ "url": "https://github.com/sponsors/ljharb"23181+ }23182+ },23183+ "node_modules/which-builtin-type": {23184+ "version": "1.2.1",23185+ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz",23186+ "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==",23187+ "license": "MIT",23188+ "dependencies": {23189+ "call-bound": "^1.0.2",23190+ "function.prototype.name": "^1.1.6",23191+ "has-tostringtag": "^1.0.2",23192+ "is-async-function": "^2.0.0",23193+ "is-date-object": "^1.1.0",23194+ "is-finalizationregistry": "^1.1.0",23195+ "is-generator-function": "^1.0.10",23196+ "is-regex": "^1.2.1",23197+ "is-weakref": "^1.0.2",23198+ "isarray": "^2.0.5",23199+ "which-boxed-primitive": "^1.1.0",23200+ "which-collection": "^1.0.2",23201+ "which-typed-array": "^1.1.16"23202+ },23203+ "engines": {23204+ "node": ">= 0.4"23205+ },23206+ "funding": {23207+ "url": "https://github.com/sponsors/ljharb"23208+ }23209+ },23210+ "node_modules/which-builtin-type/node_modules/isarray": {23211+ "version": "2.0.5",23212+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",23213+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",23214+ "license": "MIT"23215+ },23216+ "node_modules/which-collection": {23217+ "version": "1.0.2",23218+ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",23219+ "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",23220+ "license": "MIT",23221+ "dependencies": {23222+ "is-map": "^2.0.3",23223+ "is-set": "^2.0.3",23224+ "is-weakmap": "^2.0.2",23225+ "is-weakset": "^2.0.3"23226+ },23227+ "engines": {23228+ "node": ">= 0.4"23229+ },23230+ "funding": {23231+ "url": "https://github.com/sponsors/ljharb"23232+ }23233+ },23234+ "node_modules/which-typed-array": {23235+ "version": "1.1.22",23236+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz",23237+ "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==",23238+ "license": "MIT",23239+ "dependencies": {23240+ "available-typed-arrays": "^1.0.7",23241+ "call-bind": "^1.0.9",23242+ "call-bound": "^1.0.4",23243+ "for-each": "^0.3.5",23244+ "get-proto": "^1.0.1",23245+ "gopd": "^1.2.0",23246+ "has-tostringtag": "^1.0.2"23247+ },23248+ "engines": {23249+ "node": ">= 0.4"23250+ },23251+ "funding": {23252+ "url": "https://github.com/sponsors/ljharb"23253+ }23254+ },2098423255 "node_modules/widest-line": {2098523256 "version": "4.0.1",2098623257 "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz",@@ -21002,6 +23273,237 @@
2100223273 "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==",2100323274 "license": "MIT"2100423275 },23276+ "node_modules/workbox-background-sync": {23277+ "version": "7.4.1",23278+ "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-7.4.1.tgz",23279+ "integrity": "sha512-HhT7KE8tOWDm02wRNshXUnUPofMlhenF2DBdUnDPOubhizzPeItkYTmAB6td1Z2cjYPa98vzEiPLEuzn5hN66g==",23280+ "license": "MIT",23281+ "dependencies": {23282+ "idb": "^7.0.1",23283+ "workbox-core": "7.4.1"23284+ }23285+ },23286+ "node_modules/workbox-broadcast-update": {23287+ "version": "7.4.1",23288+ "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-7.4.1.tgz",23289+ "integrity": "sha512-uAlgslKLvbQY+suirIdnBCSYrcgBhjp81Nj4l1lj/Jmj0MJO2CJERnCJjT0GFVwmReV0N+zs78K6gqd5gr9/+A==",23290+ "license": "MIT",23291+ "dependencies": {23292+ "workbox-core": "7.4.1"23293+ }23294+ },23295+ "node_modules/workbox-build": {23296+ "version": "7.4.1",23297+ "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-7.4.1.tgz",23298+ "integrity": "sha512-SDhxIvEAde9Gy/5w4Yo1Jh/M49Z0qE3q0oteyE8zGq0DScxFqVBcCtIXFuLtmtxRQZCMbf0prco4VyEu3KBQuw==",23299+ "license": "MIT",23300+ "dependencies": {23301+ "@apideck/better-ajv-errors": "^0.3.1",23302+ "@babel/core": "^7.24.4",23303+ "@babel/preset-env": "^7.11.0",23304+ "@babel/runtime": "^7.11.2",23305+ "@rollup/plugin-babel": "^6.1.0",23306+ "@rollup/plugin-node-resolve": "^16.0.3",23307+ "@rollup/plugin-replace": "^6.0.3",23308+ "@rollup/plugin-terser": "^1.0.0",23309+ "@trickfilm400/rollup-plugin-off-main-thread": "^3.0.0-pre1",23310+ "ajv": "^8.6.0",23311+ "common-tags": "^1.8.0",23312+ "eta": "^4.5.1",23313+ "fast-json-stable-stringify": "^2.1.0",23314+ "fs-extra": "^9.0.1",23315+ "glob": "^11.0.1",23316+ "pretty-bytes": "^5.3.0",23317+ "rollup": "^4.53.3",23318+ "source-map": "^0.8.0-beta.0",23319+ "stringify-object": "^3.3.0",23320+ "strip-comments": "^2.0.1",23321+ "tempy": "^0.6.0",23322+ "upath": "^1.2.0",23323+ "workbox-background-sync": "7.4.1",23324+ "workbox-broadcast-update": "7.4.1",23325+ "workbox-cacheable-response": "7.4.1",23326+ "workbox-core": "7.4.1",23327+ "workbox-expiration": "7.4.1",23328+ "workbox-google-analytics": "7.4.1",23329+ "workbox-navigation-preload": "7.4.1",23330+ "workbox-precaching": "7.4.1",23331+ "workbox-range-requests": "7.4.1",23332+ "workbox-recipes": "7.4.1",23333+ "workbox-routing": "7.4.1",23334+ "workbox-strategies": "7.4.1",23335+ "workbox-streams": "7.4.1",23336+ "workbox-sw": "7.4.1",23337+ "workbox-window": "7.4.1"23338+ },23339+ "engines": {23340+ "node": ">=20.0.0"23341+ }23342+ },diff truncated
@@ -17,9 +17,18 @@
1717 "dependencies": {1818 "@docusaurus/core": "3.10.2",1919 "@docusaurus/faster": "3.10.2",20+ "@docusaurus/plugin-pwa": "^3.10.2",2021 "@docusaurus/preset-classic": "3.10.2",2122 "@docusaurus/theme-mermaid": "^3.10.2",2223 "@easyops-cn/docusaurus-search-local": "^0.55.2",24+ "@fontsource/alegreya-sans": "^5.2.8",25+ "@fontsource/comfortaa": "^5.2.8",26+ "@fontsource/grenze-gotisch": "^5.2.8",27+ "@fontsource/ibm-plex-mono": "^5.2.7",28+ "@fontsource/karla": "^5.2.8",29+ "@fontsource/nunito": "^5.2.7",30+ "@fontsource/spline-sans-mono": "^5.2.8",31+ "@fontsource/young-serif": "^5.2.7",2332 "@mdx-js/react": "^3.0.0",2433 "clsx": "^2.0.0",2534 "prism-react-renderer": "^2.3.0",@@ -0,0 +1,10 @@
1+import type { SidebarsConfig } from "@docusaurus/plugin-content-docs";2+3+// The lofi-node API reference under website/node-api-gen/ is generated by4+// tools/api_docs.ts --node; ordering comes from the emitted sidebar_position5+// frontmatter.6+const sidebars: SidebarsConfig = {7+ nodeApi: [{ type: "autogenerated", dirName: "." }],8+};9+10+export default sidebars;@@ -0,0 +1,44 @@
1+import type { SidebarsConfig } from "@docusaurus/plugin-content-docs";2+import manifest from "./node-docs-manifest.json";3+4+// Mirrors sidebars.ts: the /node/docs sidebar derives from5+// node-docs-manifest.json so navigation and the generated /node/llms.txt6+// (tools/site_llms.ts) can never disagree about which pages are published or7+// in what order. Page files themselves are assembled by tools/node_docs.ts.8+9+type ManifestItem = { id: string; label: string; source: string };10+type ManifestSection = {11+ label: string | null;12+ collapsed?: boolean;13+ items: ManifestItem[];14+};15+16+type SidebarItem =17+ | { type: "doc"; id: string; label: string }18+ | {19+ type: "category";20+ label: string;21+ collapsed: boolean;22+ items: SidebarItem[];23+ };24+25+const nodeSidebar: SidebarItem[] = (manifest.sections as ManifestSection[]).flatMap(26+ (section) => {27+ const items: SidebarItem[] = section.items.map((item) => ({28+ type: "doc" as const,29+ id: item.id,30+ label: item.label,31+ }));32+ if (section.label === null) return items;33+ return [{34+ type: "category" as const,35+ label: section.label,36+ collapsed: section.collapsed ?? true,37+ items,38+ }];39+ },40+);41+42+const sidebars: SidebarsConfig = { node: nodeSidebar };43+44+export default sidebars;@@ -0,0 +1,29 @@
1+import type { ReactNode } from "react";2+import { useId } from "react";3+4+/** The brand leaf glyph used as a cell marker on the landing pages. */5+export default function Leaf(): ReactNode {6+ const maskId = useId();7+ return (8+ <svg className="cell-leaf" viewBox="0 0 512 512" aria-hidden="true">9+ <defs>10+ <mask id={maskId}>11+ <rect width="512" height="512" fill="#fff" />12+ <path13+ d="M183 184v146h124M183 330 91 455"14+ fill="none"15+ stroke="#000"16+ strokeLinecap="round"17+ strokeLinejoin="round"18+ strokeWidth="38"19+ />20+ </mask>21+ </defs>22+ <path23+ d="M415 56C253 58 112 112 67 224c-32 80-21 173 24 231l47-49c57 29 135 21 197-20 52-34 80-93 80-160Z"24+ fill="currentColor"25+ mask={`url(#${maskId})`}26+ />27+ </svg>28+ );29+}@@ -0,0 +1,207 @@
1+import type { ReactNode } from "react";2+import { useState } from "react";3+4+// The /node hero explainer: a node you operate. Each action is one real step5+// of the lofi-node lifecycle (init → start → ticket → enroll), then the two6+// things worth feeling: revocation closing a live socket, and pairing a7+// second home. Pure state + CSS; no network, nothing measured.8+9+type Stage = "fresh" | "inited" | "started" | "ticketed" | "enrolled";10+11+type Line = { kind: "cmd" | "out" | "warn"; text: string };12+13+const TICKET_PREVIEW = "lofisync1.eyJ2IjoxLCJhcHBJZCI6ImNmZTUyZ…";14+15+export default function NodePlayground(): ReactNode {16+ const [stage, setStage] = useState<Stage>("fresh");17+ const [revoked, setRevoked] = useState(false);18+ const [paired, setPaired] = useState(false);19+ const [lines, setLines] = useState<Line[]>([20+ { kind: "out", text: "a machine of yours: laptop, NAS, corner server" },21+ ]);22+23+ const say = (...next: Line[]) =>24+ setLines((prev) => [...prev.slice(-5), ...next]);25+26+ const init = () => {27+ setStage("inited");28+ say(29+ { kind: "cmd", text: "lofi-node init --port 4802" },30+ { kind: "out", text: "config.json written · ticket-gated by default" },31+ );32+ };33+ const start = () => {34+ setStage("started");35+ say(36+ { kind: "cmd", text: "lofi-node start" },37+ { kind: "out", text: "gate :4802 · store binds loopback-only" },38+ );39+ };40+ const issue = () => {41+ setStage("ticketed");42+ setRevoked(false);43+ say(44+ { kind: "cmd", text: "lofi-node ticket issue --label phone" },45+ { kind: "out", text: `${TICKET_PREVIEW} · shown once` },46+ );47+ };48+ const enroll = () => {49+ setStage("enrolled");50+ say(51+ { kind: "cmd", text: "(on the phone) paste the ticket into the app" },52+ { kind: "out", text: "sync elected; existing local data pushes up" },53+ );54+ };55+ const revoke = () => {56+ setRevoked(true);57+ say(58+ { kind: "cmd", text: "lofi-node ticket revoke a1b2c3d4e5f6" },59+ {60+ kind: "warn",61+ text: "live socket closed 4001 · unknown ≡ revoked to probers",62+ },63+ );64+ };65+ const pair = () => {66+ setPaired(true);67+ say(68+ { kind: "cmd", text: "lofi-node pair endpoint… (at the studio)" },69+ { kind: "out", text: "upstream elected · tunnel up · rtt 23ms, direct" },70+ );71+ };72+ const reset = () => {73+ setStage("fresh");74+ setRevoked(false);75+ setPaired(false);76+ setLines([{77+ kind: "out",78+ text: "a machine of yours: laptop, NAS, corner server",79+ }]);80+ };81+82+ const linked = stage === "enrolled" && !revoked;83+84+ return (85+ <div86+ className="np"87+ data-stage={stage}88+ data-paired={paired ? "" : undefined}89+ >90+ <div className="np-scene" aria-hidden="true">91+ <div className={`np-device ${stage === "enrolled" ? "is-on" : ""}`}>92+ <span className="np-device-glyph">▯</span>93+ <span className="np-device-label">phone</span>94+ {stage === "enrolled" && (95+ <span className={`np-chip ${revoked ? "np-chip--dead" : ""}`}>96+ {revoked ? "4001" : "lofisync1…"}97+ </span>98+ )}99+ </div>100+101+ <div102+ className={`np-link ${linked ? "is-live" : ""} ${103+ revoked ? "is-cut" : ""104+ }`}105+ />106+107+ <div className={`np-home ${stage !== "fresh" ? "is-on" : ""}`}>108+ {stage === "ticketed" && (109+ <span className="np-chip np-chip--ticket">lofisync1…</span>110+ )}111+ <span className="np-home-roof" />112+ <div className="np-home-body">113+ <span114+ className={`np-part np-part--gate ${115+ stage !== "fresh" ? "is-on" : ""116+ }`}117+ >118+ gate{stage !== "fresh" && <i className="np-led" />}119+ </span>120+ <span121+ className={`np-part np-part--store ${122+ stage !== "inited" && stage !== "fresh" ? "is-on" : ""123+ }`}124+ >125+ store126+ </span>127+ </div>128+ <span className="np-home-label">home</span>129+ </div>130+131+ <div className={`np-link np-link--tunnel ${paired ? "is-live" : ""}`}>132+ {paired && (133+ <span className="np-rtt">134+ rtt 23ms135+ <br />136+ direct137+ </span>138+ )}139+ </div>140+141+ <div className={`np-home np-home--peer ${paired ? "is-on" : ""}`}>142+ <span className="np-home-roof" />143+ <div className="np-home-body">144+ <span className="np-part is-on">store</span>145+ </div>146+ <span className="np-home-label">studio</span>147+ </div>148+ </div>149+150+ <div className="np-console" role="log" aria-live="polite">151+ {lines.map((line, index) => (152+ <p key={`${index}-${line.text}`} data-kind={line.kind}>153+ {line.kind === "cmd" ? <b>$</b> : null}154+ {line.text}155+ </p>156+ ))}157+ </div>158+159+ <div className="np-actions">160+ {stage === "fresh" && (161+ <button type="button" className="np-btn np-btn--go" onClick={init}>162+ lofi-node init163+ </button>164+ )}165+ {stage === "inited" && (166+ <button type="button" className="np-btn np-btn--go" onClick={start}>167+ lofi-node start168+ </button>169+ )}170+ {stage === "started" && (171+ <button type="button" className="np-btn np-btn--go" onClick={issue}>172+ ticket issue --label phone173+ </button>174+ )}175+ {stage === "ticketed" && (176+ <button type="button" className="np-btn np-btn--go" onClick={enroll}>177+ paste it into the app178+ </button>179+ )}180+ {stage === "enrolled" && !revoked && (181+ <button type="button" className="np-btn" onClick={revoke}>182+ revoke the ticket183+ </button>184+ )}185+ {stage === "enrolled" && revoked && (186+ <button type="button" className="np-btn np-btn--go" onClick={issue}>187+ issue a fresh ticket188+ </button>189+ )}190+ {stage === "enrolled" && !paired && (191+ <button type="button" className="np-btn" onClick={pair}>192+ pair a second home193+ </button>194+ )}195+ {stage !== "fresh" && (196+ <button197+ type="button"198+ className="np-btn np-btn--ghost"199+ onClick={reset}200+ >201+ reset202+ </button>203+ )}204+ </div>205+ </div>206+ );207+}@@ -0,0 +1,127 @@
1+import type { ReactNode } from "react";2+import { useState } from "react";3+4+// The shared-store story you can run: an empty store (where writes would5+// hang), a first app provisioning its slice, a second app merging in beside6+// it, and a drifted app being refused. Mirrors the real classifier states.7+8+type Phase = "empty" | "taskapp" | "merged";9+10+export default function SliceMerge(): ReactNode {11+ const [phase, setPhase] = useState<Phase>("empty");12+ const [driftTried, setDriftTried] = useState(false);13+14+ const status = phase === "empty"15+ ? {16+ code: "no_schema",17+ note: "writes would hang; provision before syncing",18+ }19+ : phase === "taskapp"20+ ? { code: "ok", note: "head h1 · taskapp's slice enforced" }21+ : driftTried22+ ? {23+ code: "schema_drift",24+ note: "taskapp__tasks differs; surfaced, never repaired",25+ }26+ : {27+ code: "ok",28+ note: "head h2 · both slices enforced, one migration apart",29+ };30+31+ return (32+ <div className="sm" data-phase={phase}>33+ <div className="sm-bar">34+ <span className="sm-status" data-code={status.code}>35+ <i />36+ {status.code}37+ </span>38+ <span className="sm-note">{status.note}</span>39+ </div>40+41+ <div className="sm-shelf" aria-hidden="true">42+ {phase === "empty"43+ ? <span className="sm-empty">one store · nothing deployed</span>44+ : (45+ <>46+ <div className="sm-slice sm-slice--task">47+ <span className="sm-ns">taskapp__</span>48+ <span49+ className={`sm-table ${driftTried ? "is-contested" : ""}`}50+ >51+ tasks52+ </span>53+ <span className="sm-table">projects</span>54+ <span className="sm-seal">55+ {driftTried56+ ? "the store's copy no longer matches the declaration"57+ : phase === "merged"58+ ? "byte-identical through the merge"59+ : "created by taskapp's provisioning"}60+ </span>61+ </div>62+ {phase === "merged" && (63+ <div className="sm-slice sm-slice--notes">64+ <span className="sm-ns">notesapp__</span>65+ <span className="sm-table">notes</span>66+ <span className="sm-seal">67+ added via createTables migration68+ </span>69+ </div>70+ )}71+ </>72+ )}73+ </div>74+75+ <div className="sm-actions">76+ {phase === "empty" && (77+ <button78+ type="button"79+ className="np-btn np-btn--go"80+ onClick={() => setPhase("taskapp")}81+ >82+ taskapp provisions its slice83+ </button>84+ )}85+ {phase === "taskapp" && (86+ <button87+ type="button"88+ className="np-btn np-btn--go"89+ onClick={() => setPhase("merged")}90+ >91+ notesapp joins the occupied store92+ </button>93+ )}94+ {phase === "merged" && !driftTried && (95+ <button96+ type="button"97+ className="np-btn"98+ onClick={() => setDriftTried(true)}99+ >100+ a drifted taskapp variant tries101+ </button>102+ )}103+ {driftTried && (104+ <button105+ type="button"106+ className="np-btn np-btn--ghost"107+ onClick={() => setDriftTried(false)}108+ >109+ back away slowly110+ </button>111+ )}112+ {phase !== "empty" && !driftTried && (113+ <button114+ type="button"115+ className="np-btn np-btn--ghost"116+ onClick={() => {117+ setPhase("empty");118+ setDriftTried(false);119+ }}120+ >121+ reset122+ </button>123+ )}124+ </div>125+ </div>126+ );127+}@@ -5,7 +5,13 @@ import { useReducer, useRef, useState } from "react";
55 // devices. Offline writes queue in an outbox; reconnect merges. Ported from66 // the original vanilla implementation in the lofi-site landing page.77 8-type Task = { id: string; text: string; done: boolean; ts: number; origin: DeviceId };8+type Task = {9+ id: string;10+ text: string;11+ done: boolean;12+ ts: number;13+ origin: DeviceId;14+};915 type DeviceId = "A" | "B";1016 type Device = { store: Map<string, Task>; outbox: string[] };1117 type Rig = { seq: number; online: boolean; dev: Record<DeviceId, Device> };@@ -27,7 +33,13 @@ function seedRig(): Rig {
2733 ["Order more perlite", false],2834 ];2935 for (const [text, done] of seeds) {30- const task: Task = { id: `seed-${rig.seq}`, text, done, ts: rig.seq++, origin: "A" };36+ const task: Task = {37+ id: `seed-${rig.seq}`,38+ text,39+ done,40+ ts: rig.seq++,41+ origin: "A",42+ };3143 rig.dev.A.store.set(task.id, { ...task });3244 rig.dev.B.store.set(task.id, { ...task });3345 }@@ -72,12 +84,18 @@ function DeviceFrame(props: {
7284 }7385 }}7486 />75- <button type="button" aria-label={`Add task on ${name.toLowerCase()}`} onClick={submit}>87+ <button88+ type="button"89+ aria-label={`Add task on ${name.toLowerCase()}`}90+ onClick={submit}91+ >7692 <span>+</span>7793 </button>7894 </div>7995 <ul className="tasks">80- {tasks.length === 0 && <li className="tasks-empty">Nothing here yet.</li>}96+ {tasks.length === 0 && (97+ <li className="tasks-empty">Nothing here yet.</li>98+ )}8199 {tasks.map((task) => (82100 <li83101 key={task.id}@@ -90,7 +108,9 @@ function DeviceFrame(props: {
90108 type="button"91109 className="tick"92110 aria-pressed={task.done}93- aria-label={`${task.done ? "Mark not done" : "Mark done"}: ${task.text}`}111+ aria-label={`${112+ task.done ? "Mark not done" : "Mark done"113+ }: ${task.text}`}94114 onClick={() => onToggle(which, task)}95115 >96116 <svg viewBox="0 0 10 10" aria-hidden="true">@@ -126,7 +146,9 @@ export default function SyncDemo(): ReactNode {
126146 127147 const apply = (target: DeviceId, rec: Task) => {128148 const have = rig.dev[target].store.get(rec.id);129- if (!have || rec.ts > have.ts) rig.dev[target].store.set(rec.id, { ...rec });149+ if (!have || rec.ts > have.ts) {150+ rig.dev[target].store.set(rec.id, { ...rec });151+ }130152 };131153 132154 const pulse = () => {@@ -204,11 +226,21 @@ export default function SyncDemo(): ReactNode {
204226 </div>205227 206228 <div className="rig">207- <DeviceFrame which="A" name="This phone" rig={rig} onAdd={onAdd} onToggle={onToggle} />229+ <DeviceFrame230+ which="A"231+ name="This phone"232+ rig={rig}233+ onAdd={onAdd}234+ onToggle={onToggle}235+ />208236 <div className="wire" ref={wireRef} aria-hidden="true">209237 <div className="wire-line" />210238 <div className="wire-flow" />211- <div className={`wire-badge${!rig.online && pending > 0 ? " is-on" : ""}`}>239+ <div240+ className={`wire-badge${241+ !rig.online && pending > 0 ? " is-on" : ""242+ }`}243+ >212244 {pending} {pending === 1 ? "change waiting" : "changes waiting"}213245 </div>214246 </div>@@ -222,9 +254,9 @@ export default function SyncDemo(): ReactNode {
222254 </div>223255 224256 <p className="demo-foot">225- A simulation, drawn to the same model (not a live Jazz session). For the real one, and for226- the part where you close the laptop and it is all still there tomorrow:{" "}227- <code>deno task dev</code>.257+ A simulation, drawn to the same model (not a live Jazz session). For the258+ real one, and for the part where you close the laptop and it is all259+ still there tomorrow: <code>deno task dev</code>.228260 </p>229261 </div>230262 );@@ -0,0 +1,119 @@
1+import type { ReactNode } from "react";2+import { useState } from "react";3+4+// An app-connect ticket, opened up. The string is opaque base64url on the5+// wire; here the decoded payload is the interface: pick a field, read what6+// it does, flip the scope and watch who holds the admin secret.7+8+type FieldKey = "v" | "appId" | "url" | "secret" | "scope" | "label";9+10+const EXPLAIN: Record<FieldKey, { title: string; body: string }> = {11+ v: {12+ title: "Format version",13+ body:14+ "Version 1. Parsers treat unknown optional fields as forward-compatible additions, and reject unknown scopes outright rather than granting less than the ticket claims.",15+ },16+ appId: {17+ title: "The store's app id",18+ body:19+ "Which Jazz app this node hosts. The enrolling app adopts it, unless the deployment pinned its own managed app; a foreign ticket is then refused.",20+ },21+ url: {22+ title: "The gate, verbatim",23+ body:24+ "Used unchanged as the app's sync server. Jazz clients preserve a base path in the server URL, which is the whole trick that makes the next field work.",25+ },26+ secret: {27+ title: "256-bit secret, riding the path",28+ body:29+ "Every request the app ever makes (sync WebSockets, catalogue reads) carries it with zero client changes. The node stores only a digest; the string is displayable once.",30+ },31+ scope: {32+ title: "What possession grants",33+ body:34+ "sync is transport only. provision is a strict superset: store administration, with the gate injecting the node's admin secret server-side. Possession is the opt-in.",35+ },36+ label: {37+ title: "Your bookkeeping",38+ body:39+ "Issue one ticket per device or context, label it, and revocation stays scoped: one command, live sockets close with code 4001 within seconds.",40+ },41+};42+43+export default function TicketAnatomy(): ReactNode {44+ const [active, setActive] = useState<FieldKey>("secret");45+ const [provision, setProvision] = useState(false);46+47+ const field = (key: FieldKey, rendered: ReactNode) => (48+ <button49+ type="button"50+ className={`ta-field ${active === key ? "is-active" : ""}`}51+ onClick={() => setActive(key)}52+ aria-pressed={active === key}53+ >54+ {rendered}55+ </button>56+ );57+58+ return (59+ <div className="ta">60+ <p className="ta-wire" aria-hidden="true">61+ <span className="ta-prefix">lofisync1.</span>62+ <span className="ta-blob">63+ eyJ2IjoxLCJhcHBJZCI6ImNmZTUyZTQ0LTdhNTktNDIzMi04ZGJi…64+ </span>65+ <span className="ta-open">⌄ decoded</span>66+ </p>67+68+ <div className="ta-body">69+ <pre className="ta-json"><code>{"{\n "}{field("v", <>{'"v"'}: 1</>)}70+ {",\n "}71+ {field("appId", <>{'"appId"'}: "cfe52e44-7a59-4232-8dbb-bf53f27aeed6"</>)}72+ {",\n "}73+ {field(74+ "url",75+ <>76+ {'"url"'}: "http://192.168.1.10:4802/t/77+ </>,78+ )}79+ {field("secret", <span className="ta-secret">Yr3…43 chars…9Qk</span>)}80+ {field("url", <>"</>)}81+ {",\n "}82+ {field("scope", <>{'"scope"'}: "{provision ? "provision" : "sync"}"</>)}83+ {",\n "}84+ {field("label", <>{'"label"'}: "{provision ? "laptop-admin" : "phone"}"</>)}85+ {"\n}"}</code></pre>86+87+ <div className="ta-card" aria-live="polite">88+ <h3>{EXPLAIN[active].title}</h3>89+ <p>{EXPLAIN[active].body}</p>90+ {active === "scope" && (91+ <>92+ <div className="ta-scope" role="group" aria-label="Ticket scope">93+ <button94+ type="button"95+ className={!provision ? "is-active" : ""}96+ onClick={() => setProvision(false)}97+ >98+ sync99+ </button>100+ <button101+ type="button"102+ className={provision ? "is-active" : ""}103+ onClick={() => setProvision(true)}104+ >105+ provision106+ </button>107+ </div>108+ <p className={`ta-inject ${provision ? "is-on" : ""}`}>109+ {provision110+ ? "X-Jazz-Admin-Secret: injected by the gate. The secret never transits the client."111+ : "admin routes answer 401 invalid_ticket, same as an unknown secret. Nothing to enumerate."}112+ </p>113+ </>114+ )}115+ </div>116+ </div>117+ </div>118+ );119+}@@ -4,6 +4,35 @@
44 * cream #f3faf6, mint #99e5bd, jade #39725c.55 */66 7+/* Self-hosted fonts (Fontsource): bundled and precached, so offline visits8+ keep their real faces and no request leaves the origin. Latin subsets and9+ only the weights the site actually sets — the full subset files would10+ multiply the payload for scripts the docs never use. */11+/* brand mark */12+@import "@fontsource/nunito/latin-900.css";13+/* root landing display */14+@import "@fontsource/comfortaa/latin-700.css";15+/* landing body */16+@import "@fontsource/karla/latin-400.css";17+@import "@fontsource/karla/latin-500.css";18+@import "@fontsource/karla/latin-700.css";19+/* mono: landings, docs code blocks */20+@import "@fontsource/ibm-plex-mono/latin-400.css";21+@import "@fontsource/ibm-plex-mono/latin-500.css";22+@import "@fontsource/ibm-plex-mono/latin-600.css";23+/* /node display + mark */24+@import "@fontsource/young-serif/latin-400.css";25+@import "@fontsource/grenze-gotisch/latin-500.css";26+/* /node body */27+@import "@fontsource/alegreya-sans/latin-400.css";28+@import "@fontsource/alegreya-sans/latin-400-italic.css";29+@import "@fontsource/alegreya-sans/latin-500.css";30+@import "@fontsource/alegreya-sans/latin-700.css";31+/* /node mono */32+@import "@fontsource/spline-sans-mono/latin-400.css";33+@import "@fontsource/spline-sans-mono/latin-500.css";34+@import "@fontsource/spline-sans-mono/latin-600.css";35+736 :root {837 --ifm-color-primary: #39725c;938 --ifm-color-primary-dark: #336753;@@ -72,11 +101,70 @@
72101 color: #8fae9f ;73102 }74103 75-/* the footer wears the brand forest, like the navbar */104+/* Tables everywhere share the landing pages' editorial style instead of105+ Infima's boxed default: no outer borders, no striped rows, no header106+ background — mono uppercase section-label headers over a strong rule,107+ hairline row rules, and a symmetric 1rem inset on the outer columns.108+ Colors ride the theme variables, so light and dark both work. */109+.markdown table {110+ border-collapse: collapse;111+}112+.markdown table thead,113+.markdown table tr,114+.markdown table tr:nth-child(2n) {115+ background: none;116+ border: 0;117+}118+.markdown table th {119+ background: none;120+ border: 0;121+ border-bottom: 1px solid var(--ifm-color-emphasis-500);122+ font-family: var(--ifm-font-family-monospace);123+ font-weight: 500;124+ font-size: 0.82rem;125+ letter-spacing: 0.14em;126+ text-transform: uppercase;127+ text-align: left;128+ color: var(--ifm-color-primary);129+ vertical-align: middle;130+ padding: 0.55rem 1rem 0.55rem 0;131+}132+.markdown table td {133+ background: none;134+ border: 0;135+ border-bottom: 1px solid var(--ifm-color-emphasis-200);136+ padding: 0.7rem 1rem 0.7rem 0;137+ vertical-align: top;138+}139+.markdown table th:first-child,140+.markdown table td:first-child {141+ padding-left: 1rem;142+}143+144+/* The footer wears the brand forest, like the navbar — but muted: it is a145+ jump table, not a destination, so links sit dim until hovered. */76146 .footer--dark {77147 --ifm-footer-background-color: #18231f;78148 --ifm-footer-color: #8fae9f;79- --ifm-footer-link-color: #f3faf6;80- --ifm-footer-link-hover-color: #d5f26a;81- --ifm-footer-title-color: #99e5bd;149+ --ifm-footer-link-color: #8fae9f;150+ --ifm-footer-link-hover-color: #f3faf6;151+ --ifm-footer-title-color: #6f9d88;152+ --ifm-footer-padding-vertical: 2rem;153+}154+.footer--dark .footer__title {155+ font-size: 0.85rem;156+ margin-bottom: 0.35rem;157+}158+/* Each product's links flow in one row where space allows and wrap to a159+ compact grid where it doesn't. */160+.footer--dark .footer__items {161+ display: flex;162+ flex-wrap: wrap;163+ gap: 0.2rem 1.6rem;164+}165+.footer--dark .footer__item {166+ margin: 0;167+}168+.footer--dark .footer__copyright {169+ font-size: 0.82rem;82170 }@@ -15,8 +15,9 @@
1515 --ink-dim: #8fae9f;1616 --grid: 56px;1717 18- --f-display: "Nunito", ui-rounded, "Segoe UI", system-ui, sans-serif;19- --f-serif: "Fraunces", "Iowan Old Style", Georgia, serif;18+ --f-display: "Comfortaa", ui-rounded, "Segoe UI", system-ui, sans-serif;19+ /* Fraunces retired with the old hero-title; system serifs cover the var. */20+ --f-serif: "Iowan Old Style", Georgia, serif;2021 --f-body: "Karla", system-ui, -apple-system, sans-serif;2122 --f-mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;2223 @@ -51,7 +52,8 @@
5152 linear-gradient(to bottom, var(--forest-2) 1px, transparent 1px);5253 background-size: var(--grid) var(--grid);5354 opacity: 0.55;54- mask-image: radial-gradient(ellipse 120% 90% at 50% 0%, #000 30%, transparent 78%);55+ mask-image: radial-gradient(ellipse 120% 90% at 50% 0%, #000 30%, transparent56+ 78%);5557 }5658 /* --- risograph grain --------------------------------------- */5759 .landing::after {@@ -89,7 +91,8 @@
8991 .landing h2,9092 .landing h3 {9193 font-family: var(--f-display);92- font-weight: 900;94+ /* Comfortaa's heaviest weight */95+ font-weight: 700;9396 line-height: 1.06;9497 letter-spacing: -0.02em;9598 margin: 0;@@ -100,7 +103,7 @@
100103 }101104 .landing h3 {102105 font-size: 1.06rem;103- font-weight: 800;106+ font-weight: 700;104107 letter-spacing: -0.01em;105108 line-height: 1.35;106109 }@@ -148,13 +151,16 @@
148151 font-size: 0.94rem;149152 margin-bottom: 1.6rem;150153 }154+/* Inline code sits on a subtle chip so its bounds are legible — a period155+ beside bare mono text is easy to lose. */151156 .landing .inline-mono {152157 font-family: var(--f-mono);153158 font-size: 0.85em;154159 color: var(--lime);155- background: none;160+ background: rgba(34, 51, 44, 0.9);156161 border: 0;157- padding: 0;162+ border-radius: 5px;163+ padding: 0.1em 0.38em;158164 }159165 .landing .life-cmd {160166 margin-top: 0.8rem;@@ -201,6 +207,84 @@
201207 margin-bottom: 1.8rem;202208 }203209 210+/* ============================================================211+ Mark-led hero, shared by / and /node: the product logotype212+ leads, with the version pill and CTAs alongside — no big213+ headline + subheadline pair.214+ ============================================================ */215+/* The mark is branding: always Nunito black, on both pages, regardless of216+ the page's display face. */217+.landing .nmark {218+ font-family: "Nunito", ui-rounded, "Segoe UI", system-ui, sans-serif;219+ font-weight: 900;220+ font-size: clamp(3rem, 8vw, 5.2rem);221+ line-height: 1;222+ margin: 0;223+ letter-spacing: -0.015em;224+}225+.landing .nmark .lo {226+ color: var(--lime);227+}228+.landing .nmark .fi {229+ color: var(--cream);230+}231+.landing .nmark .dash {232+ color: var(--jade);233+ margin: 0 0.06em;234+}235+.landing .nmark .node {236+ color: var(--mint);237+}238+239+.landing .hero-top {240+ display: flex;241+ align-items: baseline;242+ justify-content: space-between;243+ gap: 1.4rem 2.4rem;244+ flex-wrap: wrap;245+ padding: 3.4rem 0 0;246+ margin-bottom: 2rem;247+}248+.landing .hero-side {249+ display: grid;250+ justify-items: end;251+ gap: 0.8rem;252+}253+.landing .hero-side .cta-row {254+ margin: 0;255+}256+.landing .hero-claim {257+ font-family: var(--f-display);258+ font-weight: 700;259+ font-size: clamp(1.5rem, 2.8vw, 2.2rem);260+ line-height: 1.28;261+ letter-spacing: -0.01em;262+ color: var(--cream);263+ margin: 0 0 1.7rem;264+}265+.landing .hero-main {266+ display: grid;267+ grid-template-columns: 1.05fr 0.95fr;268+ gap: 3rem;269+ align-items: center;270+ padding-bottom: 1rem;271+}272+@media (max-width: 860px) {273+ .landing .hero-top {274+ padding-top: 2.2rem;275+ }276+ .landing .hero-side {277+ justify-items: start;278+ }279+ .landing .hero-main {280+ grid-template-columns: 1fr;281+ gap: 1.6rem;282+ }283+ .landing .hero-main .hero-art {284+ max-width: 420px;285+ }286+}287+204288 /* ============================================================205289 Hero206290 ============================================================ */@@ -262,6 +346,19 @@
262346 display: block;263347 }264348 349+/* The /node landing's hero art is a quick-start terminal in the tree style. */350+.landing .hero-term {351+ font-size: 0.82rem;352+ line-height: 1.75;353+ align-self: center;354+ box-shadow: 0 24px 60px rgba(0, 0, 0, 0.28);355+}356+@media (max-width: 860px) {357+ .landing .hero-term {358+ margin-top: 1.6rem;359+ }360+}361+265362 .landing .cta-row {266363 display: flex;267364 flex-wrap: wrap;@@ -384,7 +481,8 @@
384481 top: 0.35rem;385482 bottom: 0.35rem;386483 width: 1px;387- background-image: linear-gradient(to bottom, var(--jade) 3px, transparent 3px);484+ background-image: linear-gradient(to bottom, var(--jade) 3px, transparent485+ 3px);388486 background-size: 1px 8px;389487 }390488 .landing .life-step h3 {@@ -425,7 +523,8 @@
425523 bottom: auto;426524 width: auto;427525 height: 1px;428- background-image: linear-gradient(to right, var(--jade) 3px, transparent 3px);526+ background-image: linear-gradient(to right, var(--jade) 3px, transparent527+ 3px);429528 background-size: 8px 1px;430529 }431530 }@@ -779,7 +878,8 @@
779878 margin-block: 0.2rem;780879 }781880 .landing .wire-line {782- background-image: linear-gradient(to bottom, var(--mint) 3px, transparent 3px);881+ background-image: linear-gradient(to bottom, var(--mint) 3px, transparent882+ 3px);783883 background-size: 2px 9px;784884 background-repeat: repeat-y;785885 }@@ -982,22 +1082,45 @@
9821082 display: table;9831083 margin: 0;9841084 }1085+/* Single-title tables use a real <caption>, styled as the section label —1086+ the underline runs edge to edge and assistive tech gets true semantics. */1087+.landing .tbl caption {1088+ caption-side: top;1089+ font-family: var(--f-mono);1090+ font-weight: 500;1091+ font-size: 0.82rem;1092+ letter-spacing: 0.16em;1093+ text-transform: uppercase;1094+ color: var(--mint);1095+ text-align: left;1096+ padding: 0.5rem 1rem;1097+ border-bottom: 1px solid var(--jade);1098+}1099+1100+/* Multi-column header cells keep the same label voice. */9851101 .landing .tbl th {9861102 font-family: var(--f-mono);9871103 font-weight: 500;988- font-size: 0.7rem;989- letter-spacing: 0.14em;1104+ font-size: 0.82rem;1105+ letter-spacing: 0.16em;9901106 text-transform: uppercase;9911107 color: var(--mint);9921108 text-align: left;9931109 white-space: nowrap;994- padding: 0 1rem 0.7rem 0;1110+ vertical-align: middle;1111+ padding: 0.5rem 1rem 0.5rem 0;9951112 border-bottom: 1px solid var(--jade);9961113 border-left: 0;9971114 border-right: 0;9981115 border-top: 0;9991116 background: none;10001117 }1118+/* Symmetric inset: the first column carries the same leading space the last1119+ column's trailing padding provides. */1120+.landing .tbl th:first-child,1121+.landing .tbl td:first-child {1122+ padding-left: 1rem;1123+}10011124 .landing .tbl tr {10021125 background: none;10031126 border-top: 0;@@ -1064,3 +1187,62 @@
10641187 color: var(--cream);10651188 font-weight: 700;10661189 }1190+1191+/* ============================================================1192+ Entrances. One shared rise: heroes stagger in on load, and1193+ where scroll-driven animations exist, section furniture rises1194+ as it enters the viewport. Reduced motion gets none of it.1195+ ============================================================ */1196+@media (prefers-reduced-motion: no-preference) {1197+ @keyframes lofi-rise {1198+ from {1199+ opacity: 0;1200+ transform: translateY(14px);1201+ }1202+ }1203+ .landing .hero-top,1204+ .landing .node-hero-top {1205+ animation: lofi-rise 0.5s 0.05s ease-out backwards;1206+ }1207+ .landing .hero-main > div:first-child,1208+ .landing .node-hero .np {1209+ animation: lofi-rise 0.55s 0.18s ease-out backwards;1210+ }1211+ .landing .hero-main .hero-art {1212+ animation: lofi-rise 0.6s 0.32s ease-out backwards;1213+ }1214+ @supports (animation-timeline: view()) {1215+ .landing .band-head,1216+ .landing .grid-3 .cell,1217+ .landing .life-step,1218+ .landing .anat > *,1219+ .landing .two-up > *,1220+ .landing .ta,1221+ .landing .sm,1222+ .landing .demo-shell {1223+ animation: lofi-rise 0.55s ease-out both;1224+ animation-timeline: view();1225+ animation-range: entry 5% entry 45%;1226+ }1227+ }1228+}1229+1230+/* ============================================================1231+ Selection. Controls and decorative scenes are not copy; the1232+ create command, console output, and prose remain selectable.1233+ ============================================================ */1234+.landing .np-scene,1235+.landing .sm-shelf,1236+.landing .ta-wire,1237+.landing .np-actions,1238+.landing .sm-actions,1239+.landing .cta-row,1240+.landing .btn,1241+.landing .np-btn,1242+.landing .ta-field,1243+.landing .ta-scope,1244+.landing .cmd button,1245+.landing .switch {1246+ -webkit-user-select: none;1247+ user-select: none;1248+}@@ -0,0 +1,621 @@
1+/* ============================================================2+ /node landing — lofi-node's own furniture, on the shared3+ forest/lime tokens from landing.css (scoped under .landing).4+ Three interactive rigs: the playable node (np-), the ticket5+ anatomy (ta-), and the shared-store merge (sm-).6+7+ The page carries its own, more natural type palette: Young8+ Serif's hand-hewn display, Alegreya Sans' humanist body, and9+ Spline Sans Mono's soft console — swapped in through the10+ font variables so every shared rule follows along.11+ ============================================================ */12+/* Font faces load from the site-wide stylesheet link (docusaurus.config.ts);13+ an @import here would serialize the font CSS behind the app bundle. */14+15+.landing--node {16+ --f-display: "Young Serif", "Iowan Old Style", Georgia, serif;17+ --f-body: "Alegreya Sans", "Gill Sans", system-ui, sans-serif;18+ --f-mono: "Spline Sans Mono", ui-monospace, "SF Mono", Menlo, monospace;19+ font-size: 17.5px;20+}21+.landing--node h1,22+.landing--node h2,23+.landing--node h3 {24+ font-weight: 400; /* Young Serif ships one weight; let it breathe */25+ letter-spacing: 0;26+}27+28+/* ---------- the lofi-node mark ---------- */29+.landing--node .node-hero {30+ padding: 3.4rem 0 1rem;31+}32+.landing--node .node-hero-top {33+ display: flex;34+ align-items: baseline;35+ justify-content: space-between;36+ gap: 1.4rem 2.4rem;37+ flex-wrap: wrap;38+ margin-bottom: 2rem;39+}40+/* The root page's mark is brand Nunito; this page's mark wears its own41+ face — a gothic serif with dark-fantasy blood, still legible as a42+ wordmark. */43+.landing--node .nmark {44+ font-family: "Grenze Gotisch", "Young Serif", Georgia, serif;45+ font-weight: 500;46+ letter-spacing: 0.01em;47+}48+.landing--node .node-hero-side {49+ display: grid;50+ justify-items: end;51+ gap: 0.8rem;52+}53+.landing--node .node-hero-side .cta-row {54+ margin: 0;55+}56+.landing--node .node-hero .np {57+ max-width: 920px;58+ margin: 0 auto;59+}60+.landing--node .node-hero .np-scene {61+ min-height: 168px;62+}63+@media (max-width: 860px) {64+ .landing--node .node-hero-side {65+ justify-items: start;66+ }67+}68+69+/* ---------- the playable node ---------- */70+.landing .np {71+ background: rgba(34, 51, 44, 0.55);72+ border: 1px solid var(--forest-2);73+ border-radius: var(--radius);74+ padding: 1.2rem 1.2rem 1.1rem;75+ box-shadow: 0 24px 60px rgba(0, 0, 0, 0.28);76+}77+78+.landing .np-scene {79+ display: flex;80+ align-items: center;81+ justify-content: center;82+ gap: 0;83+ min-height: 132px;84+ padding: 0.6rem 0.2rem 1rem;85+}86+87+/* Resting opacity stays legible: the scene must read at a glance before any88+ interaction. Turning on brightens; it doesn't reveal. */89+.landing .np-device {90+ display: grid;91+ justify-items: center;92+ gap: 0.2rem;93+ opacity: 0.6;94+ transition: opacity 0.3s ease;95+ min-width: 64px;96+ position: relative;97+}98+.landing .np-device.is-on {99+ opacity: 1;100+}101+.landing .np-device-glyph {102+ font-size: 4rem;103+ line-height: 1;104+ color: var(--mint);105+}106+.landing .np-device-label,107+.landing .np-home-label {108+ font-family: var(--f-mono);109+ font-size: 0.62rem;110+ letter-spacing: 0.14em;111+ text-transform: uppercase;112+ color: var(--ink-dim);113+}114+.landing .np-chip {115+ position: absolute;116+ top: -0.9rem;117+ font-family: var(--f-mono);118+ font-size: 0.58rem;119+ color: var(--forest);120+ background: var(--lime);121+ border-radius: 999px;122+ padding: 0.05rem 0.45rem;123+ white-space: nowrap;124+}125+.landing .np-chip--dead {126+ background: var(--jade);127+ color: var(--cream);128+ text-decoration: line-through;129+}130+/* The freshly issued ticket hovers above the home's roof until it is131+ pasted into the app — then it reappears pinned to the phone. */132+.landing .np-chip--ticket {133+ top: -1.6rem;134+ left: 50%;135+ transform: translateX(-50%);136+}137+138+/* No grid gap: the roof must sit flush on the body. The label carries its139+ own spacing instead. */140+.landing .np-home {141+ display: grid;142+ justify-items: center;143+ gap: 0;144+ opacity: 0.6;145+ transition: opacity 0.3s ease;146+ position: relative;147+}148+.landing .np-home .np-home-label {149+ margin-top: 0.3rem;150+}151+.landing .np-home.is-on {152+ opacity: 1;153+}154+.landing .np-home-roof {155+ width: 0;156+ height: 0;157+ border-left: 34px solid transparent;158+ border-right: 34px solid transparent;159+ border-bottom: 20px solid var(--jade);160+}161+.landing .np-home-body {162+ display: grid;163+ gap: 0.3rem;164+ background: var(--forest);165+ border: 1px solid var(--jade);166+ border-top: 0;167+ padding: 0.45rem;168+ width: 68px;169+}170+.landing .np-part {171+ font-family: var(--f-mono);172+ font-size: 0.6rem;173+ letter-spacing: 0.08em;174+ /* Left-aligned on a shared inset: centered words of different lengths175+ never share an edge, which reads as misalignment at this size. */176+ text-align: left;177+ color: var(--ink-dim);178+ border: 1px dashed var(--jade);179+ border-radius: 4px;180+ padding: 0.18rem 0.2rem 0.18rem 0.45rem;181+ opacity: 0.75;182+ transition: opacity 0.3s ease, border-color 0.3s ease;183+ position: relative;184+}185+.landing .np-part.is-on {186+ opacity: 1;187+ color: var(--cream);188+ border-style: solid;189+}190+.landing .np-led {191+ position: absolute;192+ top: 50%;193+ right: 0.3rem;194+ transform: translateY(-50%);195+ width: 5px;196+ height: 5px;197+ border-radius: 50%;198+ background: var(--lime);199+}200+201+/* Links are drawn as a repeating dash gradient rather than a dashed border202+ so the live animation can move the PATTERN (segments streaming toward the203+ store) while the element — and the rtt label inside it — stays put. */204+.landing .np-link {205+ flex: 1 1 30px;206+ max-width: 92px;207+ height: 2px;208+ --np-link-ink: var(--jade);209+ background-image: repeating-linear-gradient(210+ 90deg,211+ var(--np-link-ink) 0 6px,212+ transparent 6px 12px213+ );214+ margin: 0 0.5rem;215+ position: relative;216+}217+.landing .np-link.is-live {218+ --np-link-ink: var(--mint);219+}220+.landing .np-link.is-cut {221+ --np-link-ink: var(--jade);222+ background-image: repeating-linear-gradient(223+ 90deg,224+ var(--np-link-ink) 0 2px,225+ transparent 2px 8px226+ );227+}228+.landing .np-link--tunnel {229+ max-width: 76px;230+}231+.landing .np-link--tunnel.is-live {232+ --np-link-ink: var(--lime);233+}234+/* Two stacked lines so the label never cuts into the houses beside it. */235+.landing .np-rtt {236+ position: absolute;237+ top: 0.35rem;238+ left: 50%;239+ transform: translateX(-50%);240+ font-family: var(--f-mono);241+ font-size: 0.56rem;242+ line-height: 1.5;243+ text-align: center;244+ white-space: nowrap;245+ color: var(--ink-dim);246+}247+@media (prefers-reduced-motion: no-preference) {248+ .landing .np-led {249+ animation: np-breathe 1.6s ease-in-out infinite;250+ }251+ .landing .np-link.is-live:not(.is-cut) {252+ animation: np-march 0.8s linear infinite;253+ }254+ @keyframes np-breathe {255+ 50% {256+ opacity: 0.35;257+ }258+ }259+ /* One full dash period per loop — a seamless rightward stream. */260+ @keyframes np-march {261+ to {262+ background-position-x: 12px;263+ }264+ }265+}266+267+/* Lines pin to the bottom and grow upward, like a real terminal. */268+.landing .np-console {269+ font-family: var(--f-mono);270+ font-size: 0.72rem;271+ line-height: 1.6;272+ border-top: 1px solid var(--forest-2);273+ padding-top: 0.7rem;274+ min-height: 5.6rem;275+ display: flex;276+ flex-direction: column;277+ justify-content: flex-end;278+}279+.landing .np-console p {280+ margin: 0;281+ color: var(--ink-dim);282+ max-width: none;283+}284+.landing .np-console p[data-kind="cmd"] {285+ color: var(--cream);286+}287+.landing .np-console p[data-kind="cmd"] b {288+ color: var(--lime);289+ font-weight: 500;290+}291+.landing .np-console p[data-kind="warn"] {292+ color: var(--mint);293+}294+295+.landing .np-actions {296+ display: flex;297+ flex-wrap: wrap;298+ gap: 0.45rem;299+ margin-top: 0.8rem;300+}301+.landing .np-btn {302+ font-family: var(--f-mono);303+ font-size: 0.72rem;304+ color: var(--cream);305+ background: var(--forest);306+ border: 1px solid var(--jade);307+ border-radius: 999px;308+ padding: 0.32rem 0.85rem;309+ cursor: pointer;310+ transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;311+}312+.landing .np-btn:hover {313+ border-color: var(--mint);314+}315+.landing .np-btn--go {316+ background: var(--lime);317+ border-color: var(--lime);318+ color: var(--forest);319+ font-weight: 600;320+}321+.landing .np-btn--go:hover {322+ background: var(--mint);323+ border-color: var(--mint);324+}325+.landing .np-btn--ghost {326+ border-color: transparent;327+ color: var(--ink-dim);328+}329+.landing .np-btn--ghost:hover {330+ border-color: var(--jade);331+ color: var(--cream);332+}333+334+/* ---------- ticket anatomy ---------- */335+.landing .ta-wire {336+ font-family: var(--f-mono);337+ font-size: 0.78rem;338+ color: var(--ink-dim);339+ display: flex;340+ align-items: baseline;341+ gap: 0.4rem;342+ flex-wrap: wrap;343+ margin-bottom: 0.9rem;344+}345+.landing .ta-prefix {346+ color: var(--lime);347+}348+.landing .ta-blob {349+ overflow: hidden;350+ text-overflow: ellipsis;351+ white-space: nowrap;352+ max-width: 34ch;353+ opacity: 0.7;354+}355+.landing .ta-open {356+ color: var(--mint);357+ font-size: 0.68rem;358+ letter-spacing: 0.12em;359+ text-transform: uppercase;360+}361+362+.landing .ta-body {363+ display: grid;364+ grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);365+ gap: 1.6rem;366+ align-items: start;367+}368+.landing .ta-json {369+ font-family: var(--f-mono);370+ font-size: 0.8rem;371+ line-height: 2;372+ background: rgba(34, 51, 44, 0.5);373+ border: 1px solid var(--forest-2);374+ border-radius: var(--radius);375+ padding: 1.1rem 1.2rem;376+ margin: 0;377+ color: var(--jade);378+ overflow-x: auto;379+}380+.landing .ta-json code {381+ background: none;382+ border: 0;383+ padding: 0;384+ font: inherit;385+ color: inherit;386+}387+.landing .ta-field {388+ font: inherit;389+ color: var(--cream);390+ background: none;391+ border: 0;392+ border-bottom: 1px dashed var(--jade);393+ padding: 0.05rem 0.1rem;394+ cursor: pointer;395+ transition: background 0.15s ease, color 0.15s ease;396+}397+.landing .ta-field:hover {398+ background: rgba(153, 229, 189, 0.12);399+}400+.landing .ta-field.is-active {401+ background: var(--lime);402+ color: var(--forest);403+ border-bottom-color: transparent;404+ border-radius: 4px;405+}406+.landing .ta-secret {407+ letter-spacing: 0.06em;408+}409+410+.landing .ta-card {411+ border-left: 2px solid var(--lime);412+ padding: 0.2rem 0 0.2rem 1.1rem;413+ min-height: 11rem;414+}415+.landing .ta-card h3 {416+ margin-bottom: 0.45rem;417+}418+.landing .ta-card p {419+ font-size: 0.92rem;420+ color: var(--ink-dim);421+ margin-bottom: 0.7rem;422+}423+.landing .ta-scope {424+ display: inline-flex;425+ border: 1px solid var(--jade);426+ border-radius: 999px;427+ overflow: hidden;428+ margin-bottom: 0.6rem;429+}430+.landing .ta-scope button {431+ font-family: var(--f-mono);432+ font-size: 0.72rem;433+ background: none;434+ border: 0;435+ color: var(--ink-dim);436+ padding: 0.25rem 0.8rem;437+ cursor: pointer;438+}439+.landing .ta-scope button.is-active {440+ background: var(--lime);441+ color: var(--forest);442+ font-weight: 600;443+}444+.landing .ta-inject {445+ font-family: var(--f-mono);446+ font-size: 0.74rem;447+ color: var(--ink-dim);448+}449+.landing .ta-inject.is-on {450+ color: var(--mint);451+}452+453+@media (max-width: 860px) {454+ .landing .ta-body {455+ grid-template-columns: 1fr;456+ }457+ .landing .ta-card {458+ min-height: 0;459+ }460+}461+462+/* ---------- shared-store merge ---------- */463+.landing .sm {464+ background: rgba(34, 51, 44, 0.55);465+ border: 1px solid var(--forest-2);466+ border-radius: var(--radius);467+ padding: 1.2rem 1.3rem 1.1rem;468+}469+.landing .sm-bar {470+ display: flex;471+ align-items: baseline;472+ gap: 0.8rem;473+ flex-wrap: wrap;474+ margin-bottom: 1rem;475+}476+.landing .sm-status {477+ font-family: var(--f-mono);478+ font-size: 0.74rem;479+ display: inline-flex;480+ align-items: center;481+ gap: 0.4rem;482+ border: 1px solid var(--jade);483+ border-radius: 999px;484+ padding: 0.2rem 0.75rem;485+ color: var(--cream);486+}487+.landing .sm-status i {488+ width: 7px;489+ height: 7px;490+ border-radius: 50%;491+ background: var(--jade);492+}493+.landing .sm-status[data-code="ok"] i {494+ background: var(--lime);495+}496+.landing .sm-status[data-code="no_schema"] i,497+.landing .sm-status[data-code="schema_drift"] i {498+ background: var(--mint);499+}500+.landing .sm-note {501+ font-family: var(--f-mono);502+ font-size: 0.72rem;503+ color: var(--ink-dim);504+}505+506+.landing .sm-shelf {507+ display: flex;508+ gap: 1rem;509+ flex-wrap: wrap;510+ align-items: stretch;511+ min-height: 7.2rem;512+ border-top: 1px dashed var(--forest-2);513+ border-bottom: 1px dashed var(--forest-2);514+ padding: 0.9rem 0;515+}516+/* The empty store is a vacant slice: same footprint and radius as a real517+ one, dashed outline, waiting to be filled by the first provisioning. */518+.landing .sm-empty {519+ flex: 1 1 100%;520+ display: grid;521+ place-items: center;522+ min-height: 4.6rem;523+ border: 1px dashed var(--jade);524+ border-radius: 10px;525+ font-family: var(--f-mono);526+ font-size: 0.76rem;527+ color: var(--ink-dim);528+ font-style: italic;529+}530+.landing .sm-slice {531+ display: flex;532+ align-items: center;533+ align-content: flex-start;534+ gap: 0.5rem;535+ flex-wrap: wrap;536+ border: 1px solid var(--jade);537+ border-radius: 10px;538+ padding: 0.6rem 0.8rem;539+ /* Slices share the full shelf width on desktop and wrap on small screens. */540+ flex: 1 1 280px;541+}542+.landing .sm-slice--notes {543+ border-color: var(--lime);544+}545+@media (prefers-reduced-motion: no-preference) {546+ .landing .sm-slice--notes {547+ animation: sm-arrive 0.35s ease-out;548+ }549+ @keyframes sm-arrive {550+ from {551+ transform: translateX(14px);552+ opacity: 0;553+ }554+ }555+}556+.landing .sm-ns {557+ font-family: var(--f-mono);558+ font-size: 0.68rem;559+ letter-spacing: 0.1em;560+ color: var(--ink-dim);561+}562+.landing .sm-table {563+ font-family: var(--f-mono);564+ font-size: 0.76rem;565+ color: var(--forest);566+ background: var(--mint);567+ border-radius: 6px;568+ padding: 0.28rem 0.6rem;569+}570+.landing .sm-slice--notes .sm-table {571+ background: var(--lime);572+}573+.landing .sm-table.is-contested {574+ background: none;575+ color: var(--mint);576+ border: 1px dashed var(--mint);577+ /* The border replaces the fill; keep the chip's box the same size. */578+ padding: calc(0.28rem - 1px) calc(0.6rem - 1px);579+}580+.landing .sm-seal {581+ flex-basis: 100%;582+ font-family: var(--f-mono);583+ font-size: 0.62rem;584+ letter-spacing: 0.06em;585+ color: var(--ink-dim);586+}587+.landing .sm-actions {588+ display: flex;589+ flex-wrap: wrap;590+ gap: 0.45rem;591+ margin-top: 0.9rem;592+}593+594+/* ---------- page-level bits ---------- */595+/* Footnotes directly under the rigs are single pointers, not body prose;596+ let them use the band's full width instead of the 62ch reading measure. */597+.landing--node .band > .intro-note {598+ max-width: none;599+ margin-bottom: 0;600+ padding-top: 1rem;601+}602+603+.landing .node-paths {604+ columns: 2;605+ column-gap: 3rem;606+ max-width: none;607+}608+.landing .node-paths li {609+ break-inside: avoid;610+ margin-bottom: 0.55rem;611+ color: var(--ink-dim);612+}613+.landing .node-paths li b {614+ color: var(--cream);615+ font-weight: 600;616+}617+@media (max-width: 760px) {618+ .landing .node-paths {619+ columns: 1;620+ }621+}Not shown: this page already shows 6000 lines of diff.
Not shown: this page already shows 6000 lines of diff.
Not shown: this page already shows 6000 lines of diff.
Binary file.
Binary file.
Not shown: this page already shows 6000 lines of diff.
Not shown: this page already shows 6000 lines of diff.