blume 1.6.5 → 1.7.0

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 (179) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/bin/blume.mjs +3 -2
  3. package/dist/cli/chunk-0qhq7b8q.js +111 -0
  4. package/dist/cli/chunk-0qhq7b8q.js.map +11 -0
  5. package/dist/cli/chunk-18tjv4f7.js +96 -0
  6. package/dist/cli/chunk-18tjv4f7.js.map +10 -0
  7. package/dist/cli/chunk-27gtm2ym.js +69 -0
  8. package/dist/cli/chunk-27gtm2ym.js.map +11 -0
  9. package/dist/cli/chunk-2aj8ddew.js +72 -0
  10. package/dist/cli/chunk-2aj8ddew.js.map +10 -0
  11. package/dist/cli/chunk-3r94j3tc.js +221 -0
  12. package/dist/cli/chunk-3r94j3tc.js.map +10 -0
  13. package/dist/cli/chunk-4trphnvy.js +102 -0
  14. package/dist/cli/chunk-4trphnvy.js.map +11 -0
  15. package/dist/cli/chunk-4xyggvgf.js +21 -0
  16. package/dist/cli/chunk-4xyggvgf.js.map +10 -0
  17. package/dist/cli/chunk-5d4q7121.js +4064 -0
  18. package/dist/cli/chunk-5d4q7121.js.map +40 -0
  19. package/dist/cli/chunk-5hs6gb7n.js +32 -0
  20. package/dist/cli/chunk-5hs6gb7n.js.map +10 -0
  21. package/dist/cli/chunk-6kzzpsx8.js +26 -0
  22. package/dist/cli/chunk-6kzzpsx8.js.map +10 -0
  23. package/dist/cli/chunk-8gnpdsn1.js +952 -0
  24. package/dist/cli/chunk-8gnpdsn1.js.map +12 -0
  25. package/dist/cli/chunk-9qs6acpw.js +176 -0
  26. package/dist/cli/chunk-9qs6acpw.js.map +10 -0
  27. package/dist/cli/chunk-agy5rzxy.js +2453 -0
  28. package/dist/cli/chunk-agy5rzxy.js.map +15 -0
  29. package/dist/cli/chunk-bcy492zc.js +16 -0
  30. package/dist/cli/chunk-bcy492zc.js.map +10 -0
  31. package/dist/cli/chunk-btfr9yvw.js +41 -0
  32. package/dist/cli/chunk-btfr9yvw.js.map +10 -0
  33. package/dist/cli/chunk-cbjnx4s8.js +73 -0
  34. package/dist/cli/chunk-cbjnx4s8.js.map +10 -0
  35. package/dist/cli/chunk-cfw6x4rm.js +1967 -0
  36. package/dist/cli/chunk-cfw6x4rm.js.map +34 -0
  37. package/dist/cli/chunk-ckh3a410.js +277 -0
  38. package/dist/cli/chunk-ckh3a410.js.map +11 -0
  39. package/dist/cli/chunk-drke6t0h.js +259 -0
  40. package/dist/cli/chunk-drke6t0h.js.map +11 -0
  41. package/dist/cli/chunk-ev67ycx0.js +15 -0
  42. package/dist/cli/chunk-ev67ycx0.js.map +10 -0
  43. package/dist/cli/chunk-ey89bjj1.js +209 -0
  44. package/dist/cli/chunk-ey89bjj1.js.map +11 -0
  45. package/dist/cli/chunk-j6pxe0dt.js +69 -0
  46. package/dist/cli/chunk-j6pxe0dt.js.map +11 -0
  47. package/dist/cli/chunk-jk1zwka1.js +387 -0
  48. package/dist/cli/chunk-jk1zwka1.js.map +12 -0
  49. package/dist/cli/chunk-jtb45atp.js +467 -0
  50. package/dist/cli/chunk-jtb45atp.js.map +14 -0
  51. package/dist/cli/chunk-jxkxjsc1.js +76 -0
  52. package/dist/cli/chunk-jxkxjsc1.js.map +10 -0
  53. package/dist/cli/chunk-kwx90v78.js +81 -0
  54. package/dist/cli/chunk-kwx90v78.js.map +10 -0
  55. package/dist/cli/chunk-n0nyat6g.js +30 -0
  56. package/dist/cli/chunk-n0nyat6g.js.map +10 -0
  57. package/dist/cli/chunk-pxj10x8y.js +35 -0
  58. package/dist/cli/chunk-pxj10x8y.js.map +10 -0
  59. package/dist/cli/chunk-qq9nm3qd.js +1141 -0
  60. package/dist/cli/chunk-qq9nm3qd.js.map +19 -0
  61. package/dist/cli/chunk-s102bysw.js +5170 -0
  62. package/dist/cli/chunk-s102bysw.js.map +47 -0
  63. package/dist/cli/chunk-s5dsk8bj.js +769 -0
  64. package/dist/cli/chunk-s5dsk8bj.js.map +13 -0
  65. package/dist/cli/chunk-s5e5jt53.js +227 -0
  66. package/dist/cli/chunk-s5e5jt53.js.map +11 -0
  67. package/dist/cli/chunk-sbdqrjbb.js +81 -0
  68. package/dist/cli/chunk-sbdqrjbb.js.map +10 -0
  69. package/dist/cli/chunk-tnskyrej.js +117 -0
  70. package/dist/cli/chunk-tnskyrej.js.map +10 -0
  71. package/dist/cli/chunk-v2ymm99c.js +1016 -0
  72. package/dist/cli/chunk-v2ymm99c.js.map +13 -0
  73. package/dist/cli/chunk-v5mm027v.js +185 -0
  74. package/dist/cli/chunk-v5mm027v.js.map +11 -0
  75. package/dist/cli/chunk-vt8fgygt.js +23 -0
  76. package/dist/cli/chunk-vt8fgygt.js.map +10 -0
  77. package/dist/cli/chunk-vxv4x1n8.js +17 -0
  78. package/dist/cli/chunk-vxv4x1n8.js.map +10 -0
  79. package/dist/cli/chunk-wd27zjcz.js +60 -0
  80. package/dist/cli/chunk-wd27zjcz.js.map +10 -0
  81. package/dist/cli/chunk-x66c5yjn.js +23 -0
  82. package/dist/cli/chunk-x66c5yjn.js.map +10 -0
  83. package/dist/cli/chunk-xv91q4nm.js +5314 -0
  84. package/dist/cli/chunk-xv91q4nm.js.map +58 -0
  85. package/dist/cli/chunk-y3g15rvv.js +679 -0
  86. package/dist/cli/chunk-y3g15rvv.js.map +15 -0
  87. package/dist/cli/chunk-ye9zdkgv.js +136 -0
  88. package/dist/cli/chunk-ye9zdkgv.js.map +10 -0
  89. package/dist/cli/chunk-ynacq3ev.js +1062 -0
  90. package/dist/cli/chunk-ynacq3ev.js.map +25 -0
  91. package/dist/cli/chunk-zr3ygrq3.js +54 -0
  92. package/dist/cli/chunk-zr3ygrq3.js.map +10 -0
  93. package/dist/cli/index.js +55 -27597
  94. package/dist/cli/index.js.map +5 -243
  95. package/dist/types/ai/ask-context.d.ts +26 -0
  96. package/dist/types/components/layout/nav-utils.d.ts +33 -1
  97. package/dist/types/core/code-fences.d.ts +11 -0
  98. package/dist/types/core/package-root.d.ts +1 -1
  99. package/dist/types/core/schema.d.ts +70 -0
  100. package/dist/types/theme/fonts.d.ts +22 -22
  101. package/docs/02-deployment.mdx +22 -1
  102. package/docs/configuration/ask-ai.mdx +1 -1
  103. package/docs/configuration/customization.mdx +2 -9
  104. package/docs/content/navigation.mdx +2 -0
  105. package/docs/content/syntax.mdx +1 -1
  106. package/docs/discoverability/open-graph.mdx +4 -0
  107. package/docs/reference/cli.mdx +1 -1
  108. package/package.json +4 -2
  109. package/src/ai/api/handlers.ts +4 -7
  110. package/src/ai/api/paths.ts +8 -0
  111. package/src/ai/api/spec.ts +2 -1
  112. package/src/ai/ask-context.ts +378 -22
  113. package/src/astro/generate.ts +161 -28
  114. package/src/astro/include-hmr.ts +10 -13
  115. package/src/astro/include-refresh.ts +0 -0
  116. package/src/astro/index.ts +6 -1
  117. package/src/astro/integration.ts +280 -53
  118. package/src/astro/module-types.ts +83 -0
  119. package/src/astro/templates.ts +256 -108
  120. package/src/audit/image-size.ts +10 -8
  121. package/src/cli/command-meta.ts +77 -0
  122. package/src/cli/commands/add.ts +2 -4
  123. package/src/cli/commands/audit.ts +2 -4
  124. package/src/cli/commands/build.ts +70 -346
  125. package/src/cli/commands/check.ts +2 -4
  126. package/src/cli/commands/dev.ts +31 -42
  127. package/src/cli/commands/doctor.ts +2 -4
  128. package/src/cli/commands/eject.ts +3 -41
  129. package/src/cli/commands/eval.ts +2 -5
  130. package/src/cli/commands/init.ts +2 -4
  131. package/src/cli/commands/mcp-stdio.ts +2 -5
  132. package/src/cli/commands/preview.ts +3 -5
  133. package/src/cli/commands/sync.ts +2 -4
  134. package/src/cli/commands/translate.ts +2 -5
  135. package/src/cli/commands/validate.ts +2 -4
  136. package/src/cli/commands/version.ts +2 -4
  137. package/src/cli/eject-scripts.ts +0 -45
  138. package/src/cli/host-args.ts +16 -0
  139. package/src/cli/index.ts +84 -35
  140. package/src/cli/lazy-command.ts +47 -0
  141. package/src/components/Icon.astro +24 -0
  142. package/src/components/content/GithubInfo.astro +4 -1
  143. package/src/components/icon-sprite-middleware.ts +41 -0
  144. package/src/components/icon-sprite.ts +93 -0
  145. package/src/components/layout/IconSprite.astro +11 -0
  146. package/src/components/layout/NavTree.astro +156 -188
  147. package/src/components/layout/NavTreeCache.astro +45 -0
  148. package/src/components/layout/NavTreeScript.astro +256 -0
  149. package/src/components/layout/PageActions.astro +11 -5
  150. package/src/components/layout/PageLayout.astro +21 -3
  151. package/src/components/layout/ReferenceLayout.astro +21 -4
  152. package/src/components/layout/RootLayout.astro +44 -6
  153. package/src/components/layout/nav-cache.ts +49 -0
  154. package/src/components/layout/nav-utils.ts +69 -1
  155. package/src/components/layout/page-locale.ts +29 -0
  156. package/src/core/api-name.ts +18 -0
  157. package/src/core/code-fences.ts +48 -0
  158. package/src/core/content-assets.ts +3 -7
  159. package/src/core/includes.ts +3 -7
  160. package/src/core/package-root.ts +1 -1
  161. package/src/core/schema.ts +19 -0
  162. package/src/core/sources/normalize.ts +2 -37
  163. package/src/core/sources/obsidian.ts +3 -2
  164. package/src/core/svg-dimensions.ts +97 -0
  165. package/src/core/version-cut.ts +2 -2
  166. package/src/deploy/artifacts.ts +370 -0
  167. package/src/deploy/cloudflare-negotiation.ts +97 -32
  168. package/src/deploy/function-bundle.ts +66 -20
  169. package/src/deploy/sitemap.ts +6 -0
  170. package/src/deploy/vercel-negotiation.ts +8 -30
  171. package/src/markdown/language-icon.ts +64 -20
  172. package/src/markdown/mermaid.ts +11 -0
  173. package/src/og/cache.ts +236 -0
  174. package/src/og/card.ts +18 -16
  175. package/src/og/index.ts +8 -1
  176. package/src/openapi/render-mdx.ts +9 -5
  177. package/src/registry/eject.ts +23 -10
  178. package/src/theme/entry.ts +41 -7
  179. package/src/theme/fonts.ts +30 -23
@@ -0,0 +1,1062 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ readExpandedEntryText,
4
+ rewriteRelativeImages
5
+ } from "./chunk-v5mm027v.js";
6
+ import {
7
+ API_CATALOG_PATH,
8
+ API_PAGES_PATH,
9
+ OPENAPI_PATH,
10
+ hasApiCatalog
11
+ } from "./chunk-j6pxe0dt.js";
12
+ import {
13
+ absoluteUrl,
14
+ siteRoot
15
+ } from "./chunk-vxv4x1n8.js";
16
+ import {
17
+ HTML_COMMENT,
18
+ contentIndexable,
19
+ frontmatter_default,
20
+ isOpenApiSource,
21
+ operationOf,
22
+ operationSignature,
23
+ parseHeadingMarkers,
24
+ specAddresses,
25
+ specOf
26
+ } from "./chunk-s102bysw.js";
27
+ import {
28
+ normalizeBasePath,
29
+ withBasePath
30
+ } from "./chunk-27gtm2ym.js";
31
+
32
+ // src/ai/markdown.ts
33
+ import { readFile } from "node:fs/promises";
34
+
35
+ // src/ai/component-markdown.ts
36
+ import { markdownTable } from "markdown-table";
37
+ import { mdxToMdast } from "satteri";
38
+
39
+ // src/components/content/youtube.ts
40
+ var BARE_ID = /^[\w-]{11}$/u;
41
+ var URL_ID = /(?:youtu\.be\/|\/embed\/|\/shorts\/|\/live\/|[?&]v=)(?<id>[\w-]{11})/u;
42
+ var parseYouTubeId = (input) => {
43
+ const value = input.trim();
44
+ if (!value) {
45
+ return null;
46
+ }
47
+ if (BARE_ID.test(value)) {
48
+ return value;
49
+ }
50
+ return URL_ID.exec(value)?.groups?.id ?? null;
51
+ };
52
+
53
+ // src/markdown/features.ts
54
+ var MARKDOWN_FEATURES = {
55
+ subscript: true,
56
+ superscript: true
57
+ };
58
+ var MDX_FEATURES = {
59
+ ...MARKDOWN_FEATURES,
60
+ directive: true,
61
+ math: { singleDollarTextMath: false }
62
+ };
63
+
64
+ // src/ai/component-markdown.ts
65
+ var isString = (value) => typeof value === "string";
66
+ var isNumber = (value) => typeof value === "number";
67
+ var evaluateExpression = (raw, frontmatter) => {
68
+ try {
69
+ const value = new Function("frontmatter", `"use strict"; return (${raw});`)(frontmatter);
70
+ return { ok: true, value };
71
+ } catch {
72
+ return { ok: false, value: undefined };
73
+ }
74
+ };
75
+ var readProps = (node, frontmatter) => {
76
+ const props = {};
77
+ let lossy = false;
78
+ for (const attribute of node.attributes ?? []) {
79
+ if (attribute.type !== "mdxJsxAttribute" || !attribute.name) {
80
+ lossy = true;
81
+ continue;
82
+ }
83
+ if (attribute.value === null || attribute.value === undefined) {
84
+ props[attribute.name] = true;
85
+ } else if (isString(attribute.value)) {
86
+ props[attribute.name] = attribute.value;
87
+ } else {
88
+ const result = evaluateExpression(attribute.value.value, frontmatter);
89
+ if (result.ok) {
90
+ props[attribute.name] = result.value;
91
+ } else {
92
+ lossy = true;
93
+ }
94
+ }
95
+ }
96
+ return { lossy, props };
97
+ };
98
+ var hasOffsets = (node) => typeof node.position?.start?.offset === "number" && typeof node.position?.end?.offset === "number";
99
+ var applySplices = (text, splices) => {
100
+ let result = text;
101
+ for (const splice of [...splices].toSorted((a, b) => b.start - a.start)) {
102
+ const lineStart = result.lastIndexOf(`
103
+ `, splice.start - 1) + 1;
104
+ const prefix = result.slice(lineStart, splice.start);
105
+ const indent = /^[\t ]+$/u.test(prefix) ? prefix : "";
106
+ const replacement = indent ? splice.text.split(`
107
+ `).map((line, index) => index === 0 || line === "" ? line : `${indent}${line}`).join(`
108
+ `) : splice.text;
109
+ result = result.slice(0, splice.start) + replacement + result.slice(splice.end);
110
+ }
111
+ return result;
112
+ };
113
+ var dedent = (text) => {
114
+ const lines = text.split(`
115
+ `);
116
+ const rest = lines.slice(1).filter((line) => line.trim() !== "");
117
+ if (rest.length === 0) {
118
+ return text;
119
+ }
120
+ const indent = Math.min(...rest.map((line) => line.length - line.trimStart().length));
121
+ if (indent === 0) {
122
+ return text;
123
+ }
124
+ return [
125
+ lines[0],
126
+ ...lines.slice(1).map((line) => line.trim() === "" ? "" : line.slice(indent))
127
+ ].join(`
128
+ `);
129
+ };
130
+ var isJsxElement = (node) => node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement";
131
+ var cellText = (value) => String(value ?? "").replaceAll(/\s*\n\s*/gu, " ").replaceAll("|", "\\|").trim();
132
+ var cellCode = (value) => {
133
+ const text = cellText(value);
134
+ return text && !text.includes("`") ? `\`${text}\`` : text;
135
+ };
136
+ var isTypeEntryMap = (value) => typeof value === "object" && value !== null;
137
+ var typeTable = ({ children, props }) => {
138
+ const { type } = props;
139
+ if (!isTypeEntryMap(type)) {
140
+ return null;
141
+ }
142
+ const entries = Object.entries(type);
143
+ const rows = entries.map(([name, info]) => {
144
+ const prop = cellCode(`${name}${info.required ? "" : "?"}`);
145
+ const typeCell = info.typeDescriptionLink ? `[${cellCode(info.type)}](${cellText(info.typeDescriptionLink)})` : cellCode(info.type);
146
+ const defaultCell = info.default === undefined ? "-" : cellCode(info.default);
147
+ const description = cellText([info.description, info.typeDescription].filter((part) => isString(part) && part !== "").join(" "));
148
+ return [prop, typeCell, defaultCell, description];
149
+ });
150
+ const table = rows.length > 0 ? markdownTable([["Prop", "Type", "Default", "Description"], ...rows], {
151
+ alignDelimiters: false
152
+ }) : "";
153
+ return [table, children].filter(Boolean).join(`
154
+
155
+ `);
156
+ };
157
+ var callout = ({ children, props }) => {
158
+ const type = isString(props.type) ? props.type : "info";
159
+ const label = isString(props.title) && props.title !== "" ? props.title : type.charAt(0).toUpperCase() + type.slice(1);
160
+ if (!children) {
161
+ return `> **${label}**`;
162
+ }
163
+ const body = children.split(`
164
+ `).map((line) => line.trim() === "" ? ">" : `> ${line}`).join(`
165
+ `);
166
+ return `> **${label}**
167
+ >
168
+ ${body}`;
169
+ };
170
+ var listItem = (index, content) => {
171
+ const marker = `${index}. `;
172
+ return content.split(`
173
+ `).map((line, lineIndex) => {
174
+ if (lineIndex === 0) {
175
+ return `${marker}${line}`;
176
+ }
177
+ return line === "" ? "" : ` ${line}`;
178
+ }).join(`
179
+ `);
180
+ };
181
+ var steps = ({ childComponents, children }) => {
182
+ const items = childComponents("Step");
183
+ if (items.length === 0) {
184
+ return children;
185
+ }
186
+ return items.map((step, index) => {
187
+ const title = isString(step.props.title) && step.props.title !== "" ? `**${step.props.title}**` : "";
188
+ const content = [title, step.children].filter(Boolean).join(`
189
+
190
+ `);
191
+ return listItem(index + 1, content);
192
+ }).join(`
193
+
194
+ `);
195
+ };
196
+ var tabs = ({ childComponents, children }) => {
197
+ const items = childComponents("Tab");
198
+ if (items.length === 0) {
199
+ return children;
200
+ }
201
+ return items.map((tab, index) => {
202
+ const title = isString(tab.props.title) && tab.props.title !== "" ? tab.props.title : `Tab ${index + 1}`;
203
+ return tab.children ? `**${title}**
204
+
205
+ ${tab.children}` : `**${title}**`;
206
+ }).join(`
207
+
208
+ `);
209
+ };
210
+ var linkText = (value) => value.replaceAll(/[[\]]/gu, String.raw`\$&`);
211
+ var linkDestination = (href) => /[\s()<>]/u.test(href) ? `<${href.replaceAll(/[<>]/gu, String.raw`\$&`)}>` : href;
212
+ var textProp = (value) => {
213
+ if (isString(value)) {
214
+ return value.trim();
215
+ }
216
+ return isNumber(value) ? String(value) : "";
217
+ };
218
+ var card = ({ children, lossy, props }) => {
219
+ if (lossy) {
220
+ return null;
221
+ }
222
+ const title = textProp(props.title);
223
+ const href = isString(props.href) ? props.href.trim() : "";
224
+ const body = children.trim();
225
+ const cta = textProp(props.cta);
226
+ const label = title || href;
227
+ if (label === "") {
228
+ const rest = [body, cta].filter(Boolean).join(`
229
+
230
+ `);
231
+ return rest === "" ? null : rest;
232
+ }
233
+ const head = href === "" ? `**${label}**` : `**[${linkText(label)}](${linkDestination(href)})**`;
234
+ return [head, body, cta].filter(Boolean).join(`
235
+
236
+ `);
237
+ };
238
+ var cardGroup = ({ childBlocks }) => childBlocks().map((block) => block.markdown).join(`
239
+
240
+ `);
241
+ var youtube = ({ props }) => {
242
+ let input = "";
243
+ if (isString(props.id)) {
244
+ input = props.id;
245
+ } else if (isString(props.url)) {
246
+ input = props.url;
247
+ }
248
+ const videoId = parseYouTubeId(input);
249
+ if (!videoId) {
250
+ return null;
251
+ }
252
+ const start = isNumber(props.start) && props.start > 0 ? `&t=${Math.floor(props.start)}s` : "";
253
+ const title = isString(props.title) && props.title !== "" ? props.title : "Watch on YouTube";
254
+ return `[${title}](https://www.youtube.com/watch?v=${videoId}${start})`;
255
+ };
256
+ var inlineCode = (text) => {
257
+ const runs = text.match(/`+/gu);
258
+ const longest = runs ? Math.max(...runs.map((run) => run.length)) : 0;
259
+ const delimiter = "`".repeat(longest + 1);
260
+ const padded = text.startsWith("`") || text.endsWith("`") ? ` ${text} ` : text;
261
+ return `${delimiter}${padded}${delimiter}`;
262
+ };
263
+ var fencedBlock = (lang, code) => {
264
+ const trimmed = code.replace(/(?<!\n)\n+$/u, "");
265
+ const runs = trimmed.match(/`+/gu);
266
+ const longest = runs ? Math.max(...runs.map((run) => run.length)) : 0;
267
+ const fence = "`".repeat(Math.max(3, longest + 1));
268
+ return `${fence}${lang}
269
+ ${trimmed}
270
+ ${fence}`;
271
+ };
272
+ var exampleComponentSerializers = (examples) => ({
273
+ Component: ({ props }) => {
274
+ const path = isString(props.path) ? props.path : undefined;
275
+ const example = path === undefined ? undefined : examples[path];
276
+ return example ? fencedBlock(example.lang, example.source) : null;
277
+ }
278
+ });
279
+ var SERIALIZERS = {
280
+ Callout: callout,
281
+ Card: card,
282
+ CardGroup: cardGroup,
283
+ Steps: steps,
284
+ Tabs: tabs,
285
+ TypeTable: typeTable,
286
+ YouTube: youtube
287
+ };
288
+ var escapeRegExp = (value) => value.replaceAll(/[$()*+.?[\\\]^{|}]/gu, String.raw`\$&`);
289
+ var componentHint = (registry) => new RegExp(`<(?:${Object.keys(registry).map(escapeRegExp).join("|")})[\\s/>]`, "u");
290
+ var BUILT_IN_HINT = componentHint(SERIALIZERS);
291
+ var componentRegistry = (components) => components && Object.keys(components).length > 0 ? { ...SERIALIZERS, ...components } : SERIALIZERS;
292
+ var renderSlice = (walk, start, end, nodes) => {
293
+ const splices = [];
294
+ collectSplices(walk, nodes, splices);
295
+ const spliced = applySplices(walk.source.slice(start, end), splices.map((splice) => ({
296
+ ...splice,
297
+ end: splice.end - start,
298
+ start: splice.start - start
299
+ })));
300
+ return dedent(spliced).trim();
301
+ };
302
+ var renderChildren = (walk, node) => {
303
+ const children = (node.children ?? []).filter(hasOffsets);
304
+ const [first] = children;
305
+ if (!first) {
306
+ return "";
307
+ }
308
+ const start = first.position.start.offset;
309
+ return renderSlice(walk, start, children.at(-1)?.position.end.offset ?? start, children);
310
+ };
311
+ var renderBlock = (walk, node) => {
312
+ const serializer = isJsxElement(node) && node.name ? walk.registry[node.name] : undefined;
313
+ const text = serializer ? serializeElement(serializer, walk, node) : null;
314
+ return text ?? renderSlice(walk, node.position.start.offset, node.position.end.offset, node.children ?? []);
315
+ };
316
+ var serializeElement = (serializer, walk, node) => serializer({
317
+ ...readProps(node, walk.frontmatter),
318
+ childBlocks: () => (node.children ?? []).filter(hasOffsets).map((child) => ({
319
+ markdown: renderBlock(walk, child),
320
+ name: isJsxElement(child) ? child.name : undefined
321
+ })),
322
+ childComponents: (name) => (node.children ?? []).filter((child) => isJsxElement(child) && child.name === name).map((child) => ({
323
+ ...readProps(child, walk.frontmatter),
324
+ children: renderChildren(walk, child)
325
+ })),
326
+ children: renderChildren(walk, node),
327
+ frontmatter: walk.frontmatter ?? {}
328
+ });
329
+ var downlevelComponentNode = (node, walk) => {
330
+ const serializer = node.type === "mdxJsxFlowElement" && node.name ? walk.registry[node.name] : undefined;
331
+ return serializer && hasOffsets(node) ? serializeElement(serializer, walk, node) : null;
332
+ };
333
+ var collectSplices = (walk, nodes, out) => {
334
+ for (const node of nodes) {
335
+ const text = hasOffsets(node) ? downlevelComponentNode(node, walk) : null;
336
+ if (text !== null && hasOffsets(node)) {
337
+ out.push({
338
+ end: node.position.end.offset,
339
+ start: node.position.start.offset,
340
+ text
341
+ });
342
+ continue;
343
+ }
344
+ collectSplices(walk, node.children ?? [], out);
345
+ }
346
+ };
347
+ var downlevelComponents = (source, components, frontmatter) => {
348
+ const registry = componentRegistry(components);
349
+ const hint = registry === SERIALIZERS ? BUILT_IN_HINT : componentHint(registry);
350
+ if (!hint.test(source)) {
351
+ return source;
352
+ }
353
+ let tree;
354
+ try {
355
+ tree = mdxToMdast(source, { features: MDX_FEATURES });
356
+ } catch {
357
+ return source;
358
+ }
359
+ const splices = [];
360
+ collectSplices({ frontmatter, registry, source }, tree.children ?? [], splices);
361
+ return splices.length > 0 ? applySplices(source, splices) : source;
362
+ };
363
+
364
+ // src/deploy/rss.ts
365
+ import { escape as escapeXml } from "html-escaper";
366
+ var capitalize = (value) => value.charAt(0).toUpperCase() + value.slice(1);
367
+ var pageDate = (page) => {
368
+ const raw = page.meta.date ?? page.meta.changelog?.date;
369
+ if (!raw) {
370
+ return;
371
+ }
372
+ const date = new Date(raw);
373
+ return Number.isNaN(date.getTime()) ? undefined : date;
374
+ };
375
+ var buildRssFeeds = (project) => {
376
+ const { config } = project;
377
+ const { rss } = config.seo;
378
+ const { site } = config.deployment;
379
+ if (!(rss.enabled && site)) {
380
+ return [];
381
+ }
382
+ const base = siteRoot(site);
383
+ const deployBase = normalizeBasePath(config.deployment.base);
384
+ const rootLink = `${base}${deployBase}`;
385
+ const feeds = [];
386
+ for (const type of rss.types) {
387
+ const pages = project.graph.pages.filter((page) => page.contentType === type && !(page.meta.draft || page.meta.sidebar.hidden));
388
+ if (pages.length === 0) {
389
+ continue;
390
+ }
391
+ const items = pages.map((page) => ({
392
+ date: pageDate(page),
393
+ description: page.description,
394
+ link: encodeURI(`${base}${withBasePath(deployBase, page.route)}`),
395
+ title: page.title
396
+ })).toSorted((a, b) => (b.date?.getTime() ?? 0) - (a.date?.getTime() ?? 0)).slice(0, rss.limit);
397
+ feeds.push({
398
+ description: config.description,
399
+ items,
400
+ link: rootLink,
401
+ path: `/${type}/rss.xml`,
402
+ title: `${config.title} — ${capitalize(type)}`,
403
+ type
404
+ });
405
+ }
406
+ return feeds;
407
+ };
408
+ var renderItem = (item) => {
409
+ const parts = [
410
+ ` <title>${escapeXml(item.title)}</title>`,
411
+ ` <link>${escapeXml(item.link)}</link>`,
412
+ ` <guid isPermaLink="true">${escapeXml(item.link)}</guid>`
413
+ ];
414
+ if (item.description) {
415
+ parts.push(` <description>${escapeXml(item.description)}</description>`);
416
+ }
417
+ if (item.date) {
418
+ parts.push(` <pubDate>${item.date.toUTCString()}</pubDate>`);
419
+ }
420
+ return ` <item>
421
+ ${parts.join(`
422
+ `)}
423
+ </item>`;
424
+ };
425
+ var renderRssFeed = (feed) => {
426
+ const feedSelfHref = `${feed.link}${feed.path}`;
427
+ const channel = [
428
+ ` <title>${escapeXml(feed.title)}</title>`,
429
+ ` <link>${escapeXml(feed.link)}</link>`,
430
+ ` <description>${escapeXml(feed.description ?? feed.title)}</description>`,
431
+ ` <atom:link href="${escapeXml(feedSelfHref)}" rel="self" type="application/rss+xml" />`
432
+ ];
433
+ const items = feed.items.map(renderItem).join(`
434
+ `);
435
+ return `<?xml version="1.0" encoding="UTF-8"?>
436
+ <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
437
+ <channel>
438
+ ${channel.join(`
439
+ `)}
440
+ ${items}
441
+ </channel>
442
+ </rss>
443
+ `;
444
+ };
445
+
446
+ // src/ai/openapi-components.ts
447
+ var inlineText = (text) => text.trim().replaceAll(/\s+/gu, " ").replaceAll(/[\\`*_[\]<>~]/gu, String.raw`\$&`);
448
+ var listItem2 = (signature, operation) => {
449
+ const tail = [
450
+ inlineText(operation.summary),
451
+ operation.deprecated ? "Deprecated." : ""
452
+ ].filter(Boolean).join(" ");
453
+ return `- [${inlineCode(signature)}](${linkDestination(operation.route)})${tail ? ` — ${tail}` : ""}`;
454
+ };
455
+ var openapiComponentSerializers = (specs) => {
456
+ const spec = (source) => isString(source) ? specOf(specs, source) : undefined;
457
+ return {
458
+ ApiOverview: ({ props }) => {
459
+ const data = spec(props.source);
460
+ if (!data) {
461
+ return null;
462
+ }
463
+ const { addresses, label } = specAddresses(data);
464
+ const lines = [
465
+ data.version ? `Version ${inlineText(data.version)}` : "",
466
+ addresses.length > 0 ? `${label}: ${addresses.map(inlineCode).join(", ")}` : ""
467
+ ].filter(Boolean);
468
+ return lines.length > 0 ? lines.join(`
469
+
470
+ `) : null;
471
+ },
472
+ ApiTagOperations: ({ props }) => {
473
+ const { tag } = props;
474
+ const data = spec(props.source);
475
+ if (!(data && isString(tag))) {
476
+ return null;
477
+ }
478
+ const items = Object.values(data.operations).filter((operation) => operation.tagSlug === tag).map((operation) => listItem2(operationSignature(data, operation), operation));
479
+ return items.length > 0 ? items.join(`
480
+ `) : null;
481
+ },
482
+ Operation: ({ props }) => {
483
+ const { id } = props;
484
+ const data = spec(props.source);
485
+ if (!(data && isString(id))) {
486
+ return null;
487
+ }
488
+ const operation = operationOf(data, id);
489
+ if (!operation) {
490
+ return null;
491
+ }
492
+ const signature = inlineCode(operationSignature(data, operation));
493
+ return operation.deprecated ? `${signature}
494
+
495
+ **Deprecated.**` : signature;
496
+ }
497
+ };
498
+ };
499
+
500
+ // src/ai/serializers.ts
501
+ var projectComponentSerializers = (project) => ({
502
+ ...exampleComponentSerializers(project.examples ?? {}),
503
+ ...openapiComponentSerializers(project.sources.find(isOpenApiSource)?.openApiData() ?? {}),
504
+ ...project.config.ai.markdownComponents
505
+ });
506
+
507
+ // src/ai/skills.ts
508
+ import { join } from "pathe";
509
+
510
+ // src/ai/tar.ts
511
+ import { createTar } from "nanotar";
512
+ var encoder = new TextEncoder;
513
+
514
+ // src/ai/skills.ts
515
+ var AGENT_SKILLS_DIR = "/.well-known/agent-skills";
516
+ var AGENT_SKILLS_INDEX_PATH = "/.well-known/agent-skills/index.json";
517
+
518
+ // src/ai/visibility.ts
519
+ var CODE_FENCE_BLOCK = /^(?<fence>`{3,}|~{3,})[^\n]*\n[\s\S]*?^\k<fence>[^\n]*(?=\n|$)/gmu;
520
+ var FENCE_TOKEN = /\u0000blume-fence-(?<index>\d+)\u0000/gu;
521
+ var visibilityBlock = (audience) => new RegExp(`<Visibility\\s+for\\s*=\\s*(?:"${audience}"|'${audience}')\\s*>(?<inner>[\\s\\S]*?)</Visibility\\s*>`, "gu");
522
+ var BLOCKS = {
523
+ agents: visibilityBlock("agents"),
524
+ web: visibilityBlock("web")
525
+ };
526
+ var applyAudienceVisibility = (markdown, audience) => {
527
+ const fences = [];
528
+ const masked = markdown.replace(CODE_FENCE_BLOCK, (block) => {
529
+ fences.push(block);
530
+ return `\x00blume-fence-${fences.length - 1}\x00`;
531
+ });
532
+ let touched = false;
533
+ const filtered = masked.replaceAll(BLOCKS[audience === "agents" ? "web" : "agents"], () => {
534
+ touched = true;
535
+ return "";
536
+ }).replaceAll(BLOCKS[audience], (_match, inner) => {
537
+ touched = true;
538
+ return inner;
539
+ });
540
+ const tidied = touched ? filtered.replaceAll(/\n{3,}/gu, `
541
+
542
+ `) : filtered;
543
+ return tidied.replaceAll(FENCE_TOKEN, (token, index) => fences[Number(index)] ?? token);
544
+ };
545
+ var applyAgentVisibility = (markdown) => applyAudienceVisibility(markdown, "agents");
546
+
547
+ // src/ai/llms.ts
548
+ var pageUrl = (route, site, base = "") => {
549
+ const path = withBasePath(base, route);
550
+ return encodeURI(site ? absoluteUrl(site, path) : path);
551
+ };
552
+ var oneLine = (text) => text.replaceAll(/\s+/gu, " ").trim();
553
+ var agentResourceLines = (project) => {
554
+ const { config } = project;
555
+ const { site } = config.deployment;
556
+ const url = (path) => pageUrl(path, site, normalizeBasePath(config.deployment.base));
557
+ const lines = [
558
+ `- [llms-full.txt](${url("/llms-full.txt")}): The full Markdown of every page in one file.`,
559
+ `- [Page Markdown](${url("/index.md")}): Append \`.md\` to any page URL to fetch that page as raw Markdown.`
560
+ ];
561
+ if (config.ai.api) {
562
+ lines.push(`- [JSON API](${url(API_PAGES_PATH)}): Page index of the JSON docs API; each entry links the page's JSON and Markdown forms. Described by the OpenAPI document at ${url(OPENAPI_PATH)}.`);
563
+ }
564
+ if (config.ai.mcp.enabled) {
565
+ lines.push(`- [MCP server](${url(config.ai.mcp.route)}): Streamable HTTP Model Context Protocol server with search_docs, get_page, list_pages, and get_navigation tools, plus every page as a resource. Discovery document: ${url("/.well-known/mcp.json")}`);
566
+ }
567
+ if (config.ai.skills) {
568
+ lines.push(`- [Agent skills](${url(AGENT_SKILLS_INDEX_PATH)}): Agent Skills discovery index of the skills this site publishes.`);
569
+ }
570
+ if (hasApiCatalog(config)) {
571
+ lines.push(`- [API catalog](${url(API_CATALOG_PATH)}): RFC 9727 linkset of the APIs documented here.`);
572
+ }
573
+ if (config.seo.agentReadability) {
574
+ lines.push(`- [agent-readability.json](${url("/agent-readability.json")}): Manifest of every agent-facing artifact on this site.`);
575
+ }
576
+ if (site && config.seo.sitemap) {
577
+ lines.push(`- [Sitemap](${url("/sitemap.xml")}): Every indexable page URL with its last-modified date.`);
578
+ }
579
+ return lines;
580
+ };
581
+ var eligiblePages = (project, options = {}) => project.graph.pages.filter((page) => !(page.meta.ai.exclude || page.meta.draft || page.meta.sidebar.hidden || page.meta.seo.noindex && page.source.name !== "openapi" || options.versions === "current" && page.version !== "") && (project.config.ai.llmsTxt.openapi || page.source.name !== "openapi"));
582
+ var indexedNavigations = (project) => {
583
+ const { i18n, versions } = project.config;
584
+ const current = i18n ? i18n.locales.flatMap(({ code, label }) => {
585
+ const nav = project.graph.navigationByLocale[code];
586
+ if (!nav) {
587
+ return [];
588
+ }
589
+ return [
590
+ { label: code === i18n.defaultLocale ? undefined : label, nav }
591
+ ];
592
+ }) : [{ nav: project.graph.navigation }];
593
+ const archived = (versions?.archived ?? []).flatMap((version) => {
594
+ const byLocale = project.graph.navigationByVersion[version.id] ?? {};
595
+ const versionLabel = `${version.label ?? version.id} (archived)`;
596
+ return Object.entries(byLocale).flatMap(([code, nav]) => {
597
+ const locale = i18n?.locales.find((entry) => entry.code === code);
598
+ const label = locale && code !== i18n?.defaultLocale ? `${locale.label} — ${versionLabel}` : versionLabel;
599
+ return [{ label, nav }];
600
+ });
601
+ });
602
+ return [...current, ...archived];
603
+ };
604
+ var buildLlmsIndex = (project, options = {}) => {
605
+ const { config } = project;
606
+ const { site } = config.deployment;
607
+ const base = normalizeBasePath(config.deployment.base);
608
+ const eligible = eligiblePages(project);
609
+ const byRoute = new Map(eligible.map((page) => [page.route, page]));
610
+ const seen = new Set;
611
+ const line = (page) => {
612
+ seen.add(page.route);
613
+ const summary = page.description ? `: ${page.description}` : "";
614
+ return `- [${page.title}](${pageUrl(page.route, site, base)})${summary}`;
615
+ };
616
+ const renderLevel = (nodes, depth) => {
617
+ const list = [];
618
+ const groupBlocks = [];
619
+ for (const node of nodes) {
620
+ if (node.kind === "page") {
621
+ const page = byRoute.get(node.route);
622
+ if (page && !seen.has(page.route)) {
623
+ list.push(line(page));
624
+ }
625
+ continue;
626
+ }
627
+ const rootPage = node.route ? byRoute.get(node.route) : undefined;
628
+ const blocks2 = renderLevel(node.children, depth + 1);
629
+ if (rootPage && !seen.has(rootPage.route)) {
630
+ blocks2.unshift(line(rootPage));
631
+ }
632
+ if (blocks2.length > 0) {
633
+ groupBlocks.push(`${"#".repeat(Math.min(depth, 6))} ${node.label}`, ...blocks2);
634
+ }
635
+ }
636
+ return list.length > 0 ? [list.join(`
637
+ `), ...groupBlocks] : groupBlocks;
638
+ };
639
+ const renderNav = (nav, depth) => {
640
+ const loose = nav.sidebar.filter((node) => node.kind === "page");
641
+ const groups = nav.sidebar.filter((node) => node.kind === "group");
642
+ const looseBlocks = renderLevel(loose, depth + 1);
643
+ return [
644
+ ...looseBlocks.length > 0 ? [`${"#".repeat(depth)} Docs`, ...looseBlocks] : [],
645
+ ...renderLevel(groups, depth)
646
+ ];
647
+ };
648
+ const blocks = [];
649
+ for (const { label, nav } of indexedNavigations(project)) {
650
+ if (label) {
651
+ const localized = renderNav(nav, 3);
652
+ if (localized.length > 0) {
653
+ blocks.push(`## ${label}`, ...localized);
654
+ }
655
+ continue;
656
+ }
657
+ blocks.push(...renderNav(nav, 2));
658
+ }
659
+ const leftover = eligible.filter((page) => !seen.has(page.route)).toSorted((a, b) => a.route.localeCompare(b.route));
660
+ if (leftover.length > 0) {
661
+ blocks.push(blocks.length > 0 ? "## Other" : "## Docs", leftover.map(line).join(`
662
+ `));
663
+ }
664
+ const feeds = buildRssFeeds(project);
665
+ if (feeds.length > 0) {
666
+ blocks.push("## RSS Feeds", feeds.map((feed) => `- [${feed.title}](${pageUrl(feed.path, site, base)})`).join(`
667
+ `));
668
+ }
669
+ const skills = options.skills ?? [];
670
+ if (skills.length > 0) {
671
+ blocks.push("## Agent skills", skills.map((skill) => `- [${skill.name}](${pageUrl(`${AGENT_SKILLS_DIR}/${skill.path}`, site, base)}): ${oneLine(skill.description)}`).join(`
672
+ `));
673
+ }
674
+ blocks.push("## Agent resources", agentResourceLines(project).join(`
675
+ `));
676
+ const header = config.description ? `# ${config.title}
677
+
678
+ > ${config.description}` : `# ${config.title}`;
679
+ const { details } = config.ai.llmsTxt;
680
+ const lead = details ? `${header}
681
+
682
+ ${details}` : header;
683
+ return `${[lead, ...blocks].join(`
684
+
685
+ `)}
686
+ `;
687
+ };
688
+
689
+ // src/ai/markdown.ts
690
+ var agentMarkdown = (entry) => entry.md ?? entry.mdx;
691
+ var markdownTokenCount = (text) => Math.ceil(text.length / 4);
692
+ var buildRawMarkdown = async (project) => {
693
+ const pageById = new Map(project.graph.pages.map((page) => [page.id, page]));
694
+ const components = projectComponentSerializers(project);
695
+ const readRoute = async (route) => {
696
+ const page = pageById.get(route.id);
697
+ if (page) {
698
+ return await readExpandedEntryText(project, page);
699
+ }
700
+ return route.sourcePath ? await readFile(route.sourcePath, "utf-8") : "";
701
+ };
702
+ const entries = await Promise.all(project.manifest.routes.map(async (route) => {
703
+ let text = await readRoute(route);
704
+ if (route.sourcePath) {
705
+ text = rewriteRelativeImages({
706
+ deployBase: project.config.deployment.base,
707
+ projectRoot: project.context.root,
708
+ source: text,
709
+ sourcePath: route.sourcePath
710
+ });
711
+ }
712
+ const source = applyAgentVisibility(text);
713
+ const md = downlevelComponents(source, components, frontmatter_default(source).data);
714
+ const entry = md === source ? { mdx: source } : { md, mdx: source };
715
+ return [route.path, entry];
716
+ }));
717
+ const map = Object.fromEntries(entries);
718
+ if (!map["/"]) {
719
+ map["/"] = { mdx: buildLlmsIndex(project) };
720
+ }
721
+ return map;
722
+ };
723
+ var markdownRoutePaths = (project) => {
724
+ const paths = project.manifest.routes.map((route) => route.path);
725
+ return paths.includes("/") ? paths : [...paths, "/"];
726
+ };
727
+
728
+ // src/search/documents.ts
729
+ import { markdownToMdast, mdxToMdast as mdxToMdast2 } from "satteri";
730
+
731
+ // src/markdown/fence-meta.ts
732
+ var QUOTED_ATTR = /[\w-]+=(?:"[^"]*"|'[^']*')/gu;
733
+ var LINE_RANGE = /\{[^}]*\}/u;
734
+ var META_TOKEN = new RegExp(`${QUOTED_ATTR.source}|${LINE_RANGE.source}|\\S+`, "gu");
735
+ var metaTokens = (meta) => meta?.match(META_TOKEN) ?? [];
736
+ var RESERVED_META_KEYWORDS = new Set([
737
+ "lineNumbers",
738
+ "ts2js",
739
+ "twoslash"
740
+ ]);
741
+ var isLineRange = (token) => token.startsWith("{");
742
+
743
+ // src/markdown/code-title.ts
744
+ var TITLE_ATTR = /(?:^|\s)title=(?:"(?<dq>[^"]*)"|'(?<sq>[^']*)')/u;
745
+ var isTitleToken = (token) => token.length > 0 && !isLineRange(token) && !token.includes("=") && !RESERVED_META_KEYWORDS.has(token);
746
+ var parseCodeTitle = (raw) => {
747
+ if (!raw) {
748
+ return;
749
+ }
750
+ const scrubbed = raw.replace(QUOTED_ATTR, (attr) => attr.startsWith("title=") ? attr : " ");
751
+ const explicit = scrubbed.match(TITLE_ATTR);
752
+ const attrTitle = explicit?.groups?.dq ?? explicit?.groups?.sq;
753
+ if (attrTitle) {
754
+ return attrTitle;
755
+ }
756
+ return metaTokens(raw).find(isTitleToken);
757
+ };
758
+
759
+ // src/search/facets.ts
760
+ var isFacetValue = (value) => typeof value === "string" || typeof value === "number" || typeof value === "boolean";
761
+ var pageFacets = (page, config) => {
762
+ const declared = config.content.types[page.contentType]?.facets;
763
+ if (!declared || declared.length === 0 || !page.custom) {
764
+ return;
765
+ }
766
+ const facets = {};
767
+ for (const key of declared) {
768
+ const value = page.custom[key];
769
+ if (isFacetValue(value)) {
770
+ facets[key] = String(value);
771
+ }
772
+ }
773
+ return Object.keys(facets).length > 0 ? facets : undefined;
774
+ };
775
+
776
+ // src/search/documents.ts
777
+ var HTML_OR_JSX = /<\/?[a-zA-Z][^\n<>]*>|<\/?>/gu;
778
+ var WHITESPACE = /\s+/gu;
779
+ var INLINE_PARENTS = new Set([
780
+ "delete",
781
+ "emphasis",
782
+ "footnoteReference",
783
+ "link",
784
+ "linkReference",
785
+ "mdxJsxTextElement",
786
+ "strong",
787
+ "subscript",
788
+ "superscript",
789
+ "textDirective"
790
+ ]);
791
+ var NON_PROSE = new Set([
792
+ "image",
793
+ "imageReference",
794
+ "inlineMath",
795
+ "math",
796
+ "mdxFlowExpression",
797
+ "mdxTextExpression",
798
+ "mdxjsEsm"
799
+ ]);
800
+ var MD_PARSE = { features: { ...MARKDOWN_FEATURES, frontmatter: false } };
801
+ var MDX_PARSE = { features: { ...MDX_FEATURES, frontmatter: false } };
802
+ var parseMdx = (markdown) => {
803
+ try {
804
+ return mdxToMdast2(markdown, MDX_PARSE);
805
+ } catch {
806
+ return;
807
+ }
808
+ };
809
+ var parseMarkdown = (markdown, format) => format === "mdx" ? parseMdx(markdown) : markdownToMdast(markdown, MD_PARSE);
810
+ var collectCode = (node, out, walk) => {
811
+ if (!walk.includeCodeBlocks) {
812
+ return;
813
+ }
814
+ const title = parseCodeTitle(node.meta ?? undefined);
815
+ if (title) {
816
+ out.push(title, " ");
817
+ }
818
+ out.push(node.value, " ");
819
+ };
820
+ var separatesWords = (node) => !INLINE_PARENTS.has(node.type) || node.children.length === 0;
821
+ var collectText = (node, out, walk) => {
822
+ if (NON_PROSE.has(node.type)) {
823
+ return;
824
+ }
825
+ switch (node.type) {
826
+ case "code": {
827
+ collectCode(node, out, walk);
828
+ return;
829
+ }
830
+ case "mdxJsxFlowElement": {
831
+ const text = downlevelComponentNode(node, walk.downlevel);
832
+ if (text !== null) {
833
+ collectText(markdownToMdast(text, MD_PARSE), out, walk);
834
+ return;
835
+ }
836
+ break;
837
+ }
838
+ case "inlineCode": {
839
+ out.push(node.value);
840
+ return;
841
+ }
842
+ case "html": {
843
+ out.push(node.value.replaceAll(HTML_COMMENT, " ").replaceAll(HTML_OR_JSX, " "), " ");
844
+ return;
845
+ }
846
+ case "break": {
847
+ out.push(" ");
848
+ return;
849
+ }
850
+ case "heading": {
851
+ const last = node.children.at(-1);
852
+ const trailing = last?.type === "text" ? last : undefined;
853
+ const inner = [];
854
+ const kept = trailing ? node.children.slice(0, -1) : node.children;
855
+ for (const child of kept) {
856
+ collectText(child, inner, walk);
857
+ }
858
+ if (trailing) {
859
+ const stripped = parseHeadingMarkers(trailing.value).text;
860
+ const literal = stripped === "" && node.children.length === 1;
861
+ inner.push(literal ? trailing.value : stripped);
862
+ }
863
+ out.push(inner.join("").trimEnd(), " ");
864
+ return;
865
+ }
866
+ default: {
867
+ break;
868
+ }
869
+ }
870
+ if ("value" in node) {
871
+ out.push(node.value);
872
+ return;
873
+ }
874
+ if ("children" in node) {
875
+ for (const child of node.children) {
876
+ collectText(child, out, walk);
877
+ }
878
+ if (separatesWords(node)) {
879
+ out.push(" ");
880
+ }
881
+ }
882
+ };
883
+ var toPlainText = (markdown, format, walk) => {
884
+ const tree = parseMarkdown(markdown, format);
885
+ if (!tree) {
886
+ return "";
887
+ }
888
+ const out = [];
889
+ collectText(tree, out, walk);
890
+ return out.join("").replaceAll(WHITESPACE, " ").trim();
891
+ };
892
+ var buildCrumbIndex = (sidebar) => {
893
+ const index = new Map;
894
+ const groupRoutes = new Map;
895
+ const walk = (nodes, trail) => {
896
+ for (const node of nodes) {
897
+ if (node.kind === "group") {
898
+ if (node.route && !groupRoutes.has(node.route)) {
899
+ groupRoutes.set(node.route, {
900
+ breadcrumb: [...trail, node.label],
901
+ section: node.label
902
+ });
903
+ }
904
+ walk(node.children, [...trail, node.label]);
905
+ } else if (node.route) {
906
+ index.set(node.route, {
907
+ breadcrumb: trail,
908
+ section: trail.at(-1) ?? ""
909
+ });
910
+ }
911
+ }
912
+ };
913
+ walk(sidebar, []);
914
+ for (const [route, crumbs] of groupRoutes) {
915
+ if (!index.has(route)) {
916
+ index.set(route, crumbs);
917
+ }
918
+ }
919
+ return index;
920
+ };
921
+ var pageBody = async (project, page, options, components) => {
922
+ if (!page) {
923
+ return "";
924
+ }
925
+ const parsed = frontmatter_default(await readExpandedEntryText(project, page));
926
+ const visible = applyAudienceVisibility(parsed.content, options?.audience ?? "web");
927
+ if (options?.content === "markdown") {
928
+ return downlevelComponents(visible.trim(), components, parsed.data);
929
+ }
930
+ return toPlainText(visible, page.format, {
931
+ downlevel: {
932
+ frontmatter: parsed.data,
933
+ registry: componentRegistry(components),
934
+ source: visible
935
+ },
936
+ includeCodeBlocks: project.config.search.indexing.includeCodeBlocks
937
+ });
938
+ };
939
+ var buildSearchDocuments = async (project, options) => {
940
+ const pageById = new Map(project.graph.pages.map((page) => [page.id, page]));
941
+ const byLocale = Object.values(project.graph.navigationByLocale ?? {});
942
+ const byVersion = Object.values(project.graph.navigationByVersion ?? {}).flatMap((locales) => Object.values(locales)).map((nav) => nav.sidebar);
943
+ const sidebars = [
944
+ ...byLocale.length > 0 ? byLocale.map((nav) => nav.sidebar) : [project.graph.navigation?.sidebar ?? []],
945
+ ...byVersion
946
+ ];
947
+ const crumbs = new Map;
948
+ for (const sidebar of sidebars) {
949
+ for (const [route, crumb] of buildCrumbIndex(sidebar)) {
950
+ crumbs.set(route, crumb);
951
+ }
952
+ }
953
+ const indexable = project.manifest.routes.filter((route) => {
954
+ if (!options?.includeWhenDisabled) {
955
+ return route.indexable;
956
+ }
957
+ const page = pageById.get(route.id);
958
+ return page ? contentIndexable(page, project.config) : false;
959
+ });
960
+ const components = projectComponentSerializers(project);
961
+ return await Promise.all(indexable.map(async (route) => {
962
+ const page = pageById.get(route.id);
963
+ const body = await pageBody(project, page, options, components);
964
+ const tags = page?.meta?.search?.tags;
965
+ const crumb = crumbs.get(route.path);
966
+ const facets = page ? pageFacets(page, project.config) : undefined;
967
+ const document = {
968
+ breadcrumb: crumb?.breadcrumb ?? [],
969
+ content: body,
970
+ contentType: route.contentType,
971
+ description: page?.description ?? "",
972
+ locale: route.locale,
973
+ route: route.path,
974
+ section: crumb?.section || "Docs",
975
+ tags: tags && tags.length > 0 ? tags : undefined,
976
+ title: route.title,
977
+ version: route.version
978
+ };
979
+ if (facets) {
980
+ document.facets = facets;
981
+ }
982
+ return document;
983
+ }));
984
+ };
985
+
986
+ // src/ai/mcp/data.ts
987
+ var buildMcpData = async (project) => {
988
+ const { config, graph, manifest } = project;
989
+ const [documents, rawMarkdown] = await Promise.all([
990
+ buildSearchDocuments(project, {
991
+ audience: "agents",
992
+ includeWhenDisabled: true
993
+ }),
994
+ buildRawMarkdown(project)
995
+ ]);
996
+ const pages = Object.fromEntries(Object.entries(rawMarkdown).map(([route, entry]) => [
997
+ route,
998
+ agentMarkdown(entry)
999
+ ]));
1000
+ const pageById = new Map(graph.pages.map((page) => [page.id, page]));
1001
+ const routes = [];
1002
+ for (const route of manifest.routes) {
1003
+ if (route.hidden) {
1004
+ continue;
1005
+ }
1006
+ const page = pageById.get(route.id);
1007
+ const facets = page ? pageFacets(page, config) : undefined;
1008
+ const entry = {
1009
+ contentType: route.contentType,
1010
+ description: page?.description,
1011
+ indexable: route.indexable,
1012
+ lastModified: route.lastModified ?? null,
1013
+ locale: route.locale,
1014
+ route: route.path,
1015
+ title: route.title,
1016
+ version: route.version
1017
+ };
1018
+ if (facets) {
1019
+ entry.facets = facets;
1020
+ }
1021
+ routes.push(entry);
1022
+ }
1023
+ const data = {
1024
+ base: normalizeBasePath(config.deployment.base),
1025
+ defaultLocale: config.i18n?.defaultLocale,
1026
+ documents: documents.map((doc) => {
1027
+ const document = {
1028
+ content: doc.content,
1029
+ contentType: doc.contentType,
1030
+ description: doc.description,
1031
+ locale: doc.locale,
1032
+ route: doc.route,
1033
+ title: doc.title,
1034
+ version: doc.version
1035
+ };
1036
+ if (doc.facets) {
1037
+ document.facets = doc.facets;
1038
+ }
1039
+ return document;
1040
+ }),
1041
+ instructions: config.ai.mcp.instructions,
1042
+ name: config.ai.mcp.name ?? config.title,
1043
+ navigation: graph.navigation,
1044
+ pages,
1045
+ routes,
1046
+ site: config.deployment.site ?? null,
1047
+ version: manifest.blumeVersion
1048
+ };
1049
+ if (config.versions) {
1050
+ data.archivedVersions = config.versions.archived.map((version) => version.id);
1051
+ data.navigationByVersion = graph.navigationByVersion;
1052
+ }
1053
+ if (config.i18n) {
1054
+ data.navigationByLocale = graph.navigationByLocale;
1055
+ }
1056
+ return data;
1057
+ };
1058
+
1059
+ export { buildRssFeeds, renderRssFeed, agentMarkdown, markdownTokenCount, buildRawMarkdown, markdownRoutePaths, buildSearchDocuments, buildMcpData };
1060
+
1061
+ //# debugId=8258B61E89F8EE8E64756E2164756E21
1062
+ //# sourceMappingURL=chunk-ynacq3ev.js.map