The two landing pages become a mark-led system: the root opens with the Nunito brand mark and one working claim over the create command; /node opens with its Grenze Gotisch mark over three interactive rigs (a node you operate, a ticket you open, a store two apps share) in its own natural type palette. Navbar and footer restructure around the two products; tables share one editorial style site-wide; the /node docs carry the user-ownership narrative in their own voice. The docs also now install and read offline (closes #114): plugin-pwa with opt-in precaching, a swCustom handler covering the site's trailingSlash:false route shape the default worker cannot match, maskable icons from the brand mark, and all fonts self-hosted as latin-subset files (never inlined into the render-blocking bundle). Verified offline across /, /docs, /node, /node/docs, /node/api, and search; both landings audit 100 accessibility, 100 best-practices. The lofi-node checkout pin advances to the current HEAD so CI renders what was reviewed.
- commit
- 65f6083fc7c6509c0174dfc96157fc34a70c4a86
- parent
- 27a5b6ee8ff321fb3a2d2e612ab18151417a0ed8
- author
- Dami <dami.etoile@gmail.com>
@@ -24,7 +24,7 @@ permissions:
2424 # Bump procedure: point LOFI_NODE_REF at the new lofi-node commit SHA in the2525 # same PR that reviews the resulting /node pages.2626 env:27- LOFI_NODE_REF: f022775fde8d5990514a24aa128adcb98a76dab727+ LOFI_NODE_REF: 1f83f723ce3cac025800407806aa5c4f51bc8a232828 2929 jobs:3030 build:@@ -44,8 +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-- [Self-hosting with lofi-node](https://lofi.host/node) — run the sync backend yourself; tutorials,48- guides, and the node reference live in the site's `/node` section47+- [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` section4949 - [Optional installed-app recipes](recipes/README.md)5050 - [Advanced device-auth primitive](auth.md)5151 @@ -1,19 +1,19 @@
1-# Self-hosting with lofi-node1+# Your data, on your node22 33 <!-- Source: FelineStateMachine/lofi-node README.md; site-voice page owned by lofi docs/node/. -->44 5-lofi apps work locally with no server at all, and sync is an election the user makes — so the6-natural end of that road is owning the sync location too.7-[lofi-node](https://github.com/FelineStateMachine/lofi-node) is the first-class way to do it: one8-daemon that embeds a real Jazz sync server, [iroh](https://iroh.computer) node-to-node transport,9-and a ticket-based access gate. Browsers keep speaking Jazz's protocol; only the server URL changes,10-and that URL is user-selected data the app enrolls at runtime, not configuration a developer5+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 developer1111 compiled in.1212 1313 What the node gives you:1414 15-- **A real Jazz sync server** — SQLite-backed, health-checked, serving exactly the pinned Jazz16- version your lofi apps run. Any lofi app can use it by URL.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.1717 - **Tickets, not accounts.** A `lofisync1.` app-connect ticket carries location and access in one1818 string; possession is transport access, revocation is one command. A provision-scoped ticket1919 additionally administers the store, with the admin secret never leaving the node.@@ -47,6 +47,6 @@ Jazz server works, `status().mesh` reports `{ state: "unavailable", reason: …
4747 ## The version invariant4848 4949 A node pins the **exact** Jazz alpha of the apps it serves; version bumps are coordinated changes,50-never drive-by. Before pointing production apps at a node, confirm the pins match — a mismatched51-pair fails in protocol-level ways no amount of network configuration fixes. See50+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. See5252 [troubleshooting](troubleshooting.md) for what the mismatches look like.@@ -4,7 +4,7 @@
44 tests/provisioning_test.ts (the executable version of this tutorial). -->55 66 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, user-opt-in step that creates7+engine's writes hang rather than fail. Provisioning is the explicit step you opt into that creates88 or updates the store's schema, and this tutorial walks it end to end: first app in, then a **second99 app joining the store that already holds the first app's data**. Every step here is also executable1010 — lofi-node's `tests/provisioning_test.ts` runs this exact flow through the gate in CI.@@ -3,20 +3,28 @@
33 <!-- Source: FelineStateMachine/lofi-node docs/hosting-lofi-apps.md and cli.ts; validated flow. -->44 55 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 syncing against it. Nothing here requires a cloud account, a static7-IP, or TLS on a trusted LAN — one binary and one paste.6+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.810 9-## 1. Get the binary11+## 1. Install lofi-node1012 11-From a checkout of [lofi-node](https://github.com/FelineStateMachine/lofi-node):13+Install the tool from [JSR](https://jsr.io/@nzip/lofi-node), which puts `lofi-node` on your PATH:1214 1315 ```sh14-deno task compile16+deno install -g -A -n lofi-node jsr:@nzip/lofi-node/cli1517 ```1618 17-This produces a self-contained `dist/lofi-node` with the native transport layer embedded (macOS18-arm64/x86_64 and Linux x86_64/aarch64; on Windows the node runs LAN-only —19-[why](troubleshooting.md)). You can also run the CLI directly with `deno run -A cli.ts`.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)).2028 2129 ## 2. Initialize and start2230 @@ -59,11 +67,13 @@ the secret lives in the URL path: [Tickets explained](tickets-explained.md).
5967 6068 ## 4. Enroll the ticket in a lofi app6169 62-The user pastes the ticket into the app. On the app side that is one call —63-`enrollSyncTicket(pastedTicket)` — which declares the node as the device's sync location and elects64-sync in one step; the app's existing local data pushes up under the same account identity. The65-enrollment flow, its guardrails, and the UI surface are covered in the framework docs:66-[Sync and recovery](/docs/sync-and-recovery).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).)6777 6878 ## 5. Verify6979 @@ -3,9 +3,9 @@
33 <!-- Source: lofi #109 (design + conformance), lofi docs/store-provisioning.md and44 docs/examples/nested-namespaces.md, lofi-node #2; jazz-tools typed-app surface. -->55 6-A self-hosted store is shared infrastructure. The user brings the store; apps are tenants in it.7-This guide is the mental model that makes that safe: what a slice is, how one store's schema grows8-app by app, and why an app cannot damage a neighbor even though the store trusts its administrator.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.99 1010 ## A slice is a naming discipline, not a partition1111 @@ -60,8 +60,8 @@ Two properties fall out, both verified against a real server in lofi's conforman
6060 Stated honestly: this is a _framework_ boundary, not cryptography. Store administration (the admin6161 secret, or a provision-scoped ticket) can technically do anything; the invariant keeps apps built on6262 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 handing a user provisioning64-power does not hand them a credential to leak.63+ticket never exposes the admin secret itself — the gate injects it — so holding provisioning power64+never means holding a credential you could leak.6565 6666 ## Where each piece lives6767 @@ -3,8 +3,8 @@
33 <!-- Source: FelineStateMachine/lofi-node docs/hosting-lofi-apps.md ("Storage choice") and44 src/config.ts (StorageConfig). -->55 6-The node's store is where your users' synced data durably lives, and the choice is deliberately7-narrow because the underlying engine's honest surface is narrow: jazz-napi supports exactly a SQLite6+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 SQLite88 directory or memory today. The `storage.type` discriminator in `config.json` is the seam future99 providers slot into.1010 @@ -11,9 +11,9 @@ lofi side implements against.
1111 1212 - A **node-pairing ticket** (`endpoint…`) is an iroh endpoint address. It names a _node_ so another1313 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 a _user_ hands to a lofi app so it15- syncs against their node. It carries the store's app id and the gate URL with a 256-bit secret16- embedded in its path.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.1717 1818 ## Why the secret lives in the URL path1919 @@ -57,8 +57,9 @@ tickets per device or context so revocation is scoped.
5757 5858 ## What the app does with it5959 60-On the app side, enrollment is `enrollSyncTicket(pastedTicket)`: the ticket is validated (against61-the same rules this node enforces — the two repos share machine-readable conformance fixtures),62-declared as the device's data sink, and sync is elected in one step. The ticket URL is stored in a63-device-local record and never surfaced through the app's session snapshot — only its host and label64-are. The full app-side semantics live in [Sync and recovery](/docs/sync-and-recovery).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).@@ -52,7 +52,7 @@ before debugging anything else.
5252 5353 ## Compatibility status, honestly5454 55-The hosting flow is validated end to end against lofi's reference app with real browser clients:55+The whole flow is validated end to end against lofi's reference app with real browser clients:5656 build, boot, offline edits, reconvergence, and the full provisioning lifecycle through the gate and5757 through an iroh-paired leaf. One known upstream finding: lofi's opt-in browser convergence gate5858 fails at its final stage with a catalogue error **identically against lofi-node and against lofi's@@ -5,9 +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. Running that node yourself — one binary,10-tickets, optional node-to-node replication — is covered in the [self-hosting section](/node).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).1112 1213 ## Provision a managed Jazz app1314 @@ -5,16 +5,26 @@
55 */66 import { type Entrypoint, entrypoints } from "./entrypoints.ts";77 8-// `--node` renders the lofi-node API instead: same renderer, sourced from a9-// checkout of FelineStateMachine/lofi-node (LOFI_NODE_DIR, default10-// ../lofi-node), emitted into the /node/api docs instance. lofi-node is not11-// published to a registry; the checkout is the source of truth.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.1212 const NODE_MODE = Deno.args.includes("--node");1313 const NODE_DIR = NODE_MODE ? (Deno.env.get("LOFI_NODE_DIR") ?? "../lofi-node") : "";1414 1515 const nodeEntrypoints: readonly Entrypoint[] = [16- { jsrName: ".", file: `${NODE_DIR}/mod.ts`, page: "node", title: "Sync node" },17- { jsrName: "./testing", file: `${NODE_DIR}/testing/mod.ts`, page: "testing", title: "Testing" },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+ },1828 ];1929 2030 const activeEntrypoints = NODE_MODE ? nodeEntrypoints : entrypoints;@@ -37,8 +47,14 @@ type TsType = {
3747 // deno-lint-ignore no-explicit-any3848 [key: string]: any;3949 };40-// deno-lint-ignore no-explicit-any41-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+};4258 type TypeParam = { name: string; constraint?: TsType; default?: TsType };4359 type Declaration = {4460 kind: string;@@ -104,7 +120,9 @@ function printType(t: TsType | undefined | null): string {
104120 const v = t.value ?? {};105121 const parts: string[] = [];106122 for (const sig of v.indexSignatures ?? []) {107- parts.push(`[${(sig.params ?? []).map(printParam).join(", ")}]: ${printType(sig.tsType)}`);123+ parts.push(124+ `[${(sig.params ?? []).map(printParam).join(", ")}]: ${printType(sig.tsType)}`,125+ );108126 }109127 for (const p of v.properties ?? []) {110128 parts.push(@@ -203,10 +221,14 @@ function classSignature(name: string, def: any): string {
203221 const lines: string[] = [];204222 let head = `class ${name}${printTypeParams(def?.typeParams ?? [])}`;205223 if (def?.extends) head += ` extends ${def.extends}`;206- 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+ }207227 lines.push(`${head} {`);208228 for (const ctor of def?.constructors ?? []) {209- lines.push(` constructor(${(ctor.params ?? []).map(printParam).join(", ")});`);229+ lines.push(230+ ` constructor(${(ctor.params ?? []).map(printParam).join(", ")});`,231+ );210232 }211233 for (const prop of def?.properties ?? []) {212234 const mods = [@@ -237,7 +259,9 @@ function classSignature(name: string, def: any): string {
237259 function interfaceSignature(name: string, def: any): string {238260 const lines: string[] = [];239261 let head = `interface ${name}${printTypeParams(def?.typeParams ?? [])}`;240- 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+ }241265 lines.push(`${head} {`);242266 for (const prop of def?.properties ?? []) {243267 lines.push(@@ -347,7 +371,9 @@ function renderJsDoc(jsDoc: JsDoc | undefined): string {
347371 out.push("| Parameter | Description |");348372 out.push("| --- | --- |");349373 for (const tag of params) {350- out.push(`| \`${tag.name}\` | ${renderJsDocText(tag.doc ?? "").replaceAll("\n", " ")} |`);374+ out.push(375+ `| \`${tag.name}\` | ${renderJsDocText(tag.doc ?? "").replaceAll("\n", " ")} |`,376+ );351377 }352378 }353379 for (const tag of tags) {@@ -379,14 +405,22 @@ async function docModule(file: string): Promise<ModuleNodes> {
379405 });380406 const result = await command.output();381407 if (!result.success) {382- 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+ );383411 }384- 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;385415 if (parsed.version !== 2) {386- 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+ );387419 }388420 const modules = Object.values(parsed.nodes);389- 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+ }390424 return modules[0];391425 }392426 @@ -402,7 +436,11 @@ const KIND_LABEL: Record<string, string> = {
402436 403437 function renderSymbol(symbol: SymbolNode): string {404438 const out: string[] = [];405- 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+ ];406444 // h2 keeps the heading hierarchy sequential under the page h1407445 out.push(`## ${symbol.name}`);408446 out.push(`<sup>${kinds.join(", ")}</sup>`);@@ -421,7 +459,8 @@ function renderSymbol(symbol: SymbolNode): string {
421459 function symbolSortKey(symbol: SymbolNode): [number, string] {422460 const kindRank =423461 symbol.declarations.some((dec) =>424- dec.kind === "function" || dec.kind === "class" || dec.kind === "variable"462+ dec.kind === "function" || dec.kind === "class" ||463+ dec.kind === "variable"425464 )426465 ? 0427466 : 1;@@ -430,7 +469,8 @@ function symbolSortKey(symbol: SymbolNode): [number, string] {
430469 431470 function moduleSummary(moduleDoc: JsDoc | undefined): string {432471 const doc = moduleDoc?.doc ?? "";433- const firstSentence = doc.split(/\n\s*\n/)[0]?.replaceAll("\n", " ").trim() ?? "";472+ const firstSentence = doc.split(/\n\s*\n/)[0]?.replaceAll("\n", " ").trim() ??473+ "";434474 return renderJsDocText(firstSentence);435475 }436476 @@ -449,9 +489,8 @@ for (const entry of activeEntrypoints) {
449489 page: entry.page,450490 position: entry.page.startsWith("recipes/")451491 ? 1 +452- [...activeEntrypoints.filter((e) => e.page.startsWith("recipes/"))].findIndex((e) =>453- e.page === entry.page454- )492+ [...activeEntrypoints.filter((e) => e.page.startsWith("recipes/"))]493+ .findIndex((e) => e.page === entry.page)455494 : 1 + pageOrder.indexOf(entry.page),456495 entries: [],457496 };@@ -466,7 +505,9 @@ const denoJson = JSON.parse(
466505 description: string;467506 };468507 469-await Deno.mkdir(NODE_MODE ? OUT_DIR : `${OUT_DIR}/recipes`, { recursive: true });508+await Deno.mkdir(NODE_MODE ? OUT_DIR : `${OUT_DIR}/recipes`, {509+ recursive: true,510+});470511 471512 const indexRows: string[] = [];472513 let totalSymbols = 0;@@ -495,7 +536,9 @@ for (const group of groups.values()) {
495536 });496537 const isCommand = group.page === "cli";497538 if (!isCommand && symbols.length === 0) {498- 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+ );499542 }500543 if (!module.module_doc?.doc && symbols.length === 0) {501544 throw new Error(`No module doc or symbols for ${entry.file}.`);@@ -508,14 +551,14 @@ for (const group of groups.values()) {
508551 if (isCommand) {509552 sections.push(`## ${entry.title}`);510553 sections.push(`\`\`\`sh\ndeno run -A jsr:${jsrSpecifier}\n\`\`\``);511- 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+ }512557 } else {513- sections.push(514- NODE_MODE515- ? `\`\`\`ts\nimport * as mod from "${jsrSpecifier}"; // from a lofi-node repo checkout\n\`\`\``516- : `\`\`\`ts\nimport * as mod from "jsr:${jsrSpecifier}";\n\`\`\``,517- );518- if (module.module_doc?.doc) sections.push(renderJsDocText(module.module_doc.doc.trim()));558+ sections.push(`\`\`\`ts\nimport * as mod from "jsr:${jsrSpecifier}";\n\`\`\``);559+ if (module.module_doc?.doc) {560+ sections.push(renderJsDocText(module.module_doc.doc.trim()));561+ }519562 sections.push(...symbols.map(renderSymbol));520563 }521564 indexRows.push(@@ -536,10 +579,8 @@ for (const group of groups.values()) {
536579 }537580 538581 const indexIntro = NODE_MODE539- ? `Generated from the JSDoc of [\`@nzip/lofi-node\`](https://github.com/FelineStateMachine/lofi-node) v${denoJson.version}.540-${denoJson.description}541-542-lofi-node is consumed from a repository checkout; it is not yet published to a registry.`582+ ? `Generated from the JSDoc of [\`@nzip/lofi-node\`](https://jsr.io/@nzip/lofi-node) v${denoJson.version}.583+${denoJson.description}`543584 : `Generated from the JSDoc of [\`@nzip/lofi\`](https://jsr.io/@nzip/lofi) v${denoJson.version}.544585 ${denoJson.description}`;545586 @@ -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 @@ -41,7 +43,9 @@ for (const markdown of markdownFiles) {
4143 }4244 4345 if (broken.length > 0) {44- 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+ );4549 Deno.exit(1);4650 }4751 @@ -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,7 +83,9 @@ 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);86+ const position = Number(87+ markdown.match(/^sidebar_position:\s*(\d+)$/m)?.[1] ?? 99,88+ );8289 const url = stem === "index" ? urlPrefix : `${urlPrefix}/${stem}`;8390 apiEntries.push({8491 title: firstHeading(markdown) ?? stem,@@ -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,7 +119,7 @@ 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.`,113- `The self-hosted sync node (lofi-node) is indexed separately at ${siteUrl}/node/llms.txt` +122+ `lofi-node (user-owned sync) is indexed separately at ${siteUrl}/node/llms.txt` +114123 ` (full corpus: ${siteUrl}/node/llms-full.txt).`,115124 "",116125 ];@@ -141,7 +150,8 @@ const allDocs: DocEntry[] = [
141150 ...apiEntries,142151 ];143152 for (const doc of allDocs) {144- const markdown = stripFrontmatter(await Deno.readTextFile(doc.sourcePath)).trim();153+ const markdown = stripFrontmatter(await Deno.readTextFile(doc.sourcePath))154+ .trim();145155 fullParts.push("---", "", `<!-- Source: ${doc.url} -->`, "", markdown, "");146156 }147157 @@ -173,7 +183,9 @@ for (const section of nodeManifest.sections) {
173183 try {174184 markdown = await Deno.readTextFile(sourcePath);175185 } catch {176- console.error(`Missing ${sourcePath}; run \`deno task site:node\` first.`);186+ console.error(187+ `Missing ${sourcePath}; run \`deno task site:node\` first.`,188+ );177189 Deno.exit(1);178190 }179191 docs.push({@@ -190,7 +202,9 @@ try {
190202 await collectApiDir(NODE_API_DIR, `${siteUrl}/node/api`);191203 } catch (error) {192204 if (!(error instanceof Deno.errors.NotFound)) throw error;193- console.error(`Missing ${NODE_API_DIR}; run \`deno task site:api:node\` first.`);205+ console.error(206+ `Missing ${NODE_API_DIR}; run \`deno task site:api:node\` first.`,207+ );194208 Deno.exit(1);195209 }196210 const nodeApiEntries = [...apiEntries];@@ -199,7 +213,7 @@ nodeApiEntries.sort((a, b) => a.position - b.position || a.url.localeCompare(b.u
199213 const nodeShort: string[] = [200214 "# lofi-node",201215 "",202- "> Self-host the sync backend for lofi apps: one daemon embedding a Jazz sync server, iroh node-to-node transport, and ticket-gated access.",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.",203217 "",204218 `This file indexes the lofi-node documentation deployed at ${siteUrl}/node.`,205219 `The full corpus is available at ${siteUrl}/node/llms-full.txt; the lofi framework itself is indexed at ${siteUrl}/llms.txt.`,@@ -207,11 +221,15 @@ const nodeShort: string[] = [
207221 ];208222 for (const section of nodeSections) {209223 nodeShort.push(`## ${section.label}`, "");210- for (const doc of section.docs) nodeShort.push(`- [${doc.title}](${doc.url})`);224+ for (const doc of section.docs) {225+ nodeShort.push(`- [${doc.title}](${doc.url})`);226+ }211227 nodeShort.push("");212228 }213229 nodeShort.push("## API reference", "");214-for (const entry of nodeApiEntries) nodeShort.push(`- [${entry.title}](${entry.url})`);230+for (const entry of nodeApiEntries) {231+ nodeShort.push(`- [${entry.title}](${entry.url})`);232+}215233 nodeShort.push("");216234 217235 const nodeFull: string[] = [@@ -220,8 +238,14 @@ const nodeFull: string[] = [
220238 `> Deployed at ${siteUrl}/node. Source: https://github.com/FelineStateMachine/lofi-node`,221239 "",222240 ];223-for (const doc of [...nodeSections.flatMap((section) => section.docs), ...nodeApiEntries]) {224- const markdown = stripFrontmatter(await Deno.readTextFile(doc.sourcePath)).trim();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();225249 nodeFull.push("---", "", `<!-- Source: ${doc.url} -->`, "", markdown, "");226250 }227251 @@ -229,8 +253,14 @@ await Deno.mkdir(`${OUT_DIR}/node`, { recursive: true });
229253 await Deno.writeTextFile(`${OUT_DIR}/node/llms.txt`, nodeShort.join("\n"));230254 await Deno.writeTextFile(`${OUT_DIR}/node/llms-full.txt`, nodeFull.join("\n"));231255 232-const docCount = sections.reduce((sum, section) => sum + section.docs.length, 0);233-const nodeDocCount = nodeSections.reduce((sum, section) => sum + section.docs.length, 0);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+);234264 console.log(235265 `Generated llms.txt (${docCount} docs + ${mainApiCount} API pages), node/llms.txt ` +236266 `(${nodeDocCount} docs + ${nodeApiEntries.length} API pages), and the -full pair into ${OUT_DIR}/.`,@@ -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: {@@ -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 {@@ -194,52 +243,65 @@ const config: Config = {
194243 respectPrefersColorScheme: true,195244 },196245 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).197248 logo: {198249 alt: "lofi",199- src: "img/lofi-wordmark.svg",200- // intrinsic dimensions (800x360 viewBox at the navbar's 1.7rem height)201- // so the image never contributes layout shift202- width: 60,203- 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,204255 },205256 items: [206- { type: "docSidebar", sidebarId: "docs", position: "left", label: "Docs" },207- { to: "/api", label: "API", position: "left" },208- { to: "/node", label: "Self-host", position: "left" },209- { href: "https://jsr.io/@nzip/lofi", label: "JSR", position: "right" },210257 {211- href: "https://github.com/FelineStateMachine/lofi",212- label: "GitHub",213- 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+ ],214280 },215281 ],216282 },217283 footer: {218284 style: "dark",285+ // One column per product, parallel structure, lowercase like the nav.219286 links: [220287 {221- title: "Start",288+ title: "lofi",222289 items: [223- { label: "Getting started", to: "/docs/getting-started" },224- { label: "Data and UI", to: "/docs/data-and-ui" },225- { label: "Testing", to: "/docs/testing" },226- ],227- },228- {229- title: "Deeper",230- items: [231- { label: "Permissions", to: "/docs/permissions" },232- { label: "Sync and recovery", to: "/docs/sync-and-recovery" },233- { label: "API reference", to: "/api" },234- { label: "Self-host a sync node", to: "/node" },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" },235295 ],236296 },237297 {238- title: "Repo",298+ title: "lofi-node",239299 items: [240- { label: "GitHub", href: "https://github.com/FelineStateMachine/lofi" },241- { label: "JSR", href: "https://jsr.io/@nzip/lofi" },242- { 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" },243305 ],244306 },245307 ],@@ -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,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+}@@ -1,10 +1,11 @@
11 import type { ReactNode } from "react";2-import { useId, useState } from "react";2+import { useState } from "react";33 import Link from "@docusaurus/Link";44 import useDocusaurusContext from "@docusaurus/useDocusaurusContext";55 import useBaseUrl from "@docusaurus/useBaseUrl";66 import Layout from "@theme/Layout";77 import SyncDemo from "../components/SyncDemo";8+import Leaf from "../components/Leaf";89 910 import "../css/landing.css";1011 @@ -32,32 +33,6 @@ function CopyCmd(): ReactNode {
3233 );3334 }3435 35-function Leaf(): ReactNode {36- const maskId = useId();37- return (38- <svg className="cell-leaf" viewBox="0 0 512 512" aria-hidden="true">39- <defs>40- <mask id={maskId}>41- <rect width="512" height="512" fill="#fff" />42- <path43- d="M183 184v146h124M183 330 91 455"44- fill="none"45- stroke="#000"46- strokeLinecap="round"47- strokeLinejoin="round"48- strokeWidth="38"49- />50- </mask>51- </defs>52- <path53- 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"54- fill="currentColor"55- mask={`url(#${maskId})`}56- />57- </svg>58- );59-}60-6136 const FEATURES: Array<{ title: string; body: ReactNode }> = [6237 {6338 title: "Local-first data",@@ -78,8 +53,8 @@ const FEATURES: Array<{ title: string; body: ReactNode }> = [
7853 title: "Narrow collaboration templates",7954 body: (8055 <>81- Private resources, direct shares, and fixed-role groups sit behind a small{" "}82- <code className="inline-mono">@nzip/lofi/access</code>{" "}56+ Private resources, direct shares, and fixed-role groups sit behind a57+ small <code className="inline-mono">@nzip/lofi/access</code>{" "}8358 API. Raw Jazz permissions stay available as an escape hatch.8459 </>8560 ),@@ -106,42 +81,53 @@ export default function Home(): ReactNode {
10681 title="Local-first apps that keep working offline"10782 description="lofi generates installable Deno web apps that open immediately, keep working offline, and sync when users choose. Astro shell, Preact islands, Jazz 2 CRDT sync."10883 >109- <div className="landing">110- {/* ===================== HERO ===================== */}84+ <main className="landing">85+ {/* ===================== HERO: MARK + CLAIM ===================== */}11186 <div className="wrap">112- <header className="hero">113- <div>114- <p className="eyebrow">115- <span>@nzip/lofi · local-first meta-framework</span>116- <span className="pill">v{version} · alpha · MIT</span>117- </p>118- <h1 className="hero-title">119- Put down <span data-ink="roots">roots</span>. Skip the spinner.87+ <header>88+ <div className="hero-top">89+ <h1 className="nmark" aria-label="lofi">90+ <span className="lo">lo</span>91+ <span className="fi">fi</span>12092 </h1>121- <p className="lede hero-sub">122- lofi generates an Astro + Preact app backed by durable local data. Users123- start with a private, on-device account, no sign-in or network, and can add124- Jazz-powered sync and recovery later, without replacing that identity or rewriting125- product code.126- </p>127-128- <CopyCmd />129- <p className="cmd-note">Requires Deno 2.9+.</p>130-131- <div className="cta-row">132- <Link className="btn btn--primary" to="/docs/getting-started">Read the guide</Link>133- <Link className="btn" href="https://github.com/FelineStateMachine/lofi">134- View source135- </Link>93+ <div className="hero-side">94+ <p className="eyebrow">95+ <span>local-first meta-framework</span>96+ <span className="pill">v{version} · alpha · MIT</span>97+ </p>13698 </div>13799 </div>138- <div className="hero-art">139- <img140- src={heroArt}141- width="900"142- height="900"143- alt="A phone containing a growing plant. Its roots run below the soil line inside the device; a dotted line arcs to a second, smaller phone."144- />100+ <div className="hero-main">101+ <div>102+ <p className="hero-claim">103+ Installable web apps where the network is optional and the104+ user holds the keys.105+ </p>106+ <CopyCmd />107+ <p className="cmd-note">108+ Requires Deno 2.9+. Astro + Preact + Jazz 2, generated.109+ </p>110+ <div className="cta-row">111+ <Link className="btn btn--primary" to="/docs/getting-started">112+ Read the guide113+ </Link>114+ <Link115+ className="btn"116+ href="https://github.com/FelineStateMachine/lofi"117+ >118+ source119+ </Link>120+ </div>121+ </div>122+ <div className="hero-art">123+ <img124+ src={heroArt}125+ width="900"126+ height="900"127+ fetchPriority="high"128+ alt="A phone containing a growing plant. Its roots run below the soil line inside the device; a dotted line arcs to a second, smaller phone."129+ />130+ </div>145131 </div>146132 </header>147133 </div>@@ -157,32 +143,35 @@ export default function Home(): ReactNode {
157143 <span className="life-tag">First launch</span>158144 <h3>A private account, on the device</h3>159145 <p>160- Every user gets an on-device account at first open, even when the app has no sync161- service configured at all. Nothing to sign up or waiting.146+ Every user gets an on-device account at first open, even when147+ the app has no sync service configured at all. Nothing to sign148+ up or waiting.162149 </p>163150 </div>164151 <div className="life-step">165152 <span className="life-tag">Every launch after</span>166153 <h3>Reads from local storage, not the network</h3>167154 <p>168- The UI reads durable local data instead of waiting on a round trip. When storage169- fails, lofi surfaces it rather than quietly falling back to memory and risking the170- data.155+ The UI reads durable local data instead of waiting on a round156+ trip. When storage fails, lofi surfaces it rather than quietly157+ falling back to memory and risking the data.171158 </p>172159 </div>173160 <div className="life-step">174161 <span className="life-tag">If the user wants it</span>175162 <h3>The same account, made portable</h3>176163 <p>177- Connect managed Jazz sync and a user can back up the account they already have —178- recovering it with a passkey where supported, or a 24-word phrase. Data made179- offline comes along.164+ Connect managed Jazz sync and a user can back up the account165+ they already have — recovering it with a passkey where166+ supported, or a 24-word phrase. Data made offline comes along.180167 </p>181168 <p className="life-cmd">182169 <code className="inline-mono">deno task jazz:provision</code>183170 <br />184171 <span>185- or scaffold with <code className="inline-mono">--sync</code> from the start.172+ or scaffold with <code className="inline-mono">--sync</code>173+ {" "}174+ from the start.186175 </span>187176 </p>188177 </div>@@ -197,9 +186,10 @@ export default function Home(): ReactNode {
197186 <p className="eyebrow">at the core of the idea</p>198187 <h2>Pull the cable.</h2>199188 <p className="lede band-lede">200- This is the starter task app the generator gives you. Turn the network off, keep201- typing on both devices, then turn it back on. Nothing is lost and nothing conflicts.202- That is what the CRDT buys you.189+ This is the starter task app the generator gives you. Turn the190+ network off, keep typing on both devices, then turn it back on.191+ Nothing is lost and nothing conflicts. That is what the CRDT192+ buys you.203193 </p>204194 </div>205195 <SyncDemo />@@ -235,33 +225,40 @@ export default function Home(): ReactNode {
235225 </h2>236226 </div>237227 <div className="anat">238- <pre className="tree" aria-label="Generated project layout, coloured by ownership"><code>{"my-app/\n├── "}<span className="yours">deno.json</span>{" "}<span className="note"># tasks + one pinned version</span>{"\n├── "}<span className="yours">public/</span>{" "}<span className="note"># manifest, product icons</span>{"\n├── "}<span className="yours">src/</span>{"\n│ ├── "}<span className="yours"><b>app.ts</b></span>{" "}<span className="note"># storage, sync, passkey config</span>{"\n│ ├── "}<span className="yours"><b>schema.ts</b></span>{" "}<span className="note"># persisted data model</span>{"\n│ ├── "}<span className="yours"><b>permissions.ts</b></span>{" "}<span className="note"># private, shared, or group</span>{"\n│ ├── "}<span className="yours">islands/</span>{" "}<span className="note"># interactive Preact UI</span>{"\n│ ├── "}<span className="yours">layouts/</span>{" "}<span className="note"># document shell</span>{"\n│ ├── "}<span className="yours">pages/</span>{" "}<span className="note"># Astro routes</span>{"\n│ └── "}<span className="yours">styles/</span>{" "}<span className="note"># product styling</span>{"\n├── "}<span className="yours">tests/</span>{" "}<span className="note"># local-first journeys</span>{"\n├── "}<span className="note">.lofi/</span>{" "}<span className="note"># generated tooling (ignored)</span>{"\n└── "}<span className="note">dist/</span>{" "}<span className="note"># production PWA (ignored)</span>{"\n\n"}<span className="theirs">@nzip/lofi</span>{" "}<span className="note"># storage, identity, sync, PWA</span></code></pre>228+ <pre229+ className="tree"230+ aria-label="Generated project layout, coloured by ownership"231+ ><code>{"my-app/\n├── "}<span className="yours">deno.json</span>{" "}<span className="note"># tasks + one pinned version</span>{"\n├── "}<span className="yours">public/</span>{" "}<span className="note"># manifest, product icons</span>{"\n├── "}<span className="yours">src/</span>{"\n│ ├── "}<span className="yours"><b>app.ts</b></span>{" "}<span className="note"># storage, sync, passkey config</span>{"\n│ ├── "}<span className="yours"><b>schema.ts</b></span>{" "}<span className="note"># persisted data model</span>{"\n│ ├── "}<span className="yours"><b>permissions.ts</b></span>{" "}<span className="note"># private, shared, or group</span>{"\n│ ├── "}<span className="yours">islands/</span>{" "}<span className="note"># interactive Preact UI</span>{"\n│ ├── "}<span className="yours">layouts/</span>{" "}<span className="note"># document shell</span>{"\n│ ├── "}<span className="yours">pages/</span>{" "}<span className="note"># Astro routes</span>{"\n│ └── "}<span className="yours">styles/</span>{" "}<span className="note"># product styling</span>{"\n├── "}<span className="yours">tests/</span>{" "}<span className="note"># local-first journeys</span>{"\n├── "}<span className="note">.lofi/</span>{" "}<span className="note"># generated tooling (ignored)</span>{"\n└── "}<span className="note">dist/</span>{" "}<span className="note"># production PWA (ignored)</span>{"\n\n"}<span className="theirs">@nzip/lofi</span>{" "}<span className="note"># storage, identity, sync, PWA</span></code></pre>239232 <div>240233 <p>241- A generated project keeps your source separate from the versioned framework242- package. Product work stays in the files above; framework behaviour is imported,243- never copied into your tree. Upgrading the package updates the runtime without a234+ A generated project keeps your source separate from the235+ versioned framework package. Product work stays in the files236+ above; framework behaviour is imported, never copied into your237+ tree. Upgrading the package updates the runtime without a244238 migration through your own source.245239 </p>246240 <ul className="legend">247241 <li>248242 <span className="swatch swatch--yours" />249243 <span>250- <b>Yours.</b> Schema, permissions, config, routes, islands, styles, tests.244+ <b>Yours.</b>{" "}245+ Schema, permissions, config, routes, islands, styles,246+ tests.251247 </span>252248 </li>253249 <li>254250 <span className="swatch swatch--theirs" />255251 <span>256252 <b>lofi's.</b>{" "}257- Storage, identity, sync, lifecycle, and PWA plumbing — imported from one258- pinned version.253+ Storage, identity, sync, lifecycle, and PWA plumbing —254+ imported from one pinned version.259255 </span>260256 </li>261257 <li>262258 <span className="swatch swatch--gen" />263259 <span>264- <b>Generated.</b> Regenerated on demand and git-ignored. Never edit by hand.260+ <b>Generated.</b>{" "}261+ Regenerated on demand and git-ignored. Never edit by hand.265262 </span>266263 </li>267264 </ul>@@ -285,8 +282,8 @@ export default function Home(): ReactNode {
285282 <h2>Pinned on purpose.</h2>286283 </div>287284 <p className="dim intro-note">288- The data layer is an alpha, so every upgrade is reviewed and validated before it289- reaches a generated project.285+ The data layer is an alpha, so every upgrade is reviewed and286+ validated before it reaches a generated project.290287 </p>291288 <table className="tbl">292289 <thead>@@ -325,24 +322,26 @@ export default function Home(): ReactNode {
325322 <h2>What it isn't, yet.</h2>326323 </div>327324 <p className="dim intro-note">328- Built for mobile web apps where offline is a requirement, not a best-effort329- enhancement. Before you commit:325+ Built for mobile web apps where offline is a requirement, not326+ a best-effort enhancement. Before you commit:330327 </p>331328 <ul className="limits">332329 <li>333- <b>It is an early alpha release.</b> Expect the surface to move.330+ <b>It is an early alpha release.</b>{" "}331+ Expect the surface to move.334332 </li>335333 <li>336- <b>The data layer is Jazz 2 alpha</b>, deliberately pinned to a reviewed337- version.334+ <b>The data layer is Jazz 2 alpha</b>, deliberately pinned335+ to a reviewed version.338336 </li>339337 <li>340- <b>Browser floors are real.</b> Android Chrome 148+ and iOS Safari 16.4+.338+ <b>Browser floors are real.</b>{" "}339+ Android Chrome 148+ and iOS Safari 16.4+.341340 </li>342341 <li>343342 <b>Recovery is user-controlled.</b>{" "}344- lofi keeps no recoverable account material on the server, so the phrase has to345- be kept safe.343+ lofi keeps no recoverable account material on the server, so344+ the phrase has to be kept safe.346345 </li>347346 </ul>348347 </div>@@ -362,12 +361,7 @@ export default function Home(): ReactNode {
362361 </div>363362 <div className="two-up">364363 <table className="tbl">365- <thead>366- <tr>367- <th>Everyday development</th>368- <th></th>369- </tr>370- </thead>364+ <caption>Everyday development</caption>371365 <tbody>372366 <tr>373367 <td>@@ -379,7 +373,9 @@ export default function Home(): ReactNode {
379373 <td>380374 <code>doctor</code>381375 </td>382- <td>Checks readiness without printing config or secrets.</td>376+ <td>377+ Checks readiness without printing config or secrets.378+ </td>383379 </tr>384380 <tr>385381 <td>@@ -404,19 +400,15 @@ export default function Home(): ReactNode {
404400 </tbody>405401 </table>406402 <table className="tbl">407- <thead>408- <tr>409- <th>Sync, schema, deployment</th>410- <th></th>411- </tr>412- </thead>403+ <caption>Sync, schema, deployment</caption>413404 <tbody>414405 <tr>415406 <td>416407 <code>jazz:provision</code>417408 </td>418409 <td>419- Creates a managed Jazz app and configures <code>.env</code>.410+ Creates a managed Jazz app and configures{" "}411+ <code>.env</code>.420412 </td>421413 </tr>422414 <tr>@@ -448,7 +440,7 @@ export default function Home(): ReactNode {
448440 </div>449441 </section>450442 </div>451- </div>443+ </main>452444 </Layout>453445 );454446 }@@ -1,137 +1,209 @@
11 import type { ReactNode } from "react";22 import Link from "@docusaurus/Link";3+import useDocusaurusContext from "@docusaurus/useDocusaurusContext";34 import Layout from "@theme/Layout";5+import NodePlayground from "../components/NodePlayground";6+import TicketAnatomy from "../components/TicketAnatomy";7+import SliceMerge from "../components/SliceMerge";48 5-// The /node homepage: what lofi-node is and where its docs live. Content is6-// sourced from the lofi-node README (FelineStateMachine/lofi-node); the7-// tutorial, guide, and reference pages under /node/docs are assembled by8-// tools/node_docs.ts.9+import "../css/landing.css";10+import "../css/node-landing.css";911 10-const QUICK_START = [11- ["deno task compile", "one self-contained binary (dist/lofi-node)"],12- ["dist/lofi-node init --port 4802", "ticket-gated by default; --open opts out"],13- ["dist/lofi-node start", "gate URL + node-pairing ticket"],14- ["dist/lofi-node ticket issue --label phone", "app-connect ticket (shown once)"],15-] as const;16-17-const CARDS: Array<{ title: string; body: string; to: string; link: string }> = [18- {19- title: "Self-host your first sync node",20- body:21- "Compile the binary, start a ticket-gated node, issue an app ticket, and enroll it in a lofi app.",22- to: "/node/docs/self-host-a-sync-node",23- link: "Start the tutorial",24- },25- {26- title: "Sliceable apps and shared stores",27- body:28- "One store as shared infrastructure: slices, the namespace honesty invariant, and the four store states.",29- to: "/node/docs/sliceable-apps-and-shared-stores",30- link: "Read the guide",31- },32- {33- title: "App-ticket contract",34- body:35- "The lofisync1 format, scopes, revocation semantics, and the store-status preflight — the contract the lofi side implements.",36- to: "/node/docs/app-ticket",37- link: "Read the contract",38- },39- {40- title: "API reference",41- body:42- "createSyncNode, SyncNode, ticket codecs, and the test mesh — generated from the lofi-node sources.",43- to: "/node/api",44- link: "Browse the API",45- },46-];12+// The /node landing: lofi-node's front door. Shares the brand tokens with the13+// root landing but none of its furniture. The page is three interactive14+// rigs: a node you operate, a ticket you open, and a store two apps share.15+// lofi-node is an END USER capability (owning where their data syncs), not a16+// way to host a lofi app; the copy keeps that audience.4717 4818 export default function NodeHome(): ReactNode {19+ const { siteConfig } = useDocusaurusContext();20+ const version = siteConfig.customFields?.lofiNodeVersion as string;21+4922 return (5023 <Layout51- title="Self-host with lofi-node"52- description="One daemon that self-hosts a lofi app's sync backend: an embedded Jazz sync server, iroh node-to-node transport, and a ticket-based access gate."24+ title="lofi-node: user-owned sync"25+ description="lofi apps let their users choose where data syncs. lofi-node makes that choice real. One daemon a user runs, with an embedded Jazz sync server, iroh node-to-node transport, and a ticket-based access gate."5326 >54- <main className="container margin-vert--lg">55- <div className="row">56- <div className="col col--8 col--offset-2">57- <h1>Own your sync location</h1>58- <p>59- <strong>lofi-node</strong>{" "}60- is the first-class way to self-host the sync backend for lofi apps: one daemon61- embedding a real Jazz sync server, <a href="https://iroh.computer">iroh</a>{" "}62- node-to-node transport, and a ticket-based access gate. Browsers keep speaking Jazz's63- protocol — only the server URL changes, and that URL is{" "}64- <em>user-selected data, not developer configuration</em>: the app stores the ticket65- you issue and syncs where you point it.27+ <main className="landing landing--node">28+ {/* ===================== HERO: MARK + THE PLAYABLE NODE ===================== */}29+ <div className="wrap">30+ <header className="node-hero">31+ <div className="node-hero-top">32+ <h1 className="nmark" aria-label="lofi-node">33+ <span className="lo">lo</span>34+ <span className="fi">fi</span>35+ <span className="dash">-</span>36+ <span className="node">node</span>37+ </h1>38+ <div className="node-hero-side">39+ <p className="eyebrow">40+ <span>user-owned sync</span>41+ <span className="pill">v{version} · alpha · MIT</span>42+ </p>43+ <div className="cta-row">44+ <Link45+ className="btn btn--primary"46+ to="/node/docs/self-host-a-sync-node"47+ >48+ Host your first node49+ </Link>50+ <Link className="btn" to="/node/docs">docs</Link>51+ <Link52+ className="btn"53+ href="https://github.com/FelineStateMachine/lofi-node"54+ >55+ source56+ </Link>57+ </div>58+ </div>59+ </div>60+ <NodePlayground />61+ </header>62+ </div>63+64+ {/* ===================== TICKET ANATOMY ===================== */}65+ <div className="wrap">66+ <section className="band band--rule">67+ <div className="band-head">68+ <p className="eyebrow">A singular entry</p>69+ <h2>Open the ticket.</h2>70+ <p className="lede band-lede">71+ No accounts, no OAuth, no server-side user table. A{" "}72+ <code className="inline-mono">lofisync1.</code>{" "}73+ string carries the store's identity and a bearer secret riding74+ the URL path, which is why the app needs zero changes to sync75+ somewhere new. Click a field.76+ </p>77+ </div>78+ <TicketAnatomy />79+ <p className="dim intro-note">80+ The normative spec lives with the node and renders here from the81+ pinned source:{" "}82+ <Link to="/node/docs/app-ticket">the app-ticket contract</Link>.83+ <br />84+ Both repos test against the same conformance fixtures, so the85+ string above can't drift quietly.6686 </p>67- <ul>68- <li>69- <strong>A real Jazz sync server</strong>{" "}70- — SQLite-backed, health-checked, usable by any lofi app by URL.71- </li>72- <li>73- <strong>Node-to-node replication over iroh</strong>{" "}74- — two homes converge by ticket: dialed by key, hole-punched, no static IPs, no75- cloud dependency.76- </li>77- <li>78- <strong>Tickets, not accounts</strong>{" "}79- — possession of a <code>lofisync1.</code>{" "}80- app ticket is transport access; a provision-scoped ticket additionally administers81- the store, with the admin secret never leaving the node.82- </li>83- <li>84- <strong>One binary</strong>{" "}85- — <code>deno task compile</code>{" "}86- embeds the prebuilt native matrix and runs on macOS and Linux.87- </li>88- </ul>87+ </section>88+ </div>8989 90- <h2>Quick start</h2>91- <pre>92- <code>93- {QUICK_START.map(([cmd, note]) => `$ ${cmd}\n # ${note}\n`).join("")}94- </code>95- </pre>96- <p>97- The issued ticket carries <em>location + access</em>; the user pastes it into their98- lofi app, which stores it and uses its URL as the sync server. Enrollment on the app99- side is{" "}100- <Link to="/docs/sync-and-recovery">101- documented with the rest of sync and recovery90+ {/* ===================== SHARED STORE ===================== */}91+ <div className="wrap">92+ <section className="band band--rule">93+ <div className="band-head">94+ <p className="eyebrow">many apps, one store, no folklore</p>95+ <h2>Apps are tenants. The store is yours.</h2>96+ <p className="lede band-lede">97+ A user's node can hold several lofi apps' data at once. Each app98+ may provision only the tables under its own namespaces;99+ everything else, a neighbor's tables{" "}100+ <em>and its access rules</em>, rides through byte-for-byte. Run101+ the story below; every step of it is a real, conformance-tested102+ flow.103+ </p>104+ </div>105+ <SliceMerge />106+ <p className="dim intro-note">107+ The full model:{" "}108+ <Link to="/node/docs/sliceable-apps-and-shared-stores">109+ sliceable apps and shared stores110+ </Link>{" "}111+ and the hands-on version,{" "}112+ <Link to="/node/docs/provision-a-store">113+ provision a store102114 </Link>.103115 </p>116+ </section>117+ </div>104118 105- <div className="row margin-top--lg">106- {CARDS.map((card) => (107- <div className="col col--6 margin-bottom--lg" key={card.to}>108- <div className="card" style={{ height: "100%" }}>109- <div className="card__header">110- <h3>{card.title}</h3>111- </div>112- <div className="card__body">113- <p>{card.body}</p>114- </div>115- <div className="card__footer">116- <Link className="button button--secondary button--block" to={card.to}>117- {card.link}118- </Link>119- </div>120- </div>119+ {/* ===================== HONESTY + PATHS ===================== */}120+ <div className="wrap">121+ <section className="band band--rule">122+ <div className="two-up">123+ <div>124+ <div className="band-head band-head--tight">125+ <h2>Still curious?</h2>126+ </div>127+ <ul className="limits">128+ <li>129+ <b>The data plane is Jazz 2 alpha, pinned exactly.</b>{" "}130+ A node must run the same alpha as the apps it serves; bumps131+ are coordinated.132+ </li>133+ <li>134+ <b>Windows runs LAN-only.</b>{" "}135+ A documented native-build gap; the store works, pairing136+ reports itself unavailable instead of degrading silently.137+ </li>138+ <li>139+ <b>Tickets are bearer credentials.</b>{" "}140+ Plain http is for trusted LANs; beyond one, front the gate141+ with TLS.142+ </li>143+ <li>144+ <b>Storage is SQLite or memory</b>, the engine's honest145+ surface today. Replicate the file for off-site durability.146+ </li>147+ </ul>148+ </div>149+ <div>150+ <div className="band-head band-head--tight">151+ <h2>Keep going.</h2>121152 </div>122- ))}153+ <ul className="node-paths">154+ <li>155+ <b>156+ <Link to="/node/docs/self-host-a-sync-node">157+ Self-host a node158+ </Link>159+ </b>: compile, init, ticket, enroll.160+ </li>161+ <li>162+ <b>163+ <Link to="/node/docs/pair-two-homes">Pair two homes</Link>164+ </b>: hole-punched, no static IPs.165+ </li>166+ <li>167+ <b>168+ <Link to="/node/docs/provision-a-store">169+ Provision a store170+ </Link>171+ </b>: the merge, end to end.172+ </li>173+ <li>174+ <b>175+ <Link to="/node/docs/tickets-explained">176+ Tickets explained177+ </Link>178+ </b>: scopes, revocation, posture.179+ </li>180+ <li>181+ <b>182+ <Link to="/node/docs/cli">CLI</Link> ·{" "}183+ <Link to="/node/docs/configuration">Configuration</Link> ·184+ {" "}185+ <Link to="/node/docs/http-surface">HTTP</Link>186+ </b>: the reference shelf.187+ </li>188+ <li>189+ <b>190+ <Link to="/node/api">API reference</Link>191+ </b>: <code className="inline-mono">createSyncNode</code>192+ {" "}193+ and friends, generated from source.194+ </li>195+ <li>196+ <b>197+ <Link to="/node/llms.txt" target="_blank">198+ /node/llms.txt199+ </Link>200+ </b>: the node corpus for agents, separate from the201+ framework's.202+ </li>203+ </ul>204+ </div>123205 </div>124-125- <p>126- Full documentation: <Link to="/node/docs">/node/docs</Link> · agents can ingest{" "}127- <Link to="/node/llms.txt" target="_blank">/node/llms.txt</Link> or{" "}128- <Link to="/node/llms-full.txt" target="_blank">/node/llms-full.txt</Link> · source:129- {" "}130- <a href="https://github.com/FelineStateMachine/lofi-node">131- FelineStateMachine/lofi-node132- </a>133- </p>134- </div>206+ </section>135207 </div>136208 </main>137209 </Layout>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.