deepspace 0.10.1 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/chunk-T7V4GUIN.js +1638 -0
  3. package/dist/chunk-T7V4GUIN.js.map +1 -0
  4. package/dist/cli.js +731 -324
  5. package/dist/cli.js.map +1 -1
  6. package/dist/documentation-client-core.d.ts +18 -0
  7. package/dist/documentation-client-core.js +395 -0
  8. package/dist/documentation-client-core.js.map +1 -0
  9. package/dist/documentation-default-renderer.cjs +124 -0
  10. package/dist/documentation-react.d.ts +2 -0
  11. package/dist/documentation-react.js +35 -0
  12. package/dist/documentation-react.js.map +1 -0
  13. package/dist/documentation-runtime.js +71 -0
  14. package/dist/documentation-server-core.d.ts +11 -0
  15. package/dist/documentation-server-core.js +48 -0
  16. package/dist/documentation-server-core.js.map +1 -0
  17. package/dist/documentation.d.ts +366 -0
  18. package/dist/documentation.js +3953 -0
  19. package/dist/documentation.js.map +1 -0
  20. package/dist/feature-installer.js +145 -0
  21. package/dist/feature-installer.js.map +1 -1
  22. package/dist/index.d.ts +177 -1
  23. package/dist/index.js +133 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/public-BGRX_Cjd.d.ts +234 -0
  26. package/dist/schema.d.ts +186 -0
  27. package/dist/schema.js +610 -0
  28. package/dist/schema.js.map +1 -0
  29. package/dist/server.d.ts +83 -4
  30. package/dist/server.js +296 -1
  31. package/dist/server.js.map +1 -1
  32. package/dist/testing.d.ts +5 -8
  33. package/dist/testing.js +48 -14
  34. package/dist/testing.js.map +1 -1
  35. package/dist/worker.d.ts +249 -5
  36. package/dist/worker.js +1438 -19
  37. package/dist/worker.js.map +1 -1
  38. package/features/admin-page/src/admin-schema.ts +1 -1
  39. package/features/ai-chat/feature.json +2 -2
  40. package/features/ai-chat/src/ChatPanel.tsx +6 -11
  41. package/features/ai-chat/src/ai-chat-schema.ts +1 -2
  42. package/features/canvas/src/canvas-schema.ts +1 -1
  43. package/features/documentation/content/index.mdx +14 -0
  44. package/features/documentation/content/quickstart.mdx +12 -0
  45. package/features/documentation/documentation.json +18 -0
  46. package/features/documentation/feature.json +68 -0
  47. package/features/{docs → documents}/feature.json +27 -27
  48. package/features/{docs/src/DocsEditorPage.tsx → documents/src/DocumentsEditorPage.tsx} +74 -74
  49. package/features/{docs/src/DocsListPage.tsx → documents/src/DocumentsListPage.tsx} +102 -102
  50. package/features/{docs/src/DocsOutlinePanel.tsx → documents/src/DocumentsOutlinePanel.tsx} +9 -9
  51. package/features/{docs/src/DocsPresence.tsx → documents/src/DocumentsPresence.tsx} +18 -18
  52. package/features/{docs → documents}/src/InviteDialog.tsx +21 -21
  53. package/features/{docs → documents}/src/LibrarySidebar.tsx +25 -25
  54. package/features/documents/src/__tests__/documents-presence-access.test.ts +71 -0
  55. package/features/{docs/src/docs-library-types.ts → documents/src/documents-library-types.ts} +3 -3
  56. package/features/{docs/src/docs-schema.ts → documents/src/documents-schema.ts} +6 -6
  57. package/features/{docs/src/docs-ui.css → documents/src/documents-ui.css} +89 -89
  58. package/features/{docs → documents}/src/editor/DocEditorSurface.tsx +11 -11
  59. package/features/{docs/src/editor/DocsTiptapToolbar.tsx → documents/src/editor/DocumentsTiptapToolbar.tsx} +21 -21
  60. package/features/{docs → documents}/src/editor/useDocEditor.ts +4 -4
  61. package/features/{docs/src/use-docs-presence-access.ts → documents/src/use-documents-presence-access.ts} +51 -51
  62. package/features/file-attachments/src/attachments-schema.ts +1 -1
  63. package/features/items/src/items-schema.ts +1 -1
  64. package/features/leaderboard/src/leaderboard-schema.ts +1 -1
  65. package/features/messaging/src/ChatMultiPage.tsx +1 -1
  66. package/features/messaging/src/ChatPage.tsx +1 -1
  67. package/features/messaging/src/messaging-schema.ts +2 -2
  68. package/features/tasks/src/tasks-schema.ts +1 -1
  69. package/features/testing/src/test-schema.ts +2 -2
  70. package/package.json +26 -1
  71. package/features/docs/src/__tests__/docs-presence-access.test.ts +0 -71
@@ -0,0 +1,3953 @@
1
+ // src/documentation/build.ts
2
+ import { buildSync as buildSync3 } from "esbuild";
3
+ import { readFileSync as readFileSync6 } from "fs";
4
+ import { resolve as resolve7 } from "path";
5
+
6
+ // src/documentation/markdown.ts
7
+ import hljs from "highlight.js/lib/core";
8
+ import bash from "highlight.js/lib/languages/bash";
9
+ import css from "highlight.js/lib/languages/css";
10
+ import javascript from "highlight.js/lib/languages/javascript";
11
+ import json from "highlight.js/lib/languages/json";
12
+ import markdown from "highlight.js/lib/languages/markdown";
13
+ import typescript from "highlight.js/lib/languages/typescript";
14
+ import xml from "highlight.js/lib/languages/xml";
15
+ import yaml from "highlight.js/lib/languages/yaml";
16
+ import { Marked } from "marked";
17
+ import sanitizeHtml from "sanitize-html";
18
+ import { parse as parseYaml } from "yaml";
19
+
20
+ // src/documentation/text.ts
21
+ function errorMessage(error) {
22
+ return error instanceof Error ? error.message : String(error);
23
+ }
24
+ function escapeHtml(value) {
25
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
26
+ }
27
+ function escapeXml(value) {
28
+ return escapeHtml(value).replace(/&#39;/g, "&apos;");
29
+ }
30
+ function slugify(value) {
31
+ return value.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
32
+ }
33
+ function documentationSubject(name) {
34
+ const normalized = name.trim();
35
+ if (!normalized || /^documentation$/i.test(normalized)) return "documentation";
36
+ return /(?:\bdocs?|documentation)$/i.test(normalized) ? normalized : `${normalized} documentation`;
37
+ }
38
+ function createSlugger(fallback = "section") {
39
+ const seen = /* @__PURE__ */ new Map();
40
+ return (value) => {
41
+ const base = slugify(value) || fallback;
42
+ const count = seen.get(base) ?? 0;
43
+ seen.set(base, count + 1);
44
+ return count === 0 ? base : `${base}-${count + 1}`;
45
+ };
46
+ }
47
+
48
+ // src/documentation/types.ts
49
+ var DOCUMENTATION_CONFIG_FILE = "documentation.json";
50
+ var DOCUMENTATION_MANIFEST_VERSION = 2;
51
+ var DOCUMENTATION_CONTEXTUAL_ACTIONS = [
52
+ "copy",
53
+ "view",
54
+ "assistant",
55
+ "chatgpt",
56
+ "claude",
57
+ "mcp",
58
+ "add-mcp",
59
+ "cursor",
60
+ "vscode"
61
+ ];
62
+ var DEFAULT_DOCUMENTATION_CONTEXTUAL_ACTIONS = DOCUMENTATION_CONTEXTUAL_ACTIONS;
63
+ var DOCUMENTATION_OPENAPI_SAMPLE_LANGUAGES = ["curl", "javascript", "python", "go"];
64
+ var DEFAULT_DOCUMENTATION_OPENAPI_EXAMPLES = {
65
+ languages: ["curl", "python", "javascript", "go"],
66
+ defaults: "all",
67
+ autogenerate: true
68
+ };
69
+ var DocumentationError = class extends Error {
70
+ code;
71
+ diagnostics;
72
+ constructor(message, code, diagnostics = []) {
73
+ super(message);
74
+ this.name = "DocumentationError";
75
+ this.code = code;
76
+ this.diagnostics = diagnostics;
77
+ }
78
+ };
79
+
80
+ // src/documentation/markdown.ts
81
+ var COMPONENT_NAMES = [
82
+ "Note",
83
+ "Tip",
84
+ "Warning",
85
+ "Info",
86
+ "Card",
87
+ "Accordion",
88
+ "AccordionGroup",
89
+ "Step",
90
+ "Tab",
91
+ "CodeGroup",
92
+ "Steps",
93
+ "Tabs"
94
+ ];
95
+ var LEAF_COMPONENTS = [
96
+ "Note",
97
+ "Tip",
98
+ "Warning",
99
+ "Info",
100
+ "Card",
101
+ "Accordion",
102
+ "Step",
103
+ "Tab",
104
+ "CodeGroup"
105
+ ];
106
+ var WRAPPER_COMPONENTS = ["AccordionGroup", "Steps", "Tabs"];
107
+ for (const [name, language] of Object.entries({
108
+ bash,
109
+ css,
110
+ html: xml,
111
+ javascript,
112
+ json,
113
+ jsx: javascript,
114
+ markdown,
115
+ md: markdown,
116
+ shell: bash,
117
+ sh: bash,
118
+ ts: typescript,
119
+ tsx: typescript,
120
+ typescript,
121
+ xml,
122
+ yaml,
123
+ yml: yaml
124
+ })) {
125
+ hljs.registerLanguage(name, language);
126
+ }
127
+ var markdownRenderer = new Marked({
128
+ renderer: {
129
+ code({ text, lang }) {
130
+ const { language, title } = parseCodeFenceInfo(lang);
131
+ const highlighted = language && hljs.getLanguage(language) ? hljs.highlight(text, { language, ignoreIllegals: true }).value : escapeHtml(text);
132
+ const className = language ? ` class="language-${escapeHtml(language)} hljs"` : ' class="hljs"';
133
+ return `<pre${title ? ` data-code-title="${escapeHtml(title)}"` : ""}><code${className}>${highlighted}</code></pre>
134
+ `;
135
+ }
136
+ }
137
+ });
138
+ function stripFirstMarkdownH1(source) {
139
+ const lines = [...source.matchAll(/.*(?:\r?\n|$)/g)].filter((match) => match[0]);
140
+ let inFrontmatter = false;
141
+ let frontmatterSeen = false;
142
+ let fence;
143
+ for (const [index, match] of lines.entries()) {
144
+ const raw = match[0];
145
+ const line = raw.replace(/\r?\n$/, "");
146
+ const content = index === 0 ? line.replace(/^\uFEFF/, "") : line;
147
+ if (index === 0 && content === "---") {
148
+ inFrontmatter = true;
149
+ frontmatterSeen = true;
150
+ continue;
151
+ }
152
+ if (inFrontmatter) {
153
+ if (frontmatterSeen && /^(?:---|\.\.\.)\s*$/.test(content)) inFrontmatter = false;
154
+ continue;
155
+ }
156
+ const fenceMatch = content.match(/^ {0,3}(`{3,}|~{3,})/);
157
+ if (fenceMatch?.[1]) {
158
+ const marker = fenceMatch[1][0];
159
+ if (!fence) fence = { marker, length: fenceMatch[1].length };
160
+ else if (fence.marker === marker && fenceMatch[1].length >= fence.length) fence = void 0;
161
+ continue;
162
+ }
163
+ if (fence || !/^ {0,3}#(?:[\t ]+|$)/.test(content)) continue;
164
+ const start = match.index ?? 0;
165
+ const newline = raw.endsWith("\r\n") ? "\r\n" : raw.endsWith("\n") ? "\n" : "";
166
+ return `${source.slice(0, start)}${newline}${source.slice(start + raw.length)}`;
167
+ }
168
+ return source;
169
+ }
170
+ function parseMarkdown(source, file) {
171
+ const { frontmatter, body } = parseFrontmatter(source, file);
172
+ validateExecutableMdx(body, file);
173
+ const placeholders = [];
174
+ let prepared = protectCode(body.replace(/^\uFEFF/, ""), placeholders);
175
+ for (const name of [...LEAF_COMPONENTS, ...WRAPPER_COMPONENTS]) {
176
+ prepared = replaceComponentBlocks(prepared, name, placeholders);
177
+ }
178
+ const rendered = String(markdownRenderer.parse(prepared, { gfm: true, breaks: false }));
179
+ let restored = rendered;
180
+ for (let pass = 0; pass < placeholders.length + 1; pass++) {
181
+ let changed = false;
182
+ restored = restored.replace(
183
+ /(?:<p>)?@@DEEPSPACE_COMPONENT_(\d+)@@(?:<\/p>)?/g,
184
+ (_match, indexRaw) => {
185
+ const replacement = placeholders[Number(indexRaw)];
186
+ if (replacement === void 0) return "";
187
+ changed = true;
188
+ return replacement;
189
+ }
190
+ );
191
+ if (!changed) break;
192
+ }
193
+ restored = restored.replace(
194
+ /<div class="documentation-code-group" data-code-group-pending data-code-group-title="([^"]*)">([\s\S]*?)<\/div>/g,
195
+ (_match, title, innerHtml) => renderCodeGroup(innerHtml, title)
196
+ );
197
+ const safe = sanitizeHtml(restored, {
198
+ allowedTags: [
199
+ ...sanitizeHtml.defaults.allowedTags,
200
+ "article",
201
+ "aside",
202
+ "button",
203
+ "details",
204
+ "div",
205
+ "figure",
206
+ "figcaption",
207
+ "h1",
208
+ "h2",
209
+ "h3",
210
+ "h4",
211
+ "h5",
212
+ "h6",
213
+ "input",
214
+ "label",
215
+ "section",
216
+ "span",
217
+ "summary",
218
+ "table",
219
+ "thead",
220
+ "tbody",
221
+ "tr",
222
+ "th",
223
+ "td"
224
+ ],
225
+ allowedAttributes: {
226
+ a: ["href", "title", "target", "rel", "class"],
227
+ aside: ["class"],
228
+ button: ["type", "class", "data-copy-code"],
229
+ code: ["class"],
230
+ details: ["class", "open"],
231
+ div: ["class", "data-tab-group", "data-tab-persist"],
232
+ img: ["src", "alt", "title", "width", "height", "loading"],
233
+ input: ["type", "checked", "disabled"],
234
+ pre: ["class", "data-code-title"],
235
+ section: ["class", "data-tab", "data-tab-title"],
236
+ span: ["class"],
237
+ summary: ["class"],
238
+ table: ["class"],
239
+ "*": ["id", "aria-label", "aria-hidden", "role"]
240
+ },
241
+ allowedSchemes: ["http", "https", "mailto"],
242
+ allowProtocolRelative: false,
243
+ disallowedTagsMode: "escape",
244
+ transformTags: {
245
+ a: (_tagName, attributes) => {
246
+ const external = /^https?:\/\//i.test(attributes.href ?? "");
247
+ return {
248
+ tagName: "a",
249
+ attribs: external ? { ...attributes, target: "_blank", rel: "noopener noreferrer" } : attributes
250
+ };
251
+ },
252
+ img: (_tagName, attributes) => ({
253
+ tagName: "img",
254
+ attribs: { ...attributes, loading: attributes.loading ?? "lazy" }
255
+ })
256
+ }
257
+ });
258
+ const { html, headings } = addHeadingIds(safe);
259
+ return {
260
+ frontmatter,
261
+ markdown: body.trim(),
262
+ html,
263
+ text: htmlToText(html),
264
+ headings
265
+ };
266
+ }
267
+ function parseFrontmatter(source, file) {
268
+ if (!source.startsWith("---\n") && !source.startsWith("---\r\n")) {
269
+ return { frontmatter: {}, body: source };
270
+ }
271
+ const normalized = source.replace(/\r\n/g, "\n");
272
+ const end = normalized.indexOf("\n---\n", 4);
273
+ if (end === -1) {
274
+ throw new DocumentationError(
275
+ `Unclosed front matter in ${file}`,
276
+ "documentation_frontmatter_invalid",
277
+ [{ code: "frontmatter_unclosed", message: "Expected a closing --- line", file, line: 1 }]
278
+ );
279
+ }
280
+ let value;
281
+ try {
282
+ value = parseYaml(normalized.slice(4, end), { maxAliasCount: 20 });
283
+ } catch (error) {
284
+ throw new DocumentationError(
285
+ `Invalid front matter in ${file}: ${errorMessage(error)}`,
286
+ "documentation_frontmatter_invalid",
287
+ [{ code: "frontmatter_yaml", message: errorMessage(error), file, line: 1 }]
288
+ );
289
+ }
290
+ if (value === null) value = {};
291
+ if (typeof value !== "object" || Array.isArray(value)) {
292
+ throw new DocumentationError(
293
+ `Front matter must be a mapping in ${file}`,
294
+ "documentation_frontmatter_invalid",
295
+ [{ code: "frontmatter_shape", message: "Expected key/value front matter", file, line: 1 }]
296
+ );
297
+ }
298
+ const raw = value;
299
+ const frontmatter = {};
300
+ for (const key of ["title", "description", "slug"]) {
301
+ if (raw[key] !== void 0 && typeof raw[key] !== "string") {
302
+ throw frontmatterTypeError(file, key, "string");
303
+ }
304
+ if (typeof raw[key] === "string") frontmatter[key] = raw[key];
305
+ }
306
+ for (const key of ["hidden", "noindex"]) {
307
+ if (raw[key] !== void 0 && typeof raw[key] !== "boolean") {
308
+ throw frontmatterTypeError(file, key, "boolean");
309
+ }
310
+ if (typeof raw[key] === "boolean") frontmatter[key] = raw[key];
311
+ }
312
+ return { frontmatter, body: normalized.slice(end + 5) };
313
+ }
314
+ function replaceComponentBlocks(source, name, placeholders) {
315
+ const pattern = new RegExp(`<${name}(\\s[^>]*)?>([\\s\\S]*?)<\\/${name}>`, "g");
316
+ return source.replace(pattern, (_match, attributesRaw, inner) => {
317
+ const attributes = parseAttributes(attributesRaw ?? "");
318
+ const innerHtml = String(markdownRenderer.parse(inner.trim(), { gfm: true, breaks: false }));
319
+ const html = renderComponent(name, attributes, innerHtml);
320
+ const index = placeholders.push(html) - 1;
321
+ return `
322
+
323
+ @@DEEPSPACE_COMPONENT_${index}@@
324
+
325
+ `;
326
+ });
327
+ }
328
+ function renderComponent(name, attributes, innerHtml) {
329
+ const title = escapeHtml(attributes.title ?? attributes.label ?? "");
330
+ switch (name) {
331
+ case "Note":
332
+ case "Tip":
333
+ case "Warning":
334
+ case "Info":
335
+ return `<aside class="documentation-callout documentation-callout-${name.toLowerCase()}">${title ? `<strong>${title}</strong>` : `<strong>${name}</strong>`}${innerHtml}</aside>`;
336
+ case "Card": {
337
+ const href = safeHref(attributes.href);
338
+ const content = `${title ? `<strong>${title}</strong>` : ""}${innerHtml}`;
339
+ return href ? `<a class="documentation-card" href="${escapeHtml(href)}">${content}<span aria-hidden="true">\u2192</span></a>` : `<div class="documentation-card">${content}</div>`;
340
+ }
341
+ case "Accordion":
342
+ return `<details class="documentation-accordion"><summary>${title || "Details"}</summary>${innerHtml}</details>`;
343
+ case "AccordionGroup":
344
+ return `<div class="documentation-accordion-group">${innerHtml}</div>`;
345
+ case "Step":
346
+ return `<section class="documentation-step">${title ? `<h3>${title}</h3>` : ""}${innerHtml}</section>`;
347
+ case "Steps":
348
+ return `<div class="documentation-steps">${innerHtml}</div>`;
349
+ case "Tab":
350
+ return `<section class="documentation-tab" data-tab data-tab-title="${title || "Tab"}">${innerHtml}</section>`;
351
+ case "Tabs":
352
+ return `<div class="documentation-tabs" data-tab-group>${innerHtml}</div>`;
353
+ case "CodeGroup":
354
+ return `<div class="documentation-code-group" data-code-group-pending data-code-group-title="${title}">${innerHtml}</div>`;
355
+ }
356
+ }
357
+ function renderCodeGroup(innerHtml, title) {
358
+ const blocks = [...innerHtml.matchAll(/<pre([^>]*)>([\s\S]*?)<\/pre>/g)];
359
+ if (blocks.length === 0) {
360
+ return `<div class="documentation-code-group">${title ? `<div class="documentation-code-group-title">${title}</div>` : ""}${innerHtml}</div>`;
361
+ }
362
+ const tabs = blocks.map((match, index) => {
363
+ const attributes = match[1] ?? "";
364
+ const explicit = attributes.match(/\sdata-code-title="([^"]+)"/)?.[1];
365
+ const language = match[2]?.match(/class="language-([^\s"]+)/)?.[1];
366
+ const label = explicit ?? codeLanguageLabel(language) ?? `Example ${index + 1}`;
367
+ return `<section class="documentation-code-tab" data-tab data-tab-title="${escapeHtml(label)}"><pre${attributes}>${match[2]}</pre></section>`;
368
+ }).join("");
369
+ return `<div class="documentation-code-group" data-tab-group data-tab-persist="code">${title ? `<div class="documentation-code-group-title">${title}</div>` : ""}${tabs}</div>`;
370
+ }
371
+ function parseCodeFenceInfo(value) {
372
+ const raw = value?.trim() ?? "";
373
+ if (!raw) return {};
374
+ const language = raw.split(/\s+/, 1)[0]?.toLowerCase();
375
+ const metadata = raw.slice(language?.length ?? 0).trim();
376
+ const quotedTitle = metadata.match(/(?:^|\s)title=(?:"([^"]+)"|'([^']+)')/i);
377
+ const title = quotedTitle?.[1] ?? quotedTitle?.[2] ?? (metadata && !metadata.includes("=") ? metadata : void 0);
378
+ return {
379
+ ...language ? { language } : {},
380
+ ...title ? { title: title.trim() } : {}
381
+ };
382
+ }
383
+ function codeLanguageLabel(language) {
384
+ if (!language) return void 0;
385
+ const names = {
386
+ bash: "Shell",
387
+ javascript: "JavaScript",
388
+ js: "JavaScript",
389
+ jsx: "React",
390
+ shell: "Shell",
391
+ sh: "Shell",
392
+ ts: "TypeScript",
393
+ tsx: "React",
394
+ typescript: "TypeScript"
395
+ };
396
+ return names[language] ?? language.toUpperCase();
397
+ }
398
+ function parseAttributes(raw) {
399
+ const attributes = {};
400
+ const pattern = /([A-Za-z][A-Za-z0-9_-]*)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
401
+ for (const match of raw.matchAll(pattern)) {
402
+ attributes[match[1]] = match[2] ?? match[3] ?? "";
403
+ }
404
+ return attributes;
405
+ }
406
+ function validateExecutableMdx(body, file) {
407
+ const validationBody = stripCodeForValidation(body);
408
+ const lines = validationBody.split("\n");
409
+ for (let index = 0; index < lines.length; index++) {
410
+ const line = lines[index];
411
+ if (/^\s*(?:import|export)\s/.test(line)) {
412
+ throw new DocumentationError(
413
+ `Executable MDX is not supported in ${file}:${index + 1}`,
414
+ "documentation_mdx_unsafe",
415
+ [{
416
+ code: "mdx_module_syntax",
417
+ message: "Use the built-in documentation component vocabulary instead of import/export",
418
+ file,
419
+ line: index + 1
420
+ }]
421
+ );
422
+ }
423
+ if (/<[A-Za-z][^>]*\son[A-Za-z]+\s*=/.test(line) || /<script\b/i.test(line)) {
424
+ throw new DocumentationError(
425
+ `Active HTML is not supported in ${file}:${index + 1}`,
426
+ "documentation_mdx_unsafe",
427
+ [{
428
+ code: "active_html",
429
+ message: "Scripts and inline event handlers are prohibited",
430
+ file,
431
+ line: index + 1
432
+ }]
433
+ );
434
+ }
435
+ if (/<[A-Z][A-Za-z0-9]*[^>]*=\s*\{/.test(line)) {
436
+ throw new DocumentationError(
437
+ `JSX expressions are not supported in ${file}:${index + 1}`,
438
+ "documentation_mdx_unsafe",
439
+ [{
440
+ code: "jsx_expression",
441
+ message: "Component attributes must use quoted string values",
442
+ file,
443
+ line: index + 1
444
+ }]
445
+ );
446
+ }
447
+ }
448
+ const componentPattern = /<\/?([A-Z][A-Za-z0-9]*)\b/g;
449
+ for (const match of validationBody.matchAll(componentPattern)) {
450
+ if (!COMPONENT_NAMES.includes(match[1])) {
451
+ const line = validationBody.slice(0, match.index).split("\n").length;
452
+ throw new DocumentationError(
453
+ `Unknown documentation component <${match[1]}> in ${file}:${line}`,
454
+ "documentation_mdx_component_unknown",
455
+ [{
456
+ code: "unknown_component",
457
+ message: `Supported components: ${COMPONENT_NAMES.join(", ")}`,
458
+ file,
459
+ line
460
+ }]
461
+ );
462
+ }
463
+ }
464
+ }
465
+ function protectCode(source, placeholders) {
466
+ const fenced = source.replace(
467
+ /^([ \t]*)(`{3,}|~{3,})[^\n]*\n[\s\S]*?^\1\2[ \t]*(?=\n|$)/gm,
468
+ (block, indentation) => {
469
+ const normalized = indentation ? block.split("\n").map((line) => line.startsWith(indentation) ? line.slice(indentation.length) : line).join("\n") : block;
470
+ const html = String(markdownRenderer.parse(normalized, { gfm: true, breaks: false }));
471
+ const index = placeholders.push(html) - 1;
472
+ return `
473
+
474
+ @@DEEPSPACE_COMPONENT_${index}@@
475
+
476
+ `;
477
+ }
478
+ );
479
+ return fenced.replace(/`([^`\n]+)`/g, (_match, code) => {
480
+ const index = placeholders.push(`<code>${escapeHtml(code)}</code>`) - 1;
481
+ return `@@DEEPSPACE_COMPONENT_${index}@@`;
482
+ });
483
+ }
484
+ function stripCodeForValidation(source) {
485
+ return source.replace(
486
+ /^([ \t]*)(`{3,}|~{3,})[^\n]*\n[\s\S]*?^\1\2[ \t]*(?=\n|$)/gm,
487
+ (block) => block.replace(/[^\n]/g, " ")
488
+ ).replace(/`[^`\n]+`/g, (code) => " ".repeat(code.length));
489
+ }
490
+ function addHeadingIds(html) {
491
+ const headings = [];
492
+ const slug = createSlugger();
493
+ const output = html.replace(/<h([1-6])>([\s\S]*?)<\/h\1>/g, (_match, depthRaw, content) => {
494
+ const text = htmlToText(content);
495
+ const id = slug(text);
496
+ const depth = Number(depthRaw);
497
+ headings.push({ depth, id, text });
498
+ return `<h${depth} id="${id}">${content}<a class="documentation-heading-anchor" href="#${id}" aria-label="Link to ${escapeHtml(text)}">#</a></h${depth}>`;
499
+ });
500
+ return { html: output, headings };
501
+ }
502
+ function htmlToText(html) {
503
+ return decodeEntities(
504
+ html.replace(/<pre[\s\S]*?<\/pre>/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim()
505
+ );
506
+ }
507
+ function decodeEntities(value) {
508
+ return value.replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'");
509
+ }
510
+ function safeHref(value) {
511
+ if (!value) return "";
512
+ if (/^(?:https?:|mailto:|\/|#)/i.test(value)) return value;
513
+ return "";
514
+ }
515
+ function frontmatterTypeError(file, key, type) {
516
+ return new DocumentationError(
517
+ `Front matter ${key} must be a ${type} in ${file}`,
518
+ "documentation_frontmatter_invalid",
519
+ [{ code: "frontmatter_type", message: `${key} must be a ${type}`, file, line: 1 }]
520
+ );
521
+ }
522
+
523
+ // src/documentation/routing.ts
524
+ function collapseRoute(value) {
525
+ let route = `/${value}`.replace(/\/{2,}/g, "/");
526
+ if (route.length > 1) route = route.replace(/\/$/, "");
527
+ return route;
528
+ }
529
+ function normalizeRoute(value) {
530
+ assertSafeRoute(value);
531
+ let route = collapseRoute(value);
532
+ route = route.replace(/\.(?:md|mdx)$/i, "");
533
+ route = route.replace(/\/index$/i, "");
534
+ return route || "/";
535
+ }
536
+ function routeFromRelativePath(relativePath) {
537
+ let path = relativePath.replace(/\.(?:md|mdx)$/i, "");
538
+ if (path === "index") path = "";
539
+ else path = path.replace(/\/index$/i, "");
540
+ return normalizeRoute(path);
541
+ }
542
+ function normalizeDocumentationBasePath(value) {
543
+ if (!value || value === "/") return "";
544
+ const normalized = normalizeRoute(value);
545
+ if (normalized === "/_documentation") {
546
+ throw new DocumentationError("The internal /_documentation namespace cannot be a public documentation base", "documentation_base_invalid");
547
+ }
548
+ return normalized;
549
+ }
550
+ function documentationPublicPath(basePath, path) {
551
+ const base = normalizeDocumentationBasePath(basePath);
552
+ const suffix = path.startsWith("/") ? path : `/${path}`;
553
+ if (suffix === "/") return base || "/";
554
+ return `${base}${suffix}`;
555
+ }
556
+ function publicDocumentationReferences(source, basePath) {
557
+ const base = normalizeDocumentationBasePath(basePath);
558
+ if (!base) return source;
559
+ const publicPath = (value) => value === base || value.startsWith(`${base}/`) ? value : `${base}${value}`;
560
+ return source.replace(
561
+ /(\]\()(\/(?!\/)[^)\s]*)/g,
562
+ (_match, prefix, value) => `${prefix}${publicPath(value)}`
563
+ ).replace(
564
+ /^(\s*\[[^\]]+\]:\s*)(\/(?!\/)\S*)/gm,
565
+ (_match, prefix, value) => `${prefix}${publicPath(value)}`
566
+ ).replace(
567
+ /(\b(?:href|src)=["'])(\/(?!\/)[^"']*)/g,
568
+ (_match, prefix, value) => `${prefix}${publicPath(value)}`
569
+ );
570
+ }
571
+ function markdownUrlForRoute(route, basePath = "") {
572
+ const logical = route === "/" ? "/index.md" : `${normalizeRoute(route)}.md`;
573
+ return documentationPublicPath(basePath, logical);
574
+ }
575
+ function artifactPathsForRoute(route) {
576
+ const clean = normalizeRoute(route).replace(/^\//, "");
577
+ return clean ? {
578
+ data: `data/${clean}.json`,
579
+ html: `${clean}/index.html`,
580
+ markdown: `${clean}.md`
581
+ } : {
582
+ data: "data/index.json",
583
+ html: "index.html",
584
+ markdown: "index.md"
585
+ };
586
+ }
587
+ function routeArtifactId(route) {
588
+ return route === "/" ? "index" : normalizeRoute(route).replace(/^\//, "").replace(/[^a-z0-9]+/gi, "-");
589
+ }
590
+ function joinSiteUrl(base, route) {
591
+ const path = route.startsWith("/") ? route : `/${route}`;
592
+ return `${base.replace(/\/$/, "")}${path}`;
593
+ }
594
+ function assertSafeRoute(value) {
595
+ if (value.includes("\\") || value.includes("\0")) throw unsafeRoute(value);
596
+ let decoded = value;
597
+ for (let pass = 0; pass < 5; pass++) {
598
+ let next;
599
+ try {
600
+ next = decodeURIComponent(decoded);
601
+ } catch {
602
+ throw unsafeRoute(value);
603
+ }
604
+ if (next === decoded) break;
605
+ decoded = next;
606
+ }
607
+ if (decoded.includes("\\") || decoded.includes("\0") || decoded.split("/").some((segment) => segment === "." || segment === "..")) throw unsafeRoute(value);
608
+ }
609
+ function unsafeRoute(value) {
610
+ return new DocumentationError(
611
+ `Documentation route contains an unsafe path segment: ${value}`,
612
+ "documentation_route_invalid",
613
+ [{ code: "route_path_escape", message: value }]
614
+ );
615
+ }
616
+
617
+ // src/documentation/artifacts.ts
618
+ function pageMarkdown(page, basePath = "/docs") {
619
+ const title = `# ${page.title}`;
620
+ const body = publicDocumentationReferences(page.markdown, basePath).trim();
621
+ return `${title}
622
+ ${page.description ? `
623
+ ${page.description}
624
+ ` : ""}${body ? `
625
+ ${body}
626
+ ` : "\n"}`;
627
+ }
628
+ function createSearchEntries(pages) {
629
+ return pages.filter((page) => !page.hidden).map((page) => ({
630
+ route: page.route,
631
+ title: page.title,
632
+ ...page.description ? { description: page.description } : {},
633
+ headings: page.headings.filter((heading) => heading.depth > 1).map((heading) => heading.text),
634
+ text: page.text.slice(0, 12e3)
635
+ }));
636
+ }
637
+ function createAssistantChunks(pages) {
638
+ return pages.flatMap((page) => {
639
+ if (page.hidden) return [];
640
+ const chunks = [];
641
+ let index = 0;
642
+ const sections = page.html.split(/(?=<h[23]\b)/);
643
+ for (const section of sections) {
644
+ const headingMatch = section.match(/^<h[23] id="([^"]+)">/);
645
+ const heading = headingMatch ? page.headings.find((candidate) => candidate.id === headingMatch[1]) : void 0;
646
+ const route = heading ? `${page.route}#${heading.id}` : page.route;
647
+ const paragraphs = htmlToText(section).split(/(?<=[.!?])\s+(?=[A-Z0-9])/);
648
+ let current = "";
649
+ const flush = () => {
650
+ const text = current.trim();
651
+ if (!text) return;
652
+ chunks.push({
653
+ id: `${routeArtifactId(page.route)}:${index++}`,
654
+ route,
655
+ title: page.title,
656
+ ...heading ? { heading: heading.text } : {},
657
+ text
658
+ });
659
+ current = "";
660
+ };
661
+ for (const paragraph of paragraphs) {
662
+ if (current.length + paragraph.length > 1800) flush();
663
+ current += `${current ? " " : ""}${paragraph}`;
664
+ }
665
+ flush();
666
+ }
667
+ return chunks;
668
+ });
669
+ }
670
+ function pagesInNavigationOrder(pages, navigation) {
671
+ const byRoute = new Map(pages.map((page) => [page.route, page]));
672
+ const ordered = [];
673
+ const seen = /* @__PURE__ */ new Set();
674
+ const visit2 = (nodes) => {
675
+ for (const node of nodes) {
676
+ if (node.kind === "group") {
677
+ visit2(node.items);
678
+ continue;
679
+ }
680
+ if (node.kind !== "page" || seen.has(node.route)) continue;
681
+ const page = byRoute.get(node.route);
682
+ if (page) ordered.push(page);
683
+ seen.add(node.route);
684
+ }
685
+ };
686
+ visit2(navigation);
687
+ for (const page of pages) {
688
+ if (!seen.has(page.route)) ordered.push(page);
689
+ }
690
+ return ordered;
691
+ }
692
+ function renderLlmsIndex(pages, name, basePath = "/docs") {
693
+ const lines = [`# ${name}`, "", "> Documentation index generated by DeepSpace Documentation.", ""];
694
+ for (const page of pages.filter((candidate) => !candidate.hidden)) {
695
+ lines.push(
696
+ `- [${page.title}](${markdownUrlForRoute(page.route, basePath)})${page.description ? `: ${page.description}` : ""}`
697
+ );
698
+ }
699
+ return `${lines.join("\n")}
700
+ `;
701
+ }
702
+ function renderLlmsFull(pages, name, basePath = "/docs") {
703
+ return `# ${name}
704
+
705
+ ${pages.filter((page) => !page.hidden).map(
706
+ (page) => `${pageMarkdown(page, basePath).trim()}
707
+
708
+ Source: ${markdownUrlForRoute(page.route, basePath)}`
709
+ ).join("\n\n---\n\n")}
710
+ `;
711
+ }
712
+ function renderSitemap(pages, baseUrl) {
713
+ const urls = baseUrl ? pages.filter((page) => !page.hidden && !page.noindex).map((page) => ` <url><loc>${escapeXml(joinSiteUrl(baseUrl, page.route))}</loc></url>`).join("\n") : "";
714
+ return `<?xml version="1.0" encoding="UTF-8"?>
715
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
716
+ ${urls}${urls ? "\n" : ""}</urlset>
717
+ `;
718
+ }
719
+ function renderRobots(baseUrl, noindex) {
720
+ if (noindex) return "User-agent: *\nDisallow: /\n";
721
+ return `User-agent: *
722
+ Allow: /
723
+ ${baseUrl ? `Sitemap: ${joinSiteUrl(baseUrl, "/sitemap.xml")}
724
+ ` : ""}`;
725
+ }
726
+ function stableJson(value) {
727
+ return `${JSON.stringify(value, null, 2)}
728
+ `;
729
+ }
730
+
731
+ // src/documentation/graph.ts
732
+ import { createHash } from "crypto";
733
+ import { existsSync as existsSync4, readdirSync, readFileSync as readFileSync3 } from "fs";
734
+ import { basename as basename2, dirname, extname as extname2, join, posix, relative as relative3, resolve as resolve4, sep as sep2 } from "path";
735
+
736
+ // src/documentation/config.ts
737
+ import { existsSync as existsSync2, readFileSync } from "fs";
738
+ import { resolve as resolve2 } from "path";
739
+
740
+ // src/shared/ai-models.ts
741
+ var MULTI_STEP_AGENT_PROFILES = ["application", "documentation"];
742
+ function createMultiStepModelFactory(provider) {
743
+ return (model) => ({
744
+ ...model,
745
+ ...provider,
746
+ agentSupport: "multi-step",
747
+ agentProfiles: MULTI_STEP_AGENT_PROFILES
748
+ });
749
+ }
750
+ var anthropicModel = createMultiStepModelFactory({
751
+ provider: "anthropic",
752
+ providerLabel: "Anthropic",
753
+ transport: "messages"
754
+ });
755
+ var openAIModel = createMultiStepModelFactory({
756
+ provider: "openai",
757
+ providerLabel: "OpenAI",
758
+ transport: "chat-completions"
759
+ });
760
+ var DEEPSPACE_AI_MODELS = [
761
+ anthropicModel({
762
+ id: "claude-fable-5",
763
+ label: "Claude Fable 5",
764
+ family: "Claude 5",
765
+ recommendation: "frontier"
766
+ }),
767
+ anthropicModel({
768
+ id: "claude-opus-5",
769
+ label: "Claude Opus 5",
770
+ family: "Claude 5",
771
+ recommendation: "frontier"
772
+ }),
773
+ anthropicModel({
774
+ id: "claude-sonnet-5",
775
+ label: "Claude Sonnet 5",
776
+ family: "Claude 5",
777
+ recommendation: "balanced"
778
+ }),
779
+ anthropicModel({
780
+ id: "claude-haiku-4-5",
781
+ label: "Claude Haiku 4.5",
782
+ family: "Claude 4.5",
783
+ recommendation: "fast"
784
+ }),
785
+ openAIModel({
786
+ id: "gpt-5.6-sol",
787
+ label: "GPT-5.6 Sol",
788
+ family: "GPT-5.6",
789
+ recommendation: "frontier"
790
+ }),
791
+ openAIModel({
792
+ id: "gpt-5.6-terra",
793
+ label: "GPT-5.6 Terra",
794
+ family: "GPT-5.6",
795
+ recommendation: "balanced"
796
+ }),
797
+ openAIModel({
798
+ id: "gpt-5.6-luna",
799
+ label: "GPT-5.6 Luna",
800
+ family: "GPT-5.6",
801
+ recommendation: "fast"
802
+ }),
803
+ {
804
+ id: "gpt-oss-120b",
805
+ label: "GPT-OSS 120B",
806
+ provider: "cerebras",
807
+ providerLabel: "Cerebras",
808
+ family: "GPT-OSS",
809
+ agentSupport: "single-step",
810
+ agentProfiles: [],
811
+ transport: "chat-completions",
812
+ recommendation: "limited",
813
+ note: "Available for direct generation; the current proxy adapter has not passed multi-step tool-result continuation."
814
+ }
815
+ ];
816
+ var DEEPSPACE_AI_DEFAULTS = {
817
+ agent: "claude-sonnet-5",
818
+ directGeneration: "claude-sonnet-5",
819
+ summarization: "claude-haiku-4-5"
820
+ };
821
+ var DEEPSPACE_AGENT_PROFILES = {
822
+ application: {
823
+ id: "application",
824
+ defaultModel: DEEPSPACE_AI_DEFAULTS.agent,
825
+ maxSteps: 21,
826
+ maxToolCalls: 20,
827
+ allowedTools: "application-defined"
828
+ },
829
+ documentation: {
830
+ id: "documentation",
831
+ defaultModel: DEEPSPACE_AI_DEFAULTS.agent,
832
+ // Permit up to twenty completed read-only tools plus one final synthesis
833
+ // step. The runner disables tools once the tool-call budget is exhausted.
834
+ maxSteps: 21,
835
+ maxToolCalls: 20,
836
+ allowedTools: ["documentation_search", "documentation_read"]
837
+ }
838
+ };
839
+ function getDeepSpaceAIModel(modelId) {
840
+ if (typeof modelId !== "string") return null;
841
+ return DEEPSPACE_AI_MODELS.find((model) => model.id === modelId) ?? null;
842
+ }
843
+ function listDeepSpaceAgentModels(profileId = "application") {
844
+ const defaultModel = DEEPSPACE_AGENT_PROFILES[profileId].defaultModel;
845
+ return DEEPSPACE_AI_MODELS.filter(
846
+ (model) => model.agentProfiles.includes(profileId)
847
+ ).sort((left, right) => Number(right.id === defaultModel) - Number(left.id === defaultModel));
848
+ }
849
+ function resolveDeepSpaceAgentModel(modelId, profileId = "application") {
850
+ const profile = DEEPSPACE_AGENT_PROFILES[profileId];
851
+ const resolvedId = modelId === void 0 ? profile.defaultModel : modelId;
852
+ const model = getDeepSpaceAIModel(resolvedId);
853
+ if (!model || !model.agentProfiles.includes(profileId)) return null;
854
+ return { modelId: model.id, provider: model.provider, model, profile };
855
+ }
856
+
857
+ // src/documentation/config/schema.ts
858
+ import { z } from "zod";
859
+ var linkSchema = z.object({
860
+ label: z.string().trim().min(1),
861
+ href: z.string().trim().min(1)
862
+ });
863
+ var navigationSchema = z.lazy(
864
+ () => z.union([
865
+ z.string().trim().min(1),
866
+ linkSchema,
867
+ z.object({
868
+ group: z.string().trim().min(1),
869
+ pages: z.array(navigationSchema).min(1)
870
+ })
871
+ ])
872
+ );
873
+ var openApiExamplesSchema = z.object({
874
+ languages: z.array(z.enum(DOCUMENTATION_OPENAPI_SAMPLE_LANGUAGES)).min(1).optional(),
875
+ defaults: z.enum(["required", "all"]).optional(),
876
+ autogenerate: z.boolean().optional()
877
+ });
878
+ var openapiItemSchema = z.union([
879
+ z.string().trim().min(1).transform((source) => ({ source })),
880
+ z.object({
881
+ source: z.string().trim().min(1),
882
+ route: z.string().trim().optional(),
883
+ label: z.string().trim().min(1).optional(),
884
+ playground: z.boolean().optional(),
885
+ baseUrl: z.string().url().optional(),
886
+ examples: openApiExamplesSchema.optional()
887
+ })
888
+ ]);
889
+ var themeObjectSchema = z.object({
890
+ preset: z.string().trim().min(1).optional(),
891
+ accent: z.string().trim().optional(),
892
+ background: z.string().trim().optional(),
893
+ logo: z.string().trim().optional(),
894
+ logoDark: z.string().trim().optional(),
895
+ favicon: z.string().trim().optional(),
896
+ defaultMode: z.enum(["light", "dark", "system"]).optional()
897
+ });
898
+ var fontSpecSchema = z.union([
899
+ z.string().trim().min(1).transform((family) => ({ family })),
900
+ z.object({
901
+ family: z.string().trim().min(1),
902
+ weight: z.union([z.number().int().min(100).max(900), z.string().trim().min(1)]).optional(),
903
+ source: z.string().trim().min(1).optional(),
904
+ format: z.string().trim().min(1).optional()
905
+ })
906
+ ]);
907
+ var colorPairSchema = z.union([
908
+ z.string().trim().min(1).transform((color) => ({ light: color })),
909
+ z.object({
910
+ light: z.string().trim().min(1).optional(),
911
+ dark: z.string().trim().min(1).optional()
912
+ })
913
+ ]);
914
+ var navigationRootSchema = z.union([
915
+ z.array(navigationSchema),
916
+ z.object({
917
+ groups: z.array(
918
+ z.object({
919
+ group: z.string().trim().min(1),
920
+ pages: z.array(navigationSchema).min(1)
921
+ })
922
+ ),
923
+ global: z.record(z.string(), z.unknown()).optional()
924
+ })
925
+ ]);
926
+ var navbarSchema = z.object({
927
+ links: z.array(linkSchema).optional(),
928
+ primary: z.object({
929
+ type: z.string().optional(),
930
+ label: z.string().trim().min(1),
931
+ href: z.string().trim().min(1)
932
+ }).optional()
933
+ }).optional();
934
+ var footerSchema = z.union([
935
+ z.array(linkSchema),
936
+ z.object({ socials: z.record(z.string(), z.string().trim().min(1)).optional() })
937
+ ]);
938
+ var logoSchema = z.union([
939
+ z.string().trim().min(1),
940
+ z.object({
941
+ light: z.string().trim().min(1),
942
+ dark: z.string().trim().min(1).optional(),
943
+ href: z.string().trim().min(1).optional()
944
+ })
945
+ ]).optional();
946
+ var contextualActionsSchema = z.array(z.enum(DOCUMENTATION_CONTEXTUAL_ACTIONS)).min(1).max(DOCUMENTATION_CONTEXTUAL_ACTIONS.length).refine((actions) => new Set(actions).size === actions.length, {
947
+ message: "actions must not contain duplicates"
948
+ });
949
+ var hostnameSchema = z.hostname("must be a hostname such as docs.example.com").trim().toLowerCase().refine((value) => value.includes("."), "must include a public suffix");
950
+ var rawConfigSchema = z.object({
951
+ $schema: z.string().optional(),
952
+ name: z.string().trim().min(1),
953
+ description: z.string().trim().optional(),
954
+ source: z.string().trim().min(1).default("documentation"),
955
+ output: z.string().trim().min(1).default("dist/_documentation"),
956
+ url: z.string().url().optional(),
957
+ domains: z.array(hostnameSchema).max(1, "one compiled documentation surface has one canonical custom domain").default([]),
958
+ theme: z.union([z.string().trim().min(1), themeObjectSchema]).default({}),
959
+ colors: z.object({
960
+ primary: z.string().trim().optional(),
961
+ light: z.string().trim().optional(),
962
+ dark: z.string().trim().optional()
963
+ }).optional(),
964
+ logo: logoSchema,
965
+ favicon: z.string().trim().min(1).optional(),
966
+ appearance: z.object({
967
+ default: z.enum(["light", "dark", "system"]).optional(),
968
+ strict: z.boolean().optional()
969
+ }).optional(),
970
+ background: z.object({
971
+ color: colorPairSchema.optional(),
972
+ decoration: z.enum(["none", "gradient", "grid"]).optional()
973
+ }).optional(),
974
+ fonts: z.object({
975
+ family: z.string().trim().min(1).optional(),
976
+ body: fontSpecSchema.optional(),
977
+ heading: fontSpecSchema.optional(),
978
+ mono: fontSpecSchema.optional()
979
+ }).optional(),
980
+ styling: z.object({
981
+ codeblocks: z.enum(["dark", "system"]).optional(),
982
+ eyebrows: z.enum(["section", "breadcrumbs", "none"]).optional()
983
+ }).optional(),
984
+ navigation: navigationRootSchema.optional(),
985
+ navbar: navbarSchema,
986
+ links: z.array(linkSchema).default([]),
987
+ footer: footerSchema.default([]),
988
+ redirects: z.record(z.string(), z.string()).default({}),
989
+ api: z.object({
990
+ playground: z.object({
991
+ display: z.enum(["interactive", "simple", "none"]).optional()
992
+ }).optional(),
993
+ examples: openApiExamplesSchema.optional()
994
+ }).optional(),
995
+ openapi: z.union([openapiItemSchema, z.array(openapiItemSchema)]).optional(),
996
+ assistant: z.object({
997
+ access: z.enum(["disabled", "public", "authenticated"]).default("disabled"),
998
+ model: z.string().trim().min(1).optional(),
999
+ suggestions: z.array(z.string().trim().min(1)).max(6).optional()
1000
+ }).default({ access: "disabled" }),
1001
+ mcp: z.object({
1002
+ access: z.enum(["disabled", "public"]).default("public")
1003
+ }).default({ access: "public" }),
1004
+ contextual: z.object({
1005
+ actions: contextualActionsSchema.default([...DEFAULT_DOCUMENTATION_CONTEXTUAL_ACTIONS])
1006
+ }).default({ actions: [...DEFAULT_DOCUMENTATION_CONTEXTUAL_ACTIONS] }),
1007
+ seo: z.object({
1008
+ noindex: z.boolean().optional(),
1009
+ ogImage: z.string().trim().optional(),
1010
+ metaTags: z.record(z.string(), z.string()).optional(),
1011
+ metatags: z.record(z.string(), z.string()).optional()
1012
+ }).default({})
1013
+ });
1014
+ var RAW_CONFIG_KEYS = /* @__PURE__ */ new Set([
1015
+ "$schema",
1016
+ "name",
1017
+ "description",
1018
+ "source",
1019
+ "output",
1020
+ "url",
1021
+ "domains",
1022
+ "theme",
1023
+ "colors",
1024
+ "logo",
1025
+ "favicon",
1026
+ "appearance",
1027
+ "background",
1028
+ "fonts",
1029
+ "styling",
1030
+ "navigation",
1031
+ "navbar",
1032
+ "links",
1033
+ "footer",
1034
+ "redirects",
1035
+ "api",
1036
+ "openapi",
1037
+ "assistant",
1038
+ "mcp",
1039
+ "contextual",
1040
+ "seo"
1041
+ ]);
1042
+
1043
+ // src/documentation/config/validation.ts
1044
+ import { existsSync, realpathSync } from "fs";
1045
+ import { isAbsolute, relative, resolve } from "path";
1046
+ function validateLinks(links, field, configPath) {
1047
+ for (const link of links) {
1048
+ if (!/^(?:https?:\/\/|mailto:|\/|#)/i.test(link.href)) {
1049
+ throw new DocumentationError(
1050
+ `Unsafe ${field} link: ${link.href}`,
1051
+ "documentation_config_invalid",
1052
+ [{ code: "unsafe_link", message: `${field}: ${link.href}`, file: configPath }]
1053
+ );
1054
+ }
1055
+ }
1056
+ }
1057
+ function safeChildPath(appDir, configured, field, configPath) {
1058
+ if (isAbsolute(configured)) {
1059
+ throw new DocumentationError(
1060
+ `documentation.json ${field} must be relative to the app directory`,
1061
+ "documentation_path_invalid",
1062
+ [{ code: "absolute_path", message: `${field}: ${configured}`, file: configPath }]
1063
+ );
1064
+ }
1065
+ const path = resolve(appDir, configured);
1066
+ const rel = relative(appDir, path);
1067
+ if (rel === ".." || rel.startsWith(`..${pathSeparator()}`)) {
1068
+ throw new DocumentationError(
1069
+ `documentation.json ${field} escapes the app directory`,
1070
+ "documentation_path_invalid",
1071
+ [{ code: "path_escape", message: `${field}: ${configured}`, file: configPath }]
1072
+ );
1073
+ }
1074
+ if (existsSync(path)) {
1075
+ const realRoot = realpathSync(appDir);
1076
+ const realPath = realpathSync(path);
1077
+ const realRelative = relative(realRoot, realPath);
1078
+ if (realRelative === ".." || realRelative.startsWith(`..${pathSeparator()}`)) {
1079
+ throw new DocumentationError(
1080
+ `documentation.json ${field} resolves outside the app directory`,
1081
+ "documentation_path_invalid",
1082
+ [{ code: "path_symlink_escape", message: `${field}: ${configured}`, file: configPath }]
1083
+ );
1084
+ }
1085
+ }
1086
+ return path;
1087
+ }
1088
+ function pathSeparator() {
1089
+ return process.platform === "win32" ? "\\" : "/";
1090
+ }
1091
+ function isWithin(parent, child) {
1092
+ const rel = relative(parent, child);
1093
+ return rel !== "" && rel !== ".." && !rel.startsWith(`..${pathSeparator()}`);
1094
+ }
1095
+ function validateRedirects(redirects, configPath) {
1096
+ for (const [from, to] of Object.entries(redirects)) {
1097
+ if (!from.startsWith("/") || !to.startsWith("/")) {
1098
+ throw new DocumentationError(
1099
+ `Redirects must use absolute site paths: ${from} -> ${to}`,
1100
+ "documentation_redirect_invalid",
1101
+ [{ code: "redirect_path", message: `${from} -> ${to}`, file: configPath }]
1102
+ );
1103
+ }
1104
+ if (from === to) {
1105
+ throw new DocumentationError(
1106
+ `Redirect points to itself: ${from}`,
1107
+ "documentation_redirect_invalid",
1108
+ [{ code: "redirect_loop", message: from, file: configPath }]
1109
+ );
1110
+ }
1111
+ const seen = /* @__PURE__ */ new Set([from]);
1112
+ let cursor = to;
1113
+ while (cursor && redirects[cursor]) {
1114
+ if (seen.has(cursor)) {
1115
+ throw new DocumentationError(
1116
+ `Redirect loop includes ${cursor}`,
1117
+ "documentation_redirect_invalid",
1118
+ [{ code: "redirect_loop", message: [...seen, cursor].join(" -> "), file: configPath }]
1119
+ );
1120
+ }
1121
+ seen.add(cursor);
1122
+ cursor = redirects[cursor];
1123
+ }
1124
+ }
1125
+ }
1126
+ function humanize(value) {
1127
+ return value.split(/[-_]/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
1128
+ }
1129
+
1130
+ // src/documentation/config.ts
1131
+ function loadDocumentationConfig(appDir) {
1132
+ const resolvedAppDir = resolve2(appDir);
1133
+ const configPath = resolve2(resolvedAppDir, DOCUMENTATION_CONFIG_FILE);
1134
+ if (!existsSync2(configPath)) {
1135
+ throw new DocumentationError(
1136
+ `No ${DOCUMENTATION_CONFIG_FILE} found in ${resolvedAppDir}`,
1137
+ "documentation_config_missing",
1138
+ [{ code: "documentation_config_missing", message: `Create ${DOCUMENTATION_CONFIG_FILE}`, file: configPath }]
1139
+ );
1140
+ }
1141
+ const raw = readFileSync(configPath, "utf8");
1142
+ let input;
1143
+ try {
1144
+ input = JSON.parse(raw);
1145
+ } catch (error) {
1146
+ throw new DocumentationError(
1147
+ `${DOCUMENTATION_CONFIG_FILE} is not valid JSON: ${errorMessage(error)}`,
1148
+ "documentation_config_invalid",
1149
+ [{ code: "invalid_json", message: errorMessage(error), file: configPath }]
1150
+ );
1151
+ }
1152
+ const parsed = rawConfigSchema.safeParse(input);
1153
+ if (!parsed.success) {
1154
+ const diagnostics = parsed.error.issues.map((issue) => ({
1155
+ code: "invalid_config_value",
1156
+ message: `${issue.path.join(".") || "<root>"}: ${issue.message}`,
1157
+ file: configPath
1158
+ }));
1159
+ throw new DocumentationError(
1160
+ `${DOCUMENTATION_CONFIG_FILE} has ${diagnostics.length} validation error(s)`,
1161
+ "documentation_config_invalid",
1162
+ diagnostics
1163
+ );
1164
+ }
1165
+ const data = parsed.data;
1166
+ if (data.assistant.model !== void 0 && !resolveDeepSpaceAgentModel(data.assistant.model, "documentation")) {
1167
+ const catalogModel = getDeepSpaceAIModel(data.assistant.model);
1168
+ const reason = catalogModel ? `does not support the multi-step documentation agent (${catalogModel.note ?? "incompatible runtime contract"})` : "is not in the DeepSpace model catalog";
1169
+ const supported = listDeepSpaceAgentModels("documentation").map((model) => model.id).join(", ");
1170
+ throw new DocumentationError(
1171
+ `${DOCUMENTATION_CONFIG_FILE} has an unsupported assistant model`,
1172
+ "documentation_config_invalid",
1173
+ [
1174
+ {
1175
+ code: "unsupported_assistant_model",
1176
+ message: `assistant.model '${data.assistant.model}' ${reason}. Supported models: ${supported}`,
1177
+ file: configPath
1178
+ }
1179
+ ]
1180
+ );
1181
+ }
1182
+ const warnings = [];
1183
+ if (input && typeof input === "object" && !Array.isArray(input)) {
1184
+ for (const key of Object.keys(input)) {
1185
+ if (!RAW_CONFIG_KEYS.has(key)) {
1186
+ warnings.push({
1187
+ code: "unsupported_config_key",
1188
+ message: `Unsupported Mintlify configuration key '${key}' was ignored`,
1189
+ file: configPath
1190
+ });
1191
+ }
1192
+ }
1193
+ }
1194
+ const themeInput = typeof data.theme === "string" ? { preset: data.theme } : data.theme;
1195
+ if (typeof data.theme === "string") {
1196
+ warnings.push({
1197
+ code: "mintlify_theme_normalized",
1198
+ message: `Mintlify theme '${data.theme}' is mapped to the native Orbit surface`,
1199
+ file: configPath
1200
+ });
1201
+ }
1202
+ const logo = typeof data.logo === "string" ? { light: data.logo } : data.logo;
1203
+ const background = data.background?.color;
1204
+ const bodyFont = data.fonts?.body ?? (data.fonts?.family ? { family: data.fonts.family } : void 0);
1205
+ const headingFont = data.fonts?.heading ?? bodyFont;
1206
+ const monoFont = data.fonts?.mono;
1207
+ const navigation = Array.isArray(data.navigation) ? data.navigation : data.navigation?.groups;
1208
+ if (data.navigation && !Array.isArray(data.navigation)) {
1209
+ warnings.push({
1210
+ code: "mintlify_navigation_normalized",
1211
+ message: "Mintlify navigation.groups was normalized to native navigation",
1212
+ file: configPath
1213
+ });
1214
+ }
1215
+ const navbarLinks = [
1216
+ ...data.navbar?.links ?? [],
1217
+ ...data.navbar?.primary ? [{ label: data.navbar.primary.label, href: data.navbar.primary.href }] : []
1218
+ ];
1219
+ const footer = Array.isArray(data.footer) ? data.footer : Object.entries(data.footer.socials ?? {}).map(([label, href]) => ({
1220
+ label: humanize(label),
1221
+ href
1222
+ }));
1223
+ const config = {
1224
+ $schema: data.$schema,
1225
+ name: data.name,
1226
+ description: data.description,
1227
+ source: data.source,
1228
+ output: data.output,
1229
+ url: data.url,
1230
+ domains: [...new Set(data.domains)],
1231
+ theme: {
1232
+ ...themeInput,
1233
+ accent: themeInput.accent ?? data.colors?.primary ?? data.colors?.light ?? data.colors?.dark,
1234
+ background: background?.light ?? themeInput.background,
1235
+ backgroundDark: background && "dark" in background ? background.dark : void 0,
1236
+ backgroundDecoration: data.background?.decoration,
1237
+ bodyFont,
1238
+ headingFont,
1239
+ monoFont,
1240
+ codeBlockMode: data.styling?.codeblocks,
1241
+ eyebrowStyle: data.styling?.eyebrows,
1242
+ logo: themeInput.logo ?? logo?.light,
1243
+ logoDark: themeInput.logoDark ?? logo?.dark,
1244
+ logoHref: logo?.href,
1245
+ favicon: themeInput.favicon ?? data.favicon,
1246
+ defaultMode: data.appearance?.default ?? themeInput.defaultMode,
1247
+ strictMode: data.appearance?.strict
1248
+ },
1249
+ navigation,
1250
+ links: data.links.length > 0 ? data.links : navbarLinks,
1251
+ footer,
1252
+ redirects: data.redirects,
1253
+ openapi: normalizeOpenApi(data.openapi, data.api),
1254
+ assistant: data.assistant,
1255
+ mcp: data.mcp,
1256
+ contextual: data.contextual,
1257
+ seo: {
1258
+ ...data.seo.noindex === void 0 ? {} : { noindex: data.seo.noindex },
1259
+ ...data.seo.ogImage ? { ogImage: data.seo.ogImage } : {},
1260
+ ...data.seo.metaTags ?? data.seo.metatags ? { metaTags: data.seo.metaTags ?? data.seo.metatags } : {}
1261
+ }
1262
+ };
1263
+ validateRedirects(config.redirects, configPath);
1264
+ validateLinks(config.links, "links", configPath);
1265
+ validateLinks(config.footer, "footer", configPath);
1266
+ if (config.theme.logoHref) {
1267
+ validateLinks([{ label: "logo", href: config.theme.logoHref }], "logo.href", configPath);
1268
+ }
1269
+ const sourceDir = safeChildPath(resolvedAppDir, config.source, "source", configPath);
1270
+ const outputDir = safeChildPath(resolvedAppDir, config.output, "output", configPath);
1271
+ if (sourceDir === outputDir || isWithin(sourceDir, outputDir)) {
1272
+ throw new DocumentationError(
1273
+ "documentation.json output must not contain the documentation source directory",
1274
+ "documentation_path_collision",
1275
+ [
1276
+ {
1277
+ code: "documentation_path_collision",
1278
+ message: `source=${config.source}, output=${config.output}`,
1279
+ file: configPath
1280
+ }
1281
+ ]
1282
+ );
1283
+ }
1284
+ config.theme = {
1285
+ ...config.theme,
1286
+ ...config.theme.logo ? { logo: normalizeLocalAsset(config.theme.logo, sourceDir) } : {},
1287
+ ...config.theme.logoDark ? { logoDark: normalizeLocalAsset(config.theme.logoDark, sourceDir) } : {},
1288
+ ...config.theme.favicon ? { favicon: normalizeLocalAsset(config.theme.favicon, sourceDir) } : {},
1289
+ ...config.theme.bodyFont?.source ? {
1290
+ bodyFont: {
1291
+ ...config.theme.bodyFont,
1292
+ source: normalizeLocalAsset(config.theme.bodyFont.source, sourceDir)
1293
+ }
1294
+ } : {},
1295
+ ...config.theme.headingFont?.source ? {
1296
+ headingFont: {
1297
+ ...config.theme.headingFont,
1298
+ source: normalizeLocalAsset(config.theme.headingFont.source, sourceDir)
1299
+ }
1300
+ } : {},
1301
+ ...config.theme.monoFont?.source ? {
1302
+ monoFont: {
1303
+ ...config.theme.monoFont,
1304
+ source: normalizeLocalAsset(config.theme.monoFont.source, sourceDir)
1305
+ }
1306
+ } : {}
1307
+ };
1308
+ if (config.seo.ogImage) {
1309
+ config.seo = {
1310
+ ...config.seo,
1311
+ ogImage: normalizeLocalAsset(config.seo.ogImage, sourceDir)
1312
+ };
1313
+ }
1314
+ return { config, configPath, sourceDir, outputDir, raw, warnings };
1315
+ }
1316
+ function normalizeLocalAsset(value, sourceDir) {
1317
+ if (/^(?:https?:|data:|\/media\/)/i.test(value)) return value;
1318
+ const relativePath = value.replace(/^\.\//, "").replace(/^\/+/, "");
1319
+ if (!relativePath || !existsSync2(resolve2(sourceDir, relativePath))) return value;
1320
+ return `/media/${relativePath.split("\\").join("/")}`;
1321
+ }
1322
+ function normalizeOpenApi(value, api) {
1323
+ if (!value) return [];
1324
+ const items = Array.isArray(value) ? value : [value];
1325
+ return items.map((item) => {
1326
+ const localExamples = "examples" in item ? item.examples : void 0;
1327
+ const display = api?.playground?.display;
1328
+ return {
1329
+ ...item,
1330
+ ..."playground" in item && item.playground !== void 0 ? { playground: item.playground } : display ? { playground: display === "interactive" } : {},
1331
+ examples: {
1332
+ languages: localExamples?.languages ?? api?.examples?.languages ?? [...DEFAULT_DOCUMENTATION_OPENAPI_EXAMPLES.languages],
1333
+ defaults: localExamples?.defaults ?? api?.examples?.defaults ?? DEFAULT_DOCUMENTATION_OPENAPI_EXAMPLES.defaults,
1334
+ autogenerate: localExamples?.autogenerate ?? api?.examples?.autogenerate ?? DEFAULT_DOCUMENTATION_OPENAPI_EXAMPLES.autogenerate
1335
+ }
1336
+ };
1337
+ });
1338
+ }
1339
+
1340
+ // src/documentation/mdx.ts
1341
+ import { createProcessor } from "@mdx-js/mdx";
1342
+ import { gfmToMarkdown } from "mdast-util-gfm";
1343
+ import { toMarkdown } from "mdast-util-to-markdown";
1344
+ import remarkFrontmatter from "remark-frontmatter";
1345
+ import remarkGfm from "remark-gfm";
1346
+ import rehypeHighlight from "rehype-highlight";
1347
+ var EXECUTABLE_NODE_TYPES = /* @__PURE__ */ new Set([
1348
+ "mdxjsEsm",
1349
+ "mdxFlowExpression",
1350
+ "mdxTextExpression"
1351
+ ]);
1352
+ function parseMdx(source, file) {
1353
+ const { frontmatter, body } = parseFrontmatter(source, file);
1354
+ try {
1355
+ const processor = createProcessor({ remarkPlugins: [remarkGfm] });
1356
+ const tree = processor.parse(body);
1357
+ const staticTree = projectStaticMarkdown(tree);
1358
+ const markdown2 = toMarkdown(staticTree, {
1359
+ extensions: [gfmToMarkdown()]
1360
+ });
1361
+ return { ...parseMarkdown(markdown2, file), frontmatter };
1362
+ } catch (error) {
1363
+ if (error instanceof DocumentationError) throw error;
1364
+ const line = diagnosticLine(error);
1365
+ throw new DocumentationError(
1366
+ `Invalid MDX in ${file}: ${errorMessage(error)}`,
1367
+ "documentation_mdx_invalid",
1368
+ [{ code: "mdx_syntax", message: errorMessage(error), file, ...line ? { line } : {} }]
1369
+ );
1370
+ }
1371
+ }
1372
+ function mdxCompileOptions() {
1373
+ return {
1374
+ jsx: false,
1375
+ outputFormat: "program",
1376
+ remarkPlugins: [remarkGfm, remarkFrontmatter, removeFrontmatter, addRuntimeMetadata],
1377
+ rehypePlugins: [rehypeHighlight]
1378
+ };
1379
+ }
1380
+ function projectStaticMarkdown(node) {
1381
+ const projected = { ...node };
1382
+ delete projected.attributes;
1383
+ delete projected.name;
1384
+ projected.children = (node.children ?? []).flatMap(projectStaticNode);
1385
+ return projected;
1386
+ }
1387
+ function projectStaticNode(node) {
1388
+ if (EXECUTABLE_NODE_TYPES.has(node.type)) return [];
1389
+ if (node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement") {
1390
+ const title = jsxTitle(node);
1391
+ const titleNode = title ? [{
1392
+ type: "paragraph",
1393
+ children: [{ type: "text", value: title }]
1394
+ }] : [];
1395
+ return [...titleNode, ...(node.children ?? []).flatMap(projectStaticNode)];
1396
+ }
1397
+ return [projectStaticMarkdown(node)];
1398
+ }
1399
+ function jsxTitle(node) {
1400
+ const attribute = node.attributes?.find(
1401
+ (candidate) => candidate.type === "mdxJsxAttribute" && (candidate.name === "title" || candidate.name === "label")
1402
+ );
1403
+ return typeof attribute?.value === "string" ? attribute.value : void 0;
1404
+ }
1405
+ function removeFrontmatter() {
1406
+ return (tree) => {
1407
+ tree.children = tree.children?.filter((node) => node.type !== "yaml");
1408
+ };
1409
+ }
1410
+ function addRuntimeMetadata() {
1411
+ return (tree) => {
1412
+ const slug = createSlugger();
1413
+ visit(tree, (node) => {
1414
+ if (node.type === "heading") {
1415
+ const text = staticText(node);
1416
+ const id = slug(text);
1417
+ node.data = {
1418
+ ...node.data,
1419
+ hProperties: {
1420
+ ...asRecord(node.data?.hProperties),
1421
+ id
1422
+ }
1423
+ };
1424
+ }
1425
+ if (node.type === "code" && typeof node.meta === "string" && node.meta.trim()) {
1426
+ node.data = {
1427
+ ...node.data,
1428
+ hProperties: {
1429
+ ...asRecord(node.data?.hProperties),
1430
+ "data-code-title": node.meta.trim()
1431
+ }
1432
+ };
1433
+ }
1434
+ });
1435
+ };
1436
+ }
1437
+ function staticText(node) {
1438
+ if (node.type === "text" || node.type === "inlineCode") return node.value ?? "";
1439
+ if (EXECUTABLE_NODE_TYPES.has(node.type)) return "";
1440
+ return (node.children ?? []).map(staticText).join("");
1441
+ }
1442
+ function visit(node, visitor) {
1443
+ visitor(node);
1444
+ for (const child of node.children ?? []) visit(child, visitor);
1445
+ }
1446
+ function asRecord(value) {
1447
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
1448
+ }
1449
+ function diagnosticLine(error) {
1450
+ if (!error || typeof error !== "object" || !("line" in error)) return void 0;
1451
+ return typeof error.line === "number" ? error.line : void 0;
1452
+ }
1453
+
1454
+ // src/documentation/openapi.ts
1455
+ import { existsSync as existsSync3, readFileSync as readFileSync2, realpathSync as realpathSync2 } from "fs";
1456
+ import { basename, extname, relative as relative2, resolve as resolve3, sep } from "path";
1457
+ import { parse as parseYaml2 } from "yaml";
1458
+
1459
+ // src/documentation/openapi-object.ts
1460
+ function asOpenApiObject(value) {
1461
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
1462
+ }
1463
+ function resolveOpenApiObject(value, document2, seen = /* @__PURE__ */ new Set()) {
1464
+ const object = asOpenApiObject(value);
1465
+ if (!object || typeof object.$ref !== "string") return object;
1466
+ const reference = object.$ref;
1467
+ if (!reference.startsWith("#/") || seen.has(reference)) return void 0;
1468
+ seen.add(reference);
1469
+ let resolved = document2;
1470
+ for (const segment of reference.slice(2).split("/")) {
1471
+ resolved = asOpenApiObject(resolved)?.[segment.replace(/~1/g, "/").replace(/~0/g, "~")];
1472
+ }
1473
+ const target = asOpenApiObject(resolved);
1474
+ if (!target) return void 0;
1475
+ const siblings = Object.fromEntries(Object.entries(object).filter(([key]) => key !== "$ref"));
1476
+ return { ...target, ...siblings };
1477
+ }
1478
+
1479
+ // src/documentation/openapi-samples.ts
1480
+ var LANGUAGE_METADATA = {
1481
+ curl: { label: "cURL", syntax: "bash" },
1482
+ javascript: { label: "JavaScript", syntax: "javascript" },
1483
+ python: { label: "Python", syntax: "python" },
1484
+ go: { label: "Go", syntax: "go" }
1485
+ };
1486
+ function createOpenApiCodeSamples(operation, document2, examples, customSamples) {
1487
+ const custom = normalizeCustomSamples(customSamples);
1488
+ if (examples?.autogenerate === false || !operation.baseUrl) return custom;
1489
+ const languages = examples?.languages ?? DEFAULT_DOCUMENTATION_OPENAPI_EXAMPLES.languages;
1490
+ const defaults = examples?.defaults ?? DEFAULT_DOCUMENTATION_OPENAPI_EXAMPLES.defaults;
1491
+ const request = buildSampleRequest(operation, document2, defaults === "all");
1492
+ const customLanguages = new Set(custom.map((sample) => knownLanguage(sample.language)));
1493
+ const generated = languages.filter((language) => !customLanguages.has(language)).map(
1494
+ (language) => ({
1495
+ language,
1496
+ label: LANGUAGE_METADATA[language].label,
1497
+ syntax: LANGUAGE_METADATA[language].syntax,
1498
+ code: renderSample(language, request),
1499
+ generated: true
1500
+ })
1501
+ );
1502
+ return [...custom, ...generated];
1503
+ }
1504
+ function buildSampleRequest(operation, document2, includeOptional) {
1505
+ let path = operation.path;
1506
+ const query = new URLSearchParams();
1507
+ const headers = { Accept: "application/json" };
1508
+ const cookies = [];
1509
+ const parameters = operation.parameters.filter(
1510
+ (parameter) => parameter.required || includeOptional
1511
+ );
1512
+ for (const parameter of parameters) {
1513
+ const value = sampleParameterValue(parameter, document2);
1514
+ if (parameter.in === "path") {
1515
+ path = path.replaceAll(`{${parameter.name}}`, encodeURIComponent(String(value)));
1516
+ } else if (parameter.in === "query") {
1517
+ query.set(parameter.name, String(value));
1518
+ } else if (parameter.in === "header") {
1519
+ headers[parameter.name] = String(value);
1520
+ } else if (parameter.in === "cookie") {
1521
+ cookies.push(`${parameter.name}=${String(value)}`);
1522
+ }
1523
+ }
1524
+ path = path.replace(
1525
+ /\{([^}]+)\}/g,
1526
+ (_match, name) => encodeURIComponent(`example-${slugPart(name)}`)
1527
+ );
1528
+ applySecurity(operation.security ?? document2.security, document2, headers, query, cookies);
1529
+ if (cookies.length > 0) headers.Cookie = cookies.join("; ");
1530
+ const body = requestBodyExample(operation.requestBody, document2, includeOptional);
1531
+ if (body !== void 0) headers["Content-Type"] = "application/json";
1532
+ const separator = path.includes("?") ? "&" : "?";
1533
+ const search = query.toString();
1534
+ const url = `${operation.baseUrl?.replace(/\/$/, "") ?? ""}${path}${search ? `${separator}${search}` : ""}`;
1535
+ return { method: operation.method, url, headers, ...body === void 0 ? {} : { body } };
1536
+ }
1537
+ function sampleParameterValue(parameter, document2) {
1538
+ if (parameter.example !== void 0) return parameter.example;
1539
+ const schema = resolveOpenApiObject(parameter.schema, document2);
1540
+ if (schema) {
1541
+ if (schema.example !== void 0) return schema.example;
1542
+ if (schema.default !== void 0) return schema.default;
1543
+ if (Array.isArray(schema.enum) && schema.enum.length > 0) return schema.enum[0];
1544
+ if (schema.type === "boolean") return true;
1545
+ if (schema.type === "integer" || schema.type === "number") return 1;
1546
+ }
1547
+ return `example-${slugPart(parameter.name)}`;
1548
+ }
1549
+ function requestBodyExample(value, document2, includeOptional) {
1550
+ const requestBody = resolveOpenApiObject(value, document2);
1551
+ if (!requestBody) return void 0;
1552
+ const content = asOpenApiObject(requestBody.content);
1553
+ if (!content) return void 0;
1554
+ const jsonMedia = Object.entries(content).find(([contentType]) => contentType.endsWith("+json"));
1555
+ const media = asOpenApiObject(content["application/json"]) ?? asOpenApiObject(jsonMedia?.[1]);
1556
+ if (!media) return void 0;
1557
+ if (media.example !== void 0) return media.example;
1558
+ const namedExamples = asOpenApiObject(media.examples);
1559
+ if (namedExamples) {
1560
+ for (const example of Object.values(namedExamples)) {
1561
+ const resolved = resolveOpenApiObject(example, document2);
1562
+ if (resolved && resolved.value !== void 0) return resolved.value;
1563
+ }
1564
+ }
1565
+ return schemaExample(media.schema, document2, includeOptional, /* @__PURE__ */ new Set(), 0);
1566
+ }
1567
+ function schemaExample(value, document2, includeOptional, seen, depth) {
1568
+ if (depth > 8) return void 0;
1569
+ const schema = resolveOpenApiObject(value, document2, seen);
1570
+ if (!schema) return void 0;
1571
+ if (schema.example !== void 0) return schema.example;
1572
+ if (schema.default !== void 0) return schema.default;
1573
+ if (Array.isArray(schema.enum) && schema.enum.length > 0) return schema.enum[0];
1574
+ const alternatives = Array.isArray(schema.oneOf) ? schema.oneOf : Array.isArray(schema.anyOf) ? schema.anyOf : void 0;
1575
+ if (alternatives?.[0] !== void 0) {
1576
+ return schemaExample(alternatives[0], document2, includeOptional, seen, depth + 1);
1577
+ }
1578
+ if (Array.isArray(schema.allOf)) {
1579
+ return Object.assign(
1580
+ {},
1581
+ ...schema.allOf.map((part) => {
1582
+ const example = schemaExample(part, document2, includeOptional, seen, depth + 1);
1583
+ return asOpenApiObject(example) ?? {};
1584
+ })
1585
+ );
1586
+ }
1587
+ const properties = asOpenApiObject(schema.properties);
1588
+ if (schema.type === "object" || properties) {
1589
+ const required = new Set(
1590
+ Array.isArray(schema.required) ? schema.required.filter((name) => typeof name === "string") : []
1591
+ );
1592
+ return Object.fromEntries(
1593
+ Object.entries(properties ?? {}).filter(([name]) => includeOptional || required.has(name)).map(([name, property]) => [
1594
+ name,
1595
+ schemaExample(property, document2, includeOptional, new Set(seen), depth + 1)
1596
+ ]).filter((entry) => entry[1] !== void 0)
1597
+ );
1598
+ }
1599
+ if (schema.type === "array" || schema.items !== void 0) {
1600
+ const item = schemaExample(schema.items, document2, includeOptional, seen, depth + 1);
1601
+ return item === void 0 ? [] : [item];
1602
+ }
1603
+ if (schema.type === "boolean") return true;
1604
+ if (schema.type === "integer" || schema.type === "number") return 1;
1605
+ if (schema.type === "string") return stringExample(schema.format);
1606
+ return void 0;
1607
+ }
1608
+ function stringExample(format) {
1609
+ switch (format) {
1610
+ case "date-time":
1611
+ return "2026-01-01T00:00:00Z";
1612
+ case "date":
1613
+ return "2026-01-01";
1614
+ case "email":
1615
+ return "user@example.com";
1616
+ case "uuid":
1617
+ return "00000000-0000-4000-8000-000000000000";
1618
+ case "uri":
1619
+ case "url":
1620
+ return "https://example.com";
1621
+ default:
1622
+ return "string";
1623
+ }
1624
+ }
1625
+ function applySecurity(value, document2, headers, query, cookies) {
1626
+ if (!Array.isArray(value)) return;
1627
+ const requirement = value.map(asOpenApiObject).find(Boolean);
1628
+ if (!requirement) return;
1629
+ const schemes = asOpenApiObject(asOpenApiObject(document2.components)?.securitySchemes);
1630
+ for (const name of Object.keys(requirement)) {
1631
+ const scheme = resolveOpenApiObject(schemes?.[name], document2);
1632
+ if (!scheme) continue;
1633
+ if (scheme.type === "http" && scheme.scheme === "bearer") {
1634
+ headers.Authorization = "Bearer <token>";
1635
+ } else if (scheme.type === "http" && scheme.scheme === "basic") {
1636
+ headers.Authorization = "Basic <credentials>";
1637
+ } else if (scheme.type === "apiKey" && typeof scheme.name === "string") {
1638
+ if (scheme.in === "query") query.set(scheme.name, "<api-key>");
1639
+ else if (scheme.in === "cookie") cookies.push(`${scheme.name}=<api-key>`);
1640
+ else headers[scheme.name] = "<api-key>";
1641
+ }
1642
+ }
1643
+ }
1644
+ function renderSample(language, request) {
1645
+ switch (language) {
1646
+ case "curl":
1647
+ return renderCurl(request);
1648
+ case "javascript":
1649
+ return renderJavaScript(request);
1650
+ case "python":
1651
+ return renderPython(request);
1652
+ case "go":
1653
+ return renderGo(request);
1654
+ }
1655
+ }
1656
+ function renderCurl(request) {
1657
+ const lines = [`curl --request ${request.method} \\`, ` --url '${escapeShell(request.url)}'`];
1658
+ for (const [name, value] of Object.entries(request.headers)) {
1659
+ lines[lines.length - 1] += " \\";
1660
+ lines.push(` --header '${escapeShell(`${name}: ${value}`)}'`);
1661
+ }
1662
+ if (request.body !== void 0) {
1663
+ lines[lines.length - 1] += " \\";
1664
+ lines.push(` --data '${escapeShell(prettyJson(request.body))}'`);
1665
+ }
1666
+ return lines.join("\n");
1667
+ }
1668
+ function renderJavaScript(request) {
1669
+ const options = [
1670
+ `method: ${JSON.stringify(request.method)}`,
1671
+ `headers: ${indent(prettyJson(request.headers), 2).trimStart()}`,
1672
+ ...request.body === void 0 ? [] : [`body: JSON.stringify(${indent(prettyJson(request.body), 2).trimStart()})`]
1673
+ ];
1674
+ return [
1675
+ `const response = await fetch(${JSON.stringify(request.url)}, {`,
1676
+ options.map((option) => ` ${option},`).join("\n"),
1677
+ "})",
1678
+ "",
1679
+ "if (!response.ok) throw new Error(`Request failed: ${response.status}`)",
1680
+ "const data = await response.json()",
1681
+ "console.log(data)"
1682
+ ].join("\n");
1683
+ }
1684
+ function renderPython(request) {
1685
+ const arguments_ = [
1686
+ JSON.stringify(request.method),
1687
+ JSON.stringify(request.url),
1688
+ `headers=${pythonValue(request.headers)}`,
1689
+ ...request.body === void 0 ? [] : [`json=${pythonValue(request.body)}`]
1690
+ ];
1691
+ return [
1692
+ "import requests",
1693
+ "",
1694
+ "response = requests.request(",
1695
+ arguments_.map((argument) => ` ${argument},`).join("\n"),
1696
+ ")",
1697
+ "response.raise_for_status()",
1698
+ "print(response.json())"
1699
+ ].join("\n");
1700
+ }
1701
+ function renderGo(request) {
1702
+ const requestBody = request.body === void 0 ? "nil" : `strings.NewReader(${JSON.stringify(prettyJson(request.body))})`;
1703
+ const imports = ['"fmt"', '"io"', '"net/http"'];
1704
+ if (request.body !== void 0) imports.push('"strings"');
1705
+ return [
1706
+ "package main",
1707
+ "",
1708
+ "import (",
1709
+ ...imports.map((value) => ` ${value}`),
1710
+ ")",
1711
+ "",
1712
+ "func main() {",
1713
+ ` req, err := http.NewRequest(${JSON.stringify(request.method)}, ${JSON.stringify(request.url)}, ${requestBody})`,
1714
+ " if err != nil { panic(err) }",
1715
+ ...Object.entries(request.headers).map(
1716
+ ([name, value]) => ` req.Header.Set(${JSON.stringify(name)}, ${JSON.stringify(value)})`
1717
+ ),
1718
+ "",
1719
+ " res, err := http.DefaultClient.Do(req)",
1720
+ " if err != nil { panic(err) }",
1721
+ " defer res.Body.Close()",
1722
+ "",
1723
+ " body, err := io.ReadAll(res.Body)",
1724
+ " if err != nil { panic(err) }",
1725
+ " fmt.Println(string(body))",
1726
+ "}"
1727
+ ].join("\n");
1728
+ }
1729
+ function normalizeCustomSamples(value) {
1730
+ if (!Array.isArray(value)) return [];
1731
+ return value.flatMap((entry) => {
1732
+ const sample = asOpenApiObject(entry);
1733
+ const language = typeof sample?.lang === "string" ? sample.lang.trim().toLowerCase() : "";
1734
+ const source = typeof sample?.source === "string" ? sample.source.trim() : "";
1735
+ if (!language || !source) return [];
1736
+ const normalizedLanguage = knownLanguage(language);
1737
+ const label = typeof sample?.label === "string" && sample.label.trim() ? sample.label.trim() : normalizedLanguage ? LANGUAGE_METADATA[normalizedLanguage].label : language;
1738
+ return [
1739
+ {
1740
+ language,
1741
+ label,
1742
+ syntax: syntaxForLanguage(language),
1743
+ code: source,
1744
+ generated: false
1745
+ }
1746
+ ];
1747
+ });
1748
+ }
1749
+ function knownLanguage(value) {
1750
+ if (["curl", "bash", "shell", "sh"].includes(value)) return "curl";
1751
+ if (["javascript", "js", "node", "nodejs"].includes(value)) return "javascript";
1752
+ if (["python", "py"].includes(value)) return "python";
1753
+ if (["go", "golang"].includes(value)) return "go";
1754
+ return void 0;
1755
+ }
1756
+ function syntaxForLanguage(value) {
1757
+ const known = knownLanguage(value);
1758
+ return known ? LANGUAGE_METADATA[known].syntax : value.replace(/[^a-z0-9_+#.-]/g, "") || "text";
1759
+ }
1760
+ function prettyJson(value) {
1761
+ return JSON.stringify(value, null, 2);
1762
+ }
1763
+ function pythonValue(value) {
1764
+ return prettyJson(value).replace(/\btrue\b/g, "True").replace(/\bfalse\b/g, "False").replace(/\bnull\b/g, "None");
1765
+ }
1766
+ function escapeShell(value) {
1767
+ return value.replace(/'/g, `'"'"'`);
1768
+ }
1769
+ function indent(value, spaces) {
1770
+ const prefix = " ".repeat(spaces);
1771
+ return value.split("\n").map((line) => `${prefix}${line}`).join("\n");
1772
+ }
1773
+ function slugPart(value) {
1774
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "value";
1775
+ }
1776
+
1777
+ // src/documentation/openapi.ts
1778
+ var HTTP_METHODS = ["get", "post", "put", "patch", "delete", "options", "head", "trace"];
1779
+ function parseOpenApi(appDir, config) {
1780
+ const sourcePath = resolve3(appDir, config.source);
1781
+ assertOpenApiSourcePath(appDir, sourcePath, config.source);
1782
+ if (!existsSync3(sourcePath)) {
1783
+ throw new DocumentationError(`OpenAPI source not found: ${config.source}`, "documentation_openapi_missing", [
1784
+ { code: "openapi_missing", message: config.source, file: sourcePath }
1785
+ ]);
1786
+ }
1787
+ assertOpenApiSourcePath(appDir, realpathSync2(sourcePath), config.source);
1788
+ const raw = readFileSync2(sourcePath, "utf8");
1789
+ let parsed;
1790
+ try {
1791
+ parsed = [".yaml", ".yml"].includes(extname(sourcePath).toLowerCase()) ? parseYaml2(raw, { maxAliasCount: 50 }) : JSON.parse(raw);
1792
+ } catch (error) {
1793
+ throw new DocumentationError(
1794
+ `Invalid OpenAPI document ${config.source}: ${errorMessage(error)}`,
1795
+ "documentation_openapi_invalid",
1796
+ [{ code: "openapi_parse", message: errorMessage(error), file: sourcePath }]
1797
+ );
1798
+ }
1799
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1800
+ throw invalidOpenApi(sourcePath, "Document root must be an object");
1801
+ }
1802
+ const document2 = parsed;
1803
+ if (typeof document2.openapi !== "string" && typeof document2.swagger !== "string") {
1804
+ throw invalidOpenApi(sourcePath, "Expected an openapi or swagger version field");
1805
+ }
1806
+ if (!document2.paths || typeof document2.paths !== "object") {
1807
+ throw invalidOpenApi(sourcePath, "Expected a paths object");
1808
+ }
1809
+ const documentServer = firstServerUrl(document2.servers) ?? swaggerServerUrl(document2);
1810
+ const operations = [];
1811
+ const ids = /* @__PURE__ */ new Set();
1812
+ for (const [path, pathItem] of Object.entries(document2.paths)) {
1813
+ const normalizedPathItem = resolveOpenApiObject(pathItem, document2);
1814
+ if (!normalizedPathItem) continue;
1815
+ for (const method of HTTP_METHODS) {
1816
+ const operation = resolveOpenApiObject(
1817
+ normalizedPathItem[method],
1818
+ document2
1819
+ );
1820
+ if (!operation) continue;
1821
+ const baseUrl = config.baseUrl ?? firstServerUrl(operation.servers) ?? firstServerUrl(normalizedPathItem.servers) ?? documentServer;
1822
+ const fallbackId = `${method}-${path}`;
1823
+ const operationId = slugify(
1824
+ typeof operation.operationId === "string" ? operation.operationId : fallbackId
1825
+ );
1826
+ if (!operationId)
1827
+ throw invalidOpenApi(sourcePath, `Could not derive id for ${method} ${path}`);
1828
+ if (ids.has(operationId)) {
1829
+ throw invalidOpenApi(sourcePath, `Duplicate operation id: ${operationId}`);
1830
+ }
1831
+ ids.add(operationId);
1832
+ const summary = typeof operation.summary === "string" ? operation.summary : `${method.toUpperCase()} ${path}`;
1833
+ const parameters = mergeParameters(
1834
+ normalizedPathItem.parameters,
1835
+ operation.parameters,
1836
+ document2
1837
+ );
1838
+ const security = "security" in operation ? operation.security : document2.security;
1839
+ const normalized = {
1840
+ method: method.toUpperCase(),
1841
+ path,
1842
+ operationId,
1843
+ summary,
1844
+ ...typeof operation.description === "string" ? { description: operation.description } : {},
1845
+ tags: Array.isArray(operation.tags) ? operation.tags.filter((tag) => typeof tag === "string") : [],
1846
+ parameters,
1847
+ ..."requestBody" in operation ? { requestBody: operation.requestBody } : {},
1848
+ responses: normalizeResponses(operation.responses, document2),
1849
+ ...security === void 0 ? {} : { security },
1850
+ ...baseUrl ? { baseUrl } : {},
1851
+ playground: config.playground === true && Boolean(baseUrl)
1852
+ };
1853
+ operations.push({
1854
+ ...normalized,
1855
+ codeSamples: createOpenApiCodeSamples(
1856
+ normalized,
1857
+ document2,
1858
+ config.examples,
1859
+ operation["x-codeSamples"] ?? operation["x-code-samples"]
1860
+ )
1861
+ });
1862
+ }
1863
+ }
1864
+ if (operations.length === 0) {
1865
+ throw invalidOpenApi(sourcePath, "No HTTP operations found");
1866
+ }
1867
+ const route = normalizeRoute(config.route ?? "/api-reference");
1868
+ const title = config.label ?? (typeof document2.info?.title === "string" ? document2.info.title : basename(config.source, extname(config.source)));
1869
+ return { sourcePath, title, route, raw, operations };
1870
+ }
1871
+ function assertOpenApiSourcePath(appDir, sourcePath, configured) {
1872
+ const sourceIsReal = existsSync3(sourcePath);
1873
+ const root = sourceIsReal && existsSync3(appDir) && sourcePath === realpathSync2(sourcePath) ? realpathSync2(appDir) : resolve3(appDir);
1874
+ const rel = relative2(root, sourcePath);
1875
+ if (rel === ".." || rel.startsWith(`..${sep}`) || rel === "") {
1876
+ throw new DocumentationError(
1877
+ `OpenAPI source must be a file inside the app directory: ${configured}`,
1878
+ "documentation_openapi_path_invalid",
1879
+ [{ code: "openapi_path_escape", message: configured, file: sourcePath }]
1880
+ );
1881
+ }
1882
+ }
1883
+ function operationMarkdown(operation) {
1884
+ const lines = [
1885
+ `<div class="documentation-api-badge"><span class="method method-${operation.method.toLowerCase()}">${operation.method}</span><code>${operation.path}</code></div>`,
1886
+ ""
1887
+ ];
1888
+ if (operation.description) lines.push(operation.description, "");
1889
+ if (operation.parameters.length > 0) {
1890
+ lines.push(
1891
+ "## Parameters",
1892
+ "",
1893
+ "| Name | In | Required | Description |",
1894
+ "| --- | --- | --- | --- |"
1895
+ );
1896
+ for (const parameter of operation.parameters) {
1897
+ lines.push(
1898
+ `| \`${escapeTable(parameter.name)}\` | ${escapeTable(parameter.in)} | ${parameter.required ? "yes" : "no"} | ${escapeTable(parameter.description ?? "")} |`
1899
+ );
1900
+ }
1901
+ lines.push("");
1902
+ }
1903
+ if (operation.requestBody !== void 0) {
1904
+ lines.push("## Request body", "", "```json", prettyJson2(operation.requestBody), "```", "");
1905
+ }
1906
+ if (operation.security !== void 0) {
1907
+ lines.push("## Security", "", "```json", prettyJson2(operation.security), "```", "");
1908
+ }
1909
+ lines.push("## Responses", "");
1910
+ if (operation.responses.length === 0) {
1911
+ lines.push("No response schema is declared.", "");
1912
+ } else {
1913
+ for (const response of operation.responses) {
1914
+ lines.push(`### ${response.status}`, "", response.description || "Response", "");
1915
+ if (response.schema !== void 0) {
1916
+ lines.push("```json", prettyJson2(response.schema), "```", "");
1917
+ }
1918
+ }
1919
+ }
1920
+ if (operation.codeSamples.length > 0) {
1921
+ lines.push("## Request examples", "", '<CodeGroup title="Request">', "");
1922
+ for (const sample of operation.codeSamples) {
1923
+ const fence = codeFence(sample.code);
1924
+ const label = sample.label.replace(/["\r\n]/g, "'");
1925
+ lines.push(`${fence}${sample.syntax} title="${label}"`, sample.code, fence, "");
1926
+ }
1927
+ lines.push("</CodeGroup>", "");
1928
+ }
1929
+ return lines.join("\n");
1930
+ }
1931
+ function mergeParameters(pathParameters, operationParameters, document2) {
1932
+ const merged = /* @__PURE__ */ new Map();
1933
+ for (const values of [pathParameters, operationParameters]) {
1934
+ if (!Array.isArray(values)) continue;
1935
+ for (const value of values) {
1936
+ for (const parameter of normalizeParameter(value, document2)) {
1937
+ merged.set(`${parameter.in}\0${parameter.name}`, parameter);
1938
+ }
1939
+ }
1940
+ }
1941
+ return [...merged.values()];
1942
+ }
1943
+ function normalizeParameter(value, document2) {
1944
+ const parameter = resolveOpenApiObject(value, document2);
1945
+ if (!parameter) return [];
1946
+ if (typeof parameter.name !== "string" || typeof parameter.in !== "string") return [];
1947
+ return [
1948
+ {
1949
+ name: parameter.name,
1950
+ in: parameter.in,
1951
+ required: parameter.required === true,
1952
+ ...typeof parameter.description === "string" ? { description: parameter.description } : {},
1953
+ ..."schema" in parameter ? { schema: parameter.schema } : {},
1954
+ ..."example" in parameter ? { example: parameter.example } : {}
1955
+ }
1956
+ ];
1957
+ }
1958
+ function normalizeResponses(value, document2) {
1959
+ if (!value || typeof value !== "object" || Array.isArray(value)) return [];
1960
+ return Object.entries(value).map(([status, response]) => {
1961
+ const data = resolveOpenApiObject(response, document2);
1962
+ if (!data) {
1963
+ return { status, description: "" };
1964
+ }
1965
+ return {
1966
+ status,
1967
+ description: typeof data.description === "string" ? data.description : "",
1968
+ ..."content" in data ? { schema: data.content } : {}
1969
+ };
1970
+ });
1971
+ }
1972
+ function invalidOpenApi(file, message) {
1973
+ return new DocumentationError(`Invalid OpenAPI document: ${message}`, "documentation_openapi_invalid", [
1974
+ { code: "openapi_shape", message, file }
1975
+ ]);
1976
+ }
1977
+ function prettyJson2(value) {
1978
+ try {
1979
+ return JSON.stringify(value, null, 2);
1980
+ } catch {
1981
+ return String(value);
1982
+ }
1983
+ }
1984
+ function escapeTable(value) {
1985
+ return value.replace(/\|/g, "\\|").replace(/\n/g, " ");
1986
+ }
1987
+ function codeFence(source) {
1988
+ const longest = Math.max(0, ...[...source.matchAll(/`+/g)].map((match) => match[0].length));
1989
+ return "`".repeat(Math.max(3, longest + 1));
1990
+ }
1991
+ function firstServerUrl(value) {
1992
+ if (!Array.isArray(value)) return void 0;
1993
+ for (const server of value) {
1994
+ const object = asOpenApiObject(server);
1995
+ if (typeof object?.url === "string") return object.url;
1996
+ }
1997
+ return void 0;
1998
+ }
1999
+ function swaggerServerUrl(document2) {
2000
+ if (typeof document2.host !== "string" || !document2.host) return void 0;
2001
+ const scheme = Array.isArray(document2.schemes) ? document2.schemes.find((value) => typeof value === "string") : void 0;
2002
+ const basePath = typeof document2.basePath === "string" ? document2.basePath : "";
2003
+ return `${scheme ?? "https"}://${document2.host}${basePath}`;
2004
+ }
2005
+
2006
+ // src/documentation/graph.ts
2007
+ function validateDocumentation(appDir) {
2008
+ const loaded = loadDocumentationConfig(appDir);
2009
+ if (!existsSync4(loaded.sourceDir)) {
2010
+ throw new DocumentationError(
2011
+ `Documentation source directory not found: ${loaded.config.source}`,
2012
+ "documentation_source_missing",
2013
+ [{ code: "source_missing", message: loaded.config.source, file: loaded.sourceDir }]
2014
+ );
2015
+ }
2016
+ const warnings = [...loaded.warnings];
2017
+ const contentFiles = collectContentFiles(loaded.sourceDir);
2018
+ const sourceFiles = contentFiles.filter((path) => [".md", ".mdx"].includes(extname2(path).toLowerCase()));
2019
+ if (sourceFiles.length === 0 && loaded.config.openapi.length === 0) {
2020
+ throw new DocumentationError(
2021
+ `No .md or .mdx pages found in ${loaded.config.source}`,
2022
+ "documentation_pages_missing",
2023
+ [{ code: "pages_missing", message: loaded.config.source, file: loaded.sourceDir }]
2024
+ );
2025
+ }
2026
+ const pages = sourceFiles.map((sourcePath) => pageFromSource(loaded, sourcePath));
2027
+ const openApiHashes = [];
2028
+ const openApiGroups = [];
2029
+ for (const openapiConfig of loaded.config.openapi) {
2030
+ const api = parseOpenApi(resolve4(appDir), openapiConfig);
2031
+ openApiHashes.push(api.raw);
2032
+ const apiPages = api.operations.map((operation) => {
2033
+ const markdown2 = operationMarkdown(operation);
2034
+ const parsed = parseMarkdown(markdown2, `${api.sourcePath}#${operation.operationId}`);
2035
+ return {
2036
+ sourcePath: api.sourcePath,
2037
+ relativePath: `${relative3(resolve4(appDir), api.sourcePath)}#${operation.operationId}`,
2038
+ route: joinRoute(api.route, operation.operationId),
2039
+ title: operation.summary,
2040
+ description: operation.description,
2041
+ hidden: false,
2042
+ noindex: loaded.config.seo.noindex === true,
2043
+ markdown: markdown2,
2044
+ html: parsed.html,
2045
+ text: parsed.text,
2046
+ headings: parsed.headings,
2047
+ sourceFormat: "generated",
2048
+ kind: "openapi",
2049
+ openapi: operation
2050
+ };
2051
+ });
2052
+ pages.push(...apiPages);
2053
+ openApiGroups.push({
2054
+ kind: "group",
2055
+ label: api.title,
2056
+ items: apiPages.map((page) => ({ kind: "page", route: page.route, label: page.title }))
2057
+ });
2058
+ }
2059
+ assertUniqueRoutes(pages);
2060
+ const byRoute = new Map(pages.map((page) => [page.route, page]));
2061
+ const navigation = loaded.config.navigation ? normalizeNavigation(loaded.config.navigation, byRoute, loaded.config) : automaticNavigation(pages.filter((page) => page.kind === "page"));
2062
+ navigation.push(...openApiGroups);
2063
+ assertNavigationCoverage(navigation, pages, loaded.config);
2064
+ validatePageLinks(pages, byRoute, loaded.config.redirects);
2065
+ validateRedirectTargets(loaded.config, byRoute);
2066
+ const hash = createHash("sha256");
2067
+ hash.update(loaded.raw);
2068
+ for (const contentPath of contentFiles) {
2069
+ hash.update(relative3(resolve4(appDir), contentPath).split(sep2).join("/"));
2070
+ hash.update(readFileSync3(contentPath));
2071
+ }
2072
+ for (const raw of openApiHashes) hash.update(raw);
2073
+ const graph = {
2074
+ config: loaded.config,
2075
+ pages: pages.sort((a, b) => a.route.localeCompare(b.route)),
2076
+ navigation,
2077
+ sourceHash: hash.digest("hex")
2078
+ };
2079
+ return {
2080
+ appDir: resolve4(appDir),
2081
+ configPath: loaded.configPath,
2082
+ sourceDir: loaded.sourceDir,
2083
+ outputDir: loaded.outputDir,
2084
+ graph,
2085
+ warnings
2086
+ };
2087
+ }
2088
+ function pageFromSource(loaded, sourcePath) {
2089
+ const source = readFileSync3(sourcePath, "utf8");
2090
+ const relativePath = relative3(loaded.sourceDir, sourcePath).split(sep2).join("/");
2091
+ const rewritten = rewriteLocalReferences(source, relativePath, loaded.sourceDir, sourcePath);
2092
+ const sourceFormat = extname2(sourcePath).toLowerCase() === ".mdx" ? "mdx" : "markdown";
2093
+ const authored = sourceFormat === "mdx" ? parseMdx(rewritten, sourcePath) : parseMarkdown(rewritten, sourcePath);
2094
+ const route = authored.frontmatter.slug ? normalizeRoute(authored.frontmatter.slug) : routeFromRelativePath(relativePath);
2095
+ const title = authored.frontmatter.title ?? authored.headings.find((heading) => heading.depth === 1)?.text ?? titleFromFile(relativePath);
2096
+ const normalizedSource = stripFirstMarkdownH1(rewritten);
2097
+ const parsed = sourceFormat === "mdx" ? parseMdx(normalizedSource, sourcePath) : parseMarkdown(normalizedSource, sourcePath);
2098
+ return {
2099
+ sourcePath,
2100
+ relativePath,
2101
+ route,
2102
+ title,
2103
+ description: parsed.frontmatter.description,
2104
+ hidden: parsed.frontmatter.hidden === true,
2105
+ noindex: loaded.config.seo.noindex === true || parsed.frontmatter.noindex === true,
2106
+ markdown: parsed.markdown,
2107
+ html: parsed.html,
2108
+ text: parsed.text,
2109
+ headings: parsed.headings,
2110
+ sourceFormat,
2111
+ kind: "page"
2112
+ };
2113
+ }
2114
+ function collectContentFiles(sourceDir) {
2115
+ const files = [];
2116
+ const walk = (directory) => {
2117
+ for (const entry of readdirSync(directory, { withFileTypes: true })) {
2118
+ if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
2119
+ const path = join(directory, entry.name);
2120
+ if (entry.isSymbolicLink()) {
2121
+ throw new DocumentationError(
2122
+ `Documentation content may not contain symbolic links: ${path}`,
2123
+ "documentation_source_unsafe",
2124
+ [{ code: "source_symlink", message: path, file: path }]
2125
+ );
2126
+ }
2127
+ if (entry.isDirectory()) walk(path);
2128
+ else if (entry.isFile()) files.push(path);
2129
+ }
2130
+ };
2131
+ walk(sourceDir);
2132
+ return files.sort();
2133
+ }
2134
+ function normalizeNavigation(items, pages, config) {
2135
+ return items.map((item) => {
2136
+ if (typeof item === "string") {
2137
+ const route = navigationReferenceToRoute(item);
2138
+ const page = pages.get(route);
2139
+ if (!page) {
2140
+ throw new DocumentationError(
2141
+ `Navigation references a missing page: ${item}`,
2142
+ "documentation_navigation_invalid",
2143
+ [{ code: "navigation_missing_page", message: item }]
2144
+ );
2145
+ }
2146
+ return { kind: "page", route, label: page.title };
2147
+ }
2148
+ if ("group" in item) {
2149
+ return {
2150
+ kind: "group",
2151
+ label: item.group,
2152
+ items: normalizeNavigation(item.pages, pages, config)
2153
+ };
2154
+ }
2155
+ if (!isSafeLink(item.href)) {
2156
+ throw new DocumentationError(
2157
+ `Unsafe navigation link: ${item.href}`,
2158
+ "documentation_navigation_invalid",
2159
+ [{ code: "navigation_unsafe_link", message: item.href }]
2160
+ );
2161
+ }
2162
+ if (item.href.startsWith("/")) {
2163
+ const route = normalizeRoute(item.href);
2164
+ if (!pages.has(route) && !config.redirects[route]) {
2165
+ throw new DocumentationError(
2166
+ `Navigation link points to an unknown internal route: ${item.href}`,
2167
+ "documentation_navigation_invalid",
2168
+ [{ code: "navigation_missing_route", message: item.href }]
2169
+ );
2170
+ }
2171
+ return { kind: "link", href: route, label: item.label };
2172
+ }
2173
+ return { kind: "link", href: item.href, label: item.label };
2174
+ });
2175
+ }
2176
+ function automaticNavigation(pages) {
2177
+ return pages.filter((page) => !page.hidden).sort((a, b) => a.route.localeCompare(b.route)).map((page) => ({ kind: "page", route: page.route, label: page.title }));
2178
+ }
2179
+ function assertNavigationCoverage(navigation, pages, config) {
2180
+ if (!config.navigation) return;
2181
+ const routes = /* @__PURE__ */ new Set();
2182
+ const visit2 = (nodes) => {
2183
+ for (const node of nodes) {
2184
+ if (node.kind === "page") routes.add(node.route);
2185
+ else if (node.kind === "group") visit2(node.items);
2186
+ }
2187
+ };
2188
+ visit2(navigation);
2189
+ const orphaned = pages.filter((page) => page.kind === "page" && !page.hidden && !routes.has(page.route));
2190
+ if (orphaned.length > 0) {
2191
+ throw new DocumentationError(
2192
+ `${orphaned.length} public page(s) are missing from navigation`,
2193
+ "documentation_navigation_incomplete",
2194
+ orphaned.map((page) => ({
2195
+ code: "navigation_orphan",
2196
+ message: page.route,
2197
+ file: page.sourcePath
2198
+ }))
2199
+ );
2200
+ }
2201
+ }
2202
+ function validatePageLinks(pages, byRoute, redirects) {
2203
+ const diagnostics = [];
2204
+ for (const page of pages) {
2205
+ if (page.kind !== "page") continue;
2206
+ const pattern = /(!?)\[[^\]]*\]\(([^)\s]+)(?:\s+["'][^"']*["'])?\)/g;
2207
+ for (const match of page.markdown.matchAll(pattern)) {
2208
+ const isImage = match[1] === "!";
2209
+ const href = match[2];
2210
+ if (isImage || href.startsWith("/media/")) continue;
2211
+ if (/^(?:https?:|mailto:|#|data:)/i.test(href)) continue;
2212
+ const routePart = href.split("#")[0].split("?")[0];
2213
+ if (!routePart) continue;
2214
+ const target = routePart.startsWith("/") ? normalizeRoute(routePart) : normalizeRoute(posix.join(dirname(page.route), routePart));
2215
+ const normalized = navigationReferenceToRoute(target);
2216
+ if (!byRoute.has(normalized) && !redirects[normalized]) {
2217
+ diagnostics.push({
2218
+ code: "broken_internal_link",
2219
+ message: `${href} resolves to ${normalized}`,
2220
+ file: page.sourcePath,
2221
+ line: page.markdown.slice(0, match.index).split("\n").length
2222
+ });
2223
+ }
2224
+ }
2225
+ }
2226
+ if (diagnostics.length > 0) {
2227
+ throw new DocumentationError(
2228
+ `${diagnostics.length} broken internal documentation link(s)`,
2229
+ "documentation_links_invalid",
2230
+ diagnostics
2231
+ );
2232
+ }
2233
+ }
2234
+ function rewriteLocalReferences(source, relativePath, sourceDir, sourcePath) {
2235
+ const pageDirectory = posix.dirname(relativePath);
2236
+ return source.replace(
2237
+ /(!?)\[([^\]]*)\]\(([^)\s]+)((?:\s+["'][^"']*["'])?)\)/g,
2238
+ (match, imageMarker, label, href, title) => {
2239
+ if (/^(?:https?:|mailto:|data:|#)/i.test(href)) return match;
2240
+ const [pathPart, suffix = ""] = href.split(/(?=[?#])/);
2241
+ if (!pathPart) return match;
2242
+ const relativeTarget = pathPart.startsWith("/") ? pathPart.replace(/^\//, "") : posix.normalize(posix.join(pageDirectory, pathPart));
2243
+ if (relativeTarget === ".." || relativeTarget.startsWith("../")) {
2244
+ throw new DocumentationError(
2245
+ `Documentation reference escapes the source directory: ${href}`,
2246
+ "documentation_link_unsafe",
2247
+ [{ code: "link_path_escape", message: href, file: sourcePath }]
2248
+ );
2249
+ }
2250
+ const absoluteTarget = resolve4(sourceDir, relativeTarget);
2251
+ if (imageMarker === "!") {
2252
+ if (!existsSync4(absoluteTarget)) {
2253
+ throw new DocumentationError(
2254
+ `Documentation image not found: ${href}`,
2255
+ "documentation_asset_missing",
2256
+ [{ code: "asset_missing", message: href, file: sourcePath }]
2257
+ );
2258
+ }
2259
+ return `![${label}](/media/${relativeTarget}${suffix}${title})`;
2260
+ }
2261
+ if (/\.(?:md|mdx)$/i.test(relativeTarget)) {
2262
+ return `[${label}](${routeFromRelativePath(relativeTarget)}${suffix}${title})`;
2263
+ }
2264
+ const markdownTarget = [
2265
+ `${absoluteTarget}.mdx`,
2266
+ `${absoluteTarget}.md`,
2267
+ join(absoluteTarget, "index.mdx"),
2268
+ join(absoluteTarget, "index.md")
2269
+ ].find(existsSync4);
2270
+ if (markdownTarget) {
2271
+ const targetRelative = relative3(sourceDir, markdownTarget).split(sep2).join("/");
2272
+ return `[${label}](${routeFromRelativePath(targetRelative)}${suffix}${title})`;
2273
+ }
2274
+ if (existsSync4(absoluteTarget)) {
2275
+ return `[${label}](/media/${relativeTarget}${suffix}${title})`;
2276
+ }
2277
+ return match;
2278
+ }
2279
+ );
2280
+ }
2281
+ function validateRedirectTargets(config, byRoute) {
2282
+ for (const [from, to] of Object.entries(config.redirects)) {
2283
+ const normalizedFrom = normalizeRoute(from);
2284
+ if (byRoute.has(normalizedFrom)) {
2285
+ throw new DocumentationError(
2286
+ `Redirect source collides with a page: ${from}`,
2287
+ "documentation_redirect_invalid",
2288
+ [{ code: "redirect_page_collision", message: from }]
2289
+ );
2290
+ }
2291
+ const finalTarget = followRedirects(to, config.redirects);
2292
+ if (!byRoute.has(normalizeRoute(finalTarget))) {
2293
+ throw new DocumentationError(
2294
+ `Redirect target does not exist: ${from} -> ${to}`,
2295
+ "documentation_redirect_invalid",
2296
+ [{ code: "redirect_missing_target", message: `${from} -> ${to}` }]
2297
+ );
2298
+ }
2299
+ }
2300
+ }
2301
+ function followRedirects(route, redirects) {
2302
+ let cursor = route;
2303
+ const seen = /* @__PURE__ */ new Set();
2304
+ while (redirects[cursor] && !seen.has(cursor)) {
2305
+ seen.add(cursor);
2306
+ cursor = redirects[cursor];
2307
+ }
2308
+ return cursor;
2309
+ }
2310
+ function assertUniqueRoutes(pages) {
2311
+ const seen = /* @__PURE__ */ new Map();
2312
+ for (const page of pages) {
2313
+ const existing = seen.get(page.route);
2314
+ if (existing) {
2315
+ throw new DocumentationError(
2316
+ `Multiple documentation pages resolve to ${page.route}`,
2317
+ "documentation_route_collision",
2318
+ [existing, page].map((candidate) => ({
2319
+ code: "route_collision",
2320
+ message: candidate.route,
2321
+ file: candidate.sourcePath
2322
+ }))
2323
+ );
2324
+ }
2325
+ if (["/api", "/assets", "/data", "/media", "/mcp", "/.well-known"].some(
2326
+ (reserved) => page.route === reserved || page.route.startsWith(`${reserved}/`)
2327
+ )) {
2328
+ throw new DocumentationError(
2329
+ `Documentation route uses a reserved prefix: ${page.route}`,
2330
+ "documentation_route_reserved",
2331
+ [{ code: "reserved_route", message: page.route, file: page.sourcePath }]
2332
+ );
2333
+ }
2334
+ seen.set(page.route, page);
2335
+ }
2336
+ }
2337
+ function navigationReferenceToRoute(value) {
2338
+ return normalizeRoute(value);
2339
+ }
2340
+ function titleFromFile(relativePath) {
2341
+ const stem = basename2(relativePath, extname2(relativePath));
2342
+ const candidate = stem.toLowerCase() === "index" ? basename2(dirname(relativePath)) : stem;
2343
+ if (!candidate || candidate === ".") return "Overview";
2344
+ return candidate.split(/[-_]/).filter(Boolean).map(capitalize).join(" ");
2345
+ }
2346
+ function capitalize(value) {
2347
+ return value.charAt(0).toUpperCase() + value.slice(1);
2348
+ }
2349
+ function joinRoute(left, right) {
2350
+ return normalizeRoute(`${left}/${slugify(right)}`);
2351
+ }
2352
+ function isSafeLink(value) {
2353
+ return /^(?:https?:\/\/|mailto:|\/|#)/i.test(value);
2354
+ }
2355
+
2356
+ // src/documentation/custom-runtime.ts
2357
+ import { compileSync } from "@mdx-js/mdx";
2358
+ import { parse as parseJavaScript } from "acorn";
2359
+ import { buildSync } from "esbuild";
2360
+ import { createRequire } from "module";
2361
+ import { existsSync as existsSync5, mkdtempSync, readFileSync as readFileSync4, rmSync, writeFileSync } from "fs";
2362
+ import { tmpdir } from "os";
2363
+ import { dirname as dirname2, extname as extname3, join as join2, relative as relative4, resolve as resolve5, sep as sep3 } from "path";
2364
+ import { fileURLToPath } from "url";
2365
+ function buildCustomDocumentationRuntime(validation, basePath) {
2366
+ const sitePath = resolve5(validation.appDir, "documentation.tsx");
2367
+ const mdxPages = validation.graph.pages.filter((page) => page.sourceFormat === "mdx");
2368
+ if (!existsSync5(sitePath) && mdxPages.length === 0) return null;
2369
+ const temporaryDir = mkdtempSync(join2(tmpdir(), "deepspace-documentation-runtime-"));
2370
+ try {
2371
+ const compiledPages = mdxPages.map((page, index) => {
2372
+ const source = readFileSync4(page.sourcePath, "utf8");
2373
+ const rewritten = rewriteLocalReferences(
2374
+ source,
2375
+ page.relativePath,
2376
+ validation.sourceDir,
2377
+ page.sourcePath
2378
+ );
2379
+ const compiled = compileSync(
2380
+ publicDocumentationReferences(stripFirstMarkdownH1(rewritten), basePath),
2381
+ mdxCompileOptions()
2382
+ );
2383
+ const path = join2(temporaryDir, `page-${index}.mjs`);
2384
+ writeFileSync(path, resolveCompiledImports(String(compiled), page.sourcePath, validation.appDir));
2385
+ return { path, route: page.route };
2386
+ });
2387
+ const clientEntry = runtimeEntry("documentation-client-core.js", "runtime/client.tsx");
2388
+ const publicEntry = runtimeEntry("documentation-react.js", "runtime/public.tsx");
2389
+ const serverEntry = runtimeEntry("documentation-server-core.js", "runtime/server.tsx");
2390
+ const browserEntry = join2(temporaryDir, "browser-entry.ts");
2391
+ const serverBundleEntry = join2(temporaryDir, "server-entry.ts");
2392
+ writeFileSync(browserEntry, generatedBrowserEntry(clientEntry, publicEntry, sitePath, compiledPages));
2393
+ writeFileSync(serverBundleEntry, generatedServerEntry(serverEntry, sitePath, compiledPages));
2394
+ const reactAliases = runtimeDependencyAliases(validation.appDir);
2395
+ const alias = { ...reactAliases, "deepspace/documentation/react": publicEntry };
2396
+ const browserOutDir = join2(temporaryDir, "browser");
2397
+ const sharedBuildOptions = {
2398
+ absWorkingDir: temporaryDir,
2399
+ bundle: true,
2400
+ define: { "process.env.NODE_ENV": '"production"' },
2401
+ jsx: "automatic",
2402
+ target: ["es2022"],
2403
+ ...existsSync5(resolve5(validation.appDir, "tsconfig.json")) ? { tsconfig: resolve5(validation.appDir, "tsconfig.json") } : {},
2404
+ write: false
2405
+ };
2406
+ const browser = buildSync({
2407
+ ...sharedBuildOptions,
2408
+ alias,
2409
+ entryPoints: ["browser-entry.ts"],
2410
+ chunkNames: "documentation-page-[hash]",
2411
+ entryNames: "documentation-custom-runtime",
2412
+ format: "esm",
2413
+ loader: assetLoaders("dataurl"),
2414
+ minify: true,
2415
+ outdir: browserOutDir,
2416
+ platform: "browser",
2417
+ splitting: true
2418
+ });
2419
+ const server = buildSync({
2420
+ ...sharedBuildOptions,
2421
+ alias: { ...reactAliases, "deepspace/documentation/react": serverEntry },
2422
+ entryPoints: ["server-entry.ts"],
2423
+ format: "cjs",
2424
+ loader: { ...assetLoaders("dataurl"), ".css": "text" },
2425
+ logLevel: "silent",
2426
+ minify: false,
2427
+ outdir: join2(temporaryDir, "server"),
2428
+ platform: "node",
2429
+ target: ["node22"]
2430
+ });
2431
+ const browserAssets = (browser.outputFiles ?? []).map((output) => ({
2432
+ path: relative4(browserOutDir, output.path).split(sep3).join("/"),
2433
+ content: output.text
2434
+ }));
2435
+ if (!browserAssets.some((asset) => asset.path === "documentation-custom-runtime.js")) {
2436
+ throw new Error("Executable documentation build did not emit documentation-custom-runtime.js");
2437
+ }
2438
+ const stylesheets = browserAssets.filter((asset) => extname3(asset.path) === ".css").map((asset) => documentationPublicPath(basePath, `/assets/${asset.path}`));
2439
+ const serverJavaScript = outputWithExtension(server, ".js").text;
2440
+ const module = { exports: {} };
2441
+ const evaluate = new Function("require", "module", "exports", serverJavaScript);
2442
+ evaluate(createRequire(join2(validation.appDir, "package.json")), module, module.exports);
2443
+ if (typeof module.exports.render !== "function") {
2444
+ throw new Error("The generated documentation server renderer did not export render()");
2445
+ }
2446
+ return {
2447
+ browserAssets,
2448
+ stylesheets,
2449
+ render: (data, route) => module.exports.render?.(route, data) ?? ""
2450
+ };
2451
+ } catch (error) {
2452
+ if (error instanceof DocumentationError) throw error;
2453
+ throw new DocumentationError(
2454
+ `Unable to compile executable documentation: ${errorMessage(error)}`,
2455
+ "documentation_runtime_build_failed",
2456
+ [{ code: "runtime_compile", message: errorMessage(error), file: existsSync5(sitePath) ? sitePath : void 0 }]
2457
+ );
2458
+ } finally {
2459
+ rmSync(temporaryDir, { recursive: true, force: true });
2460
+ }
2461
+ }
2462
+ function generatedBrowserEntry(clientEntry, publicEntry, sitePath, pages) {
2463
+ const siteImport = existsSync5(sitePath) ? `import Site from ${quote(sitePath)}` : `import { DefaultDocumentation as Site } from ${quote(publicEntry)}`;
2464
+ const pageLoaders = pages.map((page) => `${quote(page.route)}: () => import(${quote(page.path)})`).join(",\n");
2465
+ return `import { hydrateDocumentation } from ${quote(clientEntry)}
2466
+ ${siteImport}
2467
+ const loaders = { ${pageLoaders} }
2468
+ const pages = {}
2469
+ async function loadPage(route) {
2470
+ const loader = loaders[route]
2471
+ if (loader && !pages[route]) pages[route] = (await loader()).default
2472
+ }
2473
+ const payload = document.getElementById('deepspace-documentation-data')
2474
+ if (payload?.textContent) await loadPage(JSON.parse(payload.textContent).page.route)
2475
+ hydrateDocumentation({ Site, pages, loadPage })
2476
+ `;
2477
+ }
2478
+ function generatedServerEntry(serverEntry, sitePath, pages) {
2479
+ const imports = pages.map((page, index) => `import Page${index} from ${quote(page.path)}`).join("\n");
2480
+ const siteImport = existsSync5(sitePath) ? `import Site from ${quote(sitePath)}` : `import { DefaultDocumentation as Site } from ${quote(serverEntry)}`;
2481
+ const pageMap = pages.map((page, index) => `${quote(page.route)}: Page${index}`).join(",\n");
2482
+ return `import { renderDocumentation } from ${quote(serverEntry)}
2483
+ ${siteImport}
2484
+ ${imports}
2485
+ const pages = { ${pageMap} }
2486
+ export function render(route, data) {
2487
+ return renderDocumentation({ data, Page: pages[route], Site })
2488
+ }
2489
+ `;
2490
+ }
2491
+ function resolveCompiledImports(code, sourcePath, appDir) {
2492
+ const tree = parseJavaScript(code, { ecmaVersion: "latest", sourceType: "module" });
2493
+ const replacements = [];
2494
+ const appRequire = createRequire(join2(appDir, "package.json"));
2495
+ visitJavaScript(tree, (node) => {
2496
+ const source = importSource(node);
2497
+ if (!source || typeof source.value !== "string" || source.start === void 0 || source.end === void 0) {
2498
+ return;
2499
+ }
2500
+ const specifier = source.value;
2501
+ if (/^(?:data:|https?:|node:)/.test(specifier)) return;
2502
+ const resolved = specifier.startsWith(".") ? resolve5(dirname2(sourcePath), specifier) : specifier === "deepspace/documentation/react" || specifier === "react" || specifier.startsWith("react/") ? specifier : appRequire.resolve(specifier);
2503
+ replacements.push({ start: source.start, end: source.end, value: quote(resolved.split(sep3).join("/")) });
2504
+ });
2505
+ return replacements.sort((left, right) => right.start - left.start).reduce(
2506
+ (output, replacement) => `${output.slice(0, replacement.start)}${replacement.value}${output.slice(replacement.end)}`,
2507
+ code
2508
+ );
2509
+ }
2510
+ function importSource(node) {
2511
+ if (node.type === "ImportDeclaration" || node.type === "ExportAllDeclaration" || node.type === "ExportNamedDeclaration" || node.type === "ImportExpression") return node.source;
2512
+ return void 0;
2513
+ }
2514
+ function visitJavaScript(node, visitor) {
2515
+ visitor(node);
2516
+ for (const value of Object.values(node)) {
2517
+ if (Array.isArray(value)) {
2518
+ for (const child of value) {
2519
+ if (isJavaScriptNode(child)) visitJavaScript(child, visitor);
2520
+ }
2521
+ } else if (isJavaScriptNode(value)) {
2522
+ visitJavaScript(value, visitor);
2523
+ }
2524
+ }
2525
+ }
2526
+ function isJavaScriptNode(value) {
2527
+ return Boolean(value && typeof value === "object" && "type" in value && typeof value.type === "string");
2528
+ }
2529
+ function runtimeEntry(builtName, sourceRelativePath) {
2530
+ const built = fileURLToPath(new URL(`./${builtName}`, import.meta.url));
2531
+ if (existsSync5(built)) return built;
2532
+ const source = fileURLToPath(new URL(`./${sourceRelativePath}`, import.meta.url));
2533
+ if (!existsSync5(source)) {
2534
+ throw new Error(`${builtName} is missing from this deepspace installation; reinstall the package`);
2535
+ }
2536
+ return source;
2537
+ }
2538
+ function runtimeDependencyAliases(appDir) {
2539
+ const require2 = createRequire(join2(appDir, "package.json"));
2540
+ return Object.fromEntries(
2541
+ ["react", "react/jsx-runtime", "react-dom", "react-dom/client", "react-dom/server"].map((specifier) => [specifier, require2.resolve(specifier)])
2542
+ );
2543
+ }
2544
+ function outputWithExtension(result, extension) {
2545
+ const output = optionalOutputWithExtension(result, extension);
2546
+ if (!output) throw new Error(`Executable documentation build did not emit a ${extension} file`);
2547
+ return output;
2548
+ }
2549
+ function optionalOutputWithExtension(result, extension) {
2550
+ return result.outputFiles?.find((output) => extname3(output.path) === extension);
2551
+ }
2552
+ function assetLoaders(imageLoader) {
2553
+ return {
2554
+ ".avif": imageLoader,
2555
+ ".css": "css",
2556
+ ".gif": imageLoader,
2557
+ ".jpeg": imageLoader,
2558
+ ".jpg": imageLoader,
2559
+ ".png": imageLoader,
2560
+ ".svg": imageLoader,
2561
+ ".webp": imageLoader,
2562
+ ".woff": imageLoader,
2563
+ ".woff2": imageLoader
2564
+ };
2565
+ }
2566
+ function quote(value) {
2567
+ return JSON.stringify(value);
2568
+ }
2569
+
2570
+ // src/documentation/output.ts
2571
+ import { createHash as createHash2 } from "crypto";
2572
+ import {
2573
+ existsSync as existsSync6,
2574
+ mkdirSync,
2575
+ readFileSync as readFileSync5,
2576
+ readdirSync as readdirSync2,
2577
+ rmSync as rmSync2,
2578
+ writeFileSync as writeFileSync2
2579
+ } from "fs";
2580
+ import { dirname as dirname3, extname as extname4, join as join3, relative as relative5, resolve as resolve6, sep as sep4 } from "path";
2581
+ import sanitizeHtml2 from "sanitize-html";
2582
+ var MEDIA_EXTENSIONS = /* @__PURE__ */ new Set([
2583
+ ".avif",
2584
+ ".gif",
2585
+ ".ico",
2586
+ ".jpeg",
2587
+ ".jpg",
2588
+ ".png",
2589
+ ".svg",
2590
+ ".webp",
2591
+ ".woff",
2592
+ ".woff2"
2593
+ ]);
2594
+ var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".css", ".js", ".jsx", ".md", ".mdx", ".ts", ".tsx"]);
2595
+ var DocumentationOutput = class {
2596
+ constructor(outputDir, appDir, sourceDir) {
2597
+ this.outputDir = outputDir;
2598
+ assertSafeOutput(appDir, sourceDir, outputDir);
2599
+ if (existsSync6(outputDir)) rmSync2(outputDir, { recursive: true, force: true });
2600
+ mkdirSync(outputDir, { recursive: true });
2601
+ }
2602
+ files = /* @__PURE__ */ new Set();
2603
+ write(relativePath, content) {
2604
+ const path = this.safePath(relativePath);
2605
+ mkdirSync(dirname3(path), { recursive: true });
2606
+ writeFileSync2(path, content);
2607
+ this.files.add(normalizeRelativePath(relativePath));
2608
+ }
2609
+ copyMedia(sourceDir, auxiliarySources) {
2610
+ const walk = (directory) => {
2611
+ for (const entry of readdirSync2(directory, { withFileTypes: true })) {
2612
+ if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
2613
+ const sourcePath = join3(directory, entry.name);
2614
+ if (entry.isDirectory()) {
2615
+ walk(sourcePath);
2616
+ continue;
2617
+ }
2618
+ const extension = extname4(entry.name).toLowerCase();
2619
+ if (SOURCE_EXTENSIONS.has(extension) || auxiliarySources.has(resolve6(sourcePath))) continue;
2620
+ if (!MEDIA_EXTENSIONS.has(extension)) {
2621
+ throw new DocumentationError(
2622
+ `Unsupported documentation media type: ${relative5(sourceDir, sourcePath)}`,
2623
+ "documentation_media_unsupported",
2624
+ [{
2625
+ code: "media_type_unsupported",
2626
+ message: extension || "(no extension)",
2627
+ file: sourcePath
2628
+ }]
2629
+ );
2630
+ }
2631
+ const mediaPath = `media/${normalizeRelativePath(relative5(sourceDir, sourcePath))}`;
2632
+ const content = extension === ".svg" ? sanitizeSvg(readFileSync5(sourcePath, "utf8"), sourcePath) : readFileSync5(sourcePath);
2633
+ this.write(mediaPath, content);
2634
+ }
2635
+ };
2636
+ walk(sourceDir);
2637
+ }
2638
+ hash() {
2639
+ const hash = createHash2("sha256");
2640
+ for (const file of [...this.files].sort()) {
2641
+ hash.update(file);
2642
+ hash.update(readFileSync5(join3(this.outputDir, file)));
2643
+ }
2644
+ return hash.digest("hex");
2645
+ }
2646
+ safePath(relativePath) {
2647
+ const path = resolve6(this.outputDir, relativePath);
2648
+ const rel = relative5(this.outputDir, path);
2649
+ if (rel === ".." || rel.startsWith(`..${sep4}`) || rel === "") {
2650
+ throw new DocumentationError(
2651
+ `Refusing documentation artifact outside the output directory: ${relativePath}`,
2652
+ "documentation_output_unsafe",
2653
+ [{ code: "artifact_path_escape", message: relativePath }]
2654
+ );
2655
+ }
2656
+ return path;
2657
+ }
2658
+ };
2659
+ function assertSafeOutput(appDir, sourceDir, outputDir) {
2660
+ const appRelative = relative5(appDir, outputDir);
2661
+ const sourceRelative = relative5(sourceDir, outputDir);
2662
+ if (outputDir === appDir || outputDir === sourceDir || appRelative === ".." || appRelative.startsWith(`..${sep4}`) || sourceRelative === "" || !sourceRelative.startsWith("..") && sourceRelative !== "") {
2663
+ throw new DocumentationError(
2664
+ `Refusing unsafe documentation output directory: ${outputDir}`,
2665
+ "documentation_output_unsafe",
2666
+ [{ code: "unsafe_output", message: outputDir }]
2667
+ );
2668
+ }
2669
+ }
2670
+ function sanitizeSvg(source, sourcePath) {
2671
+ const safe = sanitizeHtml2(source, {
2672
+ allowedTags: [
2673
+ "svg",
2674
+ "g",
2675
+ "path",
2676
+ "circle",
2677
+ "ellipse",
2678
+ "line",
2679
+ "polyline",
2680
+ "polygon",
2681
+ "rect",
2682
+ "image",
2683
+ "defs",
2684
+ "linearGradient",
2685
+ "radialGradient",
2686
+ "stop",
2687
+ "clipPath",
2688
+ "mask",
2689
+ "title",
2690
+ "desc",
2691
+ "text",
2692
+ "tspan"
2693
+ ],
2694
+ allowedAttributes: {
2695
+ svg: [
2696
+ "xmlns",
2697
+ "xmlns:xlink",
2698
+ "viewBox",
2699
+ "width",
2700
+ "height",
2701
+ "fill",
2702
+ "stroke",
2703
+ "role",
2704
+ "aria-label"
2705
+ ],
2706
+ image: ["href", "xlink:href", "x", "y", "width", "height", "preserveAspectRatio"],
2707
+ "*": [
2708
+ "id",
2709
+ "class",
2710
+ "d",
2711
+ "x",
2712
+ "y",
2713
+ "x1",
2714
+ "x2",
2715
+ "y1",
2716
+ "y2",
2717
+ "cx",
2718
+ "cy",
2719
+ "r",
2720
+ "rx",
2721
+ "ry",
2722
+ "points",
2723
+ "width",
2724
+ "height",
2725
+ "viewBox",
2726
+ "fill",
2727
+ "fill-rule",
2728
+ "fill-opacity",
2729
+ "stroke",
2730
+ "stroke-width",
2731
+ "stroke-linecap",
2732
+ "stroke-linejoin",
2733
+ "stroke-opacity",
2734
+ "opacity",
2735
+ "transform",
2736
+ "offset",
2737
+ "stop-color",
2738
+ "stop-opacity",
2739
+ "clip-path",
2740
+ "mask",
2741
+ "role",
2742
+ "aria-label",
2743
+ "aria-hidden",
2744
+ "font-family",
2745
+ "font-size",
2746
+ "font-style",
2747
+ "font-weight",
2748
+ "letter-spacing",
2749
+ "text-anchor",
2750
+ "dominant-baseline"
2751
+ ]
2752
+ },
2753
+ allowedSchemes: ["data"],
2754
+ allowProtocolRelative: false,
2755
+ disallowedTagsMode: "discard",
2756
+ parser: { lowerCaseTags: false, lowerCaseAttributeNames: false }
2757
+ });
2758
+ if (!/<svg\b/i.test(safe)) {
2759
+ throw new DocumentationError(
2760
+ `Invalid documentation SVG: ${relative5(process.cwd(), sourcePath)}`,
2761
+ "documentation_media_invalid",
2762
+ [{ code: "svg_invalid", message: "Expected an SVG root element", file: sourcePath }]
2763
+ );
2764
+ }
2765
+ return safe;
2766
+ }
2767
+ function normalizeRelativePath(path) {
2768
+ return path.split(sep4).join("/");
2769
+ }
2770
+
2771
+ // src/documentation/resource-path.ts
2772
+ var RESOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
2773
+ "7z",
2774
+ "avif",
2775
+ "css",
2776
+ "csv",
2777
+ "gif",
2778
+ "gz",
2779
+ "ico",
2780
+ "jpeg",
2781
+ "jpg",
2782
+ "js",
2783
+ "json",
2784
+ "map",
2785
+ "md",
2786
+ "mjs",
2787
+ "mp3",
2788
+ "mp4",
2789
+ "pdf",
2790
+ "png",
2791
+ "svg",
2792
+ "tar",
2793
+ "txt",
2794
+ "wasm",
2795
+ "webm",
2796
+ "webp",
2797
+ "woff",
2798
+ "woff2",
2799
+ "xml",
2800
+ "yaml",
2801
+ "yml",
2802
+ "zip"
2803
+ ]);
2804
+ function isNativeDocumentationResourcePath(pathname) {
2805
+ const leaf = pathname.slice(pathname.lastIndexOf("/") + 1);
2806
+ const extension = leaf.includes(".") ? leaf.slice(leaf.lastIndexOf(".") + 1).toLowerCase() : "";
2807
+ return extension !== "" && RESOURCE_EXTENSIONS.has(extension);
2808
+ }
2809
+ function encodeNativeDocumentationPath(pathname) {
2810
+ return pathname.split("/").map((segment) => encodeURIComponent(segment)).join("/");
2811
+ }
2812
+
2813
+ // src/documentation/skill.ts
2814
+ import { createHash as createHash3 } from "crypto";
2815
+ var AGENT_SKILLS_SCHEMA = "https://schemas.agentskills.io/discovery/0.2.0/schema.json";
2816
+ function createDocumentationSkillArtifacts(graph, baseUrl, basePath = "/docs") {
2817
+ const name = documentationSkillName(graph.config.name);
2818
+ const description = documentationSkillDescription(graph.config.name, graph.config.description);
2819
+ const root = baseUrl?.replace(/\/$/, "") ?? "";
2820
+ const markdown2 = renderDocumentationSkill(graph, name, description, root);
2821
+ const digest = createHash3("sha256").update(markdown2).digest("hex");
2822
+ return {
2823
+ name,
2824
+ description,
2825
+ markdown: markdown2,
2826
+ legacyIndex: {
2827
+ skills: [{ name, description, files: ["SKILL.md"] }]
2828
+ },
2829
+ discoveryIndex: {
2830
+ $schema: AGENT_SKILLS_SCHEMA,
2831
+ skills: [
2832
+ {
2833
+ name,
2834
+ type: "skill-md",
2835
+ description,
2836
+ url: documentationPublicPath(basePath, `/.well-known/agent-skills/${name}/skill.md`),
2837
+ digest: `sha256:${digest}`
2838
+ }
2839
+ ]
2840
+ }
2841
+ };
2842
+ }
2843
+ function documentationSkillName(value) {
2844
+ const normalized = value.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-");
2845
+ return normalized.slice(0, 64).replace(/-+$/g, "") || "documentation";
2846
+ }
2847
+ function documentationSkillDescription(name, description) {
2848
+ const detail = description?.replace(/\s+/g, " ").trim();
2849
+ const value = detail ? `Use when answering questions, integrating, or implementing work with ${name}. ${detail}` : `Use when answering questions, integrating, or implementing work with ${name}.`;
2850
+ return value.slice(0, 1024).trim();
2851
+ }
2852
+ function renderDocumentationSkill(graph, name, description, root) {
2853
+ const lines = [
2854
+ "---",
2855
+ `name: ${JSON.stringify(name)}`,
2856
+ `description: ${JSON.stringify(description)}`,
2857
+ "metadata:",
2858
+ ' version: "1.0"',
2859
+ ` source-hash: ${JSON.stringify(graph.sourceHash)}`,
2860
+ "---",
2861
+ "",
2862
+ `# ${graph.config.name}`,
2863
+ "",
2864
+ graph.config.description ?? `Use the published ${documentationSubject(graph.config.name)} as the source of truth.`,
2865
+ "",
2866
+ "## Grounding workflow",
2867
+ "",
2868
+ `1. Read [the documentation index](${root}/llms.txt) to discover the available pages.`,
2869
+ graph.config.mcp.access === "public" ? `2. Prefer the read-only MCP server at \`${root}/mcp\` for ranked search and exact page reads.` : `2. Search the index, then open the relevant page Markdown directly.`,
2870
+ `3. Read a page's canonical Markdown before making implementation claims or quoting commands.`,
2871
+ `4. Treat this release as authoritative; do not mix instructions from another version or hostname.`,
2872
+ "",
2873
+ "## Documentation map",
2874
+ "",
2875
+ ...renderNavigation(graph.navigation, graph.pages, root),
2876
+ "## Constraints",
2877
+ "",
2878
+ "- Do not invent APIs, commands, configuration keys, or supported behavior.",
2879
+ "- Preserve security, authentication, billing, and deployment boundaries stated in the documentation.",
2880
+ "- When pages disagree, prefer the more specific page and report the conflict.",
2881
+ "- Link the exact source page in the final answer when the client supports citations.",
2882
+ ""
2883
+ ];
2884
+ return lines.join("\n");
2885
+ }
2886
+ function renderNavigation(navigation, pages, root) {
2887
+ const byRoute = new Map(pages.map((page) => [page.route, page]));
2888
+ const lines = [];
2889
+ const seen = /* @__PURE__ */ new Set();
2890
+ const visit2 = (nodes, depth) => {
2891
+ for (const node of nodes) {
2892
+ if (node.kind === "group") {
2893
+ lines.push(`${"#".repeat(Math.min(depth + 2, 6))} ${node.label}`, "");
2894
+ visit2(node.items, depth + 1);
2895
+ continue;
2896
+ }
2897
+ if (node.kind !== "page" || seen.has(node.route)) continue;
2898
+ const page = byRoute.get(node.route);
2899
+ if (!page || page.hidden) continue;
2900
+ seen.add(page.route);
2901
+ const markdownRoute = markdownUrlForRoute(page.route);
2902
+ lines.push(renderPageLink(page, root, markdownRoute));
2903
+ }
2904
+ };
2905
+ visit2(navigation, 1);
2906
+ const remaining = pages.filter((page) => !page.hidden && !seen.has(page.route));
2907
+ if (remaining.length > 0) {
2908
+ lines.push("### More", "");
2909
+ for (const page of remaining) {
2910
+ const markdownRoute = markdownUrlForRoute(page.route);
2911
+ lines.push(renderPageLink(page, root, markdownRoute));
2912
+ }
2913
+ }
2914
+ lines.push("");
2915
+ return lines;
2916
+ }
2917
+ function renderPageLink(page, root, markdownRoute) {
2918
+ return `- [${page.title}](${root}${markdownRoute})${page.description ? ` \u2014 ${page.description}` : ""}`;
2919
+ }
2920
+
2921
+ // src/documentation/renderer.ts
2922
+ import { buildSync as buildSync2 } from "esbuild";
2923
+ import { createRequire as createRequire2 } from "module";
2924
+ var cachedRenderer;
2925
+ function renderDefaultDocumentation(data) {
2926
+ cachedRenderer ??= loadDefaultRenderer();
2927
+ return cachedRenderer.renderDefaultDocumentation(data);
2928
+ }
2929
+ function loadDefaultRenderer() {
2930
+ const entry = runtimeEntry("documentation-default-renderer.cjs", "runtime/default-renderer.tsx");
2931
+ if (entry.endsWith(".cjs")) {
2932
+ return createRequire2(import.meta.url)(entry);
2933
+ }
2934
+ const result = buildSync2({
2935
+ entryPoints: [entry],
2936
+ bundle: true,
2937
+ write: false,
2938
+ minify: false,
2939
+ platform: "node",
2940
+ format: "cjs",
2941
+ target: ["node22"],
2942
+ jsx: "automatic",
2943
+ define: { "process.env.NODE_ENV": '"production"' }
2944
+ });
2945
+ const source = result.outputFiles?.[0]?.text;
2946
+ if (!source) throw new Error("Default documentation renderer did not emit JavaScript");
2947
+ const module = { exports: {} };
2948
+ const evaluate = new Function("require", "module", "exports", source);
2949
+ evaluate(createRequire2(import.meta.url), module, module.exports);
2950
+ return module.exports;
2951
+ }
2952
+
2953
+ // src/documentation/runtime/styles/base.ts
2954
+ var DOCUMENTATION_BASE_CSS = String.raw`
2955
+ :root {
2956
+ --documentation-accent: #635bff;
2957
+ --documentation-accent-2: #24c8ff;
2958
+ --documentation-brand-bg: #f8fafc;
2959
+ --documentation-brand-bg-dark: #0c0e14;
2960
+ --documentation-font-body: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
2961
+ --documentation-font-heading: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
2962
+ --documentation-font-mono: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
2963
+ --documentation-bg: #fbfcfe;
2964
+ --documentation-surface: #ffffff;
2965
+ --documentation-surface-raised: #ffffff;
2966
+ --documentation-panel: #f4f6fa;
2967
+ --documentation-panel-strong: #edf0f6;
2968
+ --documentation-text: #11131a;
2969
+ --documentation-text-soft: #353a47;
2970
+ --documentation-muted: #5c6270;
2971
+ --documentation-faint: #646a78;
2972
+ --documentation-border: #e4e7ed;
2973
+ --documentation-border-strong: #d7dbe4;
2974
+ --documentation-code-bg: #11131a;
2975
+ --documentation-code-panel: #171a23;
2976
+ --documentation-code-text: #e8ebf5;
2977
+ --documentation-shadow-sm: 0 1px 2px rgba(15, 18, 28, .04), 0 2px 8px rgba(15, 18, 28, .03);
2978
+ --documentation-shadow-lg: 0 24px 80px rgba(15, 18, 28, .18), 0 6px 24px rgba(15, 18, 28, .08);
2979
+ --documentation-header-height: 66px;
2980
+ --documentation-sidebar-width: 292px;
2981
+ color-scheme: light;
2982
+ font-family: var(--documentation-font-body);
2983
+ }
2984
+
2985
+ :root[data-theme="dark"] {
2986
+ --documentation-brand-bg: var(--documentation-brand-bg-dark);
2987
+ --documentation-bg: var(--documentation-brand-bg-dark);
2988
+ --documentation-surface: #10131b;
2989
+ --documentation-surface-raised: #151923;
2990
+ --documentation-panel: #171b25;
2991
+ --documentation-panel-strong: #1e2330;
2992
+ --documentation-text: #f2f4fa;
2993
+ --documentation-text-soft: #cbd0dc;
2994
+ --documentation-muted: #999fad;
2995
+ --documentation-faint: #808797;
2996
+ --documentation-border: #252a36;
2997
+ --documentation-border-strong: #343a49;
2998
+ --documentation-code-bg: #080a0f;
2999
+ --documentation-code-panel: #10131a;
3000
+ --documentation-code-text: #eef0f7;
3001
+ --documentation-shadow-sm: 0 1px 2px rgba(0, 0, 0, .24);
3002
+ --documentation-shadow-lg: 0 28px 90px rgba(0, 0, 0, .55), 0 8px 24px rgba(0, 0, 0, .35);
3003
+ color-scheme: dark;
3004
+ }
3005
+
3006
+ * { box-sizing: border-box; }
3007
+ html { scroll-behavior: smooth; scroll-padding-top: 92px; }
3008
+ body { margin: 0; background: var(--documentation-brand-bg); color: var(--documentation-text); font-family: var(--documentation-font-body); font-size: 15.5px; line-height: 1.7; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
3009
+ :root[data-background-decoration="gradient"] body { background-image: radial-gradient(circle at 58% 7%, color-mix(in srgb, var(--documentation-accent) 7%, transparent), transparent 32%); background-attachment: fixed; }
3010
+ :root[data-background-decoration="grid"] body { background-image: linear-gradient(color-mix(in srgb, var(--documentation-border) 26%, transparent) 1px, transparent 1px), linear-gradient(90deg, color-mix(in srgb, var(--documentation-border) 26%, transparent) 1px, transparent 1px); background-attachment: fixed; background-size: 32px 32px; }
3011
+ body:has(.documentation-modal-layer), body:has(.documentation-mobile-nav) { overflow: hidden; }
3012
+ button, input, textarea { font: inherit; }
3013
+ button, a { -webkit-tap-highlight-color: transparent; }
3014
+ a { color: inherit; }
3015
+ button:focus-visible, a:focus-visible, input:focus-visible, textarea:focus-visible, summary:focus-visible { outline: 2px solid color-mix(in srgb, var(--documentation-accent) 74%, white); outline-offset: 3px; }
3016
+ ::selection { background: color-mix(in srgb, var(--documentation-accent) 24%, transparent); }
3017
+
3018
+ .documentation-skip-link { position: fixed; z-index: 200; top: -64px; left: 16px; padding: 9px 13px; border-radius: 8px; background: var(--documentation-text); color: var(--documentation-bg); font-weight: 650; text-decoration: none; }
3019
+ .documentation-skip-link:focus { top: 12px; }
3020
+ .documentation-app.is-navigating::before { content: ""; position: fixed; z-index: 240; inset: 0 auto auto 0; width: 42%; height: 2px; background: linear-gradient(90deg, var(--documentation-accent), var(--documentation-accent-2)); box-shadow: 0 0 14px color-mix(in srgb, var(--documentation-accent-2) 48%, transparent); animation: documentation-navigation-progress .8s ease-in-out infinite alternate; }
3021
+ ::view-transition-old(documentation-article) { animation: documentation-page-out .11s ease-in both; }
3022
+ ::view-transition-new(documentation-article) { animation: documentation-page-in .16s ease-out both; }
3023
+
3024
+ `;
3025
+
3026
+ // src/documentation/runtime/styles/content.ts
3027
+ var DOCUMENTATION_CONTENT_CSS = String.raw`
3028
+ .documentation-main { min-width: 0; margin-left: var(--documentation-sidebar-width); transition: margin-right .24s ease; }
3029
+ .documentation-reader-grid { display: grid; grid-template-columns: minmax(0, 670px) 212px; justify-content: center; gap: 70px; width: 100%; padding: 62px 52px 156px; }
3030
+ .documentation-article { display: flex; min-width: 0; min-height: calc(100vh - var(--documentation-header-height) - 158px); flex-direction: column; view-transition-name: documentation-article; }
3031
+ .documentation-breadcrumbs { display: none; align-items: center; gap: 5px; margin-bottom: 24px; color: var(--documentation-muted); font-size: 12px; }
3032
+ .documentation-breadcrumbs span { display: inline-flex; align-items: center; gap: 5px; }
3033
+ .documentation-breadcrumbs svg { width: 12px; height: 12px; }
3034
+ .documentation-article-header { margin-bottom: 38px; }
3035
+ .documentation-article-meta { display: flex; align-items: center; justify-content: space-between; gap: 18px; min-height: 32px; margin-bottom: 7px; }
3036
+ .documentation-eyebrow { color: color-mix(in srgb, var(--documentation-accent) 76%, var(--documentation-text)); font-size: 12px; font-weight: 700; letter-spacing: .035em; }
3037
+ .documentation-contextual-actions { position: relative; display: inline-flex; flex: 0 0 auto; align-items: stretch; height: 30px; border: 1px solid var(--documentation-border); border-radius: 8px; background: color-mix(in srgb, var(--documentation-surface) 92%, transparent); box-shadow: var(--documentation-shadow-sm); }
3038
+ .documentation-contextual-actions button, .documentation-contextual-actions a, .documentation-contextual-actions summary { display: inline-flex; align-items: center; gap: 6px; min-width: 0; height: 28px; padding: 0 9px; border: 0; background: transparent; color: var(--documentation-muted); cursor: pointer; font: 590 11px/1 var(--documentation-font-body); text-decoration: none; white-space: nowrap; }
3039
+ .documentation-contextual-actions button:hover, .documentation-contextual-actions a:hover, .documentation-contextual-actions summary:hover { color: var(--documentation-text); }
3040
+ .documentation-contextual-actions svg { flex: 0 0 auto; width: 13px; height: 13px; }
3041
+ .documentation-contextual-actions details { position: relative; border-left: 1px solid var(--documentation-border); }
3042
+ .documentation-contextual-actions summary { width: 29px; padding: 0; justify-content: center; list-style: none; }
3043
+ .documentation-contextual-actions summary::-webkit-details-marker { display: none; }
3044
+ .documentation-contextual-actions details[open] summary { background: var(--documentation-panel); color: var(--documentation-text); }
3045
+ .documentation-contextual-actions details[open] summary svg { transform: rotate(180deg); }
3046
+ .documentation-contextual-menu { position: absolute; z-index: 35; top: calc(100% + 7px); right: -1px; display: grid; width: min(316px, calc(100vw - 32px)); max-height: min(510px, calc(100vh - 120px)); overflow: auto; padding: 6px; border: 1px solid var(--documentation-border-strong); border-radius: 12px; background: var(--documentation-surface-raised); box-shadow: var(--documentation-shadow-lg); }
3047
+ .documentation-contextual-menu button, .documentation-contextual-menu a { display: grid; grid-template-columns: 30px minmax(0, 1fr) auto; align-items: center; gap: 9px; width: 100%; height: auto; min-height: 50px; padding: 6px 8px; border-radius: 8px; text-align: left; }
3048
+ .documentation-contextual-menu button:hover, .documentation-contextual-menu a:hover { background: var(--documentation-panel); }
3049
+ .documentation-contextual-action-icon { display: grid; place-items: center; width: 30px; height: 30px; border: 1px solid var(--documentation-border); border-radius: 7px; background: var(--documentation-surface); color: var(--documentation-text-soft); }
3050
+ .documentation-contextual-action-icon svg { width: 15px; height: 15px; }
3051
+ .documentation-contextual-action-copy { display: grid; min-width: 0; gap: 2px; }
3052
+ .documentation-contextual-action-copy strong { overflow: hidden; color: var(--documentation-text); font-size: 12px; font-weight: 640; line-height: 1.2; text-overflow: ellipsis; white-space: nowrap; }
3053
+ .documentation-contextual-action-copy small { overflow: hidden; color: var(--documentation-muted); font-size: 10.5px; font-weight: 450; line-height: 1.25; text-overflow: ellipsis; white-space: nowrap; }
3054
+ .documentation-contextual-menu .documentation-contextual-action-external { width: 12px; height: 12px; color: var(--documentation-faint); }
3055
+ .documentation-article-header h1 { margin: 0; color: var(--documentation-text); font-family: var(--documentation-font-heading); font-size: clamp(34px, 3vw, 41px); font-weight: 650; letter-spacing: -.038em; line-height: 1.09; overflow-wrap: anywhere; }
3056
+ .documentation-article-header h1:focus { outline: none; }
3057
+ .documentation-lede { max-width: 650px; margin: 13px 0 0; color: var(--documentation-text-soft); font-size: 17.5px; letter-spacing: -.01em; line-height: 1.56; }
3058
+
3059
+ .documentation-prose { color: var(--documentation-text-soft); }
3060
+ .documentation-prose > :first-child { margin-top: 0; }
3061
+ .documentation-prose h1, .documentation-prose h2, .documentation-prose h3, .documentation-prose h4, .documentation-prose h5, .documentation-prose h6 { color: var(--documentation-text); font-family: var(--documentation-font-heading); scroll-margin-top: 92px; }
3062
+ .documentation-prose h1 { margin: 46px 0 17px; font-size: 30px; letter-spacing: -.03em; line-height: 1.2; }
3063
+ .documentation-prose h2 { margin: 49px 0 16px; padding-top: 5px; font-size: 25px; font-weight: 625; letter-spacing: -.03em; line-height: 1.25; }
3064
+ .documentation-prose h3 { margin: 33px 0 12px; font-size: 19px; font-weight: 620; letter-spacing: -.018em; line-height: 1.35; }
3065
+ .documentation-prose h4 { margin: 27px 0 10px; font-size: 16px; font-weight: 620; }
3066
+ .documentation-heading-anchor { margin-left: 8px; color: transparent; font-size: .72em; font-weight: 500; text-decoration: none; }
3067
+ .documentation-prose h2:hover .documentation-heading-anchor, .documentation-prose h3:hover .documentation-heading-anchor, .documentation-heading-anchor:focus-visible { color: var(--documentation-faint); }
3068
+ .documentation-prose p { margin: 0 0 19px; }
3069
+ .documentation-prose ul, .documentation-prose ol { margin: 0 0 21px; padding-left: 25px; }
3070
+ .documentation-prose li { margin: 5px 0; padding-left: 3px; }
3071
+ .documentation-prose li::marker { color: var(--documentation-faint); }
3072
+ .documentation-prose strong { color: var(--documentation-text); font-weight: 610; }
3073
+ .documentation-prose a { color: color-mix(in srgb, var(--documentation-accent) 78%, var(--documentation-text)); font-weight: 560; text-decoration-color: color-mix(in srgb, var(--documentation-accent) 38%, transparent); text-decoration-thickness: 1px; text-underline-offset: 3px; }
3074
+ .documentation-prose a:hover { text-decoration-color: currentColor; }
3075
+ .documentation-prose img { display: block; max-width: 100%; height: auto; margin: 26px 0; border: 1px solid var(--documentation-border); border-radius: 12px; background: var(--documentation-surface); box-shadow: var(--documentation-shadow-sm); }
3076
+ .documentation-prose blockquote { margin: 25px 0; padding: 4px 0 4px 18px; border-left: 2px solid color-mix(in srgb, var(--documentation-accent) 45%, var(--documentation-border)); color: var(--documentation-muted); }
3077
+ .documentation-prose blockquote p:last-child { margin-bottom: 0; }
3078
+ .documentation-prose hr { margin: 44px 0; border: 0; border-top: 1px solid var(--documentation-border); }
3079
+ .documentation-prose :not(pre) > code, .documentation-assistant-message code { padding: 2px 5px; border: 1px solid var(--documentation-border); border-radius: 5px; background: var(--documentation-panel); color: var(--documentation-text); font: 520 .88em/1.4 var(--documentation-font-mono); overflow-wrap: anywhere; }
3080
+ .documentation-prose pre { position: relative; overflow: auto; margin: 23px 0; padding: 20px 20px 19px; border: 1px solid color-mix(in srgb, var(--documentation-border-strong) 50%, #000); border-radius: 12px; background: linear-gradient(145deg, var(--documentation-code-panel), var(--documentation-code-bg)); color: var(--documentation-code-text); box-shadow: 0 12px 34px rgba(7, 9, 14, .12); font: 13px/1.66 var(--documentation-font-mono); tab-size: 2; }
3081
+ .documentation-prose pre code { display: block; min-width: max-content; padding-right: 76px; background: transparent; }
3082
+ :root[data-code-mode="system"][data-theme="light"] .documentation-prose pre { border-color: var(--documentation-border); background: linear-gradient(145deg, #f7f8fb, #f1f3f7); color: #252936; box-shadow: 0 8px 26px rgba(15, 18, 28, .06); }
3083
+ :root[data-code-mode="system"][data-theme="light"] .documentation-code-actions button { border-color: rgba(17, 19, 26, .1); background: rgba(255, 255, 255, .72); color: #606675; }
3084
+ :root[data-code-mode="system"][data-theme="light"] .documentation-code-actions button:hover { background: #fff; color: #11131a; }
3085
+ :root[data-code-mode="system"][data-theme="light"] .hljs-keyword, :root[data-code-mode="system"][data-theme="light"] .hljs-selector-tag, :root[data-code-mode="system"][data-theme="light"] .hljs-literal { color: #6f42c1; }
3086
+ :root[data-code-mode="system"][data-theme="light"] .hljs-string, :root[data-code-mode="system"][data-theme="light"] .hljs-title, :root[data-code-mode="system"][data-theme="light"] .hljs-name, :root[data-code-mode="system"][data-theme="light"] .hljs-type { color: #087f5b; }
3087
+ :root[data-code-mode="system"][data-theme="light"] .hljs-number, :root[data-code-mode="system"][data-theme="light"] .hljs-meta, :root[data-code-mode="system"][data-theme="light"] .hljs-built_in { color: #a15c00; }
3088
+ :root[data-code-mode="system"][data-theme="light"] .hljs-comment, :root[data-code-mode="system"][data-theme="light"] .hljs-quote { color: #7b8190; }
3089
+ .documentation-code-actions { position: sticky; float: right; top: 0; right: 0; display: inline-flex; gap: 4px; margin: -12px -12px 0 10px; }
3090
+ .documentation-code-actions button { height: 28px; padding: 0 8px; border: 1px solid rgba(255, 255, 255, .12); border-radius: 7px; background: rgba(255, 255, 255, .07); color: #aeb5c6; cursor: pointer; font-size: 10.5px; font-weight: 650; }
3091
+ .documentation-code-actions button:hover { background: rgba(255, 255, 255, .12); color: #fff; }
3092
+ .hljs-keyword, .hljs-selector-tag, .hljs-literal, .hljs-section, .hljs-link { color: #b7a7ff; }
3093
+ .hljs-string, .hljs-title, .hljs-name, .hljs-type, .hljs-attribute, .hljs-symbol, .hljs-bullet, .hljs-addition { color: #73dfb4; }
3094
+ .hljs-number, .hljs-meta, .hljs-built_in, .hljs-builtin-name, .hljs-params { color: #f0b96b; }
3095
+ .hljs-comment, .hljs-quote, .hljs-deletion { color: #767f93; font-style: italic; }
3096
+ .hljs-variable, .hljs-template-variable, .hljs-regexp { color: #ff95b4; }
3097
+
3098
+ .documentation-prose table { display: block; width: 100%; overflow: auto; margin: 25px 0 29px; border: 1px solid var(--documentation-border); border-radius: 10px; border-collapse: separate; border-spacing: 0; background: var(--documentation-surface); font-size: 14px; }
3099
+ .documentation-prose thead { background: var(--documentation-panel); }
3100
+ .documentation-prose th, .documentation-prose td { min-width: 130px; padding: 10px 13px; border-right: 1px solid var(--documentation-border); border-bottom: 1px solid var(--documentation-border); text-align: left; vertical-align: top; }
3101
+ .documentation-prose th:last-child, .documentation-prose td:last-child { border-right: 0; }
3102
+ .documentation-prose tr:last-child td { border-bottom: 0; }
3103
+ .documentation-prose th { color: var(--documentation-text); font-size: 12px; font-weight: 700; letter-spacing: .02em; }
3104
+
3105
+ .documentation-callout { --callout-accent: var(--documentation-accent); position: relative; margin: 24px 0; padding: 16px 18px 16px 20px; overflow: hidden; border: 1px solid color-mix(in srgb, var(--callout-accent) 25%, var(--documentation-border)); border-radius: 11px; background: linear-gradient(105deg, color-mix(in srgb, var(--callout-accent) 7%, var(--documentation-surface)), var(--documentation-surface)); }
3106
+ .documentation-callout::before { content: ""; position: absolute; inset: 0 auto 0 0; width: 2px; background: var(--callout-accent); }
3107
+ .documentation-callout > strong { display: block; margin-bottom: 3px; color: color-mix(in srgb, var(--callout-accent) 72%, var(--documentation-text)); font-size: 13px; }
3108
+ .documentation-callout > :last-child { margin-bottom: 0; }
3109
+ .documentation-callout-warning { --callout-accent: #e58a21; }
3110
+ .documentation-callout-tip { --callout-accent: #17a673; }
3111
+ .documentation-callout-info { --callout-accent: #278fdb; }
3112
+ .documentation-callout-note { --callout-accent: var(--documentation-accent); }
3113
+ .documentation-card { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 12px; margin: 14px 0; padding: 16px 17px; border: 1px solid var(--documentation-border); border-radius: 11px; background: var(--documentation-surface); box-shadow: var(--documentation-shadow-sm); text-decoration: none !important; transition: border-color .16s, box-shadow .16s, transform .16s; }
3114
+ .documentation-card:hover { border-color: color-mix(in srgb, var(--documentation-accent) 42%, var(--documentation-border)); box-shadow: 0 7px 24px rgba(15, 18, 28, .07); transform: translateY(-1px); }
3115
+ .documentation-card strong { display: block; margin-bottom: 2px; }
3116
+ .documentation-accordion-group { margin: 21px 0; border: 1px solid var(--documentation-border); border-radius: 11px; overflow: hidden; }
3117
+ .documentation-accordion-group .documentation-accordion { margin: 0; border: 0; border-bottom: 1px solid var(--documentation-border); border-radius: 0; }
3118
+ .documentation-accordion-group .documentation-accordion:last-child { border-bottom: 0; }
3119
+ .documentation-accordion { margin: 14px 0; padding: 0 16px; border: 1px solid var(--documentation-border); border-radius: 10px; background: var(--documentation-surface); }
3120
+ .documentation-accordion summary { padding: 14px 25px 14px 0; color: var(--documentation-text); cursor: pointer; font-weight: 650; list-style: none; }
3121
+ .documentation-accordion summary::-webkit-details-marker { display: none; }
3122
+ .documentation-accordion summary::after { content: "+"; float: right; margin-right: -20px; color: var(--documentation-faint); font-size: 18px; font-weight: 400; line-height: 1; }
3123
+ .documentation-accordion[open] summary::after { content: "−"; }
3124
+ .documentation-accordion > :last-child { margin-bottom: 15px; }
3125
+ .documentation-steps { margin: 25px 0; counter-reset: documentation-step; }
3126
+ .documentation-step { position: relative; margin-left: 13px; padding: 0 0 29px 34px; border-left: 1px solid var(--documentation-border-strong); counter-increment: documentation-step; }
3127
+ .documentation-step:last-child { padding-bottom: 3px; border-color: transparent; }
3128
+ .documentation-step::before { content: counter(documentation-step); position: absolute; left: -14px; top: -1px; display: grid; place-items: center; width: 27px; height: 27px; border: 1px solid color-mix(in srgb, var(--documentation-accent) 46%, var(--documentation-border)); border-radius: 50%; background: var(--documentation-surface); color: color-mix(in srgb, var(--documentation-accent) 78%, var(--documentation-text)); box-shadow: 0 0 0 5px var(--documentation-bg); font-size: 11px; font-weight: 740; }
3129
+ .documentation-step h3 { margin-top: 0; }
3130
+ .documentation-tabs { margin: 23px 0; overflow: hidden; border: 1px solid var(--documentation-border); border-radius: 11px; background: var(--documentation-surface); }
3131
+ .documentation-tab-buttons { display: flex; gap: 2px; overflow-x: auto; padding: 6px; border-bottom: 1px solid var(--documentation-border); background: var(--documentation-panel); }
3132
+ .documentation-tab-buttons button { flex: 0 0 auto; padding: 6px 10px; border: 0; border-radius: 7px; background: transparent; color: var(--documentation-muted); cursor: pointer; font-size: 12.5px; font-weight: 620; }
3133
+ .documentation-tab-buttons button[aria-selected="true"] { background: var(--documentation-surface); color: var(--documentation-text); box-shadow: var(--documentation-shadow-sm); }
3134
+ .documentation-tab { padding: 16px 18px 2px; }
3135
+ .documentation-tab > :first-child { margin-top: 0; }
3136
+ .documentation-code-group { margin: 23px 0; overflow: hidden; border: 1px solid var(--documentation-border-strong); border-radius: 12px; background: var(--documentation-surface); }
3137
+ .documentation-code-group-title { padding: 8px 13px; border-bottom: 1px solid var(--documentation-border); background: var(--documentation-panel); color: var(--documentation-muted); font-size: 11px; font-weight: 650; }
3138
+ .documentation-code-group .documentation-tab-buttons { border-color: color-mix(in srgb, var(--documentation-border) 72%, transparent); background: var(--documentation-panel); }
3139
+ .documentation-code-tab { padding: 0; }
3140
+ .documentation-code-group pre { margin: 0; border: 0; border-radius: 0; box-shadow: none; }
3141
+
3142
+ .documentation-api-badge { display: flex; align-items: center; gap: 9px; margin: 0 0 25px; padding: 11px 13px; border: 1px solid var(--documentation-border); border-radius: 10px; background: var(--documentation-panel); }
3143
+ .method { display: inline-grid; place-items: center; min-width: 52px; padding: 4px 7px; border-radius: 6px; background: #3178e8; color: white; font: 750 10px/1.3 var(--documentation-font-mono); letter-spacing: .035em; }
3144
+ .method-post { background: #138b5b; }
3145
+ .method-put, .method-patch { background: #bd7215; }
3146
+ .method-delete { background: #cd464d; }
3147
+ .documentation-playground { margin: 48px 0 30px; padding: 20px; border: 1px solid var(--documentation-border); border-radius: 12px; background: var(--documentation-panel); }
3148
+ .documentation-playground-title { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
3149
+ .documentation-playground-title > div { display: flex; align-items: center; gap: 9px; }
3150
+ .documentation-playground-title code { overflow: hidden; color: var(--documentation-muted); font: 11px var(--documentation-font-mono); text-overflow: ellipsis; white-space: nowrap; }
3151
+ .documentation-playground > p { color: var(--documentation-muted); font-size: 12px; }
3152
+ .documentation-playground label { display: grid; gap: 5px; margin: 13px 0; color: var(--documentation-muted); font-size: 11px; font-weight: 650; }
3153
+ .documentation-playground input, .documentation-playground textarea { width: 100%; padding: 10px; border: 1px solid var(--documentation-border-strong); border-radius: 8px; background: var(--documentation-surface); color: var(--documentation-text); font: 12.5px/1.55 var(--documentation-font-mono); resize: vertical; }
3154
+ .documentation-playground > button { padding: 9px 13px; border: 0; border-radius: 8px; background: var(--documentation-accent); color: white; cursor: pointer; font-size: 12px; font-weight: 680; }
3155
+ .documentation-playground > button:disabled { opacity: .58; cursor: wait; }
3156
+ .documentation-playground > pre { max-height: 320px; overflow: auto; margin: 14px 0 0; padding: 13px; border-radius: 8px; background: var(--documentation-code-bg); color: var(--documentation-code-text); font: 11.5px/1.55 var(--documentation-font-mono); white-space: pre-wrap; }
3157
+
3158
+ .documentation-context-rail { position: sticky; top: 92px; align-self: start; max-height: calc(100vh - 116px); overflow: auto; padding-left: 18px; }
3159
+ .documentation-outline { display: grid; gap: 2px; }
3160
+ .documentation-outline h2 { margin: 0 0 7px; color: var(--documentation-text); font-size: 11.5px; font-weight: 700; }
3161
+ .documentation-outline a { padding: 3px 0; color: var(--documentation-muted); font-size: 11.5px; line-height: 1.42; text-decoration: none; transition: color .12s; }
3162
+ .documentation-outline a:hover, .documentation-outline a.is-active { color: var(--documentation-text); }
3163
+ .documentation-outline a.is-active { font-weight: 640; }
3164
+ .documentation-outline a.depth-3 { padding-left: 11px; }
3165
+ .documentation-feedback { display: flex; align-items: center; gap: 7px; min-height: 47px; margin-top: 58px; padding: 11px 13px; border: 1px solid var(--documentation-border); border-radius: 10px; background: var(--documentation-panel); color: var(--documentation-muted); font-size: 12px; }
3166
+ .documentation-feedback > span { margin-right: auto; }
3167
+ .documentation-feedback button { padding: 5px 9px; border: 1px solid var(--documentation-border); border-radius: 7px; background: var(--documentation-surface); color: var(--documentation-muted); cursor: pointer; font-size: 11px; }
3168
+ .documentation-feedback button:hover { color: var(--documentation-text); }
3169
+ .documentation-feedback p { display: flex; align-items: center; gap: 7px; margin: 0; }
3170
+ .documentation-feedback svg { width: 15px; color: #17a673; }
3171
+ .documentation-pagination { display: grid; grid-template-columns: 1fr 1fr; gap: 13px; margin-top: auto; padding-top: 28px; }
3172
+ .documentation-app.has-assistant .documentation-pagination { margin-bottom: 76px; transform: translateY(76px); }
3173
+ .documentation-pagination a { display: flex; flex-direction: column; min-height: 86px; padding: 14px 15px; border: 1px solid var(--documentation-border); border-radius: 10px; background: var(--documentation-surface); text-decoration: none; transition: border-color .15s, transform .15s; }
3174
+ .documentation-pagination a:hover { border-color: color-mix(in srgb, var(--documentation-accent) 38%, var(--documentation-border)); transform: translateY(-1px); }
3175
+ .documentation-pagination a:last-child { text-align: right; }
3176
+ .documentation-pagination small { margin-bottom: 7px; color: var(--documentation-faint); font-size: 10.5px; font-weight: 650; text-transform: uppercase; letter-spacing: .05em; }
3177
+ .documentation-pagination span { display: flex; align-items: center; justify-content: space-between; gap: 8px; color: var(--documentation-text); font-size: 13px; font-weight: 650; }
3178
+ .documentation-pagination a:last-child span { justify-content: flex-end; }
3179
+ .documentation-pagination svg { flex: 0 0 auto; width: 15px; }
3180
+ .documentation-pagination svg.is-back { transform: rotate(180deg); }
3181
+
3182
+ `;
3183
+
3184
+ // src/documentation/runtime/styles/navigation.ts
3185
+ var DOCUMENTATION_NAVIGATION_CSS = String.raw`
3186
+ .documentation-header { position: fixed; z-index: 50; inset: 0 0 auto; display: grid; grid-template-columns: var(--documentation-sidebar-width) minmax(300px, 1fr) auto; align-items: center; height: var(--documentation-header-height); border-bottom: 1px solid color-mix(in srgb, var(--documentation-border) 84%, transparent); background: color-mix(in srgb, var(--documentation-bg) 91%, transparent); backdrop-filter: blur(18px) saturate(1.15); transition: right .24s ease; }
3187
+ .documentation-brand { display: flex; align-items: center; gap: 10px; min-width: 0; height: 100%; padding: 0 23px; color: var(--documentation-text); text-decoration: none; }
3188
+ .documentation-brand-mark { position: relative; display: grid; place-items: center; width: 27px; height: 27px; color: var(--documentation-accent); }
3189
+ .documentation-brand-mark::before { content: ""; position: absolute; inset: -7px; border-radius: 50%; background: radial-gradient(circle, color-mix(in srgb, var(--documentation-accent-2) 15%, transparent), transparent 68%); }
3190
+ .documentation-brand-mark svg { position: relative; width: 27px; height: 27px; }
3191
+ .documentation-brand-name { overflow: hidden; font-size: 16px; font-weight: 720; letter-spacing: -.025em; text-overflow: ellipsis; white-space: nowrap; }
3192
+ .documentation-brand-product { padding-left: 10px; border-left: 1px solid var(--documentation-border-strong); color: var(--documentation-muted); font-size: 12px; font-weight: 600; letter-spacing: .04em; }
3193
+ .documentation-brand-logo { max-width: 145px; max-height: 26px; object-fit: contain; object-position: left center; }
3194
+ .documentation-brand-logo-dark { display: none; }
3195
+ :root[data-theme="dark"] .documentation-brand-logo-light { display: none; }
3196
+ :root[data-theme="dark"] .documentation-brand-logo-dark { display: block; }
3197
+
3198
+ .documentation-header-center { display: flex; align-items: center; justify-content: center; padding: 0 20px; }
3199
+ .documentation-search-trigger { display: flex; align-items: center; width: min(380px, 38vw); height: 38px; gap: 9px; padding: 0 10px 0 12px; border: 1px solid var(--documentation-border); border-radius: 10px; background: color-mix(in srgb, var(--documentation-surface) 88%, transparent); color: var(--documentation-muted); box-shadow: var(--documentation-shadow-sm); cursor: pointer; text-align: left; transition: border-color .15s, color .15s, transform .15s; }
3200
+ .documentation-search-trigger:hover { border-color: var(--documentation-border-strong); color: var(--documentation-text); transform: translateY(-1px); }
3201
+ .documentation-search-trigger > span { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
3202
+ .documentation-search-trigger svg { width: 16px; }
3203
+ .documentation-search-trigger kbd { padding: 2px 5px; border: 1px solid var(--documentation-border); border-radius: 5px; background: var(--documentation-panel); color: var(--documentation-faint); font: 600 10px/1.3 var(--documentation-font-mono); }
3204
+
3205
+ .documentation-top-links { display: flex; align-items: center; gap: 18px; height: 100%; padding: 0 24px 0 10px; }
3206
+ .documentation-top-links a { display: inline-flex; align-items: center; gap: 4px; color: var(--documentation-muted); font-size: 13px; font-weight: 560; text-decoration: none; white-space: nowrap; }
3207
+ .documentation-top-links a:hover { color: var(--documentation-text); }
3208
+ .documentation-top-links svg { width: 12px; height: 12px; }
3209
+ .documentation-mobile-actions { display: none; align-items: center; gap: 3px; padding-right: 12px; }
3210
+ .documentation-icon-button { display: grid; place-items: center; width: 38px; height: 38px; padding: 0; border: 0; border-radius: 9px; background: transparent; color: var(--documentation-muted); cursor: pointer; }
3211
+ .documentation-icon-button:hover { background: var(--documentation-panel); color: var(--documentation-text); }
3212
+
3213
+ .documentation-frame { min-height: 100vh; padding-top: var(--documentation-header-height); }
3214
+ .documentation-sidebar { position: fixed; z-index: 30; inset: var(--documentation-header-height) auto 0 0; display: flex; flex-direction: column; width: var(--documentation-sidebar-width); border-right: 1px solid var(--documentation-border); background: var(--documentation-bg); }
3215
+ .documentation-page-tree { flex: 1; overflow: auto; padding: 26px 18px 36px 22px; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: var(--documentation-border-strong) transparent; }
3216
+ .documentation-nav-section { margin: 0 0 25px; }
3217
+ .documentation-nav-section > h2 { margin: 0 0 7px; padding: 0 9px; color: var(--documentation-text-soft); font-size: 11.5px; font-weight: 720; letter-spacing: .025em; }
3218
+ .documentation-nav-link { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 33px; margin: 1px 0; padding: 6px 9px; border-radius: 8px; color: var(--documentation-muted); font-size: 13.25px; line-height: 1.38; text-decoration: none; transition: background .12s, color .12s; }
3219
+ .documentation-nav-link > span { min-width: 0; overflow-wrap: anywhere; }
3220
+ .documentation-nav-link > svg { flex: 0 0 auto; width: 12px; height: 12px; }
3221
+ .documentation-nav-link:hover { background: var(--documentation-panel); color: var(--documentation-text); }
3222
+ .documentation-nav-link.is-active { position: relative; background: linear-gradient(90deg, color-mix(in srgb, var(--documentation-accent) 11%, transparent), color-mix(in srgb, var(--documentation-accent-2) 5%, transparent)); color: color-mix(in srgb, var(--documentation-accent) 77%, var(--documentation-text)); font-weight: 650; }
3223
+ .documentation-nav-link.is-active::before { content: ""; position: absolute; inset: 8px auto 8px -5px; width: 2px; border-radius: 4px; background: linear-gradient(var(--documentation-accent), var(--documentation-accent-2)); box-shadow: 0 0 10px color-mix(in srgb, var(--documentation-accent-2) 52%, transparent); }
3224
+ .documentation-nav-folder { margin: 2px 0; }
3225
+ .documentation-nav-folder > summary { display: flex; align-items: center; justify-content: space-between; min-height: 34px; padding: 6px 9px; border-radius: 8px; color: var(--documentation-muted); cursor: pointer; font-size: 13.25px; font-weight: 620; list-style: none; }
3226
+ .documentation-nav-folder > summary::-webkit-details-marker { display: none; }
3227
+ .documentation-nav-folder > summary:hover { background: var(--documentation-panel); color: var(--documentation-text); }
3228
+ .documentation-nav-folder > summary svg { width: 13px; transition: transform .16s; }
3229
+ .documentation-nav-folder:not([open]) > summary svg { transform: rotate(-90deg); }
3230
+ .documentation-nav-folder > div { margin-left: 8px; padding-left: 9px; border-left: 1px solid var(--documentation-border); }
3231
+
3232
+ .documentation-sidebar-footer { flex: 0 0 auto; display: grid; gap: 11px; padding: 13px 20px 16px 22px; border-top: 1px solid var(--documentation-border); background: color-mix(in srgb, var(--documentation-panel) 46%, var(--documentation-bg)); }
3233
+ .documentation-sidebar-footer > nav { display: flex; flex-wrap: wrap; gap: 8px 13px; }
3234
+ .documentation-sidebar-footer a, .documentation-sidebar-footer > span { color: var(--documentation-faint); font-size: 10.5px; text-decoration: none; }
3235
+ .documentation-sidebar-footer a:hover { color: var(--documentation-text); }
3236
+ .documentation-theme-controls { display: inline-flex; width: max-content; padding: 2px; border: 1px solid var(--documentation-border); border-radius: 9px; background: var(--documentation-surface); box-shadow: var(--documentation-shadow-sm); }
3237
+ .documentation-theme-controls button { display: grid; place-items: center; width: 28px; height: 25px; padding: 0; border: 0; border-radius: 6px; background: transparent; color: var(--documentation-faint); cursor: pointer; }
3238
+ .documentation-theme-controls button:hover { color: var(--documentation-text); }
3239
+ .documentation-theme-controls button[aria-pressed="true"] { background: var(--documentation-panel-strong); color: var(--documentation-text); }
3240
+ .documentation-theme-controls svg { width: 14px; height: 14px; }
3241
+
3242
+ `;
3243
+
3244
+ // src/documentation/runtime/styles/overlays.ts
3245
+ var DOCUMENTATION_OVERLAYS_CSS = String.raw`
3246
+ .documentation-modal-layer { position: fixed; z-index: 100; inset: 0; display: flex; background: rgba(9, 11, 17, .48); backdrop-filter: blur(5px); animation: documentation-fade-in .14s ease-out; }
3247
+ .documentation-search-layer { align-items: flex-start; justify-content: center; padding: min(12vh, 100px) 16px 16px; }
3248
+ .documentation-search-dialog { width: min(680px, 100%); overflow: hidden; border: 1px solid var(--documentation-border-strong); border-radius: 14px; background: var(--documentation-surface-raised); box-shadow: var(--documentation-shadow-lg); animation: documentation-dialog-in .18s ease-out; }
3249
+ .documentation-search-input { display: flex; align-items: center; gap: 11px; height: 56px; padding: 0 15px; border-bottom: 1px solid var(--documentation-border); }
3250
+ .documentation-search-input > svg { flex: 0 0 auto; width: 18px; color: var(--documentation-muted); }
3251
+ .documentation-search-input input { min-width: 0; flex: 1; border: 0; outline: 0; background: transparent; color: var(--documentation-text); font-size: 16px; }
3252
+ .documentation-search-input input::placeholder { color: var(--documentation-faint); }
3253
+ .documentation-search-input kbd, .documentation-search-dialog footer kbd { padding: 2px 5px; border: 1px solid var(--documentation-border); border-radius: 5px; background: var(--documentation-panel); color: var(--documentation-faint); font: 600 9px/1.4 var(--documentation-font-mono); }
3254
+ .documentation-search-results { min-height: 180px; max-height: min(56vh, 500px); overflow: auto; padding: 7px; }
3255
+ .documentation-search-empty { display: grid; place-items: center; min-height: 180px; padding: 25px; color: var(--documentation-muted); text-align: center; }
3256
+ .documentation-search-empty svg { width: 29px; height: 29px; margin-bottom: 8px; color: var(--documentation-accent); }
3257
+ .documentation-search-empty strong { color: var(--documentation-text); font-size: 14px; }
3258
+ .documentation-search-empty span { font-size: 12px; }
3259
+ .documentation-search-state { padding: 28px 16px; color: var(--documentation-muted); font-size: 13px; text-align: center; }
3260
+ .documentation-search-result, .documentation-search-assistant { display: flex; align-items: center; justify-content: space-between; gap: 14px; width: 100%; min-height: 64px; padding: 10px 11px; border: 0; border-radius: 9px; background: transparent; color: var(--documentation-text); cursor: pointer; text-align: left; text-decoration: none; }
3261
+ .documentation-search-result.is-active, .documentation-search-assistant.is-active { background: var(--documentation-panel); }
3262
+ .documentation-search-result > span, .documentation-search-assistant > span { min-width: 0; flex: 1; display: grid; }
3263
+ .documentation-search-result strong, .documentation-search-assistant strong { overflow: hidden; font-size: 13px; font-weight: 670; text-overflow: ellipsis; white-space: nowrap; }
3264
+ .documentation-search-result small, .documentation-search-assistant small { overflow: hidden; color: var(--documentation-muted); font-size: 11.5px; line-height: 1.5; text-overflow: ellipsis; white-space: nowrap; }
3265
+ .documentation-search-result > svg, .documentation-search-assistant > svg:last-child { flex: 0 0 auto; width: 14px; color: var(--documentation-faint); }
3266
+ .documentation-search-assistant { border-top: 1px solid var(--documentation-border); border-radius: 0 0 9px 9px; color: color-mix(in srgb, var(--documentation-accent) 76%, var(--documentation-text)); }
3267
+ .documentation-search-assistant > svg:first-child { flex: 0 0 auto; width: 19px; }
3268
+ .documentation-search-result mark { border-radius: 3px; background: color-mix(in srgb, var(--documentation-accent) 18%, transparent); color: inherit; }
3269
+ .documentation-search-dialog > footer { display: flex; gap: 17px; padding: 8px 13px; border-top: 1px solid var(--documentation-border); background: var(--documentation-panel); color: var(--documentation-faint); font-size: 9.5px; }
3270
+ .documentation-search-dialog > footer span { display: flex; align-items: center; gap: 3px; }
3271
+
3272
+ .documentation-mobile-nav { display: flex; flex-direction: column; width: min(342px, 88vw); height: 100%; border-right: 1px solid var(--documentation-border); background: var(--documentation-bg); box-shadow: var(--documentation-shadow-lg); animation: documentation-drawer-in .2s ease-out; }
3273
+ .documentation-mobile-nav > header { display: flex; align-items: center; justify-content: space-between; min-height: 64px; border-bottom: 1px solid var(--documentation-border); }
3274
+ .documentation-mobile-nav > header .documentation-brand { border: 0; padding-left: 20px; }
3275
+ .documentation-mobile-nav > header .documentation-icon-button { margin-right: 12px; }
3276
+ .documentation-mobile-nav > .documentation-page-tree { padding-top: 22px; }
3277
+ .documentation-mobile-nav > footer { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 13px; padding: 13px 18px; border-top: 1px solid var(--documentation-border); }
3278
+ .documentation-mobile-nav > footer a { color: var(--documentation-muted); font-size: 11px; text-decoration: none; }
3279
+
3280
+ .documentation-assistant-launcher { position: fixed; z-index: 70; left: 50%; bottom: 20px; display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 0; width: min(452px, calc(100vw - 36px)); min-height: 48px; padding: 5px; border: 1px solid color-mix(in srgb, var(--documentation-border-strong) 92%, var(--documentation-accent)); border-radius: 13px; background: color-mix(in srgb, var(--documentation-surface-raised) 96%, transparent); color: var(--documentation-text); box-shadow: 0 18px 45px rgba(15, 18, 28, .11), 0 2px 8px rgba(15, 18, 28, .05); transform: translateX(-50%); backdrop-filter: blur(18px) saturate(1.1); transition: border-color .16s, box-shadow .16s, transform .16s; }
3281
+ .documentation-assistant-launcher:hover { border-color: color-mix(in srgb, var(--documentation-accent) 32%, var(--documentation-border-strong)); box-shadow: 0 20px 48px rgba(15, 18, 28, .14), 0 2px 8px rgba(15, 18, 28, .06); transform: translateX(-50%) translateY(-1px); }
3282
+ .documentation-assistant-launcher input { min-width: 0; height: 36px; padding: 0 12px; border: 0; outline: 0; background: transparent; color: var(--documentation-text); font-size: 13px; }
3283
+ .documentation-assistant-launcher input::placeholder { color: var(--documentation-muted); }
3284
+ .documentation-launcher-agent, .documentation-launcher-send { display: grid; place-items: center; height: 36px; border: 0; border-radius: 9px; cursor: pointer; }
3285
+ .documentation-launcher-agent { width: auto; padding: 0 13px 0 9px; border-right: 1px solid var(--documentation-border); border-radius: 0; background: transparent; color: var(--documentation-text-soft); font-size: 11.5px; font-weight: 650; white-space: nowrap; }
3286
+ .documentation-launcher-agent:hover { color: var(--documentation-accent); }
3287
+ .documentation-launcher-send { width: 36px; padding: 0; }
3288
+ .documentation-launcher-send { background: var(--documentation-accent); color: #fff; }
3289
+ .documentation-launcher-send:disabled { background: var(--documentation-panel); color: var(--documentation-faint); cursor: default; }
3290
+ .documentation-launcher-send svg { width: 15px; }
3291
+ .documentation-assistant { position: fixed; z-index: 90; left: 50%; bottom: 18px; display: grid; grid-template-rows: auto minmax(0, 1fr) auto auto; width: min(620px, calc(100vw - 36px)); height: min(570px, calc(100vh - 94px)); overflow: hidden; border: 1px solid var(--documentation-border-strong); border-radius: 16px; background: var(--documentation-surface); box-shadow: 0 28px 80px rgba(15, 18, 28, .18), 0 6px 22px rgba(15, 18, 28, .08); animation: documentation-assistant-in .2s ease-out; transform: translateX(-50%); }
3292
+ .documentation-assistant > header { display: flex; align-items: center; justify-content: space-between; min-height: 54px; padding: 0 11px 0 18px; border-bottom: 1px solid var(--documentation-border); }
3293
+ .documentation-assistant > header > div { display: flex; align-items: center; }
3294
+ .documentation-assistant > header > div > span { display: grid; gap: 1px; }
3295
+ .documentation-assistant > header strong { font-size: 13px; font-weight: 620; letter-spacing: -.01em; }
3296
+ .documentation-assistant > header small { color: var(--documentation-muted); font-size: 10.5px; }
3297
+ .documentation-assistant > header button { display: grid; place-items: center; width: 32px; height: 32px; padding: 0; border: 0; border-radius: 8px; background: transparent; color: var(--documentation-muted); cursor: pointer; }
3298
+ .documentation-assistant > header button:hover { background: var(--documentation-panel); color: var(--documentation-text); }
3299
+ .documentation-assistant > header button:focus-visible { outline-width: 1px; outline-offset: 1px; }
3300
+ .documentation-assistant > header button svg { width: 15px; height: 15px; }
3301
+ .documentation-assistant-messages { overflow: auto; padding: 20px 20px 14px; overscroll-behavior: contain; }
3302
+ .documentation-assistant-message { display: flex; align-items: flex-start; margin: 0 0 20px; color: var(--documentation-text-soft); font-size: 13px; line-height: 1.64; }
3303
+ .documentation-assistant-message > div { min-width: 0; max-width: 590px; }
3304
+ .documentation-assistant-message.is-user { justify-content: flex-end; margin-left: 54px; }
3305
+ .documentation-assistant-message.is-user > div { padding: 8px 11px; border: 1px solid var(--documentation-border); border-radius: 10px; background: var(--documentation-panel); color: var(--documentation-text); }
3306
+ .documentation-assistant-message.is-error > div { color: #c64b50; }
3307
+ .documentation-assistant-message a { color: color-mix(in srgb, var(--documentation-accent) 79%, var(--documentation-text)); font-weight: 580; text-underline-offset: 3px; }
3308
+ .documentation-assistant-markdown > :first-child { margin-top: 0; }
3309
+ .documentation-assistant-markdown > :last-child { margin-bottom: 0; }
3310
+ .documentation-assistant-markdown p { margin: 0 0 11px; }
3311
+ .documentation-assistant-markdown ul, .documentation-assistant-markdown ol { margin: 0 0 12px; padding-left: 20px; }
3312
+ .documentation-assistant-markdown li { margin: 3px 0; }
3313
+ .documentation-assistant-heading { display: block; margin: 15px 0 6px; color: var(--documentation-text); font-family: var(--documentation-font-heading); font-size: 13px; }
3314
+ .documentation-assistant-code { overflow: hidden; margin: 12px 0; border: 1px solid var(--documentation-border-strong); border-radius: 10px; background: var(--documentation-code-bg); color: var(--documentation-code-text); }
3315
+ .documentation-assistant-code figcaption { display: flex; align-items: center; justify-content: space-between; min-height: 34px; padding: 0 8px 0 12px; border-bottom: 1px solid rgba(255,255,255,.09); background: var(--documentation-code-panel); color: #aeb5c6; font: 600 10.5px/1.3 var(--documentation-font-mono); }
3316
+ .documentation-assistant-code button { padding: 4px 7px; border: 1px solid rgba(255,255,255,.1); border-radius: 6px; background: rgba(255,255,255,.06); color: inherit; cursor: pointer; font-size: 10px; }
3317
+ .documentation-assistant-code pre { overflow: auto; margin: 0; padding: 13px; font: 11.5px/1.58 var(--documentation-font-mono); }
3318
+ .documentation-assistant-code code { padding: 0; border: 0; background: transparent; color: inherit; font: inherit; white-space: pre; }
3319
+ .documentation-assistant-activity { display: inline-flex; align-items: center; gap: 8px; color: var(--documentation-muted); font-size: 12px; }
3320
+ .documentation-assistant-activity > span { width: 6px; height: 6px; border-radius: 50%; background: var(--documentation-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--documentation-accent) 10%, transparent); animation: documentation-activity 1.25s ease-in-out infinite; }
3321
+ .documentation-assistant-suggestions { display: grid; gap: 7px; margin-top: 26px; }
3322
+ .documentation-assistant-suggestions p { margin: 0 0 1px; color: var(--documentation-faint); font-size: 10px; font-weight: 650; letter-spacing: .035em; text-transform: uppercase; }
3323
+ .documentation-assistant-suggestions button { min-height: 38px; padding: 8px 11px; border: 1px solid var(--documentation-border); border-radius: 9px; background: var(--documentation-bg); color: var(--documentation-muted); cursor: pointer; font-size: 12px; text-align: left; transition: border-color .14s, background .14s, color .14s; }
3324
+ .documentation-assistant-suggestions button:hover { border-color: var(--documentation-border-strong); background: var(--documentation-panel); color: var(--documentation-text); }
3325
+ .documentation-assistant-composer { display: flex; align-items: flex-end; gap: 7px; margin: 0 16px 9px; padding: 7px; border: 1px solid var(--documentation-border-strong); border-radius: 11px; background: var(--documentation-bg); box-shadow: 0 3px 12px rgba(15, 18, 28, .045); }
3326
+ .documentation-assistant-composer:focus-within { border-color: color-mix(in srgb, var(--documentation-accent) 52%, var(--documentation-border)); box-shadow: 0 0 0 3px color-mix(in srgb, var(--documentation-accent) 9%, transparent); }
3327
+ .documentation-assistant-composer textarea { min-height: 41px; max-height: 160px; flex: 1; padding: 4px 5px; resize: none; border: 0; outline: 0; background: transparent; color: var(--documentation-text); font-size: 12.5px; line-height: 1.5; }
3328
+ .documentation-assistant-composer button { display: grid; place-items: center; width: 32px; height: 32px; padding: 0; border: 0; border-radius: 8px; background: var(--documentation-accent); color: white; cursor: pointer; }
3329
+ .documentation-assistant-composer button:disabled { background: var(--documentation-panel-strong); color: var(--documentation-faint); cursor: default; }
3330
+ .documentation-assistant > footer { padding: 0 18px 10px; color: var(--documentation-faint); font-size: 9.5px; text-align: center; }
3331
+ @keyframes documentation-fade-in { from { opacity: 0; } }
3332
+ @keyframes documentation-dialog-in { from { opacity: 0; transform: translateY(-8px) scale(.99); } }
3333
+ @keyframes documentation-drawer-in { from { transform: translateX(-24px); } }
3334
+ @keyframes documentation-assistant-in { from { transform: translateX(-50%) translateY(18px) scale(.985); opacity: .7; } }
3335
+ @keyframes documentation-page-out { to { opacity: 0; transform: translateY(-3px); } }
3336
+ @keyframes documentation-page-in { from { opacity: 0; transform: translateY(4px); } }
3337
+ @keyframes documentation-activity { 50% { opacity: .42; transform: scale(.82); } }
3338
+ @keyframes documentation-navigation-progress { from { transform: translateX(-20%); } to { transform: translateX(155%); } }
3339
+
3340
+ `;
3341
+
3342
+ // src/documentation/runtime/styles/responsive.ts
3343
+ var DOCUMENTATION_RESPONSIVE_CSS = String.raw`
3344
+ @media (max-width: 1320px) {
3345
+ .documentation-reader-grid { grid-template-columns: minmax(0, 640px) 190px; gap: 48px; padding-inline: 70px; }
3346
+ .documentation-top-links { gap: 13px; padding-right: 16px; }
3347
+ .documentation-top-links a { font-size: 12px; }
3348
+ }
3349
+
3350
+ @media (max-width: 1140px) {
3351
+ .documentation-reader-grid { grid-template-columns: minmax(0, 760px); }
3352
+ .documentation-context-rail { display: none; }
3353
+ .documentation-header { grid-template-columns: var(--documentation-sidebar-width) 1fr auto; }
3354
+ .documentation-top-links { display: none; }
3355
+ .documentation-breadcrumbs { display: flex; }
3356
+ }
3357
+
3358
+ @media (max-width: 900px) {
3359
+ :root { --documentation-sidebar-width: 0px; }
3360
+ .documentation-header { grid-template-columns: minmax(0, 1fr) auto; }
3361
+ .documentation-header > .documentation-brand { padding-left: 17px; border-right: 0; }
3362
+ .documentation-header-center, .documentation-sidebar { display: none; }
3363
+ .documentation-mobile-actions { display: flex; }
3364
+ .documentation-main { margin-left: 0; }
3365
+ .documentation-reader-grid { padding: 44px 30px 90px; }
3366
+ .documentation-assistant { width: min(660px, calc(100vw - 24px)); }
3367
+ }
3368
+
3369
+ @media (max-width: 600px) {
3370
+ body { font-size: 15px; }
3371
+ .documentation-brand { padding-right: 8px; }
3372
+ .documentation-brand-product { display: none; }
3373
+ .documentation-mobile-actions { padding-right: 8px; }
3374
+ .documentation-icon-button { width: 34px; }
3375
+ .documentation-reader-grid { padding: 31px 19px 72px; }
3376
+ .documentation-breadcrumbs { margin-bottom: 19px; overflow: hidden; white-space: nowrap; }
3377
+ .documentation-breadcrumbs span { flex: 0 0 auto; }
3378
+ .documentation-article-header { margin-bottom: 30px; }
3379
+ .documentation-article-meta { align-items: flex-start; }
3380
+ .documentation-eyebrow { padding-top: 8px; }
3381
+ .documentation-contextual-primary button, .documentation-contextual-primary a { max-width: 112px; overflow: hidden; text-overflow: ellipsis; }
3382
+ .documentation-article-header h1 { font-size: 32px; line-height: 1.13; }
3383
+ .documentation-lede { font-size: 17px; line-height: 1.52; }
3384
+ .documentation-prose h2 { margin-top: 42px; font-size: 23px; }
3385
+ .documentation-prose h3 { font-size: 18px; }
3386
+ .documentation-prose pre { margin-inline: -5px; padding: 17px; border-radius: 10px; font-size: 12px; }
3387
+ .documentation-code-actions { margin-right: -9px; margin-top: -9px; }
3388
+ .documentation-pagination { grid-template-columns: 1fr; }
3389
+ .documentation-app.has-assistant .documentation-pagination { margin-bottom: 124px; transform: translateY(124px); }
3390
+ .documentation-feedback { align-items: flex-start; flex-wrap: wrap; }
3391
+ .documentation-feedback > span { width: 100%; margin-bottom: 2px; }
3392
+ .documentation-search-layer { padding: 8px; align-items: stretch; }
3393
+ .documentation-search-dialog { display: grid; grid-template-rows: auto minmax(0, 1fr) auto; height: 100%; border-radius: 12px; }
3394
+ .documentation-search-results { max-height: none; }
3395
+ .documentation-search-dialog > footer { justify-content: center; }
3396
+ .documentation-assistant-launcher { bottom: 12px; width: calc(100vw - 20px); }
3397
+ .documentation-assistant-launcher kbd { display: none; }
3398
+ .documentation-assistant { bottom: 0; width: 100%; height: calc(100vh - 8px); border-width: 1px 0 0; border-radius: 18px 18px 0 0; }
3399
+ .documentation-assistant-suggestions { grid-template-columns: 1fr; }
3400
+ .documentation-playground-title { align-items: flex-start; flex-direction: column; }
3401
+ }
3402
+
3403
+ @media (prefers-reduced-motion: reduce) {
3404
+ html { scroll-behavior: auto; }
3405
+ *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; scroll-behavior: auto !important; transition-duration: .001ms !important; }
3406
+ }
3407
+ `;
3408
+
3409
+ // src/documentation/runtime/styles.ts
3410
+ var DOCUMENTATION_CSS = DOCUMENTATION_BASE_CSS + DOCUMENTATION_NAVIGATION_CSS + DOCUMENTATION_CONTENT_CSS + DOCUMENTATION_OVERLAYS_CSS + DOCUMENTATION_RESPONSIVE_CSS;
3411
+
3412
+ // src/documentation/template.ts
3413
+ function renderPage(options, assets = {}, runtime = createPageRuntime(options)) {
3414
+ const { config, page } = options;
3415
+ const { data: runtimeData, document: runtimeDocument } = runtime;
3416
+ const theme = runtimeData.config.theme;
3417
+ const accent = validCssColor(theme.accent) ? theme.accent : "#635bff";
3418
+ const background = validCssColor(theme.background) ? theme.background : "#fbfcfe";
3419
+ const backgroundDark = validCssColor(theme.backgroundDark) ? theme.backgroundDark : "#0c0e14";
3420
+ const defaultMode = theme.defaultMode ?? "system";
3421
+ const bodyFont = cssFontStack(theme.bodyFont?.family);
3422
+ const headingFont = cssFontStack(theme.headingFont?.family ?? theme.bodyFont?.family);
3423
+ const monoFont = cssMonoFontStack(theme.monoFont?.family);
3424
+ const fontFaces = renderFontFaces(theme);
3425
+ const fontPreloads = renderFontPreloads(theme);
3426
+ const markup = assets.markup ?? renderDefaultDocumentation(runtimeData);
3427
+ const assetRoot = documentationPublicPath(options.basePath, "/assets");
3428
+ return `<!doctype html>
3429
+ <html lang="en" data-theme-mode="${defaultMode}" data-theme-strict="${theme.strictMode ? "true" : "false"}" data-code-mode="${theme.codeBlockMode ?? "dark"}" data-background-decoration="${theme.backgroundDecoration ?? "none"}" data-eyebrow-style="${theme.eyebrowStyle ?? "section"}">
3430
+ <head>
3431
+ <meta charset="utf-8">
3432
+ <meta name="viewport" content="width=device-width, initial-scale=1">
3433
+ <title>${escapeHtml(runtimeDocument.title)}</title>
3434
+ <meta name="description" content="${escapeHtml(runtimeDocument.description ?? "")}">
3435
+ <meta name="generator" content="DeepSpace Documentation Orbit">
3436
+ <meta name="color-scheme" content="light dark">
3437
+ ${runtimeDocument.robots ? `<meta name="robots" content="${escapeHtml(runtimeDocument.robots)}">` : ""}
3438
+ ${runtimeDocument.canonical ? `<link rel="canonical" href="${escapeHtml(runtimeDocument.canonical)}">` : ""}
3439
+ <meta property="og:type" content="article">
3440
+ <meta property="og:title" content="${escapeHtml(runtimeDocument.openGraph["og:title"] ?? "")}">
3441
+ <meta property="og:description" content="${escapeHtml(runtimeDocument.openGraph["og:description"] ?? "")}">
3442
+ ${runtimeDocument.openGraph["og:url"] ? `<meta property="og:url" content="${escapeHtml(runtimeDocument.openGraph["og:url"])}">` : ""}
3443
+ ${config.seo.ogImage ? `<meta property="og:image" content="${escapeHtml(config.seo.ogImage)}">` : ""}
3444
+ ${renderCustomMeta(config.seo.metaTags)}
3445
+ ${theme.favicon ? `<link rel="icon" href="${escapeHtml(theme.favicon)}">` : ""}
3446
+ ${fontPreloads}
3447
+ <link rel="stylesheet" href="${assetRoot}/documentation.css">
3448
+ ${(assets.customStylesheets ?? []).map((href) => `<link rel="stylesheet" href="${escapeHtml(href)}">`).join("\n ")}
3449
+ <style>${fontFaces}:root{--documentation-accent:${accent};--documentation-brand-bg:${background};--documentation-brand-bg-dark:${backgroundDark};--documentation-font-body:${bodyFont};--documentation-font-heading:${headingFont};--documentation-font-mono:${monoFont}}</style>
3450
+ <script src="${assetRoot}/documentation-theme.js"></script>
3451
+ </head>
3452
+ <body data-route="${escapeHtml(page.route)}">
3453
+ <div id="deepspace-documentation-root">${markup}</div>
3454
+ <script id="deepspace-documentation-data" type="application/json">${serializeBootstrap(runtimeData)}</script>
3455
+ <script${assets.runtimeModule ? ' type="module"' : ""} src="${escapeHtml(assets.runtimeScript ?? `${assetRoot}/documentation-runtime.js`)}" defer></script>
3456
+ </body>
3457
+ </html>`;
3458
+ }
3459
+ function renderFontFaces(theme) {
3460
+ const fonts = [theme.bodyFont, theme.headingFont, theme.monoFont];
3461
+ const seen = /* @__PURE__ */ new Set();
3462
+ return fonts.flatMap((font) => {
3463
+ if (!font?.source || !validFontSource(font.source)) return [];
3464
+ const key = `${font.family}:${font.source}:${font.weight ?? "normal"}`;
3465
+ if (seen.has(key)) return [];
3466
+ seen.add(key);
3467
+ const family = safeFontFamily(font.family);
3468
+ const weight = typeof font.weight === "number" || /^(?:normal|bold|[1-9]00|[1-9]00\s+[1-9]00)$/i.test(String(font.weight)) ? String(font.weight ?? "normal") : "normal";
3469
+ const format = font.format && /^[a-z0-9-]{2,20}$/i.test(font.format) ? ` format("${font.format}")` : "";
3470
+ return [`@font-face{font-family:"${family}";src:url("${font.source}")${format};font-weight:${weight};font-display:swap}`];
3471
+ }).join("");
3472
+ }
3473
+ function renderFontPreloads(theme) {
3474
+ const sources = new Set(
3475
+ [theme.bodyFont, theme.headingFont, theme.monoFont].flatMap((font) => font?.source && validFontSource(font.source) ? [font.source] : [])
3476
+ );
3477
+ return [...sources].map((source) => `<link rel="preload" href="${escapeHtml(source)}" as="font" type="font/woff2" crossorigin>`).join("\n ");
3478
+ }
3479
+ function renderCustomMeta(metaTags) {
3480
+ if (!metaTags) return "";
3481
+ return Object.entries(metaTags).filter(([name]) => /^[a-z0-9][a-z0-9._:-]*$/i.test(name)).map(([name, content]) => `<meta name="${escapeHtml(name)}" content="${escapeHtml(content)}">`).join("\n ");
3482
+ }
3483
+ function createNotFoundPage() {
3484
+ return {
3485
+ sourcePath: "",
3486
+ relativePath: "",
3487
+ route: "/404",
3488
+ title: "Page not found",
3489
+ description: "The documentation page you requested does not exist.",
3490
+ hidden: true,
3491
+ noindex: true,
3492
+ markdown: "",
3493
+ html: '<p>The page may have moved. Search this documentation or return to the documentation home.</p><p><a href="/">Go to documentation home</a></p>',
3494
+ text: "Page not found",
3495
+ headings: [],
3496
+ sourceFormat: "generated",
3497
+ kind: "page"
3498
+ };
3499
+ }
3500
+ function createPageRuntime(options) {
3501
+ const { config, page, pages, navigation } = options;
3502
+ const data = createRuntimeData(config, page, pages, navigation, options.basePath);
3503
+ return {
3504
+ data,
3505
+ document: createRuntimeRouteDocumentFromData(config, page, data)
3506
+ };
3507
+ }
3508
+ function createRuntimeRouteDocumentFromData(config, page, runtimeData) {
3509
+ const data = {
3510
+ basePath: runtimeData.basePath,
3511
+ page: runtimeData.page,
3512
+ breadcrumbs: runtimeData.breadcrumbs,
3513
+ ...runtimeData.previous ? { previous: runtimeData.previous } : {},
3514
+ ...runtimeData.next ? { next: runtimeData.next } : {}
3515
+ };
3516
+ const canonical = config.url ? joinSiteUrl(config.url, page.route) : null;
3517
+ const description = page.description ?? config.description ?? `${page.title} \u2014 ${config.name}`;
3518
+ return {
3519
+ canonical,
3520
+ data,
3521
+ description,
3522
+ openGraph: {
3523
+ "og:title": page.title,
3524
+ "og:description": description,
3525
+ "og:url": canonical
3526
+ },
3527
+ robots: page.noindex ? "noindex, nofollow" : null,
3528
+ title: `${page.title} \xB7 ${config.name}`
3529
+ };
3530
+ }
3531
+ function renderRedirect(to, basePath = "/docs") {
3532
+ const safe = escapeHtml(documentationPublicPath(basePath, to));
3533
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="robots" content="noindex"><meta http-equiv="refresh" content="0;url=${safe}"><link rel="canonical" href="${safe}"><title>Redirecting\u2026</title></head><body><p>Redirecting to <a href="${safe}">${safe}</a>.</p></body></html>`;
3534
+ }
3535
+ function publicTheme(theme, basePath) {
3536
+ const publicAsset = (value) => {
3537
+ if (!value) return value;
3538
+ const logical = value.startsWith("/_documentation/") ? value.slice("/_documentation".length) : value;
3539
+ return logical.startsWith("/media/") ? documentationPublicPath(basePath, logical) : logical;
3540
+ };
3541
+ const publicFont = (font) => font ? { ...font, source: publicAsset(font.source) } : font;
3542
+ return {
3543
+ ...theme,
3544
+ logo: publicAsset(theme.logo),
3545
+ logoDark: publicAsset(theme.logoDark),
3546
+ favicon: publicAsset(theme.favicon),
3547
+ bodyFont: publicFont(theme.bodyFont),
3548
+ headingFont: publicFont(theme.headingFont),
3549
+ monoFont: publicFont(theme.monoFont)
3550
+ };
3551
+ }
3552
+ function publicPageHtml(html, basePath) {
3553
+ return html.replace(
3554
+ /(\b(?:href|src)=["'])(\/(?!\/)[^"']*)(["'])/gi,
3555
+ (_match, prefix, value, suffix) => {
3556
+ const logical = value.startsWith("/_documentation/") ? value.slice("/_documentation".length) : value;
3557
+ const publicValue = basePath && (logical === basePath || logical.startsWith(`${basePath}/`)) ? logical : documentationPublicPath(basePath, logical);
3558
+ return `${prefix}${publicValue}${suffix}`;
3559
+ }
3560
+ );
3561
+ }
3562
+ function createRuntimeData(config, page, pages, navigation, basePath) {
3563
+ const ordered = orderedRoutes(navigation);
3564
+ const pageIndex = ordered.indexOf(page.route);
3565
+ const previous = pageIndex > 0 ? pages.find((candidate) => candidate.route === ordered[pageIndex - 1]) : void 0;
3566
+ const next = pageIndex >= 0 ? pages.find((candidate) => candidate.route === ordered[pageIndex + 1]) : void 0;
3567
+ return {
3568
+ basePath,
3569
+ config: {
3570
+ name: config.name,
3571
+ ...config.description ? { description: config.description } : {},
3572
+ theme: publicTheme(config.theme, basePath),
3573
+ links: config.links,
3574
+ footer: config.footer,
3575
+ assistant: config.assistant,
3576
+ mcp: config.mcp,
3577
+ contextual: config.contextual
3578
+ },
3579
+ page: {
3580
+ route: page.route,
3581
+ title: page.title,
3582
+ ...page.description ? { description: page.description } : {},
3583
+ html: publicPageHtml(page.html, basePath),
3584
+ headings: page.headings,
3585
+ kind: page.kind,
3586
+ markdownUrl: markdownUrlForRoute(page.route, basePath),
3587
+ ...page.openapi ? { openapi: page.openapi } : {}
3588
+ },
3589
+ navigation,
3590
+ breadcrumbs: findBreadcrumbs(navigation, page.route) ?? [config.name, page.title],
3591
+ ...previous ? { previous: { route: previous.route, title: previous.title } } : {},
3592
+ ...next ? { next: { route: next.route, title: next.title } } : {}
3593
+ };
3594
+ }
3595
+ function findBreadcrumbs(nodes, route, parents = []) {
3596
+ for (const node of nodes) {
3597
+ if (node.kind === "page" && node.route === route) return [...parents, node.label];
3598
+ if (node.kind === "group") {
3599
+ const result = findBreadcrumbs(node.items, route, [...parents, node.label]);
3600
+ if (result) return result;
3601
+ }
3602
+ }
3603
+ return void 0;
3604
+ }
3605
+ function orderedRoutes(nodes) {
3606
+ return nodes.flatMap((node) => {
3607
+ if (node.kind === "page") return [node.route];
3608
+ if (node.kind === "group") return orderedRoutes(node.items);
3609
+ return [];
3610
+ });
3611
+ }
3612
+ function serializeBootstrap(data) {
3613
+ return JSON.stringify(data).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
3614
+ }
3615
+ function validCssColor(value) {
3616
+ if (!value) return false;
3617
+ return /^(?:#[0-9a-f]{3,8}|(?:rgb|hsl)a?\([0-9.,%\s]+\)|[a-z]{3,20})$/i.test(value);
3618
+ }
3619
+ function cssFontStack(value) {
3620
+ const family = safeFontFamily(value ?? "Inter");
3621
+ return `${family},ui-sans-serif,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif`;
3622
+ }
3623
+ function cssMonoFontStack(value) {
3624
+ const family = safeFontFamily(value ?? "Geist Mono");
3625
+ return `${family},ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace`;
3626
+ }
3627
+ function safeFontFamily(value) {
3628
+ return /^[a-z0-9 ,.'-]{1,80}$/i.test(value) ? value : "Inter";
3629
+ }
3630
+ function validFontSource(value) {
3631
+ return /^(?:\/[a-z0-9_./%-]+|https:\/\/[^\s"'()<>]+)$/i.test(value);
3632
+ }
3633
+ function themeBootstrap() {
3634
+ try {
3635
+ const stored = localStorage.getItem("deepspace-documentation-theme");
3636
+ const configured = document.documentElement.dataset.themeMode || "system";
3637
+ const strict = document.documentElement.dataset.themeStrict === "true";
3638
+ const mode = !strict && (stored === "light" || stored === "dark" || stored === "system") ? stored : configured;
3639
+ document.documentElement.dataset.themeMode = mode;
3640
+ document.documentElement.dataset.theme = mode === "system" ? matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : mode;
3641
+ } catch {
3642
+ document.documentElement.dataset.theme = "light";
3643
+ }
3644
+ }
3645
+ var DOCUMENTATION_THEME_BOOTSTRAP = `(${themeBootstrap.toString()})()
3646
+ `;
3647
+
3648
+ // src/documentation/build.ts
3649
+ function buildDocumentation(options) {
3650
+ const validation = validateDocumentation(options.appDir);
3651
+ const outputDir = options.outputDir ? resolve7(options.outputDir) : validation.outputDir;
3652
+ const output = new DocumentationOutput(outputDir, validation.appDir, validation.sourceDir);
3653
+ const basePath = normalizeDocumentationBasePath(options.basePath ?? "/docs");
3654
+ const renderConfig = options.baseUrl ? { ...validation.graph.config, url: options.baseUrl } : validation.graph.config;
3655
+ const write = output.write.bind(output);
3656
+ const customRuntime = buildCustomDocumentationRuntime(validation, basePath);
3657
+ if (customRuntime) {
3658
+ for (const asset of customRuntime.browserAssets) write(`assets/${asset.path}`, asset.content);
3659
+ }
3660
+ for (const page of validation.graph.pages) {
3661
+ const artifactPaths = artifactPathsForRoute(page.route);
3662
+ const renderOptions = {
3663
+ config: renderConfig,
3664
+ basePath,
3665
+ page,
3666
+ pages: validation.graph.pages,
3667
+ navigation: validation.graph.navigation
3668
+ };
3669
+ const runtime = createPageRuntime(renderOptions);
3670
+ write(artifactPaths.html, renderPage(renderOptions, customRuntime ? {
3671
+ customStylesheets: customRuntime.stylesheets,
3672
+ markup: customRuntime.render(runtime.data, page.route),
3673
+ runtimeModule: true,
3674
+ runtimeScript: documentationPublicPath(basePath, "/assets/documentation-custom-runtime.js")
3675
+ } : {}, runtime));
3676
+ write(artifactPaths.data, stableJson(runtime.document));
3677
+ write(artifactPaths.markdown, pageMarkdown(page, basePath));
3678
+ }
3679
+ for (const [from, to] of Object.entries(validation.graph.config.redirects)) {
3680
+ write(artifactPathsForRoute(from).html, renderRedirect(to, basePath));
3681
+ }
3682
+ const notFoundPage = createNotFoundPage();
3683
+ const notFoundOptions = {
3684
+ config: renderConfig,
3685
+ basePath,
3686
+ page: notFoundPage,
3687
+ pages: [notFoundPage],
3688
+ navigation: validation.graph.navigation
3689
+ };
3690
+ const notFoundRuntime = createPageRuntime(notFoundOptions);
3691
+ write("404.html", renderPage(notFoundOptions, customRuntime ? {
3692
+ customStylesheets: customRuntime.stylesheets,
3693
+ markup: customRuntime.render(notFoundRuntime.data, notFoundPage.route),
3694
+ runtimeModule: true,
3695
+ runtimeScript: documentationPublicPath(basePath, "/assets/documentation-custom-runtime.js")
3696
+ } : {}, notFoundRuntime));
3697
+ write("assets/documentation.css", DOCUMENTATION_CSS.trimStart());
3698
+ write("assets/documentation-theme.js", DOCUMENTATION_THEME_BOOTSTRAP);
3699
+ if (!customRuntime) write("assets/documentation-runtime.js", buildDocumentationRuntime().trimStart());
3700
+ write("search.json", stableJson(createSearchEntries(validation.graph.pages)));
3701
+ write("assistant-index.json", stableJson(createAssistantChunks(validation.graph.pages)));
3702
+ const openApiOperations = validation.graph.pages.flatMap(
3703
+ (page) => page.openapi ? [{ route: page.route, ...page.openapi }] : []
3704
+ );
3705
+ if (openApiOperations.length > 0) write("openapi.json", stableJson(openApiOperations));
3706
+ const machinePages = pagesInNavigationOrder(validation.graph.pages, validation.graph.navigation);
3707
+ write("llms.txt", renderLlmsIndex(machinePages, validation.graph.config.name, basePath));
3708
+ write("llms-full.txt", renderLlmsFull(machinePages, validation.graph.config.name, basePath));
3709
+ const skill = createDocumentationSkillArtifacts(validation.graph, renderConfig.url, basePath);
3710
+ write("skill.md", skill.markdown);
3711
+ write(`.well-known/skills/${skill.name}/SKILL.md`, skill.markdown);
3712
+ write(".well-known/skills/index.json", stableJson(skill.legacyIndex));
3713
+ write(`.well-known/agent-skills/${skill.name}/skill.md`, skill.markdown);
3714
+ write(".well-known/agent-skills/index.json", stableJson(skill.discoveryIndex));
3715
+ write("sitemap.xml", renderSitemap(validation.graph.pages, renderConfig.url));
3716
+ write("robots.txt", renderRobots(renderConfig.url, renderConfig.seo.noindex));
3717
+ output.copyMedia(
3718
+ validation.sourceDir,
3719
+ new Set(
3720
+ validation.graph.pages.filter((page) => page.kind === "openapi").map((page) => resolve7(page.sourcePath))
3721
+ )
3722
+ );
3723
+ const resources = [...output.files].filter((file) => isNativeDocumentationResourcePath(`/${file}`)).map((file) => encodeNativeDocumentationPath(`/${file}`)).sort();
3724
+ const outputHash = output.hash();
3725
+ const manifest = {
3726
+ version: DOCUMENTATION_MANIFEST_VERSION,
3727
+ sourceHash: validation.graph.sourceHash,
3728
+ outputHash,
3729
+ name: validation.graph.config.name,
3730
+ pageCount: validation.graph.pages.length,
3731
+ routes: [
3732
+ ...validation.graph.pages.map((page) => page.route),
3733
+ ...Object.keys(validation.graph.config.redirects)
3734
+ ].map(encodeNativeDocumentationPath).sort(),
3735
+ resources,
3736
+ assistant: validation.graph.config.assistant,
3737
+ mcp: validation.graph.config.mcp,
3738
+ domains: validation.graph.config.domains,
3739
+ basePath
3740
+ };
3741
+ write("manifest.json", stableJson(manifest));
3742
+ return {
3743
+ ...validation,
3744
+ outputDir,
3745
+ manifest,
3746
+ files: [...output.files].sort()
3747
+ };
3748
+ }
3749
+ function buildDocumentationRuntime() {
3750
+ const entry = runtimeEntry("documentation-runtime.js", "runtime/auto-client.tsx");
3751
+ if (entry.endsWith(".js")) return readFileSync6(entry, "utf8");
3752
+ const result = buildSync3({
3753
+ entryPoints: [entry],
3754
+ bundle: true,
3755
+ write: false,
3756
+ minify: true,
3757
+ platform: "browser",
3758
+ format: "iife",
3759
+ target: ["es2022"],
3760
+ jsx: "automatic",
3761
+ define: { "process.env.NODE_ENV": '"production"' }
3762
+ });
3763
+ const output = result.outputFiles?.[0]?.text;
3764
+ if (!output) {
3765
+ throw new DocumentationError("Unable to compile the documentation browser runtime", "documentation_runtime_build_failed");
3766
+ }
3767
+ return output;
3768
+ }
3769
+
3770
+ // src/documentation/vite.ts
3771
+ import { existsSync as existsSync7, readdirSync as readdirSync3, realpathSync as realpathSync3, rmSync as rmSync3 } from "fs";
3772
+ import { relative as relative6, resolve as resolve8, sep as sep5 } from "path";
3773
+ var IGNORED_DOCUMENTATION_WATCH_PATH = /^(?:\.deepspace|\.git|\.wrangler|dist|node_modules|public\/_documentation(?:-root)?|src\/router\.ts)(?:\/|$)/;
3774
+ function deepSpaceDocumentation() {
3775
+ let appDir = process.cwd();
3776
+ let outputDir = resolve8(appDir, ".deepspace", "documentation");
3777
+ let result = null;
3778
+ const rebuild = () => {
3779
+ if (!existsSync7(resolve8(appDir, DOCUMENTATION_CONFIG_FILE))) {
3780
+ rmSync3(outputDir, { recursive: true, force: true });
3781
+ rmSync3(`${outputDir}-root`, { recursive: true, force: true });
3782
+ result = null;
3783
+ return null;
3784
+ }
3785
+ const baseUrl = process.env.DEEPSPACE_DOCUMENTATION_BASE_URL;
3786
+ result = buildDocumentation({
3787
+ appDir,
3788
+ outputDir,
3789
+ ...baseUrl ? { baseUrl } : {}
3790
+ });
3791
+ if (result.manifest.domains.length) {
3792
+ buildDocumentation({
3793
+ appDir,
3794
+ outputDir: `${outputDir}-root`,
3795
+ basePath: "",
3796
+ baseUrl: `https://${result.manifest.domains[0]}`
3797
+ });
3798
+ } else rmSync3(`${outputDir}-root`, { recursive: true, force: true });
3799
+ return result;
3800
+ };
3801
+ return {
3802
+ name: "deepspace-documentation",
3803
+ config(config, environment) {
3804
+ appDir = resolve8(config.root ?? process.cwd());
3805
+ outputDir = resolve8(appDir, "public", "_documentation");
3806
+ if (environment.command === "serve") {
3807
+ rmSync3(resolve8(appDir, ".deepspace", "documentation"), { recursive: true, force: true });
3808
+ rmSync3(resolve8(appDir, ".deepspace", "documentation-root"), {
3809
+ recursive: true,
3810
+ force: true
3811
+ });
3812
+ }
3813
+ try {
3814
+ rebuild();
3815
+ } catch (error) {
3816
+ throw new Error(documentationBuildErrorMessage(error), { cause: error });
3817
+ }
3818
+ return {
3819
+ server: {
3820
+ watch: {
3821
+ ignored: ["**/.deepspace/documentation*/**", "**/public/_documentation*/**"]
3822
+ }
3823
+ }
3824
+ };
3825
+ },
3826
+ configResolved(config) {
3827
+ const resolvedRoot = resolve8(config.root);
3828
+ const rootChanged = !pathsReferToSameDirectory(appDir, resolvedRoot);
3829
+ appDir = resolvedRoot;
3830
+ outputDir = resolve8(appDir, "public", "_documentation");
3831
+ if (rootChanged) rebuild();
3832
+ },
3833
+ configureServer(server) {
3834
+ if (!result) return;
3835
+ const configPath = resolve8(appDir, DOCUMENTATION_CONFIG_FILE);
3836
+ const customRuntimePath = resolve8(appDir, "documentation.tsx");
3837
+ const watchedPaths = [configPath, customRuntimePath, result.sourceDir];
3838
+ const initialPaths = currentDocumentationPaths(appDir);
3839
+ server.watcher.add(watchedPaths);
3840
+ const initialReplayTimeout = setTimeout(() => initialPaths.clear(), 1e4);
3841
+ initialReplayTimeout.unref();
3842
+ server.watcher.on("ready", () => {
3843
+ clearTimeout(initialReplayTimeout);
3844
+ initialPaths.clear();
3845
+ });
3846
+ let timer;
3847
+ server.watcher.on("all", (event, path) => {
3848
+ const changed = canonicalPath(path);
3849
+ if ((event === "add" || event === "addDir") && initialPaths.delete(changed)) return;
3850
+ if (!shouldRebuildDocumentation(appDir, configPath, result?.sourceDir ?? "", changed))
3851
+ return;
3852
+ if (timer) clearTimeout(timer);
3853
+ timer = setTimeout(() => {
3854
+ try {
3855
+ const previousDomains = result?.manifest.domains ?? [];
3856
+ const next = rebuild();
3857
+ if (next) {
3858
+ server.config.logger.info(
3859
+ `[deepspace] rebuilt ${next.manifest.pageCount} documentation page(s)`
3860
+ );
3861
+ if (changed === canonicalPath(configPath) && !sameDomains(previousDomains, next.manifest.domains)) {
3862
+ void server.restart().catch((error) => {
3863
+ server.config.logger.error(
3864
+ `[deepspace] development Worker restart failed: ${error instanceof Error ? error.message : String(error)}`
3865
+ );
3866
+ });
3867
+ }
3868
+ }
3869
+ } catch (error) {
3870
+ server.config.logger.error(
3871
+ `[deepspace] documentation rebuild failed: ${documentationBuildErrorMessage(error)}`
3872
+ );
3873
+ }
3874
+ }, 120);
3875
+ });
3876
+ }
3877
+ };
3878
+ }
3879
+ function documentationBuildErrorMessage(error) {
3880
+ const diagnostics = error && typeof error === "object" && "diagnostics" in error ? error.diagnostics : void 0;
3881
+ if (!diagnostics?.length) return error instanceof Error ? error.message : String(error);
3882
+ return diagnostics.map(
3883
+ (diagnostic) => `${diagnostic.file ?? "documentation.json"}${diagnostic.line ? `:${diagnostic.line}` : ""}: ${diagnostic.message}`
3884
+ ).join("\n");
3885
+ }
3886
+ function sameDomains(left, right) {
3887
+ if (left.length !== right.length) return false;
3888
+ const expected = new Set(right);
3889
+ return left.every((domain) => expected.has(domain));
3890
+ }
3891
+ function currentDocumentationPaths(appDir) {
3892
+ const paths = /* @__PURE__ */ new Set();
3893
+ const directories = [appDir];
3894
+ for (const directory of directories) {
3895
+ for (const entry of readdirSync3(directory, { withFileTypes: true })) {
3896
+ const path = resolve8(directory, entry.name);
3897
+ const relativePath = relative6(appDir, path).split(sep5).join("/");
3898
+ if (IGNORED_DOCUMENTATION_WATCH_PATH.test(relativePath)) continue;
3899
+ paths.add(canonicalPath(path));
3900
+ if (entry.isDirectory()) directories.push(path);
3901
+ }
3902
+ }
3903
+ return paths;
3904
+ }
3905
+ function canonicalPath(path) {
3906
+ try {
3907
+ return realpathSync3(path);
3908
+ } catch {
3909
+ return resolve8(path);
3910
+ }
3911
+ }
3912
+ function pathsReferToSameDirectory(left, right) {
3913
+ return canonicalPath(left) === canonicalPath(right);
3914
+ }
3915
+ function isWithinPath(root, candidate) {
3916
+ if (!root) return false;
3917
+ const path = relative6(resolve8(root), resolve8(candidate));
3918
+ return path !== ".." && !path.startsWith(`..${sep5}`);
3919
+ }
3920
+ function shouldRebuildDocumentation(appDir, configPath, sourceDir, changed) {
3921
+ appDir = canonicalPath(appDir);
3922
+ configPath = canonicalPath(configPath);
3923
+ sourceDir = canonicalPath(sourceDir);
3924
+ if (changed === configPath || changed === resolve8(appDir, "documentation.tsx") || isWithinPath(sourceDir, changed)) {
3925
+ return true;
3926
+ }
3927
+ if (!isWithinPath(appDir, changed) || !/\.(?:css|js|jsx|ts|tsx)$/i.test(changed)) return false;
3928
+ const relativePath = relative6(appDir, changed).split(sep5).join("/");
3929
+ return !IGNORED_DOCUMENTATION_WATCH_PATH.test(relativePath);
3930
+ }
3931
+ export {
3932
+ AGENT_SKILLS_SCHEMA,
3933
+ DEFAULT_DOCUMENTATION_CONTEXTUAL_ACTIONS,
3934
+ DEFAULT_DOCUMENTATION_OPENAPI_EXAMPLES,
3935
+ DOCUMENTATION_CONFIG_FILE,
3936
+ DOCUMENTATION_CONTEXTUAL_ACTIONS,
3937
+ DOCUMENTATION_MANIFEST_VERSION,
3938
+ DOCUMENTATION_OPENAPI_SAMPLE_LANGUAGES,
3939
+ DocumentationError,
3940
+ buildDocumentation,
3941
+ createDocumentationSkillArtifacts,
3942
+ deepSpaceDocumentation,
3943
+ documentationSkillName,
3944
+ htmlToText,
3945
+ loadDocumentationConfig,
3946
+ normalizeRoute,
3947
+ parseFrontmatter,
3948
+ parseMarkdown,
3949
+ routeFromRelativePath,
3950
+ slugify,
3951
+ validateDocumentation
3952
+ };
3953
+ //# sourceMappingURL=documentation.js.map