From 05ca56e47c06c9a842349230706e9d0ac9bc420f Mon Sep 17 00:00:00 2001 From: RemilioNubilio <275382225+RemilioNubilio@users.noreply.github.com> Date: Fri, 4 Sep 2026 17:37:42 -0400 Subject: [PATCH] chat panel: move the inline webview script into real modules (#215) The 4,248-line inline script in chat/ui/webview.ts becomes 32 ES modules under chat/ui/panel/, typechecked against the DOM lib, bundled by scripts/buildPanel.ts (tsup IIFE, treeshake off) into the committed panel.generated.ts, and inlined by chatHtml() exactly as it inlines mdLibs.generated.ts. No behavior, message, string, or DOM change: bodies moved verbatim, the 78 top-level lets became one state object, boot order and boot posts are preserved in main.ts. The panel now imports core's quoteRefs, github, avatar, skillSigil, modelOptions, slashCommands, and engineInstall in place of its pinned copies; every change-BOTH comment is gone. Tests: goldens for the avatar and sigil code captured before the strings were deleted, unit specs for the pure pieces, an artifact spec (freshness, shape, message contract, shell ids), a jsdom pass executing the real chatHtml() output (boot posts, every inbound type, composer send, slash menu, streaming, quote cards, deadlinks), and a headless Chrome pass over the DevTools protocol with real layout. jsdom is the one new devDependency (packages/core only). Build order: pnpm build:panel regenerates the committed artifact; the surface builds and the publish path are unchanged, and the freshness spec fails CI when the artifact is stale. Co-Authored-By: Claude Fable 5.1 --- .github/workflows/ci.yml | 7 + package.json | 1 + packages/core/package.json | 7 +- packages/core/scripts/buildPanel.ts | 110 + packages/core/src/chat/ui/avatar.spec.ts | 25 + packages/core/src/chat/ui/avatar.ts | 68 +- packages/core/src/chat/ui/icons.ts | 10 + .../core/src/chat/ui/panel.chrome.spec.ts | 234 + .../core/src/chat/ui/panel.generated.spec.ts | 111 + packages/core/src/chat/ui/panel.generated.ts | 2 + packages/core/src/chat/ui/panel.jsdom.spec.ts | 244 + packages/core/src/chat/ui/panel/agents.ts | 167 + packages/core/src/chat/ui/panel/approval.ts | 307 ++ packages/core/src/chat/ui/panel/composer.ts | 331 ++ packages/core/src/chat/ui/panel/dom.ts | 22 + packages/core/src/chat/ui/panel/engine.ts | 223 + packages/core/src/chat/ui/panel/feed.ts | 364 ++ .../core/src/chat/ui/panel/format.spec.ts | 204 + packages/core/src/chat/ui/panel/format.ts | 82 + packages/core/src/chat/ui/panel/host.ts | 51 + packages/core/src/chat/ui/panel/main.ts | 397 ++ packages/core/src/chat/ui/panel/markdown.ts | 124 + packages/core/src/chat/ui/panel/market.ts | 408 ++ packages/core/src/chat/ui/panel/menus.ts | 43 + packages/core/src/chat/ui/panel/messages.ts | 65 + packages/core/src/chat/ui/panel/overlays.ts | 153 + packages/core/src/chat/ui/panel/paging.ts | 97 + packages/core/src/chat/ui/panel/profile.ts | 404 ++ packages/core/src/chat/ui/panel/publish.ts | 124 + .../core/src/chat/ui/panel/quoteCard.spec.ts | 32 + packages/core/src/chat/ui/panel/quoteCard.ts | 12 + packages/core/src/chat/ui/panel/quotes.ts | 71 + packages/core/src/chat/ui/panel/sessions.ts | 62 + packages/core/src/chat/ui/panel/shell.ts | 145 + packages/core/src/chat/ui/panel/skeleton.ts | 11 + packages/core/src/chat/ui/panel/skills.ts | 180 + packages/core/src/chat/ui/panel/slash.ts | 155 + packages/core/src/chat/ui/panel/state.ts | 99 + packages/core/src/chat/ui/panel/storage.ts | 46 + packages/core/src/chat/ui/panel/tiers.spec.ts | 94 + packages/core/src/chat/ui/panel/tiers.ts | 38 + packages/core/src/chat/ui/panel/tsconfig.json | 22 + packages/core/src/chat/ui/panel/turns.ts | 270 ++ packages/core/src/chat/ui/panel/views.ts | 51 + packages/core/src/chat/ui/panel/wallet.ts | 69 + packages/core/src/chat/ui/skillSigil.spec.ts | 16 + packages/core/src/chat/ui/skillSigil.ts | 60 +- packages/core/src/chat/ui/webview.ts | 4271 +---------------- packages/core/src/notes/quoteRefs.spec.ts | 64 +- packages/core/src/notes/quoteRefs.ts | 20 +- .../test/fixtures/panel/avatarGoldens.json | 27 + packages/core/test/fixtures/panel/contract.ts | 123 + packages/core/test/fixtures/panel/inbound.ts | 61 + .../test/fixtures/panel/sigilGoldens.json | 22 + packages/core/test/test-skills.ts | 18 +- packages/core/tsconfig.json | 3 +- pnpm-lock.yaml | 350 +- 57 files changed, 6430 insertions(+), 4347 deletions(-) create mode 100644 packages/core/scripts/buildPanel.ts create mode 100644 packages/core/src/chat/ui/avatar.spec.ts create mode 100644 packages/core/src/chat/ui/icons.ts create mode 100644 packages/core/src/chat/ui/panel.chrome.spec.ts create mode 100644 packages/core/src/chat/ui/panel.generated.spec.ts create mode 100644 packages/core/src/chat/ui/panel.generated.ts create mode 100644 packages/core/src/chat/ui/panel.jsdom.spec.ts create mode 100644 packages/core/src/chat/ui/panel/agents.ts create mode 100644 packages/core/src/chat/ui/panel/approval.ts create mode 100644 packages/core/src/chat/ui/panel/composer.ts create mode 100644 packages/core/src/chat/ui/panel/dom.ts create mode 100644 packages/core/src/chat/ui/panel/engine.ts create mode 100644 packages/core/src/chat/ui/panel/feed.ts create mode 100644 packages/core/src/chat/ui/panel/format.spec.ts create mode 100644 packages/core/src/chat/ui/panel/format.ts create mode 100644 packages/core/src/chat/ui/panel/host.ts create mode 100644 packages/core/src/chat/ui/panel/main.ts create mode 100644 packages/core/src/chat/ui/panel/markdown.ts create mode 100644 packages/core/src/chat/ui/panel/market.ts create mode 100644 packages/core/src/chat/ui/panel/menus.ts create mode 100644 packages/core/src/chat/ui/panel/messages.ts create mode 100644 packages/core/src/chat/ui/panel/overlays.ts create mode 100644 packages/core/src/chat/ui/panel/paging.ts create mode 100644 packages/core/src/chat/ui/panel/profile.ts create mode 100644 packages/core/src/chat/ui/panel/publish.ts create mode 100644 packages/core/src/chat/ui/panel/quoteCard.spec.ts create mode 100644 packages/core/src/chat/ui/panel/quoteCard.ts create mode 100644 packages/core/src/chat/ui/panel/quotes.ts create mode 100644 packages/core/src/chat/ui/panel/sessions.ts create mode 100644 packages/core/src/chat/ui/panel/shell.ts create mode 100644 packages/core/src/chat/ui/panel/skeleton.ts create mode 100644 packages/core/src/chat/ui/panel/skills.ts create mode 100644 packages/core/src/chat/ui/panel/slash.ts create mode 100644 packages/core/src/chat/ui/panel/state.ts create mode 100644 packages/core/src/chat/ui/panel/storage.ts create mode 100644 packages/core/src/chat/ui/panel/tiers.spec.ts create mode 100644 packages/core/src/chat/ui/panel/tiers.ts create mode 100644 packages/core/src/chat/ui/panel/tsconfig.json create mode 100644 packages/core/src/chat/ui/panel/turns.ts create mode 100644 packages/core/src/chat/ui/panel/views.ts create mode 100644 packages/core/src/chat/ui/panel/wallet.ts create mode 100644 packages/core/src/chat/ui/skillSigil.spec.ts create mode 100644 packages/core/test/fixtures/panel/avatarGoldens.json create mode 100644 packages/core/test/fixtures/panel/contract.ts create mode 100644 packages/core/test/fixtures/panel/inbound.ts create mode 100644 packages/core/test/fixtures/panel/sigilGoldens.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 986bfbdd..1e3c7d74 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,6 +26,9 @@ jobs: - name: Test (agent-sdk vitest suite) run: pnpm --filter @iqlabs-official/agent-sdk test + env: + # the panel Chrome layout pass must run here; a missing browser is a failure, never a skip + PANEL_CHROME_REQUIRED: "1" # The runtime smoke test (pnpm run test:run) is intentionally NOT run here: # it drives the real claude + codex CLIs and needs them installed and logged @@ -33,6 +36,10 @@ jobs: - name: Typecheck agent-sdk run: pnpm --filter @iqlabs-official/agent-sdk exec tsc --noEmit + # the panel modules need the DOM lib, which the root tsconfig excludes (cli/localhost typecheck + # core with lib ES2022 only), so they get their own tsconfig and their own gate + - name: Typecheck panel (DOM) + run: pnpm --filter @iqlabs-official/agent-sdk typecheck:panel - name: Typecheck CLI run: pnpm --filter agentnet-cli exec tsc --noEmit - name: Typecheck localhost diff --git a/package.json b/package.json index adb70311..1e1e1a66 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "scripts": { "test:run": "pnpm --filter @iqlabs-official/agent-sdk test:run", "build:core": "pnpm --filter @iqlabs-official/agent-sdk build", + "build:panel": "pnpm --filter @iqlabs-official/agent-sdk build:panel", "build:vscode": "pnpm --filter agentnet-vscode build", "build:cli": "pnpm --filter agentnet-cli build", "dev:cli": "pnpm --filter agentnet-cli dev" diff --git a/packages/core/package.json b/packages/core/package.json index 3f6c9ef3..d7d8fdaf 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -14,7 +14,10 @@ "test": "vitest run", "test:run": "tsx test/test-runtime.ts", "test:memory": "tsx test/test-memory.ts", - "test:skills": "tsx test/test-skills.ts" + "test:skills": "tsx test/test-skills.ts", + "build:panel": "tsx scripts/buildPanel.ts", + "typecheck:panel": "tsc -p src/chat/ui/panel/tsconfig.json", + "test:panel:chrome": "PANEL_CHROME_REQUIRED=1 vitest run src/chat/ui/panel.chrome.spec.ts" }, "peerDependencies": { "@iqlabs-official/solana-sdk": "^0.1.28", @@ -23,7 +26,9 @@ "devDependencies": { "@iqlabs-official/solana-sdk": "^0.1.28", "@solana/web3.js": "^1.98.0", + "@types/jsdom": "^30.0.0", "@types/node": "^20.0.0", + "jsdom": "^30.0.1", "tsup": "^8.0.0", "tsx": "^4.19.2", "tweetnacl": "^1.0.3", diff --git a/packages/core/scripts/buildPanel.ts b/packages/core/scripts/buildPanel.ts new file mode 100644 index 00000000..cb53595f --- /dev/null +++ b/packages/core/scripts/buildPanel.ts @@ -0,0 +1,110 @@ +// Builds the chat panel script from src/chat/ui/panel/main.ts (real ES modules) into ONE browser +// IIFE and writes it into src/chat/ui/panel.generated.ts as a string constant. WHY a committed +// generated file, like mdLibs.generated.ts: core is consumed as raw .ts by every surface's bundler +// and typechecker, so chatHtml() must inline the panel with zero runtime file access, and an +// ignored artifact would add a mandatory pre-step to every tsc/tsup/vsce invocation. +// panel.generated.spec.ts rebuilds the bundle in-process and fails when the committed file is stale. +// +// pnpm build:panel rebuild once +// pnpm build:panel --watch rebuild on every change under panel/ +import { build, type Options } from "tsup"; +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { dirname, join } from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; + +const CORE_ROOT = join(dirname(fileURLToPath(import.meta.url)), ".."); +const ENTRY = join(CORE_ROOT, "src", "chat", "ui", "panel", "main.ts"); +const DEST = join(CORE_ROOT, "src", "chat", "ui", "panel.generated.ts"); +const OUT_NAME = "panel.global.js"; // tsup's file name for an iife entry called "panel" + +function buildOptions(outDir: string): Options { + return { + entry: { panel: ENTRY }, + format: ["iife"], + platform: "browser", + target: "es2022", + // tsup resolves a tsconfig from process.cwd() unless told otherwise, so without this the + // output depended on the invoking directory: the panel's own config is the one that + // describes these modules, and the strict directive is emitted deliberately below + // instead of being inherited from whichever tsconfig happened to be found. + tsconfig: join(CORE_ROOT, "src", "chat", "ui", "panel", "tsconfig.json"), + banner: { js: '"use strict";' }, + // tsup's rollup treeshake pass rewrote data (it dropped CHAT_MODEL_OPTIONS properties and an + // unused `let` write when this migration was tried); esbuild alone keeps the code verbatim. + treeshake: false, + minify: false, + sourcemap: false, + dts: false, + splitting: false, + clean: false, + silent: true, + config: false, + outDir, + esbuildOptions(o) { + o.charset = "utf8"; // keep the Korean UI strings literal instead of \u escapes + o.legalComments = "none"; + o.absWorkingDir = CORE_ROOT; // path comments must not depend on the invoking cwd (determinism) + }, + }; +} + +// The un-escape sentinels: the legacy script was a template literal whose backslashes were +// doubled, so a surviving `\\n` or a ` { throw new Error("panel bundle: " + why); }; + if (!js.startsWith('"use strict";')) fail("does not start with \"use strict\""); + if (js.includes(" tag)"); + if (js.includes("process.")) fail("contains process. (node-only code reached the browser bundle)"); + if (js.includes("require(")) fail("contains require( (a CommonJS dependency reached the bundle)"); + const escaped = js.split("\n").filter((l) => l.includes("\\\\n") && !l.trim().startsWith("//")); + if (escaped.length) fail("double-escaped newline outside a comment: " + escaped[0].trim().slice(0, 80)); +} + +export async function bundlePanel(): Promise { + const outDir = mkdtempSync(join(tmpdir(), "agentnet-panel-")); + try { + await build(buildOptions(outDir)); + const js = readFileSync(join(outDir, OUT_NAME), "utf8"); + assertBundle(js); + return js; + } finally { + rmSync(outDir, { recursive: true, force: true }); + } +} + +// A JSON string literal is a valid JS string literal: no decoder is needed and the text stays +// grep-able; non-ASCII stays literal (the file is UTF-8 like webview.ts). +export function renderPanelModule(js: string): string { + return ( + "// AUTO-GENERATED by scripts/buildPanel.ts from src/chat/ui/panel/ -- DO NOT EDIT; re-run pnpm build:panel\n" + + `export const PANEL_SCRIPT = ${JSON.stringify(js)};\n` + ); +} + +function writeGenerated(js: string): void { + writeFileSync(DEST, renderPanelModule(js), "utf8"); + console.log(`wrote ${DEST} (${(js.length / 1024).toFixed(0)} KB)`); +} + +async function main(): Promise { + if (!process.argv.includes("--watch")) { + writeGenerated(await bundlePanel()); + return; + } + // watch mode keeps its temp dir for the process lifetime; each successful rebuild rewrites DEST + const outDir = mkdtempSync(join(tmpdir(), "agentnet-panel-watch-")); + await build({ + ...buildOptions(outDir), + watch: true, + onSuccess: async () => { + const js = readFileSync(join(outDir, OUT_NAME), "utf8"); + assertBundle(js); + writeGenerated(js); + }, + }); +} + +if (import.meta.url === pathToFileURL(process.argv[1]).href) { + main().catch((e) => { console.error(e); process.exit(1); }); +} diff --git a/packages/core/src/chat/ui/avatar.spec.ts b/packages/core/src/chat/ui/avatar.spec.ts new file mode 100644 index 00000000..21a4c01a --- /dev/null +++ b/packages/core/src/chat/ui/avatar.spec.ts @@ -0,0 +1,25 @@ +// Pins hashSeed/avatarSvg to the goldens captured from the string-shipped AVATAR_SCRIPT right +// before the #215 module port, so the real exports cannot drift from what the panel rendered. +import { describe, it, expect } from "vitest"; +import { hashSeed, avatarSvg } from "./avatar.js"; +import goldens from "../../../test/fixtures/panel/avatarGoldens.json"; + +describe("chat/ui/avatar: hashSeed and avatarSvg match the pre-migration goldens", () => { + it("reproduces every golden byte-for-byte", () => { + expect(goldens).toHaveLength(5); + for (const g of goldens) { + expect(hashSeed(g.seed)).toBe(g.hash); + expect(avatarSvg(g.seed)).toBe(g.svg); + } + }); + + it("drops the shared `; -// The client-side generator the webview runs (string injected into its - + `; } diff --git a/packages/core/src/notes/quoteRefs.spec.ts b/packages/core/src/notes/quoteRefs.spec.ts index 6108f8fa..237898cb 100644 --- a/packages/core/src/notes/quoteRefs.spec.ts +++ b/packages/core/src/notes/quoteRefs.spec.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from "vitest"; -import { extractQuoteRefs, parseNoteRef, QUOTE_REFS_MAX } from "./quoteRefs.js"; +import { extractQuoteRefs, parseNoteRef, splitQuoteRefs, QUOTE_REFS_MAX } from "./quoteRefs.js"; const W = "C3EPAsjHq6DHLDzG2bXySFpUYmQ5AUqDXDfEiEsCekrH"; const id = (n: string) => `note:${W}:1787813695000:${n}`; @@ -59,3 +59,65 @@ describe("notes/quoteRefs — the >>note: text convention", () => { expect(parseNoteRef(undefined)).toBeNull(); }); }); + +describe("notes/quoteRefs: splitQuoteRefs, the panel's inline-render input", () => { + it("alternates text and ref segments in order and skips empty text runs", () => { + expect(splitQuoteRefs(`see >>${id("aaaaaa")} and >>${id("bbbbbb")}!`)).toEqual([ + { text: "see " }, + { ref: id("aaaaaa"), author: W }, + { text: " and " }, + { ref: id("bbbbbb"), author: W }, + { text: "!" }, + ]); + expect(splitQuoteRefs(`>>${id("aaaaaa")}`)).toEqual([{ ref: id("aaaaaa"), author: W }]); + expect(splitQuoteRefs(`>>${id("aaaaaa")}>>${id("bbbbbb")}`)).toEqual([ + { ref: id("aaaaaa"), author: W }, + { ref: id("bbbbbb"), author: W }, + ]); + expect(splitQuoteRefs("")).toEqual([]); + expect(splitQuoteRefs("plain text")).toEqual([{ text: "plain text" }]); + }); + + it("keeps duplicates and never caps: the per-view accounting belongs to the renderer", () => { + const many = Array.from({ length: QUOTE_REFS_MAX + 3 }, () => `>>${id("aaaaaa")}`).join(" "); + expect(splitQuoteRefs(many).filter((s) => "ref" in s)).toHaveLength(QUOTE_REFS_MAX + 3); + }); + + it("refuses a ref glued to alphanumerics and accepts 4-6 char nonces", () => { + expect(splitQuoteRefs(`>>${id("9n4iry")}glued`)).toEqual([{ text: `>>${id("9n4iry")}glued` }]); + expect(splitQuoteRefs(`>>${id("9n4iry")}9`)).toEqual([{ text: `>>${id("9n4iry")}9` }]); + expect(splitQuoteRefs(`>>${id("ab12")}`)).toEqual([{ ref: id("ab12"), author: W }]); + expect(splitQuoteRefs(`>>${id("ab12cd")}`)).toEqual([{ ref: id("ab12cd"), author: W }]); + expect(splitQuoteRefs(`>>${id("ab12cd3")}`)).toEqual([{ text: `>>${id("ab12cd3")}` }]); + expect(splitQuoteRefs(`>>${id("ab1")}`)).toEqual([{ text: `>>${id("ab1")}` }]); + }); + + it("splits exactly like the retired panel regex did", () => { + // webview.ts W:4115 before #215, kept here only as the equivalence oracle: split() with the + // capture group put refs at odd indexes and (possibly empty) text runs at even ones. + const LEGACY_PANEL_SPLIT_RE = /(>>note:[1-9A-HJ-NP-Za-km-z]{32,44}:[0-9]{10,16}:[a-z0-9]{4,6})(?![A-Za-z0-9])/; + const texts = [ + "", + "no refs here", + `>>${id("aaaaaa")}`, + `lead >>${id("aaaaaa")}`, + `>>${id("aaaaaa")} trail`, + `a >>${id("aaaaaa")} b >>${id("bbbbbb")} c`, + `>>${id("aaaaaa")}>>${id("bbbbbb")}`, + `>>${id("aaaaaa")} twice >>${id("aaaaaa")}`, + `glued >>${id("aaaaaa")}x and ok >>${id("bbbbbb")}.`, + `line one\n>>${id("ab12")}\nline three`, + `한글 앞 >>${id("cd34ef")} 한글 뒤`, + `>>tx:${W} and (>>${id("aaaaaa")}) and >>${id("ab12cd3")}`, + ]; + for (const text of texts) { + const parts = text.split(LEGACY_PANEL_SPLIT_RE); + const legacy = parts.filter((p, i) => i % 2 === 1 || p); + const segs = splitQuoteRefs(text).map((s) => ("ref" in s ? ">>" + s.ref : s.text)); + expect(segs).toEqual(legacy); + expect(splitQuoteRefs(text).filter((s) => "ref" in s).map((s) => (s as { author: string }).author)).toEqual( + parts.filter((_, i) => i % 2 === 1).map(() => W), + ); + } + }); +}); diff --git a/packages/core/src/notes/quoteRefs.ts b/packages/core/src/notes/quoteRefs.ts index be377bc5..4fd22f2d 100644 --- a/packages/core/src/notes/quoteRefs.ts +++ b/packages/core/src/notes/quoteRefs.ts @@ -17,8 +17,7 @@ // but shorter when the random tail is, so 4-6 (slice(2, 8) caps at 6). The trailing lookahead refuses a // ref glued to alphanumeric text: a corrupted id would fetch a nonexistent post // and render a deadlink for a ref that actually resolves, so no match is safer. -// The VS Code panel template cannot import this (it is emitted browser JS), so -// webview.ts carries a byte-identical copy: change BOTH or the surfaces diverge. +// The VS Code panel (chat/ui/panel/quotes.ts) imports this module, so there is one grammar. const QUOTE_REF = />>(note:([1-9A-HJ-NP-Za-km-z]{32,44}):(\d{10,16}):([a-z0-9]{4,6}))(?![A-Za-z0-9])/g; export interface QuoteRef { @@ -50,3 +49,20 @@ export function parseNoteRef(id: string | undefined): QuoteRef | null { const refs = extractQuoteRefs(`>>${id}`); return refs.length === 1 && refs[0].ref === id ? refs[0] : null; } + +/** + * The text as alternating text and ref segments, in order, so a renderer can interleave text + * nodes and quote markers. No cap and no dedupe: those are per-view policies (extractQuoteRefs + * caps the reads; the panel counts unique cards per render pass). Empty text runs are skipped. + */ +export function splitQuoteRefs(text: string): Array<{ text: string } | QuoteRef> { + const out: Array<{ text: string } | QuoteRef> = []; + let last = 0; + for (const m of text.matchAll(QUOTE_REF)) { + if (m.index > last) out.push({ text: text.slice(last, m.index) }); + out.push({ ref: m[1], author: m[2] }); + last = m.index + m[0].length; + } + if (last < text.length) out.push({ text: text.slice(last) }); + return out; +} diff --git a/packages/core/test/fixtures/panel/avatarGoldens.json b/packages/core/test/fixtures/panel/avatarGoldens.json new file mode 100644 index 00000000..36e583e7 --- /dev/null +++ b/packages/core/test/fixtures/panel/avatarGoldens.json @@ -0,0 +1,27 @@ +[ + { + "seed": "default", + "hash": 2470140894, + "svg": "" + }, + { + "seed": "", + "hash": 2166136261, + "svg": "" + }, + { + "seed": "9xQeWvG816bUx9EPjHmaT23yvVM2ZZBq7ftHbAr3o1z", + "hash": 3241799227, + "svg": "" + }, + { + "seed": "11111111111111111111111111111111", + "hash": 551368101, + "svg": "" + }, + { + "seed": "So11111111111111111111111111111111111111112", + "hash": 296707927, + "svg": "" + } +] diff --git a/packages/core/test/fixtures/panel/contract.ts b/packages/core/test/fixtures/panel/contract.ts new file mode 100644 index 00000000..23541d5d --- /dev/null +++ b/packages/core/test/fixtures/panel/contract.ts @@ -0,0 +1,123 @@ +// The panel's host message contract, captured from the legacy webview script before the #215 +// module cut (plan 4.1) so the artifact and render specs can pin it: every type the panel posts +// (vscode.postMessage sites, including the ones built as an object first), every type the +// message listener handles in chain order, and the boot posts in the order they leave. +export const PANEL_OUTBOUND_TYPES: string[] = [ + "installEngine", + "model", + "mode", + "effort", + "platform", + "approvalDecision", + "delete", + "open", + "interrupt", + "claudeAuthCode", + "startClaudeLogin", + "startCodexLogin", + "logoutEngine", + "new", + "clear", + "slashCommand", + "send", + "newTab", + "openCloud", + "disconnectCloud", + "pickCloud", + "wallet", + "publishSkill", + "getBlogPost", + "listAgents", + "getBlogFeed", + "getBlogComments", + "postBlogComment", + "postAgentNote", + "getAgentProfile", + "getGithubStatus", + "submitGithubToken", + "registerWorkRepo", + "buyAllSkills", + "getBalance", + "ownedSkills", + "setSkillShopping", + "getSkillShopping", + "searchSkills", + "getSkillDetail", + "buySkill", + "getSkillDoc", + "postNote", + "reEquipSkill", + "disposeSkill", + "airdrop", + "setHeliusKey", + "useDefaultRpc", + "getRpcStatus", + "reconnectCloud", + "disconnectWallet", + "loadMore", + "ready", +]; + +export const PANEL_INBOUND_TYPES: string[] = [ + "message", + "sessions", + "notice", + "status", + "loading", + "clear", + "turnEnd", + "modelOptions", + "usage", + "skillActive", + "rpcStatus", + "skillShopping", + "searchResults", + "searchError", + "skillDetail", + "skillDoc", + "postNoteResult", + "notes", + "ownedSkills", + "buyResult", + "airdropResult", + "githubStatus", + "workRepoRegistered", + "disposeResult", + "reEquipResult", + "agents", + "agentProfile", + "blogFeed", + "blogPost", + "blogComments", + "blogCommentResult", + "buyAllResult", + "agentNoteResult", + "balance", + "publishProgress", + "publishResult", + "platform", + "cliStatus", + "engineUpdate", + "claudeLoginUrl", + "claudeLoginStatus", + "codexLoginChallenge", + "codexLoginStatus", + "toast", + "openUrl", + "storage", + "cloudSync", + "wallet", + "page", + "older", + "approval", + "approvalDismiss", +]; + +export const PANEL_BOOT_POSTS: string[] = [ + "ownedSkills", + "getSkillShopping", + "getRpcStatus", + "ready", + "wallet", + "getBalance", +]; diff --git a/packages/core/test/fixtures/panel/inbound.ts b/packages/core/test/fixtures/panel/inbound.ts new file mode 100644 index 00000000..0495bf74 --- /dev/null +++ b/packages/core/test/fixtures/panel/inbound.ts @@ -0,0 +1,61 @@ +// One minimal host -> panel payload per inbound type (plan 4.1), in PANEL_INBOUND_TYPES order, +// each carrying only the fields its listener branch reads. The jsdom render pass dispatches +// every one of them to prove the listener never throws on the host's smallest legal shapes. +export const WALLET = "9xQeWvG816bUx9EPjHmaT23yvVM2ZZBq7ftHbAr3o1z"; +export const NOTE_ID = `note:${WALLET}:1725400000000:k7x2mq`; +export const MINT = "MintAAAA1111111111111111111111111111111111"; + +export const PANEL_INBOUND_PAYLOADS: Array<{ type: string } & Record> = [ + { type: "message", msg: { role: "assistant", cli: "claude", text: "hello **world**", durationMs: 1200, model: "opus" } }, + { type: "sessions", list: [{ sessionId: "s1", title: "first session", ts: Date.now() }], cloud: "none", activeId: "s1" }, + { type: "notice", text: "a notice" }, + { type: "status", status: { cli: "claude", sessionId: "s1", model: "opus", mode: "acceptEdits", effort: "default", contextTokens: 1500 } }, + { type: "loading" }, + { type: "clear" }, + { type: "turnEnd" }, + { type: "modelOptions", cli: "claude", options: [{ value: "opus", label: "Opus" }] }, + { type: "usage", contextTokens: 12000 }, + { type: "skillActive", origin: "nft", mint: MINT, name: "code-review" }, + { type: "rpcStatus", status: { dasReady: true, hasKey: true, masked: "...ab12", network: "devnet" } }, + { type: "skillShopping", on: true }, + { type: "searchResults", results: [{ id: MINT, name: "code-review", type: "skill" }] }, + { type: "searchError", message: "gateway down" }, + { type: "skillDetail", detail: { card: { id: MINT, name: "code-review", type: "skill" }, skillText: "# code-review" } }, + { type: "skillDoc", name: "code-review", text: "# code-review\nbody" }, + { type: "postNoteResult", ok: true }, + { type: "notes", skillId: MINT, notes: [] }, + { type: "ownedSkills", names: ["code-review"], mints: { "code-review": MINT }, meta: {}, disposedMints: {}, workflowMints: [] }, + { type: "buyResult", ok: false, error: "insufficient funds", code: "insufficient_funds" }, + { type: "airdropResult", ok: true }, + { type: "githubStatus", hasToken: false }, + { type: "workRepoRegistered", ok: false, error: "not a public repo" }, + { type: "disposeResult", ok: false, error: "unequip failed" }, + { type: "reEquipResult", ok: false, error: "re-equip failed" }, + { type: "agents", agents: [] }, + { type: "agentProfile", profile: { wallet: WALLET, self: false, createdSkills: [], ownedSkills: [] } }, + { type: "blogFeed", posts: [] }, + { type: "blogPost", postId: NOTE_ID, post: null }, + { type: "blogComments", postId: NOTE_ID, threads: [] }, + { type: "blogCommentResult", postId: NOTE_ID, ok: true }, + { type: "buyAllResult", ok: true, bought: 0, failed: 0 }, + { type: "agentNoteResult", ok: true, agentWallet: WALLET }, + { type: "balance", lamports: 1_500_000_000 }, + { type: "publishProgress", phase: "mint", signed: 1, total: 3 }, + { type: "publishResult", ok: false, error: "publish failed" }, + { type: "platform", cli: "claude" }, + { type: "cliStatus", claude: "ok", codex: "ok" }, + { type: "engineUpdate", cli: "codex" }, + { type: "claudeLoginUrl", url: "https://example.invalid/claude-login" }, + { type: "claudeLoginStatus", status: "done" }, + { type: "codexLoginChallenge", url: "https://example.invalid/codex-login", code: "ABCD-EFGH" }, + { type: "codexLoginStatus", status: "done" }, + { type: "toast", text: "a toast" }, + { type: "openUrl", url: "https://example.invalid/open" }, + { type: "storage", info: { connected: false }, options: [] }, + { type: "cloudSync", status: { ok: true } }, + { type: "wallet", address: WALLET }, + { type: "page", hasMore: false, cursor: null }, + { type: "older", messages: [{ role: "user", text: "older question" }, { role: "assistant", cli: "claude", text: "older answer" }], hasMore: false, cursor: null }, + { type: "approval", req: { id: "ap1", kind: "bash", tool: "Bash", title: "Run a command", command: "ls -la", cli: "claude" } }, + { type: "approvalDismiss", id: "ap1" }, +]; diff --git a/packages/core/test/fixtures/panel/sigilGoldens.json b/packages/core/test/fixtures/panel/sigilGoldens.json new file mode 100644 index 00000000..50f9a726 --- /dev/null +++ b/packages/core/test/fixtures/panel/sigilGoldens.json @@ -0,0 +1,22 @@ +[ + { + "name": "", + "svg": "" + }, + { + "name": "code-review", + "svg": "" + }, + { + "name": "iq-onchain-db", + "svg": "" + }, + { + "name": "한글 스킬", + "svg": "" + }, + { + "name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "svg": "" + } +] diff --git a/packages/core/test/test-skills.ts b/packages/core/test/test-skills.ts index 2f469c5c..eda5de6b 100644 --- a/packages/core/test/test-skills.ts +++ b/packages/core/test/test-skills.ts @@ -58,8 +58,9 @@ console.log("2. Preserve publisher-authored frontmatter"); }; const md = toSkillMd(meta, MINT); check("no second frontmatter block", (md.match(/^---/gm) || []).length === 2); - check("authored name kept", md.includes("name: my-skill")); - check("authored user-invocable kept", md.includes("user-invocable: true")); + // quote-agnostic: normalizeFrontmatter re-emits plain scalars quoted so codex's strict YAML loads them + check("authored name kept", /^name: ["']?my-skill["']?$/m.test(md)); + check("authored user-invocable kept", /^user-invocable: ["']?true["']?$/m.test(md)); check("synthesized description NOT injected", !md.includes('description: "ignored"')); } @@ -116,10 +117,11 @@ console.log("5. codex skill-firing detection from the output stream"); check("plain message → no skill signal", msgOnly.skill === undefined); } -// 6. Message contract ↔ VSCode webview agreement. The webview is an HTML string -// (no compile-time typecheck), so guard that every marketplace `type` it emits/ -// handles is a real message in the shared contract — a typo or a removed message -// fails here instead of silently no-op'ing on that surface. +// 6. Message contract ↔ VSCode webview agreement. The panel script is a typechecked module +// bundle now (chat/ui/panel/), but its message types are still string literals inlined +// into the HTML, so guard that every marketplace `type` it emits/handles is a real message +// in the shared contract: a typo or a removed message fails here instead of silently +// no-op'ing on that surface. Quote-agnostic, because esbuild emits double quotes. console.log("6. webview market messages match the shared contract"); { const html = chatHtml(); @@ -127,10 +129,10 @@ console.log("6. webview market messages match the shared contract"); const REQUESTS = ["searchSkills", "buySkill", "buyAllSkills", "ownedSkills"]; const EVENTS = ["searchResults", "buyResult", "buyAllResult", "ownedSkills", "skillActive"]; for (const t of REQUESTS) { - check(`webview sends '${t}'`, html.includes(`type: '${t}'`)); + check(`webview sends '${t}'`, new RegExp(`type: ['"]${t}['"]`).test(html)); } for (const t of EVENTS) { - check(`webview handles '${t}'`, html.includes(`m.type === '${t}'`)); + check(`webview handles '${t}'`, new RegExp(`m\\.type === ['"]${t}['"]`).test(html)); } } diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index 7b5da3b4..964d461e 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -11,5 +11,6 @@ "outDir": "dist", "types": ["node"] }, - "include": ["src", "test"] + "include": ["src", "test"], + "exclude": ["src/chat/ui/panel"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 00fb4078..f57ff507 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,9 +44,15 @@ importers: '@solana/web3.js': specifier: ^1.98.0 version: 1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) + '@types/jsdom': + specifier: ^30.0.0 + version: 30.0.0 '@types/node': specifier: ^20.0.0 version: 20.19.42 + jsdom: + specifier: ^30.0.1 + version: 30.0.1(@noble/hashes@2.2.0) tsup: specifier: ^8.0.0 version: 8.5.1(jiti@2.7.0)(postcss@8.5.15)(tsx@4.22.4)(typescript@5.9.3) @@ -61,7 +67,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.1.8 - version: 4.1.8(@types/node@20.19.42)(vite@7.3.5(@types/node@20.19.42)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)) + version: 4.1.8(@types/node@20.19.42)(jsdom@30.0.1(@noble/hashes@2.2.0))(vite@7.3.5(@types/node@20.19.42)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)) packages/mcp: devDependencies: @@ -273,6 +279,14 @@ packages: zod: optional: true + '@asamuzakjp/css-color@6.0.7': + resolution: {integrity: sha512-vC/bk1Lz7Tn/EfU9/apOTBk80/8dyGyWMowPoV1tJ52muDGsDqt2HPT2klrFUiY60MQmQv9q8yIht15JnBgDGw==} + engines: {node: ^22.13.0 || >=24.0.0} + + '@asamuzakjp/dom-selector@8.3.2': + resolution: {integrity: sha512-93Z1N+BQNXysodoicpOIyNh2drHfz/CTf9nnT0FEx72GJcIiwgydD7tGAr78j41LsYn3hlRn+LdGPuBLn1Bl8Q==} + engines: {node: ^22.13.0 || >=24.0.0} + '@babel/code-frame@7.29.7': resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} @@ -360,6 +374,10 @@ packages: resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} + '@bramus/specificity@2.4.2': + resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} + hasBin: true + '@coral-xyz/anchor-errors@0.31.1': resolution: {integrity: sha512-NhNEku4F3zzUSBtrYz84FzYWm48+9OvmT1Hhnwr6GnPQry2dsEqH/ti/7ASjjpoFTWRnPXrjAIT1qM6Isop+LQ==} engines: {node: '>=10'} @@ -374,6 +392,42 @@ packages: peerDependencies: '@solana/web3.js': ^1.69.0 + '@csstools/color-helpers@6.1.1': + resolution: {integrity: sha512-gLNsunvwf3mCi5u5o46/Z/JcJMnhbHSaZ69rkgPzNM3J4s8hWwpPUQB6/tt0EDFyCiWzxANlx+2LJwpYj4zS1w==} + engines: {node: '>=20.19.0'} + + '@csstools/css-calc@3.3.0': + resolution: {integrity: sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-color-parser@4.2.2': + resolution: {integrity: sha512-3QKjR/vxyjcSXBLgb6lP0S3MGdvwbmqSsvLPbYdVORqPDc8FX1HAJ0Spk38bxaRXgvENTA47tlhhbb5Z2e8hEg==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-parser-algorithms@4.0.0': + resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.1.12': + resolution: {integrity: sha512-3vLQK+dXxhBMR2Wx99PTCifE+vHtW2ndZWyla8yK813ev6oGhyn8Lja8jCyGAWTJ+LEYZK7EVtJxrDj8ztevJw==} + peerDependencies: + css-tree: ^3.2.1 + peerDependenciesMeta: + css-tree: + optional: true + + '@csstools/css-tokenizer@4.0.0': + resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} + engines: {node: '>=20.19.0'} + '@esbuild/aix-ppc64@0.27.7': resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} engines: {node: '>=18'} @@ -686,6 +740,15 @@ packages: cpu: [x64] os: [win32] + '@exodus/bytes@1.15.1': + resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + '@noble/hashes': ^1.8.0 || ^2.0.0 + peerDependenciesMeta: + '@noble/hashes': + optional: true + '@hono/node-server@1.19.14': resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==} engines: {node: '>=18.14.1'} @@ -1124,6 +1187,9 @@ packages: '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + '@types/jsdom@30.0.0': + resolution: {integrity: sha512-uAHGxujGE0cDaKGdK28zgDotFtNA7MKq5DXl8LrfdxdCI8VHcg15oJz+amHTChPNI5JpgEPQWc2xFdrw3em/nQ==} + '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} @@ -1144,6 +1210,9 @@ packages: '@types/react@19.2.17': resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} + '@types/tough-cookie@4.0.5': + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} @@ -1260,6 +1329,9 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + bidi-js@1.0.3: + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + bigint-buffer@1.1.5: resolution: {integrity: sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==} engines: {node: '>= 10.0.0'} @@ -1448,9 +1520,17 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + css-tree@3.2.1: + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + data-urls@7.0.0: + resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -1460,6 +1540,9 @@ packages: supports-color: optional: true + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} @@ -1519,6 +1602,10 @@ packages: resolution: {integrity: sha512-SkE2t82KlkkxQRVMVLAGKxLfORGQfrkx5dkj+vlgXRVNEdPc4eZcR+J/Fvj8C+yKSFH5L0q3NFlyufOVQnCcYQ==} engines: {node: '>=10.13.0'} + entities@8.0.0: + resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} + engines: {node: '>=20.19.0'} + environment@1.1.0: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} @@ -1707,6 +1794,10 @@ packages: resolution: {integrity: sha512-2NFaIyNVgJmBs/ecmtGzlmluTFs5cHEWGTdu0t1HBwYzoGXOL5nUQBRMXsXWla5i4KkG//QMzVP88m1+I3fdAQ==} engines: {node: '>=16.9.0'} + html-encoding-sniffer@6.0.0: + resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + http-errors@2.0.1: resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} engines: {node: '>= 0.8'} @@ -1802,6 +1893,9 @@ packages: resolution: {integrity: sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==} engines: {node: '>=0.10.0'} + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-promise@4.0.0: resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} @@ -1840,6 +1934,15 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + jsdom@30.0.1: + resolution: {integrity: sha512-52v7mUVUfNQVYYqE1lcdaymWL0njO7lTLUog6ZvW2U5KsbiLk/GnZlVJ+qx0xfNJZ6Gn+KSpPNE52vurbxZwrA==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} + peerDependencies: + canvas: ^3.2.3 + peerDependenciesMeta: + canvas: + optional: true + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -1952,6 +2055,10 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + lru-cache@11.5.2: + resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -1967,6 +2074,9 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mdn-data@2.27.1: + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + media-typer@1.1.0: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} @@ -2061,6 +2171,9 @@ packages: parse5@6.0.1: resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} + parse5@8.0.1: + resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} + parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} @@ -2126,6 +2239,10 @@ packages: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + qrcode.react@4.2.0: resolution: {integrity: sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==} peerDependencies: @@ -2211,6 +2328,10 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} @@ -2337,6 +2458,9 @@ packages: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + tailwindcss@4.3.0: resolution: {integrity: sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==} @@ -2372,6 +2496,13 @@ packages: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} + tldts-core@7.4.11: + resolution: {integrity: sha512-CW3WN2rIIE/Of21mulhgnGOwoDyEFNygyIBOONSdyAuSATgMMUCpLeUlB+E8sAwA5xRV9hYPl+kyZ9citHCaKg==} + + tldts@7.4.11: + resolution: {integrity: sha512-aBiNayCfTQxuIJBm06M+xR14cYaYlDlSXZbgsnKzKNxDKUVq7KFwTjwBSsb7m9Y5xO8WfPnBc63WaYFMTGlvqw==} + hasBin: true + toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} @@ -2379,9 +2510,17 @@ packages: toml@3.0.0: resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} + tough-cookie@6.0.2: + resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==} + engines: {node: '>=16'} + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tr46@6.0.0: + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + engines: {node: '>=20'} + tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -2441,6 +2580,13 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici-types@8.10.2: + resolution: {integrity: sha512-7/+aSjzkUoLc92hV22bTW4aGanXf800zbwguhcICs0OAoCF9wDOE4wkopQ+SqfhXZm8mCK8gHpdTs7pZUWzK3w==} + + undici@8.10.2: + resolution: {integrity: sha512-/y4/bH9YNU5hi9NIrpOuvGXFcxrj3CMrV+/AYpowAYTpHn8gX/XPFjNy766FPoYY0miQhdW977JFWKGNhBdwyQ==} + engines: {node: '>=22.19.0'} + unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} @@ -2549,9 +2695,29 @@ packages: jsdom: optional: true + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + webidl-conversions@8.0.1: + resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} + engines: {node: '>=20'} + + whatwg-mimetype@5.0.0: + resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} + engines: {node: '>=20'} + + whatwg-url@16.0.1: + resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + whatwg-url@17.1.0: + resolution: {integrity: sha512-3GeworPmc2ZfEEHP7lEbUfBX/L75wdEsi0rLNhXcXxnoN5jyq0SL5gCy06SGW2cyTIZdTvWIDQNQoza++vKeaw==} + engines: {node: ^22.14.0 || >=24.0.0} + whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} @@ -2613,6 +2779,13 @@ packages: resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} engines: {node: '>=18'} + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -2692,6 +2865,21 @@ snapshots: optionalDependencies: zod: 4.4.3 + '@asamuzakjp/css-color@6.0.7': + dependencies: + '@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.2.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + lru-cache: 11.5.2 + + '@asamuzakjp/dom-selector@8.3.2': + dependencies: + bidi-js: 1.0.3 + css-tree: 3.2.1 + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.5.2 + '@babel/code-frame@7.29.7': dependencies: '@babel/helper-validator-identifier': 7.29.7 @@ -2806,6 +2994,10 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 + '@bramus/specificity@2.4.2': + dependencies: + css-tree: 3.2.1 + '@coral-xyz/anchor-errors@0.31.1': {} '@coral-xyz/anchor@0.32.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)': @@ -2835,6 +3027,30 @@ snapshots: bn.js: 5.2.3 buffer-layout: 1.2.2 + '@csstools/color-helpers@6.1.1': {} + + '@csstools/css-calc@3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-color-parser@4.2.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/color-helpers': 6.1.1 + '@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.1.12(css-tree@3.2.1)': + optionalDependencies: + css-tree: 3.2.1 + + '@csstools/css-tokenizer@4.0.0': {} + '@esbuild/aix-ppc64@0.27.7': optional: true @@ -2991,6 +3207,10 @@ snapshots: '@esbuild/win32-x64@0.28.0': optional: true + '@exodus/bytes@1.15.1(@noble/hashes@2.2.0)': + optionalDependencies: + '@noble/hashes': 2.2.0 + '@hono/node-server@1.19.14(hono@4.12.25)': dependencies: hono: 4.12.25 @@ -3427,6 +3647,13 @@ snapshots: '@types/estree@1.0.9': {} + '@types/jsdom@30.0.0': + dependencies: + '@types/node': 20.19.42 + '@types/tough-cookie': 4.0.5 + parse5: 8.0.1 + undici-types: 8.10.2 + '@types/node@12.20.55': {} '@types/node@20.19.42': @@ -3448,6 +3675,8 @@ snapshots: dependencies: csstype: 3.2.3 + '@types/tough-cookie@4.0.5': {} + '@types/trusted-types@2.0.7': optional: true @@ -3566,6 +3795,10 @@ snapshots: baseline-browser-mapping@2.10.36: {} + bidi-js@1.0.3: + dependencies: + require-from-string: 2.0.2 + bigint-buffer@1.1.5: dependencies: bindings: 1.5.0 @@ -3748,12 +3981,26 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + css-tree@3.2.1: + dependencies: + mdn-data: 2.27.1 + source-map-js: 1.2.1 + csstype@3.2.3: {} + data-urls@7.0.0(@noble/hashes@2.2.0): + dependencies: + whatwg-mimetype: 5.0.0 + whatwg-url: 16.0.1(@noble/hashes@2.2.0) + transitivePeerDependencies: + - '@noble/hashes' + debug@4.4.3: dependencies: ms: 2.1.3 + decimal.js@10.6.0: {} + deepmerge@4.3.1: {} default-browser-id@5.0.1: {} @@ -3800,6 +4047,8 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.3 + entities@8.0.0: {} + environment@1.1.0: {} es-define-property@1.0.1: {} @@ -4028,6 +4277,12 @@ snapshots: hono@4.12.25: {} + html-encoding-sniffer@6.0.0(@noble/hashes@2.2.0): + dependencies: + '@exodus/bytes': 1.15.1(@noble/hashes@2.2.0) + transitivePeerDependencies: + - '@noble/hashes' + http-errors@2.0.1: dependencies: depd: 2.0.0 @@ -4129,6 +4384,8 @@ snapshots: dependencies: kind-of: 3.2.2 + is-potential-custom-element-name@1.0.1: {} + is-promise@4.0.0: {} is-unicode-supported@2.1.0: {} @@ -4169,6 +4426,32 @@ snapshots: js-tokens@4.0.0: {} + jsdom@30.0.1(@noble/hashes@2.2.0): + dependencies: + '@asamuzakjp/css-color': 6.0.7 + '@asamuzakjp/dom-selector': 8.3.2 + '@bramus/specificity': 2.4.2 + '@csstools/css-syntax-patches-for-csstree': 1.1.12(css-tree@3.2.1) + '@exodus/bytes': 1.15.1(@noble/hashes@2.2.0) + css-tree: 3.2.1 + data-urls: 7.0.0(@noble/hashes@2.2.0) + decimal.js: 10.6.0 + html-encoding-sniffer: 6.0.0(@noble/hashes@2.2.0) + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.5.2 + parse5: 8.0.1 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 6.0.2 + undici: 8.10.2 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 8.0.1 + whatwg-mimetype: 5.0.0 + whatwg-url: 17.1.0(@noble/hashes@2.2.0) + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - '@noble/hashes' + jsesc@3.1.0: {} json-schema-to-ts@3.1.1: @@ -4247,6 +4530,8 @@ snapshots: dependencies: js-tokens: 4.0.0 + lru-cache@11.5.2: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -4259,6 +4544,8 @@ snapshots: math-intrinsics@1.1.0: {} + mdn-data@2.27.1: {} + media-typer@1.1.0: {} merge-descriptors@2.0.0: {} @@ -4334,6 +4621,10 @@ snapshots: parse5@6.0.1: {} + parse5@8.0.1: + dependencies: + entities: 8.0.0 + parseurl@1.3.3: {} patch-console@2.0.0: {} @@ -4383,6 +4674,8 @@ snapshots: forwarded: 0.2.0 ipaddr.js: 1.9.1 + punycode@2.3.1: {} + qrcode.react@4.2.0(react@19.2.7): dependencies: react: 19.2.7 @@ -4494,6 +4787,10 @@ snapshots: safer-buffer@2.1.2: {} + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + scheduler@0.23.2: dependencies: loose-envify: 1.4.0 @@ -4644,6 +4941,8 @@ snapshots: dependencies: has-flag: 4.0.0 + symbol-tree@3.2.4: {} + tailwindcss@4.3.0: {} tapable@2.3.3: {} @@ -4671,12 +4970,26 @@ snapshots: tinyrainbow@3.1.0: {} + tldts-core@7.4.11: {} + + tldts@7.4.11: + dependencies: + tldts-core: 7.4.11 + toidentifier@1.0.1: {} toml@3.0.0: {} + tough-cookie@6.0.2: + dependencies: + tldts: 7.4.11 + tr46@0.0.3: {} + tr46@6.0.0: + dependencies: + punycode: 2.3.1 + tree-kill@1.2.2: {} ts-algebra@2.0.0: {} @@ -4735,6 +5048,10 @@ snapshots: undici-types@6.21.0: {} + undici-types@8.10.2: {} + + undici@8.10.2: {} + unpipe@1.0.0: {} update-browserslist-db@1.2.3(browserslist@4.28.2): @@ -4769,7 +5086,7 @@ snapshots: lightningcss: 1.32.0 tsx: 4.22.4 - vitest@4.1.8(@types/node@20.19.42)(vite@7.3.5(@types/node@20.19.42)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)): + vitest@4.1.8(@types/node@20.19.42)(jsdom@30.0.1(@noble/hashes@2.2.0))(vite@7.3.5(@types/node@20.19.42)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)): dependencies: '@vitest/expect': 4.1.8 '@vitest/mocker': 4.1.8(vite@7.3.5(@types/node@20.19.42)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)) @@ -4793,11 +5110,36 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 20.19.42 + jsdom: 30.0.1(@noble/hashes@2.2.0) transitivePeerDependencies: - msw + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + webidl-conversions@3.0.1: {} + webidl-conversions@8.0.1: {} + + whatwg-mimetype@5.0.0: {} + + whatwg-url@16.0.1(@noble/hashes@2.2.0): + dependencies: + '@exodus/bytes': 1.15.1(@noble/hashes@2.2.0) + tr46: 6.0.0 + webidl-conversions: 8.0.1 + transitivePeerDependencies: + - '@noble/hashes' + + whatwg-url@17.1.0(@noble/hashes@2.2.0): + dependencies: + '@exodus/bytes': 1.15.1(@noble/hashes@2.2.0) + tr46: 6.0.0 + webidl-conversions: 8.0.1 + transitivePeerDependencies: + - '@noble/hashes' + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 @@ -4849,6 +5191,10 @@ snapshots: dependencies: is-wsl: 3.1.1 + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + y18n@5.0.8: {} yallist@3.1.1: {}