History

nicer ui

Rhys Sullivan committed 8 months ago

Commit ece99cf1 parent 8f20d5b14 files · +843 −160Browse files

Unified · Split

14 files
  1. apps/web/package.json +3 −0
  2. apps/web/src/components/document-panel.tsx +2 −13
  3. apps/web/src/components/icons.tsx +10 −0
  4. apps/web/src/components/markdown.tsx +53 −0
  5. apps/web/src/components/shell.tsx +9 −109
  6. apps/web/src/globals.css +10 −0
  7. apps/web/src/lib/format.ts +133 −0
  8. apps/web/src/views/source-detail.tsx +330 −22
  9. bun.lock +187 −0
  10. packages/codemode-openapi/src/index.ts +5 −1
  11. packages/codemode-openapi/src/openapi-tool-presentation.ts +37 −2
  12. packages/control-plane/src/runtime/source-inspection.ts +15 −7
  13. packages/control-plane/src/runtime/workspace-execution-environment.ts +12 −5
  14. packages/react/src/index.ts +37 −1
apps/web/package.json+3−0
@@ -11,15 +11,18 @@ · 10 unmodified lines
1111 "dependencies": {
1212 "@executor-v3/react": "workspace:*",
1313 "@executor-v3/server": "workspace:*",
14+ "@streamdown/code": "^1.0.3",
1415 "@tanstack/react-router": "^1.132.7",
1516 "class-variance-authority": "^0.7.1",
1617 "clsx": "^2.1.1",
1718 "effect": "catalog:",
19+ "prettier": "^3.8.1",
1820 "react": "^19.2.0",
1921 "react-dom": "^19.2.0",
2022 "react-grab": "^0.1.26",
2123 "scheduler": "^0.27.0",
2224 "shiki": "^4.0.1",
25+ "streamdown": "^2.3.0",
2326 "tailwind-merge": "^3.5.0"
2427 },
2528 "devDependencies": {
apps/web/src/components/document-panel.tsx+2−13
@@ -1,15 +1,7 @@
1−import { useMemo } from "react";
21 import { cn } from "../lib/utils";
2+import { useFormattedCode } from "../lib/format";
33 import { CodeBlock } from "./code-block";
44
5−function prettyJson(raw: string): string {
6− try {
7− return JSON.stringify(JSON.parse(raw), null, 2);
8− } catch {
9− return raw;
10− }
11−}
12−
135 export function DocumentPanel(props: {
146 title: string;
157 body: string | null;
@@ -17,10 +9,7 @@ export function DocumentPanel(props: { · 1 unmodified line
179 empty: string;
1810 compact?: boolean;
1911 }) {
20− const formatted = useMemo(
21− () => (props.body ? prettyJson(props.body) : null),
22− [props.body],
23− );
12+ const formatted = useFormattedCode(props.body, props.lang);
2413
2514 return (
2615 <section
apps/web/src/components/icons.tsx+10−0
@@ -107,3 +107,13 @@ export function IconSpinner({ className, ...props }: IconProps) { · 106 unmodified lines
107107 </svg>
108108 );
109109 }
110+
111+export function IconInfo({ className, ...props }: IconProps) {
112+ return (
113+ <svg viewBox="0 0 16 16" fill="none" className={cn("size-4", className)} {...props}>
114+ <circle cx="8" cy="8" r="5.75" stroke="currentColor" strokeWidth="1.2" />
115+ <path d="M8 7v3.25" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" />
116+ <circle cx="8" cy="4.75" r="0.8" fill="currentColor" />
117+ </svg>
118+ );
119+}
apps/web/src/components/markdown.tsx+53−0
@@ -0,0 +1,53 @@
1+import { Streamdown } from "streamdown";
2+import { createCodePlugin } from "@streamdown/code";
3+
4+const codePlugin = createCodePlugin({
5+ themes: ["vitesse-dark", "vitesse-dark"],
6+});
7+
8+const PROSE_CLASSES = [
9+ "text-[13px] leading-relaxed text-muted-foreground",
10+ // paragraphs
11+ "[&_p]:mb-[0.4em] [&_p:last-child]:mb-0",
12+ // bold
13+ "[&_strong]:text-foreground [&_strong]:font-semibold",
14+ "[&_b]:text-foreground [&_b]:font-semibold",
15+ // inline code
16+ "[&_code]:font-mono [&_code]:text-xs [&_code]:bg-muted [&_code]:border [&_code]:border-border",
17+ "[&_code]:rounded-sm [&_code]:px-1.5 [&_code]:py-px [&_code]:text-primary",
18+ // pre blocks
19+ "[&_pre]:bg-muted [&_pre]:border [&_pre]:border-border [&_pre]:rounded-md",
20+ "[&_pre]:px-3 [&_pre]:py-2 [&_pre]:overflow-x-auto [&_pre]:my-2 [&_pre]:text-xs [&_pre]:leading-relaxed",
21+ "[&_pre_code]:bg-transparent [&_pre_code]:border-0 [&_pre_code]:p-0 [&_pre_code]:text-inherit",
22+ // links
23+ "[&_a]:text-primary [&_a]:underline [&_a]:underline-offset-2",
24+ "[&_a]:decoration-primary/30 hover:[&_a]:decoration-primary/80",
25+ // lists
26+ "[&_ul]:pl-5 [&_ul]:my-1.5 [&_ol]:pl-5 [&_ol]:my-1.5",
27+ "[&_li]:mb-0.5 [&_li_::marker]:text-muted-foreground",
28+ // tables
29+ "[&_table]:w-full [&_table]:border-collapse [&_table]:text-xs [&_table]:my-2",
30+ "[&_th]:border [&_th]:border-border [&_th]:px-2 [&_th]:py-1 [&_th]:text-left [&_th]:bg-muted [&_th]:font-semibold [&_th]:text-foreground",
31+ "[&_td]:border [&_td]:border-border [&_td]:px-2 [&_td]:py-1 [&_td]:text-left [&_td]:bg-background",
32+ // headings
33+ "[&_h1]:font-semibold [&_h1]:text-foreground [&_h1]:mt-2 [&_h1]:mb-1 [&_h1]:text-[15px]",
34+ "[&_h2]:font-semibold [&_h2]:text-foreground [&_h2]:mt-2 [&_h2]:mb-1 [&_h2]:text-sm",
35+ "[&_h3]:font-semibold [&_h3]:text-foreground [&_h3]:mt-2 [&_h3]:mb-1 [&_h3]:text-[13px]",
36+ "[&_h4]:font-semibold [&_h4]:text-foreground [&_h4]:mt-2 [&_h4]:mb-1 [&_h4]:text-[13px]",
37+ // blockquote
38+ "[&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-3 [&_blockquote]:my-1.5 [&_blockquote]:text-muted-foreground",
39+ // hr
40+ "[&_hr]:border-0 [&_hr]:border-t [&_hr]:border-border [&_hr]:my-2",
41+ // images
42+ "[&_img]:max-w-full [&_img]:rounded",
43+].join(" ");
44+
45+export function Markdown(props: { children: string; className?: string }) {
46+ return (
47+ <div className={props.className ? `${PROSE_CLASSES} ${props.className}` : PROSE_CLASSES}>
48+ <Streamdown plugins={{ code: codePlugin }} controls={{ code: true }} linkSafety={{ enabled: false }}>
49+ {props.children}
50+ </Streamdown>
51+ </div>
52+ );
53+}
apps/web/src/components/shell.tsx+9−109
@@ -1,31 +1,9 @@
1−import { useState, useMemo } from "react";
21 import { Link, Outlet, useMatchRoute } from "@tanstack/react-router";
32 import { useSources, type Source } from "@executor-v3/react";
43 import { cn } from "../lib/utils";
5−import { IconSources, IconChevron } from "./icons";
4+import { IconSources } from "./icons";
65 import { LoadableBlock } from "./loadable";
76
8−// ── Kind labels + ordering ───────────────────────────────────────────────
9−
10−const kindMeta: Record<string, { label: string; order: number }> = {
11− mcp: { label: "MCP", order: 0 },
12− openapi: { label: "OpenAPI", order: 1 },
13− graphql: { label: "GraphQL", order: 2 },
14− internal: { label: "Internal", order: 3 },
15−};
16−
17−function groupByKind(sources: ReadonlyArray<Source>) {
18− const groups = new Map<string, Source[]>();
19− for (const s of sources) {
20− const list = groups.get(s.kind) ?? [];
21− list.push(s);
22− groups.set(s.kind, list);
23− }
24− return [...groups.entries()].sort(
25− (a, b) => (kindMeta[a[0]]?.order ?? 99) - (kindMeta[b[0]]?.order ?? 99),
26− );
27−}
28−
297 // ── Status dot color ─────────────────────────────────────────────────────
308
319 const statusColor: Record<string, string> = {
@@ -63,7 +41,7 @@ export function AppShell() { · 31 unmodified lines
6341 <nav className="flex flex-1 flex-col p-2 overflow-y-auto">
6442 <NavItem to="/" label="Dashboard" active={!!isHome} />
6543
66− {/* Sources tree */}
44+ {/* Sources */}
6745 <div className="mt-5 mb-1 px-2.5 text-[10px] font-medium uppercase tracking-widest text-muted-foreground/50">
6846 Sources
6947 </div>
@@ -74,7 +52,11 @@ export function AppShell() { · 4 unmodified lines
7452 No sources yet
7553 </div>
7654 ) : (
77− <SourceTree sources={items} matchRoute={matchRoute} />
55+ <div className="flex flex-col gap-px">
56+ {items.map((source) => (
57+ <SourceItem key={source.id} source={source} matchRoute={matchRoute} />
58+ ))}
59+ </div>
7860 )
7961 }
8062 </LoadableBlock>
@@ -96,87 +78,13 @@ export function AppShell() { · 15 unmodified lines
9678 );
9779 }
9880
99−// ── SourceTree ───────────────────────────────────────────────────────────
100−
101−function SourceTree(props: {
102− sources: ReadonlyArray<Source>;
103− matchRoute: ReturnType<typeof useMatchRoute>;
104−}) {
105− const groups = useMemo(() => groupByKind(props.sources), [props.sources]);
106−
107− // If there's only one kind, skip the group headers
108− if (groups.length === 1) {
109− return (
110− <div className="flex flex-col gap-px">
111− {groups[0]![1].map((source) => (
112− <SourceItem key={source.id} source={source} matchRoute={props.matchRoute} depth={0} />
113− ))}
114− </div>
115− );
116− }
117−
118− return (
119− <div className="flex flex-col gap-0.5">
120− {groups.map(([kind, sources]) => (
121− <KindGroup key={kind} kind={kind} sources={sources} matchRoute={props.matchRoute} />
122− ))}
123− </div>
124− );
125−}
126−
127−// ── KindGroup (collapsible) ──────────────────────────────────────────────
128−
129−function KindGroup(props: {
130− kind: string;
131− sources: Source[];
132− matchRoute: ReturnType<typeof useMatchRoute>;
133−}) {
134− // Auto-expand if any source in this group is active
135− const hasActive = props.sources.some((s) =>
136− props.matchRoute({ to: "/sources/$sourceId", params: { sourceId: s.id }, fuzzy: true }),
137− );
138− const [open, setOpen] = useState(true);
139−
140− return (
141− <div>
142− {/* Group header */}
143− <button
144− type="button"
145− onClick={() => setOpen((o) => !o)}
146− className="group flex w-full items-center gap-1.5 rounded-md px-2 py-1 text-[11px] font-medium text-muted-foreground/60 transition-colors hover:text-muted-foreground"
147− >
148− <IconChevron
149− className={cn(
150− "size-2.5 shrink-0 text-muted-foreground/30 transition-transform duration-150",
151− open && "rotate-90",
152− )}
153− />
154− <span className="uppercase tracking-wider">{kindMeta[props.kind]?.label ?? props.kind}</span>
155− <span className="ml-auto tabular-nums text-[10px] text-muted-foreground/30">
156− {props.sources.length}
157− </span>
158− </button>
159−
160− {/* Children */}
161− {open && (
162− <div className="flex flex-col gap-px mt-px">
163− {props.sources.map((source) => (
164− <SourceItem key={source.id} source={source} matchRoute={props.matchRoute} depth={1} />
165− ))}
166− </div>
167− )}
168− </div>
169− );
170−}
171−
17281 // ── SourceItem ───────────────────────────────────────────────────────────
17382
17483 function SourceItem(props: {
17584 source: Source;
17685 matchRoute: ReturnType<typeof useMatchRoute>;
177− depth: number;
17886 }) {
179− const { source, matchRoute, depth } = props;
87+ const { source, matchRoute } = props;
18088 const active = matchRoute({
18189 to: "/sources/$sourceId",
18290 params: { sourceId: source.id },
@@ -189,22 +97,14 @@ function SourceItem(props: { · 6 unmodified lines
18997 params={{ sourceId: source.id }}
19098 search={{ tab: "model" }}
19199 className={cn(
192− "group flex items-center gap-2 rounded-md py-1.5 text-[12px] transition-colors",
193− depth === 0 ? "px-2.5" : "pl-6 pr-2.5",
100+ "group flex items-center gap-2 rounded-md px-2.5 py-1.5 text-[13px] transition-colors",
194101 active
195102 ? "bg-sidebar-active text-foreground font-medium"
196103 : "text-sidebar-foreground hover:bg-sidebar-active/60 hover:text-foreground",
197104 )}
198105 >
199− {/* Tree connector line for nested items */}
200− {depth > 0 && (
201− <span className="absolute left-[18px] h-full w-px bg-sidebar-border" aria-hidden />
202− )}
203−
204106 <IconSources className="size-3 shrink-0 text-muted-foreground/40" />
205107 <span className="flex-1 truncate">{source.name}</span>
206−
207− {/* Status dot + tooltip */}
208108 <span
209109 className={cn("size-1.5 shrink-0 rounded-full", statusColor[source.status] ?? "bg-muted-foreground/30")}
210110 title={source.status}
apps/web/src/globals.css+10−0
@@ -1,6 +1,8 @@
11 @import "tailwindcss";
22 @import "tw-animate-css";
33
4+@source "../../node_modules/streamdown/dist/*.js";
5+
46 @custom-variant dark (&:is(.dark *));
57
68 @theme inline {
@@ -105,3 +107,11 @@ · 98 unmodified lines
105107 background: oklch(0.38 0.005 260);
106108 }
107109 }
110+
111+/* Streamdown code block overrides */
112+[data-streamdown="code-block-body"] {
113+ background-color: var(--muted) !important;
114+ border: 1px solid var(--border);
115+ border-radius: 0.375rem;
116+}
117+
apps/web/src/lib/format.ts+133−0
@@ -0,0 +1,133 @@
1+import { useState, useEffect } from "react";
2+
3+// Cache formatted results so we don't re-format on every render
4+const formatCache = new Map<string, string>();
5+
6+function formatCacheKey(code: string, parser: string): string {
7+ return `${parser}::${code.length}::${code}`;
8+}
9+
10+/**
11+ * Lazily load prettier + parsers and format code.
12+ * Returns the original string if formatting fails.
13+ */
14+async function formatCode(
15+ code: string,
16+ parser: "json" | "typescript" | "babel",
17+): Promise<string> {
18+ const key = formatCacheKey(code, parser);
19+ const cached = formatCache.get(key);
20+ if (cached) return cached;
21+
22+ try {
23+ const [prettier, parserPlugin] = await Promise.all([
24+ import("prettier/standalone"),
25+ parser === "json"
26+ ? import("prettier/plugins/babel")
27+ : import("prettier/plugins/typescript"),
28+ ]);
29+
30+ // both babel and typescript parsers need estree
31+ const estree = await import("prettier/plugins/estree");
32+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
33+ const plugins: any[] = [parserPlugin, estree];
34+
35+ let input = code;
36+ let unwrap = false;
37+
38+ // Type signature strings (e.g. `{ body: { ... } }`) aren't valid TS
39+ // on their own. Wrap as `type T = ...` so prettier can parse them.
40+ if (parser === "typescript") {
41+ input = `type __T = ${code}`;
42+ unwrap = true;
43+ }
44+
45+ const result = await prettier.format(input, {
46+ parser: parser === "json" ? "json" : "typescript",
47+ plugins,
48+ printWidth: 60,
49+ tabWidth: 2,
50+ semi: true,
51+ singleQuote: false,
52+ trailingComma: "all",
53+ });
54+
55+ let trimmed = result.trimEnd();
56+
57+ // Strip the `type __T = ` wrapper and trailing `;`
58+ if (unwrap) {
59+ trimmed = trimmed
60+ .replace(/^type __T =\s*/, "")
61+ .replace(/;$/, "")
62+ .trimEnd();
63+ }
64+
65+ formatCache.set(key, trimmed);
66+ return trimmed;
67+ } catch {
68+ // If formatting fails, return original
69+ return code;
70+ }
71+}
72+
73+/**
74+ * Detect the right parser for a code string.
75+ */
76+function detectParser(
77+ code: string,
78+ langHint?: string,
79+): "json" | "typescript" | null {
80+ if (langHint === "json" || langHint === "jsonc") return "json";
81+ if (langHint === "typescript" || langHint === "ts") return "typescript";
82+
83+ const trimmed = code.trimStart();
84+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) return "json";
85+
86+ return null;
87+}
88+
89+/**
90+ * Hook: formats code with prettier. Returns the formatted string
91+ * (or the original while loading / if formatting fails).
92+ */
93+export function useFormattedCode(
94+ code: string | null,
95+ langHint?: string,
96+): string | null {
97+ const [formatted, setFormatted] = useState<string | null>(code);
98+
99+ useEffect(() => {
100+ if (!code) {
101+ setFormatted(null);
102+ return;
103+ }
104+
105+ const parser = detectParser(code, langHint);
106+ if (!parser) {
107+ setFormatted(code);
108+ return;
109+ }
110+
111+ // Check cache synchronously first
112+ const key = formatCacheKey(code, parser);
113+ const cached = formatCache.get(key);
114+ if (cached) {
115+ setFormatted(cached);
116+ return;
117+ }
118+
119+ // Set original immediately, then replace with formatted
120+ setFormatted(code);
121+
122+ let cancelled = false;
123+ formatCode(code, parser).then((result) => {
124+ if (!cancelled) setFormatted(result);
125+ });
126+
127+ return () => {
128+ cancelled = true;
129+ };
130+ }, [code, langHint]);
131+
132+ return formatted;
133+}
apps/web/src/views/source-detail.tsx+330−22
@@ -1,9 +1,10 @@
1−import { useState, useEffect, useCallback, useRef } from "react";
1+import { useState, useEffect, useCallback, useRef, useMemo } from "react";
22 import { useNavigate } from "@tanstack/react-router";
33 import {
44 useSourceInspection,
55 useSourceToolDetail,
66 useSourceDiscovery,
7+ usePrefetchToolDetail,
78 type Loadable,
89 type SourceInspection,
910 type SourceInspectionToolDetail,
@@ -18,11 +19,14 @@ import { · 8 unmodified lines
1819 IconSearch,
1920 IconChevron,
2021 IconTool,
22+ IconFolder,
2123 IconCopy,
2224 IconCheck,
2325 IconClose,
2426 IconEmpty,
27+ IconInfo,
2528 } from "../components/icons";
29+import { Markdown } from "../components/markdown";
2630
2731 // ---------------------------------------------------------------------------
2832 // Types
@@ -125,6 +129,7 @@ export function SourceDetailPage(props: { · 96 unmodified lines
125129 onSelectTool={(toolPath) =>
126130 void navigate({ search: (prev) => ({ ...prev, tool: toolPath, tab: "model" }) })
127131 }
132+ sourceId={sourceId}
128133 />
129134 )}
130135 {search.tab === "discover" && (
@@ -172,6 +177,7 @@ function ModelView(props: { · 41 unmodified lines
172177 detail: Loadable<SourceInspectionToolDetail | null>;
173178 selectedToolPath: string | null;
174179 onSelectTool: (toolPath: string) => void;
180+ sourceId: string;
175181 }) {
176182 const [search, setSearch] = useState("");
177183 const searchRef = useRef<HTMLInputElement>(null);
@@ -244,23 +250,22 @@ function ModelView(props: { · 66 unmodified lines
244250 </span>
245251 </div>
246252
247− {/* Tool list */}
253+ {/* Tool tree */}
248254 <div className="flex-1 overflow-y-auto">
249255 {filteredTools.length === 0 ? (
250256 <div className="p-4 text-center text-[13px] text-muted-foreground/50">
251257 {terms.length > 0 ? "No tools match your search" : "No tools available"}
252258 </div>
253259 ) : (
254− <div className="p-1.5 space-y-px">
255− {filteredTools.map((tool) => (
256− <ToolListItem
257− key={tool.path}
258− tool={tool}
259− active={tool.path === props.selectedToolPath}
260− onSelect={() => props.onSelectTool(tool.path)}
261− search={search}
262− />
263− ))}
260+ <div className="p-1.5">
261+ <ToolTree
262+ tools={filteredTools}
263+ selectedToolPath={props.selectedToolPath}
264+ onSelectTool={props.onSelectTool}
265+ search={search}
266+ isFiltered={terms.length > 0}
267+ sourceId={props.sourceId}
268+ />
264269 </div>
265270 )}
266271 </div>
@@ -285,6 +290,215 @@ function ModelView(props: { · 18 unmodified lines
285290 );
286291 }
287292
293+// ---------------------------------------------------------------------------
294+// Tool Tree (nested by . segments)
295+// ---------------------------------------------------------------------------
296+
297+type ToolTreeNode = {
298+ segment: string;
299+ tool?: SourceInspection["tools"][number];
300+ children: Map<string, ToolTreeNode>;
301+};
302+
303+function buildToolTree(tools: SourceInspection["tools"]): ToolTreeNode {
304+ const root: ToolTreeNode = { segment: "", children: new Map() };
305+ for (const tool of tools) {
306+ const parts = tool.path.split(".");
307+ let node = root;
308+ for (const part of parts) {
309+ if (!node.children.has(part)) {
310+ node.children.set(part, { segment: part, children: new Map() });
311+ }
312+ node = node.children.get(part)!;
313+ }
314+ node.tool = tool;
315+ }
316+ return root;
317+}
318+
319+function ToolTree(props: {
320+ tools: SourceInspection["tools"];
321+ selectedToolPath: string | null;
322+ onSelectTool: (path: string) => void;
323+ search: string;
324+ isFiltered: boolean;
325+ sourceId: string;
326+}) {
327+ const tree = useMemo(() => buildToolTree(props.tools), [props.tools]);
328+ const prefetch = usePrefetchToolDetail();
329+ const entries = [...tree.children.values()].sort((a, b) =>
330+ a.segment.localeCompare(b.segment),
331+ );
332+
333+ return (
334+ <div className="flex flex-col gap-px">
335+ {entries.map((node) => (
336+ <ToolTreeNodeView
337+ key={node.segment}
338+ node={node}
339+ depth={0}
340+ selectedToolPath={props.selectedToolPath}
341+ onSelectTool={props.onSelectTool}
342+ search={props.search}
343+ defaultOpen={props.isFiltered}
344+ sourceId={props.sourceId}
345+ prefetch={prefetch}
346+ />
347+ ))}
348+ </div>
349+ );
350+}
351+
352+function ToolTreeNodeView(props: {
353+ node: ToolTreeNode;
354+ depth: number;
355+ selectedToolPath: string | null;
356+ onSelectTool: (path: string) => void;
357+ search: string;
358+ defaultOpen: boolean;
359+ sourceId: string;
360+ prefetch: (sourceId: string, toolPath: string) => () => void;
361+}) {
362+ const { node, depth, selectedToolPath, onSelectTool, search, defaultOpen, sourceId, prefetch } = props;
363+ const hasChildren = node.children.size > 0;
364+ const isLeaf = !!node.tool && !hasChildren;
365+
366+ // Check if any descendant is the selected tool
367+ const hasSelectedDescendant = useMemo(() => {
368+ if (!selectedToolPath) return false;
369+ function check(n: ToolTreeNode): boolean {
370+ if (n.tool?.path === selectedToolPath) return true;
371+ for (const child of n.children.values()) {
372+ if (check(child)) return true;
373+ }
374+ return false;
375+ }
376+ return check(node);
377+ }, [node, selectedToolPath]);
378+
379+ const [open, setOpen] = useState(defaultOpen || hasSelectedDescendant);
380+
381+ // Auto-open when search filter is active or selection moves into this group
382+ useEffect(() => {
383+ if (defaultOpen || hasSelectedDescendant) setOpen(true);
384+ }, [defaultOpen, hasSelectedDescendant]);
385+
386+ // Leaf: render as a tool item
387+ if (isLeaf) {
388+ return (
389+ <ToolListItem
390+ tool={node.tool!}
391+ active={node.tool!.path === selectedToolPath}
392+ onSelect={() => onSelectTool(node.tool!.path)}
393+ search={search}
394+ depth={depth}
395+ sourceId={sourceId}
396+ prefetch={prefetch}
397+ />
398+ );
399+ }
400+
401+ // Folder: collapsible group
402+ const paddingLeft = 8 + depth * 16;
403+ const sortedChildren = [...node.children.values()].sort((a, b) =>
404+ a.segment.localeCompare(b.segment),
405+ );
406+
407+ const leafCount = countToolLeaves(node);
408+
409+ return (
410+ <div>
411+ {node.tool ? (
412+ // Node is both a folder AND a tool (rare)
413+ <div className="flex items-center">
414+ <button
415+ type="button"
416+ onClick={() => setOpen((o) => !o)}
417+ className="shrink-0 rounded p-0.5 text-muted-foreground/30 hover:text-muted-foreground"
418+ style={{ marginLeft: paddingLeft }}
419+ >
420+ <IconChevron
421+ className={cn(
422+ "size-2.5 transition-transform duration-150",
423+ open && "rotate-90",
424+ )}
425+ />
426+ </button>
427+ <ToolListItem
428+ tool={node.tool}
429+ active={node.tool.path === selectedToolPath}
430+ onSelect={() => onSelectTool(node.tool!.path)}
431+ search={search}
432+ depth={-1}
433+ className="flex-1 pl-1"
434+ sourceId={sourceId}
435+ prefetch={prefetch}
436+ />
437+ </div>
438+ ) : (
439+ // Pure folder
440+ <button
441+ type="button"
442+ onClick={() => setOpen((o) => !o)}
443+ className={cn(
444+ "group flex w-full items-center gap-1.5 rounded-md py-1 pr-2.5 text-[12px] transition-colors hover:bg-accent/40",
445+ open ? "text-foreground/80" : "text-muted-foreground/60",
446+ )}
447+ style={{ paddingLeft }}
448+ >
449+ <IconChevron
450+ className={cn(
451+ "size-2.5 shrink-0 text-muted-foreground/30 transition-transform duration-150",
452+ open && "rotate-90",
453+ )}
454+ />
455+ <IconFolder className={cn(
456+ "size-3 shrink-0",
457+ open ? "text-primary/60" : "text-muted-foreground/30",
458+ )} />
459+ <span className="flex-1 truncate text-left font-mono">
460+ {highlightMatch(node.segment, search)}
461+ </span>
462+ <span className="shrink-0 tabular-nums text-[10px] text-muted-foreground/25">
463+ {leafCount}
464+ </span>
465+ </button>
466+ )}
467+
468+ {open && hasChildren && (
469+ <div className="relative flex flex-col gap-px">
470+ <span
471+ className="absolute top-0 bottom-1 w-px bg-border/40"
472+ style={{ left: paddingLeft + 5 }}
473+ aria-hidden
474+ />
475+ {sortedChildren.map((child) => (
476+ <ToolTreeNodeView
477+ key={child.segment}
478+ node={child}
479+ depth={depth + 1}
480+ selectedToolPath={selectedToolPath}
481+ onSelectTool={onSelectTool}
482+ search={search}
483+ defaultOpen={defaultOpen}
484+ sourceId={sourceId}
485+ prefetch={prefetch}
486+ />
487+ ))}
488+ </div>
489+ )}
490+ </div>
491+ );
492+}
493+
494+function countToolLeaves(node: ToolTreeNode): number {
495+ let count = node.tool ? 1 : 0;
496+ for (const child of node.children.values()) {
497+ count += countToolLeaves(child);
498+ }
499+ return count;
500+}
501+
288502 // ---------------------------------------------------------------------------
289503 // ToolListItem
290504 // ---------------------------------------------------------------------------
@@ -294,8 +508,14 @@ function ToolListItem(props: { · 3 unmodified lines
294508 active: boolean;
295509 onSelect: () => void;
296510 search: string;
511+ depth: number;
512+ className?: string;
513+ sourceId: string;
514+ prefetch: (sourceId: string, toolPath: string) => () => void;
297515 }) {
298516 const ref = useRef<HTMLButtonElement>(null);
517+ const paddingLeft = props.depth >= 0 ? 8 + props.depth * 16 + 8 : undefined;
518+ const prefetchedRef = useRef(false);
299519
300520 useEffect(() => {
301521 if (props.active && ref.current) {
@@ -303,21 +523,47 @@ function ToolListItem(props: { · 1 unmodified line
303523 }
304524 }, [props.active]);
305525
526+ // Prefetch when visible in viewport
527+ useEffect(() => {
528+ const el = ref.current;
529+ if (!el || prefetchedRef.current) return;
530+
531+ const observer = new IntersectionObserver(
532+ (entries) => {
533+ if (entries[0]?.isIntersecting && !prefetchedRef.current) {
534+ prefetchedRef.current = true;
535+ props.prefetch(props.sourceId, props.tool.path);
536+ observer.disconnect();
537+ }
538+ },
539+ { threshold: 0 },
540+ );
541+ observer.observe(el);
542+ return () => observer.disconnect();
543+ }, [props.prefetch, props.sourceId, props.tool.path]);
544+
545+ // Show only the last segment (the actual method name) since parents show the namespace
546+ const label = props.depth >= 0
547+ ? props.tool.path.split(".").pop() ?? props.tool.path
548+ : props.tool.path;
549+
306550 return (
307551 <button
308552 ref={ref}
309553 type="button"
310554 onClick={props.onSelect}
311555 className={cn(
312− "group flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left transition-colors",
556+ "group flex w-full items-center gap-2 rounded-md py-1.5 pr-2.5 text-left transition-colors",
313557 props.active
314− ? "bg-primary/10 text-foreground border-l-2 border-l-primary -ml-px"
558+ ? "bg-primary/10 text-foreground border-l-2 border-l-primary"
315559 : "hover:bg-accent/50 text-foreground/70 hover:text-foreground",
560+ props.className,
316561 )}
562+ style={paddingLeft != null ? { paddingLeft } : undefined}
317563 >
318564 <IconTool className="size-3 shrink-0 text-muted-foreground/40" />
319565 <span className="flex-1 truncate font-mono text-[12px]">
320− {highlightMatch(props.tool.path, props.search)}
566+ {highlightMatch(label, props.search)}
321567 </span>
322568 {props.tool.method && <MethodBadge method={props.tool.method} />}
323569 </button>
@@ -328,9 +574,34 @@ function ToolListItem(props: { · 4 unmodified lines
328574 // ToolDetailPanel
329575 // ---------------------------------------------------------------------------
330576
577+type OutputTypeExplanation = {
578+ title: string;
579+ description: string;
580+};
581+
582+const getOutputTypeExplanation = (outputType: string | null | undefined): OutputTypeExplanation | null => {
583+ if (outputType === "void") {
584+ return {
585+ title: "No response body",
586+ description: "This operation succeeds without returning JSON. The HTTP response carries status only.",
587+ };
588+ }
589+
590+ if (outputType === "{}") {
591+ return {
592+ title: "Empty JSON object",
593+ description: "This operation returns a response body, but the schema has no fields.",
594+ };
595+ }
596+
597+ return null;
598+};
599+
600+
331601 function ToolDetailPanel(props: { detail: SourceInspectionToolDetail }) {
332602 const { detail } = props;
333603 const [copiedField, setCopiedField] = useState<string | null>(null);
604+ const outputTypeExplanation = getOutputTypeExplanation(detail.summary.outputType);
334605
335606 const copy = useCallback((text: string, field: string) => {
336607 void navigator.clipboard.writeText(text).then(() => {
@@ -368,13 +639,36 @@ function ToolDetailPanel(props: { detail: SourceInspectionToolDetail }) { · 31 unmodified lines
368639
369640 {/* Scrollable body */}
370641 <div className="flex-1 overflow-y-auto">
371− <div className="px-5 py-4 space-y-5">
372− {/* Summary cards */}
373− <div className="grid grid-cols-2 gap-2 lg:grid-cols-4">
374− <MetricCard label="Input type" value={detail.summary.inputType ?? "unknown"} />
375− <MetricCard label="Output type" value={detail.summary.outputType ?? "unknown"} />
376− <MetricCard label="Tool ID" value={detail.summary.toolId} mono />
377− <MetricCard label="Path template" value={detail.summary.pathTemplate ?? "n/a"} mono />
642+ <div className="px-5 py-4 space-y-4">
643+ {/* Description */}
644+ {detail.summary.description && (
645+ <Markdown>{detail.summary.description}</Markdown>
646+ )}
647+
648+ {/* Type signatures */}
649+ <div className="grid grid-cols-1 gap-3 xl:grid-cols-2">
650+ <DocumentPanel title="Input type" body={detail.summary.inputType ?? null} lang="typescript" empty="No input type." />
651+ <DocumentPanel title="Output type" body={detail.summary.outputType ?? null} lang="typescript" empty="No output type." />
652+ </div>
653+ {outputTypeExplanation && (
654+ <div className="flex items-start gap-2 rounded-lg border border-border bg-card/40 px-3 py-2.5 text-[12px] text-muted-foreground">
655+ <IconInfo className="mt-0.5 size-3.5 shrink-0 text-primary/80" />
656+ <div>
657+ <div className="font-medium text-foreground/90">{outputTypeExplanation.title}</div>
658+ <p className="mt-0.5 leading-relaxed">{outputTypeExplanation.description}</p>
659+ </div>
660+ </div>
661+ )}
662+
663+ {/* Metadata row */}
664+ <div className="flex flex-wrap items-center gap-2 text-[11px]">
665+ <MetricPill label="Tool ID" value={detail.summary.toolId} />
666+ {detail.summary.pathTemplate && (
667+ <MetricPill label="Path" value={detail.summary.pathTemplate} />
668+ )}
669+ {detail.summary.method && (
670+ <MethodBadge method={detail.summary.method} />
671+ )}
378672 </div>
379673
380674 {/* Schema panels */}
@@ -531,6 +825,20 @@ function MetricCard(props: { label: string; value: string; mono?: boolean }) { · 150 unmodified lines
531825 );
532826 }
533827
828+function MetricPill(props: { label: string; value: string }) {
829+ return (
830+ <span className="inline-flex items-center gap-1.5 rounded-md border border-border bg-card/60 px-2 py-1">
831+ <span className="text-[10px] font-medium uppercase tracking-wider text-muted-foreground/50">
832+ {props.label}
833+ </span>
834+ <span className="font-mono text-[11px] text-foreground/70">
835+ {props.value}
836+ </span>
837+ </span>
838+ );
839+}
840+
841+
534842 function CopyButton(props: {
535843 text: string;
536844 field: string;
bun.lock+187−0
@@ -51,15 +51,18 @@ · 50 unmodified lines
5151 "dependencies": {
5252 "@executor-v3/react": "workspace:*",
5353 "@executor-v3/server": "workspace:*",
54+ "@streamdown/code": "^1.0.3",
5455 "@tanstack/react-router": "^1.132.7",
5556 "class-variance-authority": "^0.7.1",
5657 "clsx": "^2.1.1",
5758 "effect": "catalog:",
59+ "prettier": "^3.8.1",
5860 "react": "^19.2.0",
5961 "react-dom": "^19.2.0",
6062 "react-grab": "^0.1.26",
6163 "scheduler": "^0.27.0",
6264 "shiki": "^4.0.1",
65+ "streamdown": "^2.3.0",
6366 "tailwind-merge": "^3.5.0",
6467 },
6568 "devDependencies": {
@@ -606,6 +609,8 @@ · 540 unmodified lines
606609
607610 "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
608611
612+ "@streamdown/code": ["@streamdown/code@1.1.0", "", { "dependencies": { "shiki": "^3.19.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0" } }, "sha512-swypCjtE6vv01bnEtPeaw2ew9cbL2nbsLc06HAIK3K6nYXj5WDA8VLR6GEiwdh7HLIPt5dGze+PJ0eJVkqesug=="],
613+
609614 "@tailwindcss/node": ["@tailwindcss/node@4.2.1", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.31.1", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.1" } }, "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg=="],
610615
611616 "@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.1", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.1", "@tailwindcss/oxide-darwin-arm64": "4.2.1", "@tailwindcss/oxide-darwin-x64": "4.2.1", "@tailwindcss/oxide-freebsd-x64": "4.2.1", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.1", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.1", "@tailwindcss/oxide-linux-arm64-musl": "4.2.1", "@tailwindcss/oxide-linux-x64-gnu": "4.2.1", "@tailwindcss/oxide-linux-x64-musl": "4.2.1", "@tailwindcss/oxide-wasm32-wasi": "4.2.1", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.1", "@tailwindcss/oxide-win32-x64-msvc": "4.2.1" } }, "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw=="],
@@ -660,14 +665,20 @@ · 48 unmodified lines
660665
661666 "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="],
662667
668+ "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
669+
663670 "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="],
664671
665672 "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
666673
674+ "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="],
675+
667676 "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="],
668677
669678 "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
670679
680+ "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
681+
671682 "@types/mssql": ["@types/mssql@9.1.9", "", { "dependencies": { "@types/node": "*", "tarn": "^3.0.1", "tedious": "*" } }, "sha512-P0nCgw6vzY23UxZMnbI4N7fnLGANt4LI4yvxze1paPj+LuN28cFv5EI+QidP8udnId/BKhkcRhm/BleNsjK65A=="],
672683
673684 "@types/node": ["@types/node@24.11.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-fPxQqz4VTgPI/IQ+lj9r0h+fDR66bzoeMGHp8ASee+32OSGIkeASsoZuJixsQoVef1QJbeubcPBxKk22QVoWdw=="],
@@ -722,6 +733,8 @@ · 48 unmodified lines
722733
723734 "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="],
724735
736+ "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="],
737+
725738 "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
726739
727740 "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
@@ -764,10 +777,14 @@ · 36 unmodified lines
764777
765778 "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
766779
780+ "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="],
781+
767782 "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="],
768783
769784 "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="],
770785
786+ "character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="],
787+
771788 "check-error": ["check-error@2.1.3", "", {}, "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA=="],
772789
773790 "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="],
@@ -802,6 +819,8 @@ · 28 unmodified lines
802819
803820 "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
804821
822+ "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="],
823+
805824 "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="],
806825
807826 "default-browser": ["default-browser@5.5.0", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw=="],
@@ -838,6 +857,8 @@ · 30 unmodified lines
838857
839858 "enhanced-resolve": ["enhanced-resolve@5.20.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ=="],
840859
860+ "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
861+
841862 "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
842863
843864 "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
@@ -852,6 +873,10 @@ · 8 unmodified lines
852873
853874 "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
854875
876+ "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
877+
878+ "estree-util-is-identifier-name": ["estree-util-is-identifier-name@3.0.0", "", {}, "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg=="],
879+
855880 "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
856881
857882 "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
@@ -872,6 +897,8 @@ · 14 unmodified lines
872897
873898 "express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="],
874899
900+ "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
901+
875902 "fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="],
876903
877904 "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
@@ -920,12 +947,28 @@ · 42 unmodified lines
920947
921948 "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
922949
950+ "hast-util-from-parse5": ["hast-util-from-parse5@8.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "hastscript": "^9.0.0", "property-information": "^7.0.0", "vfile": "^6.0.0", "vfile-location": "^5.0.0", "web-namespaces": "^2.0.0" } }, "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg=="],
951+
952+ "hast-util-parse-selector": ["hast-util-parse-selector@4.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A=="],
953+
954+ "hast-util-raw": ["hast-util-raw@9.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "hast-util-from-parse5": "^8.0.0", "hast-util-to-parse5": "^8.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "parse5": "^7.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw=="],
955+
956+ "hast-util-sanitize": ["hast-util-sanitize@5.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "unist-util-position": "^5.0.0" } }, "sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg=="],
957+
923958 "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="],
924959
960+ "hast-util-to-jsx-runtime": ["hast-util-to-jsx-runtime@2.3.6", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="],
961+
962+ "hast-util-to-parse5": ["hast-util-to-parse5@8.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA=="],
963+
925964 "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="],
926965
966+ "hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="],
967+
927968 "hono": ["hono@4.12.5", "", {}, "sha512-3qq+FUBtlTHhtYxbxheZgY8NIFnkkC/MR8u5TTsr7YZ3wixryQ3cCwn3iZbg8p8B88iDBBAYSfZDS75t8MN7Vg=="],
928969
970+ "html-url-attributes": ["html-url-attributes@3.0.1", "", {}, "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ=="],
971+
929972 "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="],
930973
931974 "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
@@ -944,22 +987,34 @@ · 12 unmodified lines
944987
945988 "ini": ["ini@4.1.3", "", {}, "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg=="],
946989
990+ "inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="],
991+
947992 "ip-address": ["ip-address@10.0.1", "", {}, "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA=="],
948993
949994 "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
950995
996+ "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="],
997+
998+ "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="],
999+
1000+ "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="],
1001+
9511002 "is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
9521003
9531004 "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
9541005
9551006 "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
9561007
1008+ "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="],
1009+
9571010 "is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="],
9581011
9591012 "is-interactive": ["is-interactive@2.0.0", "", {}, "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ=="],
9601013
9611014 "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
9621015
1016+ "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
1017+
9631018 "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
9641019
9651020 "is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="],
@@ -1046,28 +1101,108 @@ · 80 unmodified lines
10461101
10471102 "log-symbols": ["log-symbols@6.0.0", "", { "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" } }, "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw=="],
10481103
1104+ "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="],
1105+
10491106 "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="],
10501107
10511108 "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
10521109
10531110 "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
10541111
1112+ "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="],
1113+
1114+ "marked": ["marked@17.0.4", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-NOmVMM+KAokHMvjWmC5N/ZOvgmSWuqJB8FoYI019j4ogb/PeRMKoKIjReZ2w3376kkA8dSJIP8uD993Kxc0iRQ=="],
1115+
10551116 "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
10561117
1118+ "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="],
1119+
1120+ "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.3", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q=="],
1121+
1122+ "mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", "mdast-util-gfm-strikethrough": "^2.0.0", "mdast-util-gfm-table": "^2.0.0", "mdast-util-gfm-task-list-item": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="],
1123+
1124+ "mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="],
1125+
1126+ "mdast-util-gfm-footnote": ["mdast-util-gfm-footnote@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0" } }, "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ=="],
1127+
1128+ "mdast-util-gfm-strikethrough": ["mdast-util-gfm-strikethrough@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg=="],
1129+
1130+ "mdast-util-gfm-table": ["mdast-util-gfm-table@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg=="],
1131+
1132+ "mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="],
1133+
1134+ "mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="],
1135+
1136+ "mdast-util-mdx-jsx": ["mdast-util-mdx-jsx@3.2.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" } }, "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q=="],
1137+
1138+ "mdast-util-mdxjs-esm": ["mdast-util-mdxjs-esm@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg=="],
1139+
1140+ "mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="],
1141+
10571142 "mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="],
10581143
1144+ "mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="],
1145+
1146+ "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="],
1147+
10591148 "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
10601149
10611150 "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
10621151
10631152 "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
10641153
1154+ "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="],
1155+
1156+ "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="],
1157+
1158+ "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="],
1159+
1160+ "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="],
1161+
1162+ "micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="],
1163+
1164+ "micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="],
1165+
1166+ "micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg=="],
1167+
1168+ "micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="],
1169+
1170+ "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="],
1171+
1172+ "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="],
1173+
1174+ "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="],
1175+
1176+ "micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="],
1177+
1178+ "micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="],
1179+
1180+ "micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="],
1181+
10651182 "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="],
10661183
1184+ "micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="],
1185+
1186+ "micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="],
1187+
1188+ "micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="],
1189+
1190+ "micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="],
1191+
1192+ "micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="],
1193+
10671194 "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="],
10681195
1196+ "micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="],
1197+
1198+ "micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="],
1199+
1200+ "micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="],
1201+
10691202 "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="],
10701203
1204+ "micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="],
1205+
10711206 "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="],
10721207
10731208 "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="],
@@ -1128,6 +1263,10 @@ · 54 unmodified lines
11281263
11291264 "oxc-resolver": ["oxc-resolver@11.19.1", "", { "optionalDependencies": { "@oxc-resolver/binding-android-arm-eabi": "11.19.1", "@oxc-resolver/binding-android-arm64": "11.19.1", "@oxc-resolver/binding-darwin-arm64": "11.19.1", "@oxc-resolver/binding-darwin-x64": "11.19.1", "@oxc-resolver/binding-freebsd-x64": "11.19.1", "@oxc-resolver/binding-linux-arm-gnueabihf": "11.19.1", "@oxc-resolver/binding-linux-arm-musleabihf": "11.19.1", "@oxc-resolver/binding-linux-arm64-gnu": "11.19.1", "@oxc-resolver/binding-linux-arm64-musl": "11.19.1", "@oxc-resolver/binding-linux-ppc64-gnu": "11.19.1", "@oxc-resolver/binding-linux-riscv64-gnu": "11.19.1", "@oxc-resolver/binding-linux-riscv64-musl": "11.19.1", "@oxc-resolver/binding-linux-s390x-gnu": "11.19.1", "@oxc-resolver/binding-linux-x64-gnu": "11.19.1", "@oxc-resolver/binding-linux-x64-musl": "11.19.1", "@oxc-resolver/binding-openharmony-arm64": "11.19.1", "@oxc-resolver/binding-wasm32-wasi": "11.19.1", "@oxc-resolver/binding-win32-arm64-msvc": "11.19.1", "@oxc-resolver/binding-win32-ia32-msvc": "11.19.1", "@oxc-resolver/binding-win32-x64-msvc": "11.19.1" } }, "sha512-qE/CIg/spwrTBFt5aKmwe3ifeDdLfA2NESN30E42X/lII5ClF8V7Wt6WIJhcGZjp0/Q+nQ+9vgxGk//xZNX2hg=="],
11301265
1266+ "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="],
1267+
1268+ "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
1269+
11311270 "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
11321271
11331272 "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
@@ -1148,6 +1287,8 @@ · 14 unmodified lines
11481287
11491288 "postgres": ["postgres@3.4.8", "", {}, "sha512-d+JFcLM17njZaOLkv6SCev7uoLaBtfK86vMUXhW1Z4glPWh4jozno9APvW/XKFJ3CCxVoC7OL38BqRydtu5nGg=="],
11501289
1290+ "prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="],
1291+
11511292 "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="],
11521293
11531294 "prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="],
@@ -1182,6 +1323,22 @@ · 28 unmodified lines
11821323
11831324 "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="],
11841325
1326+ "rehype-harden": ["rehype-harden@1.1.8", "", { "dependencies": { "unist-util-visit": "^5.0.0" } }, "sha512-Qn7vR1xrf6fZCrkm9TDWi/AB4ylrHy+jqsNm1EHOAmbARYA6gsnVJBq/sdBh6kmT4NEZxH5vgIjrscefJAOXcw=="],
1327+
1328+ "rehype-raw": ["rehype-raw@7.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-raw": "^9.0.0", "vfile": "^6.0.0" } }, "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww=="],
1329+
1330+ "rehype-sanitize": ["rehype-sanitize@6.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-sanitize": "^5.0.0" } }, "sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg=="],
1331+
1332+ "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="],
1333+
1334+ "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="],
1335+
1336+ "remark-rehype": ["remark-rehype@11.1.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw=="],
1337+
1338+ "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="],
1339+
1340+ "remend": ["remend@1.2.2", "", {}, "sha512-4ZJgIB9EG9fQE41mOJCRHMmnxDTKHWawQoJWZyUbZuj680wVyogu2ihnj8Edqm7vh2mo/TWHyEZpn2kqeDvS7w=="],
1341+
11851342 "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
11861343
11871344 "restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="],
@@ -1254,6 +1411,8 @@ · 66 unmodified lines
12541411
12551412 "stdin-discarder": ["stdin-discarder@0.2.2", "", {}, "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ=="],
12561413
1414+ "streamdown": ["streamdown@2.4.0", "", { "dependencies": { "clsx": "^2.1.1", "hast-util-to-jsx-runtime": "^2.3.6", "html-url-attributes": "^3.0.1", "marked": "^17.0.1", "rehype-harden": "^1.1.8", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remend": "1.2.2", "tailwind-merge": "^3.4.0", "unified": "^11.0.5", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-fRk4HEYNznRLmxoVeT8wsGBwHF6/Yrdey6k+ZrE1Qtp4NyKwm7G/6e2Iw8penY4yLx31TlAHWT5Bsg1weZ9FZg=="],
1415+
12571416 "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
12581417
12591418 "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
@@ -1266,6 +1425,10 @@ · 6 unmodified lines
12661425
12671426 "strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="],
12681427
1428+ "style-to-js": ["style-to-js@1.1.21", "", { "dependencies": { "style-to-object": "1.0.14" } }, "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ=="],
1429+
1430+ "style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="],
1431+
12691432 "tailwind-merge": ["tailwind-merge@3.5.0", "", {}, "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A=="],
12701433
12711434 "tailwindcss": ["tailwindcss@4.2.1", "", {}, "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw=="],
@@ -1300,6 +1463,8 @@ · 28 unmodified lines
13001463
13011464 "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
13021465
1466+ "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="],
1467+
13031468 "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
13041469
13051470 "turbo": ["turbo@2.8.14", "", { "optionalDependencies": { "turbo-darwin-64": "2.8.14", "turbo-darwin-arm64": "2.8.14", "turbo-linux-64": "2.8.14", "turbo-linux-arm64": "2.8.14", "turbo-windows-64": "2.8.14", "turbo-windows-arm64": "2.8.14" }, "bin": { "turbo": "bin/turbo" } }, "sha512-UCTxeMNYT1cKaHiIFdLCQ7ulI+jw5i5uOnJOrRXsgUD7G3+OjlUjwVd7JfeVt2McWSVGjYA3EVW/v1FSsJ5DtA=="],
@@ -1326,6 +1491,8 @@ · 20 unmodified lines
13261491
13271492 "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
13281493
1494+ "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="],
1495+
13291496 "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="],
13301497
13311498 "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="],
@@ -1348,6 +1515,8 @@ · 16 unmodified lines
13481515
13491516 "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
13501517
1518+ "vfile-location": ["vfile-location@5.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg=="],
1519+
13511520 "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="],
13521521
13531522 "vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="],
@@ -1358,6 +1527,8 @@ · 4 unmodified lines
13581527
13591528 "walk-up-path": ["walk-up-path@4.0.0", "", {}, "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A=="],
13601529
1530+ "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="],
1531+
13611532 "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
13621533
13631534 "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="],
@@ -1384,6 +1555,8 @@ · 20 unmodified lines
13841555
13851556 "@react-grab/cli/commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="],
13861557
1558+ "@streamdown/code/shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="],
1559+
13871560 "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="],
13881561
13891562 "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="],
@@ -1402,10 +1575,24 @@ · 12 unmodified lines
14021575
14031576 "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
14041577
1578+ "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
1579+
14051580 "tedious/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
14061581
14071582 "vite/esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="],
14081583
1584+ "@streamdown/code/shiki/@shikijs/core": ["@shikijs/core@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA=="],
1585+
1586+ "@streamdown/code/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA=="],
1587+
1588+ "@streamdown/code/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g=="],
1589+
1590+ "@streamdown/code/shiki/@shikijs/langs": ["@shikijs/langs@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg=="],
1591+
1592+ "@streamdown/code/shiki/@shikijs/themes": ["@shikijs/themes@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA=="],
1593+
1594+ "@streamdown/code/shiki/@shikijs/types": ["@shikijs/types@3.23.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ=="],
1595+
14091596 "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="],
14101597
14111598 "vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="],
packages/codemode-openapi/src/index.ts+5−1
@@ -12,7 +12,11 @@ export { · 11 unmodified lines
1212 OpenApiToolDefinitionSchema,
1313 type OpenApiToolDefinition,
1414 } from "./openapi-definitions";
15−export { buildOpenApiToolPresentation, type OpenApiToolPresentation } from "./openapi-tool-presentation";
15+export {
16+ buildOpenApiToolPresentation,
17+ openApiOutputTypeSignatureFromSchemaJson,
18+ type OpenApiToolPresentation,
19+} from "./openapi-tool-presentation";
1620 export {
1721 OpenApiToolInvocationError,
1822 createOpenApiToolsFromManifest,
packages/codemode-openapi/src/openapi-tool-presentation.ts+37−2
@@ -18,6 +18,41 @@ const parseJson = (value: string): unknown | undefined => { · 17 unmodified lines
1818 }
1919 };
2020
21+const asRecord = (value: unknown): Record<string, unknown> =>
22+ typeof value === "object" && value !== null && !Array.isArray(value)
23+ ? (value as Record<string, unknown>)
24+ : {};
25+
26+const isStrictEmptyObjectSchema = (value: unknown): boolean => {
27+ const schema = asRecord(value);
28+ if (schema.type !== "object" && schema.properties === undefined) {
29+ return false;
30+ }
31+
32+ const properties = asRecord(schema.properties);
33+ return Object.keys(properties).length === 0 && schema.additionalProperties === false;
34+};
35+
36+export const openApiOutputTypeSignatureFromSchemaJson = (
37+ schemaJson: string | undefined,
38+ maxLength: number = 320,
39+ ): string => {
40+ if (!schemaJson) {
41+ return "void";
42+ }
43+
44+ const parsed = parseJson(schemaJson);
45+ if (parsed === undefined) {
46+ return "unknown";
47+ }
48+
49+ if (isStrictEmptyObjectSchema(parsed)) {
50+ return "{}";
51+ }
52+
53+ return typeSignatureFromSchemaJson(schemaJson, "unknown", maxLength);
54+};
55+
2156 const firstExample = (
2257 examples: ReadonlyArray<OpenApiExample> | undefined,
2358 ): OpenApiExample | undefined => examples?.[0];
@@ -82,8 +117,8 @@ export const buildOpenApiToolPresentation = (input: { · 58 unmodified lines
82117 const exampleOutputJson = buildExampleOutputJson(input.definition.documentation);
83118
84119 return {
85− inputType: typeSignatureFromSchemaJson(inputSchemaJson, "unknown", 320),
86− outputType: typeSignatureFromSchemaJson(outputSchemaJson, "unknown", 320),
120+ inputType: typeSignatureFromSchemaJson(inputSchemaJson, "unknown", Infinity),
121+ outputType: openApiOutputTypeSignatureFromSchemaJson(outputSchemaJson, Infinity),
87122 ...(inputSchemaJson ? { inputSchemaJson } : {}),
88123 ...(outputSchemaJson ? { outputSchemaJson } : {}),
89124 ...(exampleInputJson ? { exampleInputJson } : {}),
packages/control-plane/src/runtime/source-inspection.ts+15−7
@@ -3,6 +3,7 @@ import { · 2 unmodified lines
33 buildOpenApiToolPresentation,
44 compileOpenApiToolDefinitions,
55 extractOpenApiManifest,
6+ openApiOutputTypeSignatureFromSchemaJson,
67 type OpenApiToolDefinition,
78 type OpenApiToolManifest,
89 } from "@executor-v3/codemode-openapi";
@@ -162,19 +163,26 @@ const persistedToolSummaryFromArtifact = (input: { · 153 unmodified lines
162163 inputType: typeSignatureFromSchemaJson(
163164 input.artifact.inputSchemaJson,
164165 "unknown",
165− 320,
166+ Infinity,
166167 ),
167168 }
168169 : {}),
169− ...(input.artifact.outputSchemaJson
170+ ...(input.artifact.providerKind === "openapi"
170171 ? {
171− outputType: typeSignatureFromSchemaJson(
172− input.artifact.outputSchemaJson,
173− "unknown",
174− 320,
172+ outputType: openApiOutputTypeSignatureFromSchemaJson(
173+ input.artifact.outputSchemaJson ?? undefined,
174+ Infinity,
175175 ),
176176 }
177− : {}),
177+ : input.artifact.outputSchemaJson
178+ ? {
179+ outputType: typeSignatureFromSchemaJson(
180+ input.artifact.outputSchemaJson,
181+ "unknown",
182+ Infinity,
183+ ),
184+ }
185+ : {}),
178186 });
179187
180188 const openApiToolRecord = (input: {
packages/control-plane/src/runtime/workspace-execution-environment.ts+12−5
@@ -19,6 +19,7 @@ import { · 18 unmodified lines
1919 compileOpenApiToolDefinitions,
2020 createOpenApiToolsFromManifest,
2121 extractOpenApiManifest,
22+ openApiOutputTypeSignatureFromSchemaJson,
2223 type OpenApiToolDefinition,
2324 type OpenApiToolManifest,
2425 } from "@executor-v3/codemode-openapi";
@@ -232,11 +233,17 @@ const toPersistedDescriptor = (input: { · 207 unmodified lines
232233 "unknown",
233234 320,
234235 ),
235− outputType: typeSignatureFromSchemaJson(
236− input.artifact.outputSchemaJson ?? undefined,
237− "unknown",
238− 320,
239− ),
236+ outputType:
237+ input.artifact.providerKind === "openapi"
238+ ? openApiOutputTypeSignatureFromSchemaJson(
239+ input.artifact.outputSchemaJson ?? undefined,
240+ 320,
241+ )
242+ : typeSignatureFromSchemaJson(
243+ input.artifact.outputSchemaJson ?? undefined,
244+ "unknown",
245+ 320,
246+ ),
240247 inputSchemaJson: input.includeSchemas ? input.artifact.inputSchemaJson ?? undefined : undefined,
241248 outputSchemaJson: input.includeSchemas ? input.artifact.outputSchemaJson ?? undefined : undefined,
242249 ...(input.artifact.providerKind ? { providerKind: input.artifact.providerKind } : {}),
packages/react/src/index.ts+37−1
@@ -1,5 +1,5 @@
11 import { Atom, Result } from "@effect-atom/atom";
2−import { RegistryProvider, useAtomValue } from "@effect-atom/atom-react";
2+import { RegistryContext, RegistryProvider, useAtomValue } from "@effect-atom/atom-react";
33 import type {
44 LocalInstallation,
55 Source,
@@ -315,6 +315,42 @@ export const useSourceDiscovery = (input: { · 309 unmodified lines
315315 return workspace.enabled ? results : pendingLoadable(workspace.workspace);
316316 };
317317
318+// ---------------------------------------------------------------------------
319+// Prefetching
320+// ---------------------------------------------------------------------------
321+
322+/**
323+ * Returns a function that imperatively triggers a fetch for a tool detail.
324+ * Because the atom family uses `Atom.keepAlive`, the fetched result persists
325+ * in the registry. When the user later selects the tool, `useSourceToolDetail`
326+ * gets an instant cache hit.
327+ *
328+ * Usage: call the returned function on pointer-enter with a short delay.
329+ * It returns a cleanup function (unmounts the atom subscription).
330+ */
331+export const usePrefetchToolDetail = () => {
332+ const registry = React.useContext(RegistryContext);
333+ const workspace = useWorkspaceRequestContext();
334+
335+ return React.useCallback(
336+ (sourceId: string, toolPath: string): (() => void) => {
337+ if (!workspace.enabled) return () => {};
338+ const requestedSourceId = sourceId as Source["id"];
339+ const atom = sourceInspectionToolAtom(
340+ encodeAtomKey([
341+ workspace.enabled,
342+ workspace.workspaceId,
343+ workspace.accountId,
344+ requestedSourceId,
345+ toolPath,
346+ ]),
347+ );
348+ // mount triggers the fetch; returns a dispose function
349+ return registry.mount(atom);
350+ },
351+ [registry, workspace.enabled, workspace.workspaceId, workspace.accountId],
352+ );
353+};
318354
319355 export type {
320356 LocalInstallation,