blume 2.0.0 → 2.0.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 (454) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +110 -0
  3. package/README.md +2 -2
  4. package/dist/cli/{chunk-f2972sbt.js → chunk-00gs3wqs.js} +1 -1
  5. package/dist/cli/{chunk-fa25z98p.js → chunk-273ygyr4.js} +4 -3
  6. package/dist/cli/chunk-273ygyr4.js.map +11 -0
  7. package/dist/cli/{chunk-pat2zzwc.js → chunk-3yce002v.js} +21 -8
  8. package/dist/cli/{chunk-pat2zzwc.js.map → chunk-3yce002v.js.map} +4 -4
  9. package/dist/cli/{chunk-d1tadaw7.js → chunk-4e9b9ra6.js} +17 -7
  10. package/dist/cli/chunk-4e9b9ra6.js.map +10 -0
  11. package/dist/cli/{chunk-zg2gtj10.js → chunk-5r8g91qn.js} +352 -676
  12. package/dist/cli/chunk-5r8g91qn.js.map +34 -0
  13. package/dist/cli/{chunk-11j0384y.js → chunk-6dtt0zfn.js} +14 -14
  14. package/dist/cli/{chunk-11j0384y.js.map → chunk-6dtt0zfn.js.map} +3 -3
  15. package/dist/cli/{chunk-bw22s759.js → chunk-6k4ftwze.js} +38 -19
  16. package/dist/cli/{chunk-bw22s759.js.map → chunk-6k4ftwze.js.map} +4 -4
  17. package/dist/cli/{chunk-sqn5t4q0.js → chunk-7mbqtmgb.js} +16 -7
  18. package/dist/cli/chunk-7mbqtmgb.js.map +10 -0
  19. package/dist/cli/{chunk-79njf86q.js → chunk-7vtckvaw.js} +21 -19
  20. package/dist/cli/chunk-7vtckvaw.js.map +11 -0
  21. package/dist/cli/{chunk-fh5hj5jt.js → chunk-8g8ytmgx.js} +17 -18
  22. package/dist/cli/{chunk-fh5hj5jt.js.map → chunk-8g8ytmgx.js.map} +2 -2
  23. package/dist/cli/{chunk-1w8dp3qb.js → chunk-91ws1n6j.js} +18 -15
  24. package/dist/cli/chunk-91ws1n6j.js.map +10 -0
  25. package/dist/cli/{chunk-7ez8ny0t.js → chunk-bbnwccaz.js} +2 -2
  26. package/dist/cli/{chunk-5a2z0198.js → chunk-bfwp9vp6.js} +18 -10
  27. package/dist/cli/chunk-bfwp9vp6.js.map +10 -0
  28. package/dist/cli/{chunk-ernrthtr.js → chunk-d1v5rhy0.js} +14 -15
  29. package/dist/cli/{chunk-ernrthtr.js.map → chunk-d1v5rhy0.js.map} +2 -2
  30. package/dist/cli/{chunk-a9kptbw5.js → chunk-ddndchfr.js} +21 -6
  31. package/dist/cli/chunk-ddndchfr.js.map +14 -0
  32. package/dist/cli/{chunk-zxccj738.js → chunk-esh98wmb.js} +1 -1
  33. package/dist/cli/{chunk-88cpgt6h.js → chunk-fsmrqk8a.js} +1 -1
  34. package/dist/cli/{chunk-b5aj94ah.js → chunk-g698a744.js} +5 -5
  35. package/dist/cli/{chunk-jts8mvcz.js → chunk-gs7r695n.js} +9 -3
  36. package/dist/cli/{chunk-jts8mvcz.js.map → chunk-gs7r695n.js.map} +3 -3
  37. package/dist/cli/{chunk-41za066z.js → chunk-h2ez8dzb.js} +4 -4
  38. package/dist/cli/{chunk-by2290sx.js → chunk-h7k3nq3v.js} +2 -2
  39. package/dist/cli/{chunk-j8mw0za6.js → chunk-hqp2ajnh.js} +252 -123
  40. package/dist/cli/chunk-hqp2ajnh.js.map +35 -0
  41. package/dist/cli/{chunk-mnqj32sj.js → chunk-hr8ne106.js} +119 -50
  42. package/dist/cli/chunk-hr8ne106.js.map +13 -0
  43. package/dist/cli/{chunk-mwt1k8n7.js → chunk-j85scx15.js} +74 -30
  44. package/dist/cli/chunk-j85scx15.js.map +10 -0
  45. package/dist/cli/{chunk-z01ze5c1.js → chunk-k7pj68a8.js} +63 -22
  46. package/dist/cli/chunk-k7pj68a8.js.map +11 -0
  47. package/dist/cli/{chunk-2q1dwty4.js → chunk-mqc662a6.js} +8 -8
  48. package/dist/cli/{chunk-2q1dwty4.js.map → chunk-mqc662a6.js.map} +4 -4
  49. package/dist/cli/{chunk-y3e45rc8.js → chunk-n1yg3tj3.js} +4 -4
  50. package/dist/cli/{chunk-bctazmbk.js → chunk-nfcyttvj.js} +17 -6
  51. package/dist/cli/chunk-nfcyttvj.js.map +10 -0
  52. package/dist/cli/{chunk-6k8vp3ta.js → chunk-pbg5a4s3.js} +60 -28
  53. package/dist/cli/chunk-pbg5a4s3.js.map +19 -0
  54. package/dist/cli/{chunk-d80hr03s.js → chunk-ppzjqwx2.js} +24 -17
  55. package/dist/cli/{chunk-d80hr03s.js.map → chunk-ppzjqwx2.js.map} +4 -4
  56. package/dist/cli/{chunk-beat36xx.js → chunk-qkb5a8sa.js} +25 -10
  57. package/dist/cli/chunk-qkb5a8sa.js.map +10 -0
  58. package/dist/cli/{chunk-bnbmcwfb.js → chunk-sqw4ekg1.js} +5 -5
  59. package/dist/cli/{chunk-bnbmcwfb.js.map → chunk-sqw4ekg1.js.map} +3 -3
  60. package/dist/cli/{chunk-xaz13gwg.js → chunk-v6ya5kcb.js} +3268 -1150
  61. package/dist/cli/chunk-v6ya5kcb.js.map +189 -0
  62. package/dist/cli/{chunk-tzne8qfq.js → chunk-w4bxdvsa.js} +18 -15
  63. package/dist/cli/chunk-w4bxdvsa.js.map +10 -0
  64. package/dist/cli/{chunk-f7t03s3g.js → chunk-wdrt2k2v.js} +2 -2
  65. package/dist/cli/{chunk-z1f5arsg.js → chunk-xh43dwgw.js} +170 -55
  66. package/dist/cli/chunk-xh43dwgw.js.map +36 -0
  67. package/dist/cli/{chunk-pnnvybbk.js → chunk-zp79m0ts.js} +5 -5
  68. package/dist/cli/{chunk-pnnvybbk.js.map → chunk-zp79m0ts.js.map} +2 -2
  69. package/dist/cli/index.js +160 -35
  70. package/dist/cli/index.js.map +4 -4
  71. package/dist/types/ai/agent-readability.d.ts +1 -1
  72. package/dist/types/ai/agent-surface.d.ts +32 -0
  73. package/dist/types/ai/api/paths.d.ts +1 -1
  74. package/dist/types/ai/api-catalog.d.ts +7 -1
  75. package/dist/types/ai/ask-context.d.ts +14 -7
  76. package/dist/types/ai/ask.d.ts +43 -43
  77. package/dist/types/ai/component-markdown.d.ts +4 -4
  78. package/dist/types/ai/index.d.ts +3 -3
  79. package/dist/types/ai/openapi-components.d.ts +1 -1
  80. package/dist/types/ai/relative-links.d.ts +9 -4
  81. package/dist/types/ai/serializers.d.ts +1 -1
  82. package/dist/types/ai/static-expression.d.ts +28 -0
  83. package/dist/types/ai/visibility.d.ts +1 -1
  84. package/dist/types/analytics/databuddy.d.ts +43 -0
  85. package/dist/types/analytics/index.d.ts +2 -0
  86. package/dist/types/analytics/schema.d.ts +14 -0
  87. package/dist/types/cli/env.d.ts +5 -0
  88. package/dist/types/cli/init/scaffold.d.ts +19 -3
  89. package/dist/types/cli/init/starter-spec.d.ts +11 -0
  90. package/dist/types/core/base-path.d.ts +9 -0
  91. package/dist/types/core/config-input.d.ts +21 -21
  92. package/dist/types/core/config.d.ts +5 -5
  93. package/dist/types/core/data.d.ts +3 -3
  94. package/dist/types/core/graph.d.ts +2 -0
  95. package/dist/types/core/i18n-ui.d.ts +37 -8
  96. package/dist/types/core/i18n.d.ts +7 -1
  97. package/dist/types/core/links.d.ts +3 -1
  98. package/dist/types/core/load-module.d.ts +10 -0
  99. package/dist/types/core/locale-links.d.ts +12 -2
  100. package/dist/types/core/meta.d.ts +5 -1
  101. package/dist/types/core/nav-diagnostics.d.ts +10 -0
  102. package/dist/types/core/navigation.d.ts +38 -0
  103. package/dist/types/core/ordering-prefix.d.ts +4 -0
  104. package/dist/types/core/safe-links.d.ts +3 -1
  105. package/dist/types/core/schema.d.ts +57 -18
  106. package/dist/types/core/sources/github-releases.d.ts +5 -0
  107. package/dist/types/core/sources/lower.d.ts +14 -2
  108. package/dist/types/core/sources/normalize.d.ts +10 -1
  109. package/dist/types/core/sources/remote.d.ts +11 -1
  110. package/dist/types/core/sources/resolve.d.ts +12 -0
  111. package/dist/types/core/sources/types.d.ts +31 -0
  112. package/dist/types/core/types.d.ts +9 -0
  113. package/dist/types/core/unrecognized-keys.d.ts +1 -1
  114. package/dist/types/deploy/adapters/node.d.ts +5 -2
  115. package/dist/types/deploy/adapters/types.d.ts +7 -0
  116. package/dist/types/deploy/cloudflare-negotiation.d.ts +3 -2
  117. package/dist/types/deploy/headers.d.ts +31 -7
  118. package/dist/types/deploy/node-headers.d.ts +43 -8
  119. package/dist/types/deploy/platforms/netlify.d.ts +27 -2
  120. package/dist/types/deploy/platforms/node.d.ts +6 -5
  121. package/dist/types/deploy/platforms/types.d.ts +7 -0
  122. package/dist/types/deploy/platforms/vercel.d.ts +3 -2
  123. package/dist/types/deploy/redirects.d.ts +7 -2
  124. package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
  125. package/dist/types/openapi/model.d.ts +20 -6
  126. package/dist/types/search/documents.d.ts +1 -1
  127. package/dist/types/search/orama-index.d.ts +1 -1
  128. package/dist/types/search/sync/algolia.d.ts +3 -1
  129. package/docs/01-quickstart.mdx +3 -2
  130. package/docs/02-deployment.mdx +21 -12
  131. package/docs/03-upgrading.mdx +22 -9
  132. package/docs/04-migrating.mdx +4 -4
  133. package/docs/08-faq.mdx +12 -5
  134. package/docs/advanced/changelog.mdx +1 -1
  135. package/docs/advanced/custom-pages.mdx +5 -3
  136. package/docs/advanced/skills.mdx +1 -1
  137. package/docs/cli/audit.mdx +5 -5
  138. package/docs/cli/doctor.mdx +4 -4
  139. package/docs/cli/evals.mdx +9 -9
  140. package/docs/cli/index.mdx +6 -3
  141. package/docs/cli/translate.mdx +8 -8
  142. package/docs/configuration/analytics.mdx +23 -2
  143. package/docs/configuration/{ask-ai.mdx → assistant.mdx} +20 -20
  144. package/docs/configuration/customization.mdx +5 -4
  145. package/docs/configuration/index.mdx +7 -5
  146. package/docs/configuration/meta.ts +1 -1
  147. package/docs/configuration/search.mdx +2 -2
  148. package/docs/content/components.mdx +1 -1
  149. package/docs/content/frontmatter.mdx +5 -1
  150. package/docs/content/i18n.mdx +3 -3
  151. package/docs/content/index.mdx +4 -2
  152. package/docs/content/islands.mdx +1 -1
  153. package/docs/content/meta.mdx +5 -3
  154. package/docs/content/navigation.mdx +29 -4
  155. package/docs/content/sources.mdx +18 -12
  156. package/docs/content/versioning.mdx +1 -0
  157. package/docs/discoverability/agent-discovery.mdx +22 -8
  158. package/docs/discoverability/index.mdx +3 -3
  159. package/docs/discoverability/llms-txt.mdx +2 -5
  160. package/docs/discoverability/markdown.mdx +5 -3
  161. package/docs/discoverability/mcp.mdx +4 -0
  162. package/docs/discoverability/metadata.mdx +3 -2
  163. package/docs/discoverability/open-graph.mdx +6 -4
  164. package/docs/discoverability/rss.mdx +3 -1
  165. package/docs/index.mdx +2 -2
  166. package/docs/references/asyncapi.mdx +1 -1
  167. package/docs/references/graphql.mdx +2 -2
  168. package/docs/references/openapi.mdx +3 -3
  169. package/package.json +1 -1
  170. package/skills/blume/SKILL.md +5 -5
  171. package/skills/blume-migrate/SKILL.md +6 -6
  172. package/skills/blume-migrate/references/docusaurus.md +6 -6
  173. package/skills/blume-migrate/references/mintlify.md +3 -3
  174. package/skills/blume-migrate/references/monorepo.md +1 -1
  175. package/skills/blume-migrate/references/nextra.md +1 -1
  176. package/skills/blume-migrate/references/starlight.md +5 -5
  177. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +2 -2
  178. package/skills/blume-update-docs/references/audit-checklist.md +1 -1
  179. package/src/ai/agent-readability.ts +4 -4
  180. package/src/ai/agent-surface.ts +56 -0
  181. package/src/ai/api/paths.ts +1 -1
  182. package/src/ai/api/spec.ts +15 -2
  183. package/src/ai/api-catalog.ts +7 -1
  184. package/src/ai/ask-context.ts +21 -9
  185. package/src/ai/ask-data.ts +28 -14
  186. package/src/ai/ask.ts +84 -71
  187. package/src/ai/component-markdown.ts +43 -37
  188. package/src/ai/cors.ts +3 -3
  189. package/src/ai/index.ts +16 -16
  190. package/src/ai/link-headers.ts +8 -3
  191. package/src/ai/llms.ts +4 -3
  192. package/src/ai/markdown.ts +23 -11
  193. package/src/ai/mcp/server.ts +78 -7
  194. package/src/ai/openapi-components.ts +1 -1
  195. package/src/ai/relative-links.ts +78 -10
  196. package/src/ai/serializers.ts +1 -1
  197. package/src/ai/static-expression.ts +416 -0
  198. package/src/ai/visibility.ts +46 -15
  199. package/src/analytics/databuddy.ts +67 -0
  200. package/src/analytics/head.ts +4 -0
  201. package/src/analytics/index.ts +2 -0
  202. package/src/analytics/posthog.ts +21 -3
  203. package/src/analytics/schema.ts +2 -0
  204. package/src/astro/generate.ts +90 -45
  205. package/src/astro/module-types.ts +1 -1
  206. package/src/astro/runtime-deps.ts +6 -6
  207. package/src/astro/templates.ts +87 -49
  208. package/src/audit/catalog.ts +2 -2
  209. package/src/audit/checks/assets.ts +25 -5
  210. package/src/audit/checks/content.ts +20 -2
  211. package/src/audit/checks/i18n.ts +46 -8
  212. package/src/audit/checks/indexability.ts +39 -19
  213. package/src/audit/checks/links.ts +14 -0
  214. package/src/audit/checks/llms.ts +6 -3
  215. package/src/audit/checks/network.ts +3 -1
  216. package/src/audit/checks/og-image.ts +10 -0
  217. package/src/audit/checks/robots.ts +6 -1
  218. package/src/audit/checks/sitemap.ts +56 -31
  219. package/src/audit/checks/social.ts +21 -2
  220. package/src/audit/crawl.ts +88 -15
  221. package/src/audit/report.ts +54 -17
  222. package/src/audit/run.ts +1 -0
  223. package/src/audit/types.ts +18 -0
  224. package/src/audit/url.ts +37 -6
  225. package/src/blume-modules.d.ts +2 -2
  226. package/src/cli/build-failure.ts +50 -0
  227. package/src/cli/commands/audit.ts +8 -1
  228. package/src/cli/commands/build.ts +19 -5
  229. package/src/cli/commands/check.ts +2 -0
  230. package/src/cli/commands/doctor.ts +7 -5
  231. package/src/cli/commands/eval.ts +4 -10
  232. package/src/cli/commands/init.ts +15 -5
  233. package/src/cli/commands/migrate.ts +2 -2
  234. package/src/cli/commands/preview.ts +15 -0
  235. package/src/cli/commands/sync.ts +2 -0
  236. package/src/cli/commands/translate.ts +12 -9
  237. package/src/cli/commands/upgrade.ts +13 -2
  238. package/src/cli/eject-scripts.ts +32 -7
  239. package/src/cli/env.ts +12 -1
  240. package/src/cli/init/scaffold.ts +117 -11
  241. package/src/cli/init/starter-spec.ts +235 -0
  242. package/src/cli/required-secrets.ts +3 -3
  243. package/src/components/content/AccordionItem.astro +26 -22
  244. package/src/components/content/Badge.astro +2 -9
  245. package/src/components/content/Card.astro +2 -2
  246. package/src/components/content/Component.astro +9 -3
  247. package/src/components/content/Expandable.astro +5 -1
  248. package/src/components/content/Frame.astro +2 -7
  249. package/src/components/content/GithubInfo.astro +12 -2
  250. package/src/components/content/Prompt.astro +2 -7
  251. package/src/components/content/Tabs.astro +54 -5
  252. package/src/components/content/Tile.astro +1 -1
  253. package/src/components/content/Tooltip.astro +69 -7
  254. package/src/components/content/Tree.astro +7 -2
  255. package/src/components/content/TypeTable.astro +10 -5
  256. package/src/components/content/Update.astro +8 -2
  257. package/src/components/content/auto-type-table.ts +4 -1
  258. package/src/components/content/badge-color.ts +17 -0
  259. package/src/components/content/base-href.ts +18 -3
  260. package/src/components/content/inline-markdown.ts +27 -7
  261. package/src/components/copy-feedback.ts +36 -9
  262. package/src/components/islands/{AskAI.astro → Assistant.astro} +10 -10
  263. package/src/components/islands/{ask-ai.tsx → assistant.tsx} +39 -25
  264. package/src/components/islands/hooks.ts +21 -14
  265. package/src/components/islands/webmcp.ts +12 -8
  266. package/src/components/layout/Banner.astro +23 -4
  267. package/src/components/layout/Header.astro +32 -19
  268. package/src/components/layout/Logo.astro +5 -0
  269. package/src/components/layout/NavSelector.astro +6 -1
  270. package/src/components/layout/NavTabMenu.astro +133 -0
  271. package/src/components/layout/NavTree.astro +15 -6
  272. package/src/components/layout/NavTreeCache.astro +5 -2
  273. package/src/components/layout/NavTreeScript.astro +45 -5
  274. package/src/components/layout/PageLayout.astro +28 -14
  275. package/src/components/layout/Pagination.astro +7 -7
  276. package/src/components/layout/ReferenceLayout.astro +5 -2
  277. package/src/components/layout/RootLayout.astro +27 -14
  278. package/src/components/layout/Search.astro +18 -14
  279. package/src/components/layout/analytics-client.ts +3 -1
  280. package/src/components/layout/drawer-inert.ts +1 -1
  281. package/src/components/openapi/GraphqlType.astro +11 -3
  282. package/src/components/openapi/MessageComposer.astro +1 -1
  283. package/src/components/openapi/Operation.astro +21 -5
  284. package/src/components/openapi/PanelTabs.astro +4 -1
  285. package/src/components/openapi/Playground.astro +8 -2
  286. package/src/components/openapi/RequestPanel.astro +9 -5
  287. package/src/components/openapi/SchemaProperty.astro +11 -33
  288. package/src/components/openapi/SchemaTable.astro +27 -52
  289. package/src/components/openapi/description.ts +2 -2
  290. package/src/components/openapi/helpers.ts +90 -12
  291. package/src/components/openapi/message-composer.ts +8 -0
  292. package/src/components/openapi/message-model.ts +12 -2
  293. package/src/components/openapi/message.ts +4 -1
  294. package/src/components/openapi/operation-model.ts +40 -8
  295. package/src/components/openapi/panel.ts +29 -5
  296. package/src/components/openapi/playground-client.ts +37 -7
  297. package/src/components/openapi/playground-schema.ts +25 -6
  298. package/src/components/openapi/request.ts +2 -0
  299. package/src/components/openapi/schema-tree.ts +203 -0
  300. package/src/components/openapi/snippets.ts +30 -15
  301. package/src/components/openapi/validate-json.ts +1 -1
  302. package/src/core/base-path.ts +15 -0
  303. package/src/core/code-fences.ts +1 -1
  304. package/src/core/config-input.ts +23 -23
  305. package/src/core/config.ts +21 -7
  306. package/src/core/data.ts +3 -3
  307. package/src/core/diagnostics.ts +217 -30
  308. package/src/core/graph.ts +120 -11
  309. package/src/core/i18n-ui.ts +74 -11
  310. package/src/core/i18n.ts +13 -2
  311. package/src/core/links.ts +7 -4
  312. package/src/core/load-module.ts +20 -0
  313. package/src/core/locale-links.ts +17 -17
  314. package/src/core/manifest.ts +3 -2
  315. package/src/core/meta.ts +69 -6
  316. package/src/core/nav-diagnostics.ts +56 -1
  317. package/src/core/navigation.ts +245 -76
  318. package/src/core/ordering-prefix.ts +27 -0
  319. package/src/core/project-graph.ts +23 -1
  320. package/src/core/request-body.ts +1 -1
  321. package/src/core/safe-href.ts +53 -1
  322. package/src/core/safe-links.ts +11 -2
  323. package/src/core/schema.ts +95 -34
  324. package/src/core/server-features.ts +2 -2
  325. package/src/core/sources/assets.ts +83 -41
  326. package/src/core/sources/contentful-rich-text.ts +3 -2
  327. package/src/core/sources/contentful.ts +25 -12
  328. package/src/core/sources/filesystem.ts +5 -1
  329. package/src/core/sources/github-releases.ts +106 -5
  330. package/src/core/sources/lexical.ts +9 -4
  331. package/src/core/sources/lower.ts +79 -26
  332. package/src/core/sources/mdx-remote.ts +1 -0
  333. package/src/core/sources/normalize.ts +132 -30
  334. package/src/core/sources/notion.ts +26 -10
  335. package/src/core/sources/obsidian.ts +17 -3
  336. package/src/core/sources/payload.ts +1 -0
  337. package/src/core/sources/portable-text.ts +63 -44
  338. package/src/core/sources/remote.ts +18 -2
  339. package/src/core/sources/resolve.ts +52 -33
  340. package/src/core/sources/sanity.ts +1 -0
  341. package/src/core/sources/strapi-blocks.ts +9 -4
  342. package/src/core/sources/strapi.ts +1 -0
  343. package/src/core/sources/types.ts +31 -0
  344. package/src/core/types.ts +9 -0
  345. package/src/core/ui-packs/ar.ts +17 -5
  346. package/src/core/ui-packs/bg.ts +17 -5
  347. package/src/core/ui-packs/bn.ts +17 -5
  348. package/src/core/ui-packs/ca.ts +17 -5
  349. package/src/core/ui-packs/cs.ts +17 -5
  350. package/src/core/ui-packs/da.ts +17 -5
  351. package/src/core/ui-packs/de.ts +17 -5
  352. package/src/core/ui-packs/el.ts +17 -5
  353. package/src/core/ui-packs/es.ts +17 -5
  354. package/src/core/ui-packs/fa.ts +17 -5
  355. package/src/core/ui-packs/fi.ts +17 -5
  356. package/src/core/ui-packs/fr.ts +17 -5
  357. package/src/core/ui-packs/he.ts +17 -5
  358. package/src/core/ui-packs/hi.ts +17 -5
  359. package/src/core/ui-packs/hr.ts +17 -5
  360. package/src/core/ui-packs/hu.ts +17 -5
  361. package/src/core/ui-packs/id.ts +17 -5
  362. package/src/core/ui-packs/it.ts +17 -5
  363. package/src/core/ui-packs/ja.ts +17 -5
  364. package/src/core/ui-packs/ko.ts +17 -5
  365. package/src/core/ui-packs/nl.ts +17 -5
  366. package/src/core/ui-packs/no.ts +17 -5
  367. package/src/core/ui-packs/pl.ts +17 -5
  368. package/src/core/ui-packs/pt-br.ts +17 -5
  369. package/src/core/ui-packs/pt.ts +17 -5
  370. package/src/core/ui-packs/ro.ts +17 -5
  371. package/src/core/ui-packs/ru.ts +17 -5
  372. package/src/core/ui-packs/sk.ts +17 -5
  373. package/src/core/ui-packs/sr.ts +17 -5
  374. package/src/core/ui-packs/sv.ts +17 -5
  375. package/src/core/ui-packs/th.ts +17 -5
  376. package/src/core/ui-packs/tr.ts +17 -5
  377. package/src/core/ui-packs/uk.ts +17 -5
  378. package/src/core/ui-packs/vi.ts +17 -5
  379. package/src/core/ui-packs/zh-tw.ts +17 -5
  380. package/src/core/ui-packs/zh.ts +17 -5
  381. package/src/core/unrecognized-keys.ts +1 -1
  382. package/src/core/version-cut.ts +17 -2
  383. package/src/core/versions.ts +4 -1
  384. package/src/deploy/adapters/node.ts +5 -2
  385. package/src/deploy/adapters/registry.ts +2 -1
  386. package/src/deploy/adapters/types.ts +13 -1
  387. package/src/deploy/artifacts.ts +25 -5
  388. package/src/deploy/cloudflare-negotiation.ts +23 -4
  389. package/src/deploy/headers.ts +67 -51
  390. package/src/deploy/node-headers.ts +148 -27
  391. package/src/deploy/platforms/cloudflare.ts +1 -0
  392. package/src/deploy/platforms/netlify.ts +82 -5
  393. package/src/deploy/platforms/node.ts +7 -5
  394. package/src/deploy/platforms/static.ts +1 -0
  395. package/src/deploy/platforms/types.ts +7 -0
  396. package/src/deploy/platforms/vercel.ts +9 -3
  397. package/src/deploy/redirects.ts +14 -3
  398. package/src/deploy/vercel-negotiation.ts +35 -3
  399. package/src/eval/agents.ts +10 -2
  400. package/src/eval/run.ts +25 -0
  401. package/src/markdown/base-links.ts +55 -8
  402. package/src/markdown/index.ts +6 -3
  403. package/src/markdown/relative-links.ts +3 -23
  404. package/src/markdown/route-snapshot.ts +37 -0
  405. package/src/og/card.ts +20 -4
  406. package/src/og/derive.ts +145 -4
  407. package/src/openapi/graphql-build.ts +28 -2
  408. package/src/openapi/model.ts +77 -13
  409. package/src/openapi/render-mdx.ts +10 -1
  410. package/src/registry/eject.ts +135 -37
  411. package/src/search/documents.ts +24 -3
  412. package/src/search/orama-index.ts +1 -1
  413. package/src/search/sync/algolia.ts +36 -2
  414. package/src/sources/registry.ts +5 -0
  415. package/src/theme/entry.ts +11 -4
  416. package/src/translate/agents.ts +6 -1
  417. package/src/translate/ledger.ts +26 -3
  418. package/src/translate/meta.ts +11 -3
  419. package/src/translate/report.ts +1 -1
  420. package/src/translate/run.ts +11 -5
  421. package/src/translate/validate.ts +10 -1
  422. package/src/translate/work-list.ts +36 -3
  423. package/src/upgrade/upgrade.ts +37 -5
  424. package/dist/cli/chunk-1w8dp3qb.js.map +0 -10
  425. package/dist/cli/chunk-5a2z0198.js.map +0 -10
  426. package/dist/cli/chunk-6k8vp3ta.js.map +0 -19
  427. package/dist/cli/chunk-79njf86q.js.map +0 -11
  428. package/dist/cli/chunk-a9kptbw5.js.map +0 -14
  429. package/dist/cli/chunk-abh8yjkn.js +0 -31
  430. package/dist/cli/chunk-abh8yjkn.js.map +0 -10
  431. package/dist/cli/chunk-bctazmbk.js.map +0 -10
  432. package/dist/cli/chunk-beat36xx.js.map +0 -10
  433. package/dist/cli/chunk-d1tadaw7.js.map +0 -10
  434. package/dist/cli/chunk-fa25z98p.js.map +0 -11
  435. package/dist/cli/chunk-j8mw0za6.js.map +0 -35
  436. package/dist/cli/chunk-mnqj32sj.js.map +0 -12
  437. package/dist/cli/chunk-mwt1k8n7.js.map +0 -10
  438. package/dist/cli/chunk-nk3ts2xk.js +0 -51
  439. package/dist/cli/chunk-nk3ts2xk.js.map +0 -10
  440. package/dist/cli/chunk-sqn5t4q0.js.map +0 -10
  441. package/dist/cli/chunk-tzne8qfq.js.map +0 -10
  442. package/dist/cli/chunk-xaz13gwg.js.map +0 -182
  443. package/dist/cli/chunk-z01ze5c1.js.map +0 -10
  444. package/dist/cli/chunk-z1f5arsg.js.map +0 -36
  445. package/dist/cli/chunk-zg2gtj10.js.map +0 -35
  446. /package/dist/cli/{chunk-f2972sbt.js.map → chunk-00gs3wqs.js.map} +0 -0
  447. /package/dist/cli/{chunk-7ez8ny0t.js.map → chunk-bbnwccaz.js.map} +0 -0
  448. /package/dist/cli/{chunk-zxccj738.js.map → chunk-esh98wmb.js.map} +0 -0
  449. /package/dist/cli/{chunk-88cpgt6h.js.map → chunk-fsmrqk8a.js.map} +0 -0
  450. /package/dist/cli/{chunk-b5aj94ah.js.map → chunk-g698a744.js.map} +0 -0
  451. /package/dist/cli/{chunk-41za066z.js.map → chunk-h2ez8dzb.js.map} +0 -0
  452. /package/dist/cli/{chunk-by2290sx.js.map → chunk-h7k3nq3v.js.map} +0 -0
  453. /package/dist/cli/{chunk-y3e45rc8.js.map → chunk-n1yg3tj3.js.map} +0 -0
  454. /package/dist/cli/{chunk-f7t03s3g.js.map → chunk-wdrt2k2v.js.map} +0 -0
@@ -1,11 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/cli/commands/init.ts", "../src/cli/init/questions.ts"],
4
- "sourcesContent": [
5
- "import * as clack from \"@clack/prompts\";\nimport { defineCommand } from \"citty\";\nimport { resolve } from \"pathe\";\n\nimport { ensureGitignore } from \"../../core/gitignore.ts\";\nimport { eject } from \"../../registry/eject.ts\";\nimport { commandMeta } from \"../command-meta.ts\";\nimport { updatePackageScripts } from \"../eject-scripts.ts\";\nimport { installDependencies } from \"../init/install.ts\";\nimport { collectAnswers } from \"../init/questions.ts\";\nimport {\n applyPlan,\n buildPlan,\n commandsFor,\n detectPackageManager,\n nextSteps,\n PACKAGE_MANAGERS,\n readExistingPackage,\n TEMPLATES,\n validateContentDir,\n workspaceNote,\n} from \"../init/scaffold.ts\";\nimport type { InitAnswers } from \"../init/scaffold.ts\";\nimport { logger } from \"../log.ts\";\n\n/** The `cd` line that opens every next-steps hint, when one is needed. */\nconst cdStep = (answers: InitAnswers): string[] =>\n answers.directory === \".\" ? [] : [`cd ${answers.directory}`];\n\n/**\n * Install the scaffolded project's dependencies. Interactive runs hide the\n * package manager's output behind a spinner; `--yes`/CI runs stream it. On\n * failure the scaffold is left intact and the exact retry command is printed\n * before exiting non-zero, so a CI wrapper can tell the two outcomes apart.\n */\nconst installScaffold = async (\n root: string,\n answers: InitAnswers,\n interactive: boolean\n): Promise<void> => {\n const command = commandsFor(answers.packageManager).install;\n const spinner = interactive ? clack.spinner() : undefined;\n if (spinner) {\n spinner.start(`Installing dependencies (${command})`);\n } else {\n logger.info(`Installing dependencies (${command})…`);\n }\n const outcome = await installDependencies(root, answers.packageManager, {\n quiet: interactive,\n });\n if (outcome.failure === undefined) {\n if (spinner) {\n spinner.stop(\"Installed dependencies\");\n } else {\n logger.success(\"Installed dependencies\");\n }\n return;\n }\n const retry = [...cdStep(answers), command];\n const hint = `Project created successfully, but dependency installation failed.\\n\\nRetry with:\\n\\n ${retry.join(\"\\n \")}\\n`;\n if (spinner) {\n spinner.stop(\"Dependency installation failed\");\n clack.log.error(outcome.failure);\n clack.note(hint.trimEnd());\n clack.outro(\"Scaffolded without dependencies.\");\n } else {\n logger.error(outcome.failure);\n logger.box(hint.trimEnd());\n }\n process.exit(1);\n};\n\n/**\n * Eject the freshly scaffolded project, or print the install-then-eject path.\n * Eject jiti-loads the scaffolded blume.config.ts, whose `import { defineConfig }\n * from \"blume\"` only resolves once dependencies are installed (or blume is\n * hoisted from an ancestor node_modules, as in a monorepo) — so the fallback\n * below is the common path when the install step was skipped.\n */\nconst ejectScaffold = async (\n root: string,\n answers: InitAnswers,\n needsInstall: boolean\n): Promise<void> => {\n const commands = commandsFor(answers.packageManager);\n const cd = cdStep(answers);\n const install = needsInstall ? [commands.install] : [];\n try {\n const { dependencies } = await eject(root);\n // The scaffolded scripts point at the Blume CLI; the ejected app runs\n // Astro directly (mirroring the standalone `blume eject` command).\n const added = await updatePackageScripts(root, dependencies);\n logger.success(\"Ejected to a standalone Astro project.\");\n // The packages eject added aren't installed yet, even when the scaffold's\n // own install already ran, so install comes first either way.\n const ejectInstall = added.length > 0 ? [commands.install] : install;\n const steps = [...cd, ...ejectInstall, commands.dev];\n logger.box(`Next steps:\\n\\n ${steps.join(\"\\n \")}`);\n } catch (error) {\n // SAFETY: eject and the script rewrite throw Error instances; only the\n // message is surfaced in the fallback hint.\n logger.warn(\n `Scaffolded, but eject needs the project's dependencies installed to load blume.config.ts: ${(error as Error).message}`\n );\n const steps = [...cd, ...install, `${commands.exec} blume eject --yes`];\n logger.box(`Next steps:\\n\\n ${steps.join(\"\\n \")}`);\n }\n};\n\n/**\n * Print the closing next steps. An existing package.json was left alone, so\n * the steps add what it lacks; a workspace `note` already printed ahead of an\n * install that ran, so it closes the steps only when the install is still the\n * user's to run.\n */\nconst printNextSteps = async (\n root: string,\n answers: InitAnswers,\n outcome: {\n createdPackage: boolean;\n interactive: boolean;\n needsInstall: boolean;\n note: string | undefined;\n }\n): Promise<void> => {\n const existing = outcome.createdPackage\n ? undefined\n : await readExistingPackage(root);\n const steps = nextSteps(\n answers,\n outcome.needsInstall,\n existing,\n outcome.needsInstall ? outcome.note : undefined\n );\n if (outcome.interactive) {\n clack.note(steps.trimEnd());\n clack.outro(\"You're all set.\");\n } else {\n // consola's box pads its content itself; a trailing newline would add\n // blank rows at the bottom.\n logger.box(steps.trimEnd());\n }\n};\n\n/**\n * Validate the enum-valued flags up front, exiting with the accepted values\n * on a typo so a bad flag never reaches the prompts.\n */\nconst resolveFlags = (args: {\n \"package-manager\"?: string;\n template?: string;\n}) => {\n const template = TEMPLATES.find((candidate) => candidate === args.template);\n if (args.template !== undefined && template === undefined) {\n logger.error(\n `Unknown template \"${args.template}\" (use ${TEMPLATES.join(\" | \")}).`\n );\n process.exit(1);\n }\n const pm = PACKAGE_MANAGERS.find(\n (candidate) => candidate === args[\"package-manager\"]\n );\n if (args[\"package-manager\"] !== undefined && pm === undefined) {\n logger.error(\n `Unknown package manager \"${args[\"package-manager\"]}\" (use ${PACKAGE_MANAGERS.join(\" | \")}).`\n );\n process.exit(1);\n }\n return { pm, template };\n};\n\nexport const initCommand = defineCommand({\n args: {\n \"content-dir\": {\n description: \"Content directory.\",\n type: \"string\",\n },\n dir: {\n description: \"Directory to scaffold into (default: current directory).\",\n required: false,\n type: \"positional\",\n },\n eject: {\n description: \"Eject to a standalone Astro project after scaffolding.\",\n type: \"boolean\",\n },\n install: {\n default: true,\n description: \"Install dependencies after scaffolding.\",\n negativeDescription: \"Skip installing dependencies after scaffolding.\",\n type: \"boolean\",\n },\n \"package-manager\": {\n description:\n \"Package manager to install with and print in the next steps (npm|pnpm|yarn|bun).\",\n type: \"string\",\n },\n template: {\n description: \"Starter template: docs | api | sdk | changelog.\",\n type: \"string\",\n },\n yes: {\n description: \"Skip prompts and scaffold with defaults.\",\n type: \"boolean\",\n },\n },\n meta: commandMeta.init,\n async run({ args }) {\n const cwd = process.cwd();\n const { pm, template } = resolveFlags(args);\n\n const interactive =\n !args.yes &&\n process.stdin.isTTY === true &&\n clack.isTTY(process.stdout) &&\n !clack.isCI();\n\n let answers: InitAnswers;\n if (interactive) {\n clack.intro(\"blume init\");\n const collected = await collectAnswers(\n clack,\n {\n contentDir: args[\"content-dir\"],\n directory: args.dir,\n packageManager: pm,\n template,\n },\n { cwd, userAgent: process.env.npm_config_user_agent }\n );\n if (collected === null) {\n clack.cancel(\"Cancelled — nothing was written.\");\n process.exit(0);\n }\n answers = collected;\n } else {\n answers = {\n contentDir: args[\"content-dir\"] ?? \"docs\",\n directory: args.dir ?? \".\",\n packageManager:\n pm ?? detectPackageManager(process.env.npm_config_user_agent),\n sources: [\"filesystem\"],\n template: template ?? \"docs\",\n title: \"My Docs\",\n };\n }\n\n const root = resolve(cwd, answers.directory);\n // Interactive runs validate this inline, but an explicit --content-dir\n // flag skips that prompt, so guard here in both modes.\n if (validateContentDir(root, answers.contentDir) !== undefined) {\n logger.error(\n `Invalid --content-dir \"${answers.contentDir}\" (must be a path inside the project).`\n );\n process.exit(1);\n }\n\n const sink = interactive ? clack.log : logger;\n const env = { userAgent: process.env.npm_config_user_agent };\n const { createdPackage } = await applyPlan(\n buildPlan(root, answers, env),\n sink\n );\n // A workspace the project joins keeps its own package-manager config,\n // which `init` leaves alone; say what it still needs before installing.\n const note = workspaceNote(root, answers, env);\n\n // Keep installed dependencies, Blume's generated runtime (`.blume/`), and\n // build output (`dist/`) out of version control. Idempotent: creates\n // `.gitignore` when absent and skips entries already present\n // (trailing-slash agnostic).\n const ignored = await ensureGitignore(root, [\n \"node_modules/\",\n \".blume/\",\n \"dist/\",\n ]);\n if (ignored.length > 0) {\n sink.success(`Added ${ignored.join(\", \")} to .gitignore`);\n }\n\n // A newly written package.json is the only one `init` knows lists blume;\n // an existing one is left alone, and so are its dependencies.\n const shouldInstall = createdPackage && args.install;\n if (note) {\n (interactive ? clack.log : logger).warn(note);\n }\n if (shouldInstall) {\n await installScaffold(root, answers, interactive);\n }\n const needsInstall = createdPackage && !shouldInstall;\n\n if (args.eject) {\n await ejectScaffold(root, answers, needsInstall);\n return;\n }\n\n await printNextSteps(root, answers, {\n createdPackage,\n interactive,\n needsInstall,\n note,\n });\n },\n});\n",
6
- "import { basename, resolve } from \"pathe\";\n\nimport {\n detectPackageManager,\n titleize,\n validateContentDir,\n} from \"./scaffold.ts\";\nimport type {\n InitAnswers,\n PackageManager,\n SourceKind,\n Template,\n} from \"./scaffold.ts\";\n\n/** One prompt option: value plus display label and optional hint. */\ninterface PromptOption<Value> {\n hint?: string;\n label: string;\n value: Value;\n}\n\n/**\n * The prompt surface `collectAnswers` needs — structurally satisfied by the\n * `@clack/prompts` module itself, and by plain fakes in tests. Every prompt\n * resolves to a cancel symbol when the user aborts.\n */\nexport interface Prompter {\n multiselect: (opts: {\n initialValues?: SourceKind[];\n message: string;\n options: PromptOption<SourceKind>[];\n required?: boolean;\n }) => Promise<SourceKind[] | symbol>;\n select: (opts: {\n message: string;\n options: PromptOption<Template>[];\n }) => Promise<Template | symbol>;\n text: (opts: {\n defaultValue?: string;\n initialValue?: string;\n message: string;\n placeholder?: string;\n validate?: (value: string | undefined) => string | undefined;\n }) => Promise<string | symbol>;\n}\n\n/** Flag values the user passed explicitly; each one skips its question. */\nexport interface InitFlags {\n contentDir?: string;\n directory?: string;\n packageManager?: PackageManager;\n template?: Template;\n}\n\nconst cancelled = (value: string | string[] | symbol): value is symbol =>\n typeof value === \"symbol\";\n\n/**\n * Run the interactive init flow, returning the collected answers or `null`\n * when the user cancels. All questions run before anything is written, so a\n * cancel never leaves a partial scaffold behind.\n */\nexport const collectAnswers = async (\n prompter: Prompter,\n flags: InitFlags,\n defaults: { cwd: string; userAgent?: string }\n): Promise<InitAnswers | null> => {\n const directory =\n flags.directory ??\n (await prompter.text({\n defaultValue: \".\",\n message: \"Where should we create your project?\",\n placeholder: \"./my-docs\",\n }));\n if (cancelled(directory)) {\n return null;\n }\n\n const root = resolve(defaults.cwd, directory);\n const title = await prompter.text({\n initialValue: titleize(basename(root)),\n message: \"What's your docs site called?\",\n validate: (value) =>\n value?.trim() ? undefined : \"Give your docs site a name.\",\n });\n if (cancelled(title)) {\n return null;\n }\n\n const template =\n flags.template ??\n (await prompter.select({\n message: \"Which template?\",\n options: [\n { hint: \"Markdown docs site\", label: \"docs\", value: \"docs\" },\n { hint: \"OpenAPI reference at /api\", label: \"api\", value: \"api\" },\n { hint: \"SDK docs with an install page\", label: \"sdk\", value: \"sdk\" },\n {\n hint: \"Docs plus a changelog tab\",\n label: \"changelog\",\n value: \"changelog\",\n },\n ],\n }));\n if (cancelled(template)) {\n return null;\n }\n\n const picked = await prompter.multiselect({\n initialValues: [\"filesystem\"],\n message: \"Where does your content live?\",\n options: [\n { hint: \"Local .mdx files\", label: \"filesystem\", value: \"filesystem\" },\n { hint: \"An Obsidian vault\", label: \"obsidian\", value: \"obsidian\" },\n {\n hint: \"Changelog from GitHub Releases\",\n label: \"github-releases\",\n value: \"github-releases\",\n },\n { hint: \"A Notion database\", label: \"notion\", value: \"notion\" },\n { hint: \"A Sanity dataset\", label: \"sanity\", value: \"sanity\" },\n {\n hint: \"A Contentful content type\",\n label: \"contentful\",\n value: \"contentful\",\n },\n { hint: \"A Payload collection\", label: \"payload\", value: \"payload\" },\n { hint: \"A Strapi content type\", label: \"strapi\", value: \"strapi\" },\n {\n hint: \"MDX fetched from a GitHub repo\",\n label: \"mdx-remote\",\n value: \"mdx-remote\",\n },\n ],\n required: false,\n });\n if (cancelled(picked)) {\n return null;\n }\n // No selection desugars to the implicit local source, same as the schema.\n const sources: SourceKind[] = picked.length === 0 ? [\"filesystem\"] : picked;\n\n let contentDir = flags.contentDir ?? \"docs\";\n if (flags.contentDir === undefined && sources.includes(\"filesystem\")) {\n const answer = await prompter.text({\n defaultValue: \"docs\",\n message: \"Content directory?\",\n placeholder: \"docs\",\n validate: (value) => validateContentDir(root, value || \"docs\"),\n });\n if (cancelled(answer)) {\n return null;\n }\n contentDir = answer;\n }\n\n return {\n contentDir,\n directory,\n packageManager:\n flags.packageManager ?? detectPackageManager(defaults.userAgent),\n sources,\n template,\n title,\n };\n};\n"
7
- ],
8
- "mappings": "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",
9
- "debugId": "1529DCF0EE516DB064756E2164756E21",
10
- "names": []
11
- }
@@ -1,14 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/search/documents.ts", "../src/markdown/fence-meta.ts", "../src/markdown/code-title.ts", "../src/search/facets.ts", "../src/ai/mcp/data.ts"],
4
- "sourcesContent": [
5
- "import type { Nodes } from \"mdast\";\nimport { markdownToMdast, mdxToMdast } from \"satteri\";\n\nimport {\n componentRegistry,\n downlevelComponentNode,\n downlevelComponents,\n} from \"../ai/component-markdown.ts\";\nimport type {\n ComponentMarkdown,\n DownlevelWalk,\n MdastNode as DownlevelNode,\n} from \"../ai/component-markdown.ts\";\nimport { projectComponentSerializers } from \"../ai/serializers.ts\";\nimport { applyAudienceVisibility } from \"../ai/visibility.ts\";\nimport type { VisibilityAudience } from \"../ai/visibility.ts\";\nimport matter from \"../core/frontmatter.ts\";\nimport { parseHeadingMarkers } from \"../core/heading-markers.ts\";\nimport { contentIndexable } from \"../core/manifest.ts\";\nimport type { BlumeProject } from \"../core/project-graph.ts\";\nimport { HTML_COMMENT } from \"../core/sources/normalize.ts\";\nimport { readExpandedEntryText } from \"../core/sources/read.ts\";\nimport type { NavNode, PageRecord } from \"../core/types.ts\";\nimport { parseCodeTitle } from \"../markdown/code-title.ts\";\nimport { MARKDOWN_FEATURES, MDX_FEATURES } from \"../markdown/features.ts\";\nimport { pageFacets } from \"./facets.ts\";\n\n/** A document indexed by the client-side search providers (Orama, FlexSearch). */\nexport interface SearchDocument {\n route: string;\n title: string;\n description: string;\n content: string;\n /** Ancestor section labels for the result breadcrumb, e.g. `[\"Guides\"]`. */\n breadcrumb: string[];\n /** Top-level section label, used by the search filter pills. */\n section: string;\n /** Locale code, so the dialog can filter results to the active language. */\n locale: string;\n /** Resolved page `type` (`doc`, `blog`, a custom `rfc`…), for type filters. */\n contentType: string;\n /** Docs version (`\"\"` for the current docs), so results scope to the viewed version. */\n version: string;\n /** Frontmatter `search.tags`, surfaced for hosted-provider faceting. */\n tags?: string[];\n /**\n * Declared facet values (`content.types.<type>.facets`), key → value.\n * Filterable through the MCP tools' `filters` input.\n */\n facets?: Record<string, string>;\n}\n\n/**\n * A record uploaded to a hosted search backend (Algolia, Orama Cloud,\n * Typesense, Mixedbread). `_id` is the stable per-page key; each sync adapts it\n * to the backend's own id field (`objectID`, `id`, …).\n */\nexport interface SearchRecord {\n _id: string;\n url: string;\n title: string;\n description: string;\n content: string;\n /** Locale code, carried as a facet for per-language filtering. */\n locale: string;\n /**\n * Docs version, carried as a facet for per-version filtering. The current\n * docs upload as `\"current\"` — hosted backends treat an empty facet value\n * unreliably, so the sentinel stands in for the empty version id.\n */\n version: string;\n /** Single faceting tag (the first frontmatter tag, when present). */\n tag?: string;\n}\n\n// Tag-shaped only: a name (or closing slash/fragment) right after `<`, and no\n// newline inside. Applied *within* html/JSX nodes so their inner prose is\n// kept; the surrounding Markdown is walked as a tree, so a bare `<` in prose\n// (\"costs < 5 credits\") is ordinary text and never at risk.\nconst HTML_OR_JSX = /<\\/?[a-zA-Z][^\\n<>]*>|<\\/?>/gu;\nconst WHITESPACE = /\\s+/gu;\n\n// Parents whose children are inline: no separator is inserted after them, or\n// `re*ally*` would index as `re ally`. Every other parent is block-shaped and\n// ends with a space so adjacent paragraphs/headings/cells don't fuse.\nconst INLINE_PARENTS = new Set([\n \"delete\",\n \"emphasis\",\n \"footnoteReference\",\n \"link\",\n \"linkReference\",\n \"mdxJsxTextElement\",\n \"strong\",\n \"subscript\",\n \"superscript\",\n \"textDirective\",\n]);\n\n// Nodes that never render as prose: image alt text was never indexed, MDX\n// expressions (`{props.x}`) and ESM (`export const meta`) are code, and math\n// is LaTeX source rendered by KaTeX, not searchable words.\nconst NON_PROSE = new Set<Nodes[\"type\"]>([\n \"image\",\n \"imageReference\",\n \"inlineMath\",\n \"math\",\n \"mdxFlowExpression\",\n \"mdxTextExpression\",\n \"mdxjsEsm\",\n]);\n\ntype PageFormat = PageRecord[\"format\"];\n\n/** What the plain-text walk carries: the code-block policy and the downlevel context. */\ninterface Walk {\n includeCodeBlocks: boolean;\n downlevel: DownlevelWalk;\n}\n\n// Front matter is already off (core/frontmatter.ts) by the time a body gets\n// here, so a body that opens with a `---` divider must read as a thematic\n// break rather than a second front matter block.\nconst MD_PARSE = { features: { ...MARKDOWN_FEATURES, frontmatter: false } };\nconst MDX_PARSE = { features: { ...MDX_FEATURES, frontmatter: false } };\n\nconst parseMdx = (markdown: string): Nodes | undefined => {\n try {\n return mdxToMdast(markdown, MDX_PARSE);\n } catch {\n return undefined;\n }\n};\n\n/**\n * Parse a page body the way the renderer reads it. `.mdx` pages go through\n * the MDX grammar with the renderer's feature set, so components become\n * `mdxJsx*` nodes whose children — prose and fences, however they're indented\n * — walk like top-level content, and `:::` directives and `$$` math parse as\n * such; Markdown would instead fold a tight component into one html node and\n * read its indented children as an indented code block. A page the MDX\n * grammar rejects (an HTML comment, an unclosed tag) fails to render too, so\n * it has no body to index: reading it as Markdown instead would put its ESM,\n * expressions, and raw tags in the index as prose.\n */\nconst parseMarkdown = (\n markdown: string,\n format: PageFormat\n): Nodes | undefined =>\n format === \"mdx\" ? parseMdx(markdown) : markdownToMdast(markdown, MD_PARSE);\n\n// Fenced code is excluded from the plain index by default (ranking noise) —\n// the \"markdown\" extraction keeps it for Ask AI grounding. Code-heavy docs opt\n// in via `search.indexing.includeCodeBlocks`, which indexes the fence body and\n// its rendered title (```ts blume.config.ts) but never the fence markers,\n// language, or other meta keywords.\nconst collectCode = (\n node: Extract<Nodes, { type: \"code\" }>,\n out: string[],\n walk: Walk\n): void => {\n if (!walk.includeCodeBlocks) {\n return;\n }\n const title = parseCodeTitle(node.meta ?? undefined);\n if (title) {\n out.push(title, \" \");\n }\n // The trailing space keeps the fence apart from what follows it.\n out.push(node.value, \" \");\n};\n\n// Block boundaries separate words; so does an empty inline element (`<br />`,\n// a self-closing icon), which otherwise fuses its neighbors.\nconst separatesWords = (node: Extract<Nodes, { children: unknown }>): boolean =>\n !INLINE_PARENTS.has(node.type) || node.children.length === 0;\n\n/** Fold one mdast node into the plain-text accumulator. */\nconst collectText = (node: Nodes, out: string[], walk: Walk): void => {\n if (NON_PROSE.has(node.type)) {\n return;\n }\n switch (node.type) {\n case \"code\": {\n collectCode(node, out, walk);\n return;\n }\n // A component with a serializer indexes as the text the page shows —\n // a Card's title and call to action, a TypeTable's descriptions — read\n // from the Markdown the serializer emits (its contract is Markdown, not\n // MDX). One without a serializer walks its children like any block.\n case \"mdxJsxFlowElement\": {\n // SAFETY: DownlevelNode is a structural subset of Satteri's mdast\n // output — the walk checks for offsets before reading them.\n const text = downlevelComponentNode(\n node as DownlevelNode,\n walk.downlevel\n );\n if (text !== null) {\n collectText(markdownToMdast(text, MD_PARSE), out, walk);\n return;\n }\n break;\n }\n // Inline code is kept verbatim — `Array<T>` is a type parameter, not a\n // tag, and its tokens must stay searchable.\n case \"inlineCode\": {\n out.push(node.value);\n return;\n }\n // A raw-HTML run. In a `.md` page (or an `.mdx` page MDX couldn't parse)\n // CommonMark folds a block-level `<Callout>` with no blank lines into ONE\n // html node holding all its inner prose, so the node can't just be\n // dropped — strip the tag-shaped runs and keep the text.\n case \"html\": {\n out.push(\n node.value.replaceAll(HTML_COMMENT, \" \").replaceAll(HTML_OR_JSX, \" \"),\n \" \"\n );\n return;\n }\n case \"break\": {\n out.push(\" \");\n return;\n }\n // Trailing heading markers (`[#custom-id]`, `{#custom-id}`, `[!toc]`,\n // `[toc]`) are anchor\n\n // metadata, not prose — strip them so they never pollute the index. Only\n // a marker that ends the heading's final plain-text child counts,\n // mirroring the renderer: a heading ending in inline code or an image\n // keeps its bracketed text on the page (so it stays searchable), and a\n // heading that is nothing but markers renders them literally.\n case \"heading\": {\n const last = node.children.at(-1);\n const trailing = last?.type === \"text\" ? last : undefined;\n const inner: string[] = [];\n const kept = trailing ? node.children.slice(0, -1) : node.children;\n for (const child of kept) {\n collectText(child, inner, walk);\n }\n if (trailing) {\n const stripped = parseHeadingMarkers(trailing.value).text;\n const literal = stripped === \"\" && node.children.length === 1;\n inner.push(literal ? trailing.value : stripped);\n }\n out.push(inner.join(\"\").trimEnd(), \" \");\n return;\n }\n default: {\n break;\n }\n }\n if (\"value\" in node) {\n out.push(node.value);\n return;\n }\n if (\"children\" in node) {\n for (const child of node.children) {\n collectText(child, out, walk);\n }\n if (separatesWords(node)) {\n out.push(\" \");\n }\n }\n};\n\n/**\n * Reduce Markdown/MDX to plain, searchable text: parse (GFM included) and walk\n * the tree instead of regex-stripping the source, so reference-style links,\n * autolinks, setext headings, tables, and literal `*`/`~`/`>` in prose all\n * reduce correctly. This feeds the client index *and* every hosted-provider\n * record, so anything lost here is a permanent search-quality loss.\n */\nconst toPlainText = (\n markdown: string,\n format: PageFormat,\n walk: Walk\n): string => {\n const tree = parseMarkdown(markdown, format);\n if (!tree) {\n return \"\";\n }\n const out: string[] = [];\n collectText(tree, out, walk);\n return out.join(\"\").replaceAll(WHITESPACE, \" \").trim();\n};\n\ninterface Crumbs {\n breadcrumb: string[];\n section: string;\n}\n\n/**\n * Map each page route to its ancestor section labels by walking the nav sidebar\n * once. The nearest ancestor group — the sidebar section a page appears under —\n * becomes its `section`, the dimension the search filter pills group by, so the\n * pills mirror the visible sidebar (and honor folder-meta renames).\n */\nconst buildCrumbIndex = (sidebar: NavNode[]): Map<string, Crumbs> => {\n const index = new Map<string, Crumbs>();\n // A config-sidebar section's landing page (the group's `root`) lives on the\n // *group* node, not on any page leaf — record it under the group's own label\n // so the section's landing page carries the same facet as its children. A\n // real page leaf for the route (filesystem sidebars emit index pages as\n // leaves) wins, so group routes are merged in only where no leaf claimed one.\n const groupRoutes = new Map<string, Crumbs>();\n const walk = (nodes: NavNode[], trail: string[]): void => {\n for (const node of nodes) {\n if (node.kind === \"group\") {\n if (node.route && !groupRoutes.has(node.route)) {\n groupRoutes.set(node.route, {\n breadcrumb: [...trail, node.label],\n section: node.label,\n });\n }\n walk(node.children, [...trail, node.label]);\n } else if (node.route) {\n index.set(node.route, {\n breadcrumb: trail,\n section: trail.at(-1) ?? \"\",\n });\n }\n }\n };\n walk(sidebar, []);\n for (const [route, crumbs] of groupRoutes) {\n if (!index.has(route)) {\n index.set(route, crumbs);\n }\n }\n return index;\n};\n\ninterface BuildOptions {\n includeWhenDisabled?: boolean;\n content?: \"markdown\" | \"plain\";\n audience?: VisibilityAudience;\n}\n\n/**\n * Read a page's body and reduce it per the extraction options. The\n * `\"markdown\"` body is agent-facing, so its components are downleveled in the\n * source with the serializers every agent surface uses (front matter in scope\n * for prop expressions). The plain body is parsed as written and walked, with\n * the same serializers applied per component on the way, so the index reads a\n * `<Card>` or `<TypeTable>` as the text it shows.\n */\nconst pageBody = async (\n project: BlumeProject,\n page: PageRecord | undefined,\n options: Pick<BuildOptions, \"audience\" | \"content\"> | undefined,\n components: Record<string, ComponentMarkdown>\n): Promise<string> => {\n if (!page) {\n return \"\";\n }\n const parsed = matter(await readExpandedEntryText(project, page));\n const visible = applyAudienceVisibility(\n parsed.content,\n options?.audience ?? \"web\"\n );\n if (options?.content === \"markdown\") {\n return downlevelComponents(visible.trim(), components, parsed.data);\n }\n return toPlainText(visible, page.format, {\n downlevel: {\n frontmatter: parsed.data,\n registry: componentRegistry(components),\n source: visible,\n },\n includeCodeBlocks: project.config.search.indexing.includeCodeBlocks,\n });\n};\n\n/**\n * Build search documents from the content graph. Only indexable pages are\n * included (per the route manifest), and content comes from the source files,\n * so the index is identical in dev and build.\n *\n * Pass `includeWhenDisabled` to index pages on their content merits even when\n * the search provider is `none` — used by the MCP server, which is a separate\n * feature from on-page search.\n *\n * `content` selects the extraction: `\"plain\"` (default) strips Markdown to bare\n * searchable text; `\"markdown\"` keeps the body's Markdown — code blocks, lists,\n * headings — for Ask AI grounding, where fenced examples are often the answer\n * and stripping them makes the model unable to cite content the docs do contain.\n * The `\"markdown\"` body is agent-facing, so components are downleveled with the\n * same serializers the `.md` mirror, llms-full.txt and MCP `get_page` use: a\n * page whose body is a `<CardGroup>` of `<Card>`s is prose to a reader and bare\n * JSX to anything reading the source, and section landing pages are exactly\n * that shape.\n *\n * `audience` resolves `<Visibility>` blocks before extraction: `\"web\"`\n * (default) keeps web-only content and drops agents-only blocks — the site\n * search and hosted syncs must not surface content the page hides — while\n * `\"agents\"` mirrors llms-full.txt/MCP `get_page` (web removed, agents kept).\n *\n * Whether fenced code joins the plain extraction is site-wide policy, read\n * from `search.indexing.includeCodeBlocks` here rather than threaded by each\n * caller, so the client index, hosted syncs, eject, and the MCP `search_docs`\n * index can't drift apart.\n */\nexport const buildSearchDocuments = async (\n project: BlumeProject,\n options?: BuildOptions\n): Promise<SearchDocument[]> => {\n const pageById = new Map(project.graph.pages.map((page) => [page.id, page]));\n\n // Build the crumb index from every locale's sidebar (their nodes carry\n // locale-prefixed routes), so localized pages get the right section/breadcrumb.\n // Falls back to the single default-locale nav when i18n is off.\n const byLocale = Object.values(project.graph.navigationByLocale ?? {});\n // Archived versions' trees contribute too, so snapshot pages get their own\n // section/breadcrumb instead of falling through to the \"Docs\" default.\n const byVersion = Object.values(project.graph.navigationByVersion ?? {})\n .flatMap((locales) => Object.values(locales))\n .map((nav) => nav.sidebar);\n const sidebars = [\n ...(byLocale.length > 0\n ? byLocale.map((nav) => nav.sidebar)\n : [project.graph.navigation?.sidebar ?? []]),\n ...byVersion,\n ];\n const crumbs = new Map<string, Crumbs>();\n for (const sidebar of sidebars) {\n for (const [route, crumb] of buildCrumbIndex(sidebar)) {\n crumbs.set(route, crumb);\n }\n }\n\n const indexable = project.manifest.routes.filter((route) => {\n if (!options?.includeWhenDisabled) {\n return route.indexable;\n }\n // A fallback route renders the fallback locale's page at another locale's\n // URL, so indexing it would only repeat that page as a second hit — the\n // reason site search leaves it out too.\n if (route.fallback) {\n return false;\n }\n const page = pageById.get(route.id);\n return page ? contentIndexable(page, project.config) : false;\n });\n\n // Built once — `downlevelComponents` rebuilds its registry per call\n // otherwise.\n const components = projectComponentSerializers(project);\n\n return await Promise.all(\n indexable.map(async (route) => {\n const page = pageById.get(route.id);\n const body = await pageBody(project, page, options, components);\n const tags = page?.meta?.search?.tags;\n const crumb = crumbs.get(route.path);\n const facets = page ? pageFacets(page, project.config) : undefined;\n const document: SearchDocument = {\n breadcrumb: crumb?.breadcrumb ?? [],\n content: body,\n contentType: route.contentType,\n description: page?.description ?? \"\",\n locale: route.locale,\n route: route.path,\n section: crumb?.section || \"Docs\",\n tags: tags && tags.length > 0 ? tags : undefined,\n title: route.title,\n version: route.version,\n };\n if (facets) {\n document.facets = facets;\n }\n return document;\n })\n );\n};\n\n/**\n * Map per-page search documents to the flat record shape hosted backends\n * ingest. One record per page, keyed by route; the first tag becomes the\n * faceting `tag`.\n */\nexport const toSearchRecords = (documents: SearchDocument[]): SearchRecord[] =>\n documents.map((doc) => ({\n _id: doc.route,\n content: doc.content,\n description: doc.description,\n locale: doc.locale,\n tag: doc.tags?.[0],\n title: doc.title,\n url: doc.route,\n version: doc.version || \"current\",\n }));\n",
6
- "/**\n * The shared fence-meta grammar: how the tokens after a code fence's language\n * (```ts title=\"...\" {1,3-5} lineNumbers) split, and which of them carry\n * reserved meaning. Both the Shiki-side meta reader (`code-title.ts`) and the\n * MDAST plugins that rewrite marked fences (`ts2js.ts`) consume this, so the\n * two layers can't drift on token boundaries or keyword lists.\n */\n\n/**\n * Any quoted `key=\"...\"` attr (spaces allowed inside the quotes). The body\n * excludes only the delimiting quote, so `title=\"foo's file.ts\"` (an\n * apostrophe inside double quotes) still matches. The left boundary stops\n * `subtitle=\"...\"` (or any `*title=` attr) from reading as a title.\n */\nexport const QUOTED_ATTR = /[\\w-]+=(?:\"[^\"]*\"|'[^']*')/gu;\n\n/**\n * A Shiki `{1,3-5}` line-range token, braces included. Shiki tolerates\n * whitespace inside the braces (`{1, 3-5}`), so the range is one token even\n * when it contains spaces — split on whitespace alone, `3-5}` would surface\n * as a bare word and be promoted to the block title.\n */\nconst LINE_RANGE = /\\{[^}]*\\}/u;\n\n/**\n * One fence-meta token: a quoted attribute, a line range, or a bare word, so\n * a keyword inside a quoted value (`title=\"enable ts2js later\"`) never reads\n * as a bare token and a spaced line range never splits.\n */\nconst META_TOKEN = new RegExp(\n `${QUOTED_ATTR.source}|${LINE_RANGE.source}|\\\\S+`,\n \"gu\"\n);\n\n/** Split a raw fence meta string into its tokens. */\nexport const metaTokens = (meta: string | null | undefined): string[] =>\n meta?.match(META_TOKEN) ?? [];\n\n/**\n * Bare keywords with reserved meaning after the language; never promoted to\n * a block title.\n */\nexport const RESERVED_META_KEYWORDS: ReadonlySet<string> = new Set([\n \"lineNumbers\",\n \"ts2js\",\n \"twoslash\",\n]);\n\n/** A Shiki `{1,3-5}` line-range token. */\nexport const isLineRange = (token: string): boolean => token.startsWith(\"{\");\n",
7
- "/**\n * Code-fence meta. A Shiki transformer reads the tokens after the language and\n * promotes them to attributes on the rendered `<pre>`:\n *\n * - a title — the first bare token (```ts blume.config.ts) or `title=\"...\"` —\n * becomes `data-title`; the theme's code header shows it, falling back to the\n * language label.\n * - the `lineNumbers` keyword (```ts file.ts lineNumbers) becomes\n * `data-line-numbers`; the theme renders a counter-driven line-number gutter.\n */\n\nimport {\n isLineRange,\n metaTokens,\n QUOTED_ATTR,\n RESERVED_META_KEYWORDS,\n} from \"./fence-meta.ts\";\n\n/** The slice of Shiki's transformer `this` context Blume reads. */\ninterface CodeMetaContext {\n options: { meta?: { __raw?: string } };\n}\n\n/** The `<pre>` hast node a Shiki `pre` hook receives. */\ninterface PreNode {\n properties: Record<string, boolean | number | string | undefined>;\n}\n\n/** A Shiki-compatible transformer, typed structurally to avoid a Shiki dep. */\nexport interface CodeTitleTransformer {\n name: string;\n pre: (this: CodeMetaContext, node: PreNode) => void;\n}\n\nconst TITLE_ATTR = /(?:^|\\s)title=(?:\"(?<dq>[^\"]*)\"|'(?<sq>[^']*)')/u;\nconst LINE_NUMBERS = /(?:^|\\s)lineNumbers(?=\\s|$)/u;\n\n// Quoted attrs are blanked before keyword/bare-token scans so a quoted value\n// can't leak tokens (`title=\"enable lineNumbers later\"`).\nconst withoutQuotedAttrs = (raw: string): string =>\n raw.replace(QUOTED_ATTR, \" \");\n\n// The first bare token is the title (```ts blume.config.ts): a non-empty token\n// that isn't a Shiki line range (`{1,3-5}`), a `key=value` attr, or a reserved\n// keyword.\nconst isTitleToken = (token: string): boolean =>\n token.length > 0 &&\n !isLineRange(token) &&\n !token.includes(\"=\") &&\n !RESERVED_META_KEYWORDS.has(token);\n\n/** The title a fence's meta string promotes to `data-title`, if any. */\nexport const parseCodeTitle = (raw: string | undefined): string | undefined => {\n if (!raw) {\n return undefined;\n }\n // Blank every *other* quoted attr first, so a `title=\"…\"` embedded in\n // another attribute's value (`caption='set title=\"X\" here'`) can't be\n // promoted to the block title.\n const scrubbed = raw.replace(QUOTED_ATTR, (attr) =>\n attr.startsWith(\"title=\") ? attr : \" \"\n );\n const explicit = scrubbed.match(TITLE_ATTR);\n const attrTitle = explicit?.groups?.dq ?? explicit?.groups?.sq;\n if (attrTitle) {\n return attrTitle;\n }\n // The shared tokenizer keeps a quoted attr (rejected below by its `=`) and\n // a spaced line range (`{1, 3-5}`) whole, so neither can shed a fragment\n // that reads as a bare title.\n return metaTokens(raw).find(isTitleToken);\n};\n\nconst hasLineNumbers = (raw: string | undefined): boolean =>\n Boolean(raw && LINE_NUMBERS.test(withoutQuotedAttrs(raw)));\n\n/** Build the transformer. Runs after Shiki's built-in `data-language` hook. */\nexport const codeTitleTransformer = (): CodeTitleTransformer => ({\n name: \"blume:code-meta\",\n pre(node) {\n const raw = this.options.meta?.__raw;\n const title = parseCodeTitle(raw);\n if (title) {\n node.properties.dataTitle = title;\n }\n if (hasLineNumbers(raw)) {\n node.properties.dataLineNumbers = true;\n }\n },\n});\n",
8
- "import type { ResolvedConfig } from \"../core/schema.ts\";\nimport type { PageRecord } from \"../core/types.ts\";\n\n/**\n * Resolve a page's facet values: the subset of its validated custom\n * frontmatter named by its content type's `facets` declaration. Strings facet\n * as-is; numbers and booleans are stringified so an enum-like `priority: 1`\n * or `enforced: true` still filters; any other shape (objects, arrays,\n * transformed dates) is not a facet value and is skipped. Returns `undefined`\n * when nothing facets, so the field stays absent from serialized documents\n * rather than shipping as `{}` on every page.\n */\n/** Whether a custom frontmatter value stringifies into a usable facet value. */\nconst isFacetValue = <T>(value: T): value is T & (string | number | boolean) =>\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\";\n\nexport const pageFacets = (\n page: Pick<PageRecord, \"contentType\" | \"custom\">,\n config: ResolvedConfig\n): Record<string, string> | undefined => {\n const declared = config.content.types[page.contentType]?.facets;\n if (!declared || declared.length === 0 || !page.custom) {\n return;\n }\n const facets: Record<string, string> = {};\n for (const key of declared) {\n const value = page.custom[key];\n if (isFacetValue(value)) {\n facets[key] = String(value);\n }\n }\n return Object.keys(facets).length > 0 ? facets : undefined;\n};\n",
9
- "import { normalizeBasePath } from \"../../core/base-path.ts\";\nimport type { BlumeProject } from \"../../core/project-graph.ts\";\nimport type { Navigation } from \"../../core/types.ts\";\nimport { buildSearchDocuments } from \"../../search/documents.ts\";\nimport { pageFacets } from \"../../search/facets.ts\";\nimport type { OramaDoc } from \"../../search/orama-index.ts\";\nimport { agentMarkdown, buildRawMarkdown } from \"../markdown.ts\";\n\n/** A page entry surfaced by the `list_pages` MCP tool. */\nexport interface McpRoute {\n contentType: string;\n description?: string;\n /** Declared facet values (`content.types.<type>.facets`), key → value. */\n facets?: Record<string, string>;\n indexable: boolean;\n lastModified: string | null;\n /** Resolved locale code (the default locale when not under i18n). */\n locale: string;\n route: string;\n title: string;\n /** Docs version (`\"\"` for the current docs). */\n version: string;\n}\n\n/**\n * The self-contained snapshot the generated MCP endpoint serves. Bundles the\n * search documents, raw page Markdown, route list, and navigation so the server\n * works regardless of the configured search provider and needs no filesystem\n * access at request time. Serialized to `generated/mcp-data.json`.\n */\nexport interface McpData {\n /**\n * Normalized `deployment.base` (`\"\"` or `/seg`), layered onto routes when\n * emitting URLs — the site is base-less and routes are base-less manifest\n * paths, matching the sitemap/llms.txt convention.\n */\n base: string;\n /**\n * The site's `i18n.defaultLocale`, when i18n is configured. Selects a\n * word-segmenting Orama tokenizer for every non-Latin script, so\n * `search_docs` can match CJK, Cyrillic, Greek, Hebrew, or Devanagari\n * content.\n */\n defaultLocale?: string;\n /**\n * Archived docs version ids, in configured order. Present only on a\n * versioned site; its presence is what makes `search_docs`/`list_pages`\n * default to the current docs.\n */\n archivedVersions?: string[];\n documents: OramaDoc[];\n instructions?: string;\n name: string;\n navigation: Navigation;\n /** Per-locale trees for a locale-aware `get_navigation` (i18n sites only). */\n navigationByLocale?: Record<string, Navigation>;\n /** Per-archived-version trees, keyed by version id then locale code. */\n navigationByVersion?: Record<string, Record<string, Navigation>>;\n pages: Record<string, string>;\n routes: McpRoute[];\n site: string | null;\n version: string;\n}\n\n/** Build the MCP data snapshot from a resolved project. */\nexport const buildMcpData = async (project: BlumeProject): Promise<McpData> => {\n const { config, graph, manifest } = project;\n const [documents, rawMarkdown] = await Promise.all([\n // The MCP server is independent of on-page search, so index docs even when\n // the search provider is `none`. Documents are agent-facing, so\n // `<Visibility>` resolves like `get_page`/llms-full.txt (web-only content\n // removed, agents-only kept).\n buildSearchDocuments(project, {\n audience: \"agents\",\n includeWhenDisabled: true,\n }),\n buildRawMarkdown(project),\n ]);\n\n // `get_page` serves the agent variant: components downleveled to Markdown.\n const pages = Object.fromEntries(\n Object.entries(rawMarkdown).map(([route, entry]) => [\n route,\n agentMarkdown(entry),\n ])\n );\n\n const pageById = new Map(graph.pages.map((page) => [page.id, page]));\n\n const routes: McpRoute[] = [];\n // Fallback routes render the fallback locale's page at another locale's\n // URL: listing them would pass that page off as a translation, once per\n // locale. `get_page` still resolves them through `pages`, as llms.txt and\n // search leave them out.\n for (const route of manifest.routes) {\n if (route.hidden || route.fallback) {\n continue;\n }\n const page = pageById.get(route.id);\n const facets = page ? pageFacets(page, config) : undefined;\n const entry: McpRoute = {\n contentType: route.contentType,\n description: page?.description,\n indexable: route.indexable,\n lastModified: route.lastModified ?? null,\n locale: route.locale,\n route: route.path,\n title: route.title,\n version: route.version,\n };\n if (facets) {\n entry.facets = facets;\n }\n routes.push(entry);\n }\n\n const data: McpData = {\n base: normalizeBasePath(config.deployment.options.base),\n defaultLocale: config.i18n?.defaultLocale,\n documents: documents.map((doc) => {\n const document: OramaDoc = {\n content: doc.content,\n contentType: doc.contentType,\n description: doc.description,\n locale: doc.locale,\n route: doc.route,\n title: doc.title,\n version: doc.version,\n };\n if (doc.facets) {\n document.facets = doc.facets;\n }\n return document;\n }),\n instructions: config.agents.mcp.instructions,\n name: config.agents.mcp.name ?? config.title,\n navigation: graph.navigation,\n pages,\n routes,\n site: config.deployment.options.site ?? null,\n version: manifest.blumeVersion,\n };\n if (config.versions) {\n data.archivedVersions = config.versions.archived.map(\n (version) => version.id\n );\n data.navigationByVersion = graph.navigationByVersion;\n }\n if (config.i18n) {\n data.navigationByLocale = graph.navigationByLocale;\n }\n return data;\n};\n"
10
- ],
11
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;AACA;;;ACaO,IAAM,cAAc;AAQ3B,IAAM,aAAa;AAOnB,IAAM,aAAa,IAAI,OACrB,GAAG,YAAY,UAAU,WAAW,eACpC,IACF;AAGO,IAAM,aAAa,CAAC,SACzB,MAAM,MAAM,UAAU,KAAK,CAAC;AAMvB,IAAM,yBAA8C,IAAI,IAAI;AAAA,EACjE;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,IAAM,cAAc,CAAC,UAA2B,MAAM,WAAW,GAAG;;;ACf3E,IAAM,aAAa;AAWnB,IAAM,eAAe,CAAC,UACpB,MAAM,SAAS,KACf,CAAC,YAAY,KAAK,KAClB,CAAC,MAAM,SAAS,GAAG,KACnB,CAAC,uBAAuB,IAAI,KAAK;AAG5B,IAAM,iBAAiB,CAAC,QAAgD;AAAA,EAC7E,IAAI,CAAC,KAAK;AAAA,IACR;AAAA,EACF;AAAA,EAIA,MAAM,WAAW,IAAI,QAAQ,aAAa,CAAC,SACzC,KAAK,WAAW,QAAQ,IAAI,OAAO,GACrC;AAAA,EACA,MAAM,WAAW,SAAS,MAAM,UAAU;AAAA,EAC1C,MAAM,YAAY,UAAU,QAAQ,MAAM,UAAU,QAAQ;AAAA,EAC5D,IAAI,WAAW;AAAA,IACb,OAAO;AAAA,EACT;AAAA,EAIA,OAAO,WAAW,GAAG,EAAE,KAAK,YAAY;AAAA;;;ACzD1C,IAAM,eAAe,CAAI,UACvB,OAAO,UAAU,YACjB,OAAO,UAAU,YACjB,OAAO,UAAU;AAEZ,IAAM,aAAa,CACxB,MACA,WACuC;AAAA,EACvC,MAAM,WAAW,OAAO,QAAQ,MAAM,KAAK,cAAc;AAAA,EACzD,IAAI,CAAC,YAAY,SAAS,WAAW,KAAK,CAAC,KAAK,QAAQ;AAAA,IACtD;AAAA,EACF;AAAA,EACA,MAAM,SAAiC,CAAC;AAAA,EACxC,WAAW,OAAO,UAAU;AAAA,IAC1B,MAAM,QAAQ,KAAK,OAAO;AAAA,IAC1B,IAAI,aAAa,KAAK,GAAG;AAAA,MACvB,OAAO,OAAO,OAAO,KAAK;AAAA,IAC5B;AAAA,EACF;AAAA,EACA,OAAO,OAAO,KAAK,MAAM,EAAE,SAAS,IAAI,SAAS;AAAA;;;AH8CnD,IAAM,cAAc;AACpB,IAAM,aAAa;AAKnB,IAAM,iBAAiB,IAAI,IAAI;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAKD,IAAM,YAAY,IAAI,IAAmB;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAaD,IAAM,WAAW,EAAE,UAAU,KAAK,mBAAmB,aAAa,MAAM,EAAE;AAC1E,IAAM,YAAY,EAAE,UAAU,KAAK,cAAc,aAAa,MAAM,EAAE;AAEtE,IAAM,WAAW,CAAC,aAAwC;AAAA,EACxD,IAAI;AAAA,IACF,OAAO,WAAW,UAAU,SAAS;AAAA,IACrC,MAAM;AAAA,IACN;AAAA;AAAA;AAeJ,IAAM,gBAAgB,CACpB,UACA,WAEA,WAAW,QAAQ,SAAS,QAAQ,IAAI,gBAAgB,UAAU,QAAQ;AAO5E,IAAM,cAAc,CAClB,MACA,KACA,SACS;AAAA,EACT,IAAI,CAAC,KAAK,mBAAmB;AAAA,IAC3B;AAAA,EACF;AAAA,EACA,MAAM,QAAQ,eAAe,KAAK,QAAQ,SAAS;AAAA,EACnD,IAAI,OAAO;AAAA,IACT,IAAI,KAAK,OAAO,GAAG;AAAA,EACrB;AAAA,EAEA,IAAI,KAAK,KAAK,OAAO,GAAG;AAAA;AAK1B,IAAM,iBAAiB,CAAC,SACtB,CAAC,eAAe,IAAI,KAAK,IAAI,KAAK,KAAK,SAAS,WAAW;AAG7D,IAAM,cAAc,CAAC,MAAa,KAAe,SAAqB;AAAA,EACpE,IAAI,UAAU,IAAI,KAAK,IAAI,GAAG;AAAA,IAC5B;AAAA,EACF;AAAA,EACA,QAAQ,KAAK;AAAA,SACN,QAAQ;AAAA,MACX,YAAY,MAAM,KAAK,IAAI;AAAA,MAC3B;AAAA,IACF;AAAA,SAKK,qBAAqB;AAAA,MAGxB,MAAM,OAAO,uBACX,MACA,KAAK,SACP;AAAA,MACA,IAAI,SAAS,MAAM;AAAA,QACjB,YAAY,gBAAgB,MAAM,QAAQ,GAAG,KAAK,IAAI;AAAA,QACtD;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAAA,SAGK,cAAc;AAAA,MACjB,IAAI,KAAK,KAAK,KAAK;AAAA,MACnB;AAAA,IACF;AAAA,SAKK,QAAQ;AAAA,MACX,IAAI,KACF,KAAK,MAAM,WAAW,cAAc,GAAG,EAAE,WAAW,aAAa,GAAG,GACpE,GACF;AAAA,MACA;AAAA,IACF;AAAA,SACK,SAAS;AAAA,MACZ,IAAI,KAAK,GAAG;AAAA,MACZ;AAAA,IACF;AAAA,SASK,WAAW;AAAA,MACd,MAAM,OAAO,KAAK,SAAS,GAAG,EAAE;AAAA,MAChC,MAAM,WAAW,MAAM,SAAS,SAAS,OAAO;AAAA,MAChD,MAAM,QAAkB,CAAC;AAAA,MACzB,MAAM,OAAO,WAAW,KAAK,SAAS,MAAM,GAAG,EAAE,IAAI,KAAK;AAAA,MAC1D,WAAW,SAAS,MAAM;AAAA,QACxB,YAAY,OAAO,OAAO,IAAI;AAAA,MAChC;AAAA,MACA,IAAI,UAAU;AAAA,QACZ,MAAM,WAAW,oBAAoB,SAAS,KAAK,EAAE;AAAA,QACrD,MAAM,UAAU,aAAa,MAAM,KAAK,SAAS,WAAW;AAAA,QAC5D,MAAM,KAAK,UAAU,SAAS,QAAQ,QAAQ;AAAA,MAChD;AAAA,MACA,IAAI,KAAK,MAAM,KAAK,EAAE,EAAE,QAAQ,GAAG,GAAG;AAAA,MACtC;AAAA,IACF;AAAA,aACS;AAAA,MACP;AAAA,IACF;AAAA;AAAA,EAEF,IAAI,WAAW,MAAM;AAAA,IACnB,IAAI,KAAK,KAAK,KAAK;AAAA,IACnB;AAAA,EACF;AAAA,EACA,IAAI,cAAc,MAAM;AAAA,IACtB,WAAW,SAAS,KAAK,UAAU;AAAA,MACjC,YAAY,OAAO,KAAK,IAAI;AAAA,IAC9B;AAAA,IACA,IAAI,eAAe,IAAI,GAAG;AAAA,MACxB,IAAI,KAAK,GAAG;AAAA,IACd;AAAA,EACF;AAAA;AAUF,IAAM,cAAc,CAClB,UACA,QACA,SACW;AAAA,EACX,MAAM,OAAO,cAAc,UAAU,MAAM;AAAA,EAC3C,IAAI,CAAC,MAAM;AAAA,IACT,OAAO;AAAA,EACT;AAAA,EACA,MAAM,MAAgB,CAAC;AAAA,EACvB,YAAY,MAAM,KAAK,IAAI;AAAA,EAC3B,OAAO,IAAI,KAAK,EAAE,EAAE,WAAW,YAAY,GAAG,EAAE,KAAK;AAAA;AAcvD,IAAM,kBAAkB,CAAC,YAA4C;AAAA,EACnE,MAAM,QAAQ,IAAI;AAAA,EAMlB,MAAM,cAAc,IAAI;AAAA,EACxB,MAAM,OAAO,CAAC,OAAkB,UAA0B;AAAA,IACxD,WAAW,QAAQ,OAAO;AAAA,MACxB,IAAI,KAAK,SAAS,SAAS;AAAA,QACzB,IAAI,KAAK,SAAS,CAAC,YAAY,IAAI,KAAK,KAAK,GAAG;AAAA,UAC9C,YAAY,IAAI,KAAK,OAAO;AAAA,YAC1B,YAAY,CAAC,GAAG,OAAO,KAAK,KAAK;AAAA,YACjC,SAAS,KAAK;AAAA,UAChB,CAAC;AAAA,QACH;AAAA,QACA,KAAK,KAAK,UAAU,CAAC,GAAG,OAAO,KAAK,KAAK,CAAC;AAAA,MAC5C,EAAO,SAAI,KAAK,OAAO;AAAA,QACrB,MAAM,IAAI,KAAK,OAAO;AAAA,UACpB,YAAY;AAAA,UACZ,SAAS,MAAM,GAAG,EAAE,KAAK;AAAA,QAC3B,CAAC;AAAA,MACH;AAAA,IACF;AAAA;AAAA,EAEF,KAAK,SAAS,CAAC,CAAC;AAAA,EAChB,YAAY,OAAO,WAAW,aAAa;AAAA,IACzC,IAAI,CAAC,MAAM,IAAI,KAAK,GAAG;AAAA,MACrB,MAAM,IAAI,OAAO,MAAM;AAAA,IACzB;AAAA,EACF;AAAA,EACA,OAAO;AAAA;AAiBT,IAAM,WAAW,OACf,SACA,MACA,SACA,eACoB;AAAA,EACpB,IAAI,CAAC,MAAM;AAAA,IACT,OAAO;AAAA,EACT;AAAA,EACA,MAAM,SAAS,oBAAO,MAAM,sBAAsB,SAAS,IAAI,CAAC;AAAA,EAChE,MAAM,UAAU,wBACd,OAAO,SACP,SAAS,YAAY,KACvB;AAAA,EACA,IAAI,SAAS,YAAY,YAAY;AAAA,IACnC,OAAO,oBAAoB,QAAQ,KAAK,GAAG,YAAY,OAAO,IAAI;AAAA,EACpE;AAAA,EACA,OAAO,YAAY,SAAS,KAAK,QAAQ;AAAA,IACvC,WAAW;AAAA,MACT,aAAa,OAAO;AAAA,MACpB,UAAU,kBAAkB,UAAU;AAAA,MACtC,QAAQ;AAAA,IACV;AAAA,IACA,mBAAmB,QAAQ,OAAO,OAAO,SAAS;AAAA,EACpD,CAAC;AAAA;AAgCI,IAAM,uBAAuB,OAClC,SACA,YAC8B;AAAA,EAC9B,MAAM,WAAW,IAAI,IAAI,QAAQ,MAAM,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;AAAA,EAK3E,MAAM,WAAW,OAAO,OAAO,QAAQ,MAAM,sBAAsB,CAAC,CAAC;AAAA,EAGrE,MAAM,YAAY,OAAO,OAAO,QAAQ,MAAM,uBAAuB,CAAC,CAAC,EACpE,QAAQ,CAAC,YAAY,OAAO,OAAO,OAAO,CAAC,EAC3C,IAAI,CAAC,QAAQ,IAAI,OAAO;AAAA,EAC3B,MAAM,WAAW;AAAA,IACf,GAAI,SAAS,SAAS,IAClB,SAAS,IAAI,CAAC,QAAQ,IAAI,OAAO,IACjC,CAAC,QAAQ,MAAM,YAAY,WAAW,CAAC,CAAC;AAAA,IAC5C,GAAG;AAAA,EACL;AAAA,EACA,MAAM,SAAS,IAAI;AAAA,EACnB,WAAW,WAAW,UAAU;AAAA,IAC9B,YAAY,OAAO,UAAU,gBAAgB,OAAO,GAAG;AAAA,MACrD,OAAO,IAAI,OAAO,KAAK;AAAA,IACzB;AAAA,EACF;AAAA,EAEA,MAAM,YAAY,QAAQ,SAAS,OAAO,OAAO,CAAC,UAAU;AAAA,IAC1D,IAAI,CAAC,SAAS,qBAAqB;AAAA,MACjC,OAAO,MAAM;AAAA,IACf;AAAA,IAIA,IAAI,MAAM,UAAU;AAAA,MAClB,OAAO;AAAA,IACT;AAAA,IACA,MAAM,OAAO,SAAS,IAAI,MAAM,EAAE;AAAA,IAClC,OAAO,OAAO,iBAAiB,MAAM,QAAQ,MAAM,IAAI;AAAA,GACxD;AAAA,EAID,MAAM,aAAa,4BAA4B,OAAO;AAAA,EAEtD,OAAO,MAAM,QAAQ,IACnB,UAAU,IAAI,OAAO,UAAU;AAAA,IAC7B,MAAM,OAAO,SAAS,IAAI,MAAM,EAAE;AAAA,IAClC,MAAM,OAAO,MAAM,SAAS,SAAS,MAAM,SAAS,UAAU;AAAA,IAC9D,MAAM,OAAO,MAAM,MAAM,QAAQ;AAAA,IACjC,MAAM,QAAQ,OAAO,IAAI,MAAM,IAAI;AAAA,IACnC,MAAM,SAAS,OAAO,WAAW,MAAM,QAAQ,MAAM,IAAI;AAAA,IACzD,MAAM,WAA2B;AAAA,MAC/B,YAAY,OAAO,cAAc,CAAC;AAAA,MAClC,SAAS;AAAA,MACT,aAAa,MAAM;AAAA,MACnB,aAAa,MAAM,eAAe;AAAA,MAClC,QAAQ,MAAM;AAAA,MACd,OAAO,MAAM;AAAA,MACb,SAAS,OAAO,WAAW;AAAA,MAC3B,MAAM,QAAQ,KAAK,SAAS,IAAI,OAAO;AAAA,MACvC,OAAO,MAAM;AAAA,MACb,SAAS,MAAM;AAAA,IACjB;AAAA,IACA,IAAI,QAAQ;AAAA,MACV,SAAS,SAAS;AAAA,IACpB;AAAA,IACA,OAAO;AAAA,GACR,CACH;AAAA;;;AIxZK,IAAM,eAAe,OAAO,YAA4C;AAAA,EAC7E,QAAQ,QAAQ,OAAO,aAAa;AAAA,EACpC,OAAO,WAAW,eAAe,MAAM,QAAQ,IAAI;AAAA,IAKjD,qBAAqB,SAAS;AAAA,MAC5B,UAAU;AAAA,MACV,qBAAqB;AAAA,IACvB,CAAC;AAAA,IACD,iBAAiB,OAAO;AAAA,EAC1B,CAAC;AAAA,EAGD,MAAM,QAAQ,OAAO,YACnB,OAAO,QAAQ,WAAW,EAAE,IAAI,EAAE,OAAO,WAAW;AAAA,IAClD;AAAA,IACA,cAAc,KAAK;AAAA,EACrB,CAAC,CACH;AAAA,EAEA,MAAM,WAAW,IAAI,IAAI,MAAM,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;AAAA,EAEnE,MAAM,SAAqB,CAAC;AAAA,EAK5B,WAAW,SAAS,SAAS,QAAQ;AAAA,IACnC,IAAI,MAAM,UAAU,MAAM,UAAU;AAAA,MAClC;AAAA,IACF;AAAA,IACA,MAAM,OAAO,SAAS,IAAI,MAAM,EAAE;AAAA,IAClC,MAAM,SAAS,OAAO,WAAW,MAAM,MAAM,IAAI;AAAA,IACjD,MAAM,QAAkB;AAAA,MACtB,aAAa,MAAM;AAAA,MACnB,aAAa,MAAM;AAAA,MACnB,WAAW,MAAM;AAAA,MACjB,cAAc,MAAM,gBAAgB;AAAA,MACpC,QAAQ,MAAM;AAAA,MACd,OAAO,MAAM;AAAA,MACb,OAAO,MAAM;AAAA,MACb,SAAS,MAAM;AAAA,IACjB;AAAA,IACA,IAAI,QAAQ;AAAA,MACV,MAAM,SAAS;AAAA,IACjB;AAAA,IACA,OAAO,KAAK,KAAK;AAAA,EACnB;AAAA,EAEA,MAAM,OAAgB;AAAA,IACpB,MAAM,kBAAkB,OAAO,WAAW,QAAQ,IAAI;AAAA,IACtD,eAAe,OAAO,MAAM;AAAA,IAC5B,WAAW,UAAU,IAAI,CAAC,QAAQ;AAAA,MAChC,MAAM,WAAqB;AAAA,QACzB,SAAS,IAAI;AAAA,QACb,aAAa,IAAI;AAAA,QACjB,aAAa,IAAI;AAAA,QACjB,QAAQ,IAAI;AAAA,QACZ,OAAO,IAAI;AAAA,QACX,OAAO,IAAI;AAAA,QACX,SAAS,IAAI;AAAA,MACf;AAAA,MACA,IAAI,IAAI,QAAQ;AAAA,QACd,SAAS,SAAS,IAAI;AAAA,MACxB;AAAA,MACA,OAAO;AAAA,KACR;AAAA,IACD,cAAc,OAAO,OAAO,IAAI;AAAA,IAChC,MAAM,OAAO,OAAO,IAAI,QAAQ,OAAO;AAAA,IACvC,YAAY,MAAM;AAAA,IAClB;AAAA,IACA;AAAA,IACA,MAAM,OAAO,WAAW,QAAQ,QAAQ;AAAA,IACxC,SAAS,SAAS;AAAA,EACpB;AAAA,EACA,IAAI,OAAO,UAAU;AAAA,IACnB,KAAK,mBAAmB,OAAO,SAAS,SAAS,IAC/C,CAAC,YAAY,QAAQ,EACvB;AAAA,IACA,KAAK,sBAAsB,MAAM;AAAA,EACnC;AAAA,EACA,IAAI,OAAO,MAAM;AAAA,IACf,KAAK,qBAAqB,MAAM;AAAA,EAClC;AAAA,EACA,OAAO;AAAA;",
12
- "debugId": "341A56DACBB5F6DE64756E2164756E21",
13
- "names": []
14
- }
@@ -1,31 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/core/sources/watch.ts
4
- var BLUME_IGNORE_DIRS = [
5
- ".blume",
6
- ".blume-verify",
7
- ".cache",
8
- ".git",
9
- ".next",
10
- ".turbo",
11
- ".vercel",
12
- "dist",
13
- "node_modules"
14
- ];
15
- var baselineScanIgnore = () => BLUME_IGNORE_DIRS.map((dir) => `**/${dir}/**`);
16
- var BLUME_WATCH_IGNORE_DIRS = BLUME_IGNORE_DIRS;
17
- var excludeDirSegments = (patterns) => patterns.map((pattern) => /^(?<dir>[^*/]+)\/\*\*$/u.exec(pattern)?.groups?.dir).filter((dir) => dir !== undefined);
18
- var ignoringWatchListener = (onChange, ignoreDirs = BLUME_WATCH_IGNORE_DIRS, ignoreSegment = () => false) => {
19
- const ignore = new Set(ignoreDirs);
20
- return (_event, filename) => {
21
- if (filename !== null && filename.split(/[/\\]/u).some((segment) => ignore.has(segment) || ignoreSegment(segment))) {
22
- return;
23
- }
24
- onChange();
25
- };
26
- };
27
-
28
- export { BLUME_IGNORE_DIRS, baselineScanIgnore, BLUME_WATCH_IGNORE_DIRS, excludeDirSegments, ignoringWatchListener };
29
-
30
- //# debugId=E327C8C451E6563064756E2164756E21
31
- //# sourceMappingURL=chunk-abh8yjkn.js.map
@@ -1,10 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/core/sources/watch.ts"],
4
- "sourcesContent": [
5
- "import type { WatchListener } from \"node:fs\";\n\n/**\n * Directory segments that are never authored content: VCS, dependency trees,\n * Blume's own generated project and build output, and framework/deploy caches.\n * Both the content scan and the dev watcher skip these unconditionally, on top\n * of the user's `content.exclude`.\n *\n * The scan needs them because a broadly-scoped `content.root` — `\".\"` or an app\n * dir that also holds `node_modules`/`dist`, the common shape when migrating a\n * docs app that lives at the repo or app root — would otherwise glob thousands\n * of stray markdown files out of dependencies and build artifacts. `content.root`\n * defaults to `docs/` where this rarely bites, but any wider root hits it.\n *\n * The watcher needs them because a recursive `fs.watch` rooted at the project\n * also sees Blume's own `.blume/` output, which the dev server rewrites on every\n * render (e.g. `.blume/.astro/data-store.json`). Left unfiltered, each such write\n * re-triggers a rescan + runtime regeneration whose writes land back under\n * `.blume/` and fire the watcher again: a self-sustaining loop that stalls page\n * renders and floods the console. `fs.watch` has no ignore option, so we filter\n * by the changed path in the callback.\n */\nexport const BLUME_IGNORE_DIRS = [\n \".blume\",\n // The isolated `blume check --isolated` runtime. A sibling of `.blume`, it is\n // written while a dev server runs; without this the content-layer watcher (or\n // a `.`-rooted fs.watch) would treat its generation as a content change and\n // reload — the very thing `--isolated` promises not to do.\n \".blume-verify\",\n \".cache\",\n \".git\",\n \".next\",\n \".turbo\",\n \".vercel\",\n \"dist\",\n \"node_modules\",\n];\n\n/**\n * Baseline scan-ignore globs applied to every filesystem source, unioned with\n * the user's `content.exclude`. Kept in sync with the watcher via the shared\n * {@link BLUME_IGNORE_DIRS} so `load()` and `watch()` never disagree on what is\n * content. `**\\/<dir>/**` matches the directory at the content root or nested.\n */\nexport const baselineScanIgnore = (): string[] =>\n BLUME_IGNORE_DIRS.map((dir) => `**/${dir}/**`);\n\n/**\n * Alias retained for the dev watcher's call site and its tests; the watcher and\n * the scan share the same never-content directory set.\n */\nexport const BLUME_WATCH_IGNORE_DIRS = BLUME_IGNORE_DIRS;\n\n/** Extract single-segment ignore dirs (`foo`) from `foo/**`-style excludes. */\nexport const excludeDirSegments = (patterns: readonly string[]): string[] =>\n patterns\n .map((pattern) => /^(?<dir>[^*/]+)\\/\\*\\*$/u.exec(pattern)?.groups?.dir)\n .filter((dir): dir is string => dir !== undefined);\n\n/**\n * Build a recursive-watch listener that fires `onChange` for content changes but\n * ignores events whose path crosses an ignored directory segment — one in\n * `ignoreDirs`, or one `ignoreSegment` rejects, for a scan whose skip rule is\n * a shape (every dot-name) rather than a list. A missing `filename` — rare;\n * the platform couldn't name the changed path — falls through to `onChange`\n * rather than silently dropping a real edit. Exported for testing.\n */\nexport const ignoringWatchListener = (\n onChange: () => void,\n ignoreDirs: Iterable<string> = BLUME_WATCH_IGNORE_DIRS,\n ignoreSegment: (segment: string) => boolean = () => false\n): WatchListener<string> => {\n const ignore = new Set(ignoreDirs);\n return (_event, filename) => {\n if (\n filename !== null &&\n filename\n .split(/[/\\\\]/u)\n .some((segment) => ignore.has(segment) || ignoreSegment(segment))\n ) {\n return;\n }\n onChange();\n };\n};\n"
6
- ],
7
- "mappings": ";;;AAsBO,IAAM,oBAAoB;AAAA,EAC/B;AAAA,EAKA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAQO,IAAM,qBAAqB,MAChC,kBAAkB,IAAI,CAAC,QAAQ,MAAM,QAAQ;AAMxC,IAAM,0BAA0B;AAGhC,IAAM,qBAAqB,CAAC,aACjC,SACG,IAAI,CAAC,YAAY,0BAA0B,KAAK,OAAO,GAAG,QAAQ,GAAG,EACrE,OAAO,CAAC,QAAuB,QAAQ,SAAS;AAU9C,IAAM,wBAAwB,CACnC,UACA,aAA+B,yBAC/B,gBAA8C,MAAM,UAC1B;AAAA,EAC1B,MAAM,SAAS,IAAI,IAAI,UAAU;AAAA,EACjC,OAAO,CAAC,QAAQ,aAAa;AAAA,IAC3B,IACE,aAAa,QACb,SACG,MAAM,QAAQ,EACd,KAAK,CAAC,YAAY,OAAO,IAAI,OAAO,KAAK,cAAc,OAAO,CAAC,GAClE;AAAA,MACA;AAAA,IACF;AAAA,IACA,SAAS;AAAA;AAAA;",
8
- "debugId": "E327C8C451E6563064756E2164756E21",
9
- "names": []
10
- }
@@ -1,10 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/cli/commands/preview.ts"],
4
- "sourcesContent": [
5
- "import { existsSync } from \"node:fs\";\n\nimport { preview } from \"astro\";\nimport { defineCommand } from \"citty\";\nimport { join } from \"pathe\";\n\nimport { loadConfig } from \"../../core/config.ts\";\nimport { resolveProjectContext } from \"../../core/project.ts\";\nimport { deployOutputDir } from \"../../deploy/adapter-output.ts\";\nimport { parsePort } from \"../args.ts\";\nimport { commandMeta } from \"../command-meta.ts\";\nimport { normalizeHost } from \"../host-args.ts\";\nimport { logger } from \"../log.ts\";\n\nexport const previewCommand = defineCommand({\n args: {\n host: { description: \"Network host to bind.\", type: \"string\" },\n port: { description: \"Port to listen on.\", type: \"string\" },\n },\n meta: commandMeta.preview,\n async run({ args }) {\n const root = process.cwd();\n const { config } = await loadConfig(root);\n const context = resolveProjectContext(root, config);\n\n // `blume dev` writes `.blume/astro.config.mjs` too, so the runtime alone\n // doesn't mean there's anything to serve: the build output must exist.\n if (\n !existsSync(join(context.outDir, \"astro.config.mjs\")) ||\n !existsSync(deployOutputDir(config, context))\n ) {\n logger.error(\"No build found. Run `blume build` first.\");\n process.exit(1);\n }\n\n await preview({\n logLevel: \"info\",\n root: context.outDir,\n server: {\n // `normalizeHost` maps a bare `--host` (citty parses it as \"\") to\n // `true` so Vite binds all interfaces instead of the hostname \"\".\n host: normalizeHost(args.host),\n port: parsePort(args.port),\n },\n });\n },\n});\n"
6
- ],
7
- "mappings": ";;;;;;;;;;;;;;;;;;;AAAA;AAEA;AACA;AACA;AAUO,IAAM,iBAAiB,cAAc;AAAA,EAC1C,MAAM;AAAA,IACJ,MAAM,EAAE,aAAa,yBAAyB,MAAM,SAAS;AAAA,IAC7D,MAAM,EAAE,aAAa,sBAAsB,MAAM,SAAS;AAAA,EAC5D;AAAA,EACA,MAAM,YAAY;AAAA,OACZ,IAAG,GAAG,QAAQ;AAAA,IAClB,MAAM,OAAO,QAAQ,IAAI;AAAA,IACzB,QAAQ,WAAW,MAAM,WAAW,IAAI;AAAA,IACxC,MAAM,UAAU,sBAAsB,MAAM,MAAM;AAAA,IAIlD,IACE,CAAC,WAAW,KAAK,QAAQ,QAAQ,kBAAkB,CAAC,KACpD,CAAC,WAAW,gBAAgB,QAAQ,OAAO,CAAC,GAC5C;AAAA,MACA,OAAO,MAAM,0CAA0C;AAAA,MACvD,QAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,IAEA,MAAM,QAAQ;AAAA,MACZ,UAAU;AAAA,MACV,MAAM,QAAQ;AAAA,MACd,QAAQ;AAAA,QAGN,MAAM,cAAc,KAAK,IAAI;AAAA,QAC7B,MAAM,UAAU,KAAK,IAAI;AAAA,MAC3B;AAAA,IACF,CAAC;AAAA;AAEL,CAAC;",
8
- "debugId": "7FC21538FC698FBD64756E2164756E21",
9
- "names": []
10
- }
@@ -1,10 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/upgrade/upgrade.ts"],
4
- "sourcesContent": [
5
- "import { existsSync } from \"node:fs\";\nimport { readFile, writeFile } from \"node:fs/promises\";\n\nimport { join, relative, resolve } from \"pathe\";\nimport { glob } from \"tinyglobby\";\nimport { z } from \"zod\";\n\nimport {\n analyzeComponentOverrides,\n ComponentOverridesError,\n} from \"../core/component-overrides.ts\";\nimport { ConfigValidationError, loadConfig } from \"../core/config.ts\";\nimport { BlumeError, locateFrontmatterKey } from \"../core/diagnostics.ts\";\nimport matter from \"../core/frontmatter.ts\";\nimport { createModuleLoader } from \"../core/load-module.ts\";\nimport { findComponentsFile, findConfigFile } from \"../core/project.ts\";\nimport { pageMetaSchema } from \"../core/schema.ts\";\nimport { baselineScanIgnore } from \"../core/sources/watch.ts\";\nimport type { Diagnostic } from \"../core/types.ts\";\nimport {\n DEFAULT_CONTENT_EXCLUDE,\n DEFAULT_CONTENT_INCLUDE,\n} from \"../sources/filesystem.ts\";\n\n/**\n * `blume upgrade`'s logic, kept out of the command module so it runs (and is\n * covered) in-process: bump the `blume` dependency to the running CLI's major,\n * then check the project against it — the config through the same loader\n * every command uses, which names each removed or renamed field and its\n * replacement, `components.ts` through the same static planner the build\n * runs, and each page's front matter for fields the schema removed. The\n * command prints the findings or hands them to a coding agent.\n */\n\n/** The upgrade guide's route on the Blume docs site. */\nexport const UPGRADE_GUIDE_URL = \"https://useblume.dev/docs/upgrading\";\n\n/** The upgrade guide's file inside the published package's bundled docs. */\nexport const UPGRADE_GUIDE_FILE = join(\"docs\", \"03-upgrading.mdx\");\n\n/** What `bumpBlumeDependency` did to `package.json`. */\nexport type DependencyBump =\n | {\n field: DependencyField;\n from: string;\n status: \"bumped\";\n to: string;\n }\n | { range: string; status: \"current\" }\n | { status: \"missing\" };\n\ntype DependencyField = \"dependencies\" | \"devDependencies\";\n\nconst DEPENDENCY_FIELDS: DependencyField[] = [\n \"dependencies\",\n \"devDependencies\",\n];\n\n/** The slice of `package.json` the bump reads and rewrites. */\ninterface PackageJson {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\n// Leading comparators and a `v` may precede the major (`^1.7.3`, `>=1 <2`,\n// `v1`); a protocol (`workspace:`, `file:`, `npm:`) or a tag (`latest`) names\n// no major at all, and those ranges are left to the user.\nconst RANGE_MAJOR = /^[\\s<=>^~v]*(?<major>\\d+)/u;\n\n/** The major version a dependency range pins, or null when it names none. */\nexport const rangeMajor = (range: string): number | null => {\n const major = RANGE_MAJOR.exec(range)?.groups?.major;\n return major === undefined ? null : Number(major);\n};\n\n// The file's own indentation (two spaces, four, or a tab), so a fallback\n// rewrite doesn't reformat a package.json the user keeps differently.\nconst INDENT = /^(?<indent>[ \\t]+)\"/mu;\n\nconst REGEXP_SPECIAL = /[$()*+.?[\\\\\\]^{|}]/gu;\nconst escapeRegExp = (value: string): string =>\n value.replaceAll(REGEXP_SPECIAL, String.raw`\\$&`);\n\n/**\n * `text` with the `blume` range under `field` replaced in place, so the file\n * keeps its own formatting, key order, and inline objects; null when the entry\n * isn't written the plain way (an escaped key or value).\n */\nconst replaceRange = (\n text: string,\n field: DependencyField,\n from: string,\n to: string\n): string | null => {\n const value = JSON.stringify(from);\n const entry = new RegExp(`\"blume\"\\\\s*:\\\\s*${escapeRegExp(value)}`, \"gu\");\n entry.lastIndex = Math.max(text.indexOf(JSON.stringify(field)), 0);\n const match = entry.exec(text);\n if (!match) {\n return null;\n }\n const end = match.index + match[0].length;\n return `${text.slice(0, end - value.length)}${JSON.stringify(to)}${text.slice(end)}`;\n};\n\n/**\n * Point `package.json`'s `blume` dependency at `^version` when it pins an\n * older major. A range already on this major, or one that names no version\n * (`workspace:*`, `latest`), is reported as current and left alone; a\n * project with no `package.json`, or none that lists `blume`, is `missing`.\n * Only the range changes: the rest of the file is kept byte for byte.\n */\nexport const bumpBlumeDependency = async (\n root: string,\n version: string\n): Promise<DependencyBump> => {\n const path = join(root, \"package.json\");\n if (!existsSync(path)) {\n return { status: \"missing\" };\n }\n const text = await readFile(path, \"utf-8\");\n // SAFETY: package.json is a JSON object; only its dependency maps, which\n // map package names to range strings, are read.\n const pkg = JSON.parse(text) as PackageJson;\n const field = DEPENDENCY_FIELDS.find(\n (name) => pkg[name]?.blume !== undefined\n );\n const from = field ? pkg[field]?.blume : undefined;\n if (!(field && from)) {\n return { status: \"missing\" };\n }\n const current = rangeMajor(from);\n const target = rangeMajor(version);\n if (current === null || target === null || current >= target) {\n return { range: from, status: \"current\" };\n }\n const to = `^${version}`;\n const indent = INDENT.exec(text)?.groups?.indent ?? \" \";\n const updated = { ...pkg, [field]: { ...pkg[field], blume: to } };\n await writeFile(\n path,\n replaceRange(text, field, from, to) ??\n `${JSON.stringify(updated, null, indent)}\\n`\n );\n return { field, from, status: \"bumped\", to };\n};\n\n/**\n * Whether `root` is no Blume project at all: no config file and no `blume`\n * dependency, so the upgrade would only check the defaults and report the\n * folder ready. A zero-config project still lists `blume`.\n */\nexport const isOutsideBlumeProject = (\n root: string,\n bump: DependencyBump\n): boolean => bump.status === \"missing\" && findConfigFile(root) === null;\n\n// ---------------------------------------------------------------------------\n// Removed `blume build` flags\n// ---------------------------------------------------------------------------\n\n/** A `blume build` flag Blume 2 removed, bare or with an `=value`. */\nconst REMOVED_BUILD_FLAG = /^--(?<flag>adapter|base|output)(?:=|$)/u;\n\n/** The removed `blume build` flags among `args`, in order, each once. */\nexport const removedBuildFlags = (args: readonly string[]): string[] => [\n ...new Set(\n args.flatMap((arg) => {\n const flag = REMOVED_BUILD_FLAG.exec(arg)?.groups?.flag;\n return flag ? [flag] : [];\n })\n ),\n];\n\n/** The flags as the reader typed them: `--adapter, --output`. */\nexport const flagList = (flags: string[]): string =>\n flags.map((flag) => `--${flag}`).join(\", \");\n\n/** What replaces each removed flag: the `deployment` config. */\nexport const removedBuildFlagsAdvice = (flags: string[]): string => {\n const advice: string[] = [];\n if (flags.includes(\"adapter\") || flags.includes(\"output\")) {\n advice.push(\n 'name the host in blume.config.ts with `deployment: vercel()` (or `netlify()`, `cloudflare()`, `node()`) from \"blume/deploy\" — a host adapter builds for the server, and `output: \"static\"` keeps it static'\n );\n }\n if (flags.includes(\"base\")) {\n advice.push(\n 'set the subpath with `deployment: { base: \"/docs\" }`, or the host adapter\\'s `base` option'\n );\n }\n return `Drop ${flagList(flags)} and ${advice.join(\"; \")}.`;\n};\n\n// A command that runs the Blume CLI: the bare bin, a versioned package\n// (`blume@2`), or a path to it (`./node_modules/.bin/blume`).\nconst BLUME_BIN = /(?:^|[/\\\\])blume(?:@\\S+)?$/u;\nconst COMMAND_SEPARATOR = /&&|\\|\\||[;|]/u;\nconst WHITESPACE = /\\s+/u;\n\n/** The arguments each `blume build` in a script passes, flattened. */\nconst buildArgs = (script: string): string[] =>\n script.split(COMMAND_SEPARATOR).flatMap((command) => {\n const words = command.trim().split(WHITESPACE);\n const at = words.findIndex(\n (word, index) => BLUME_BIN.test(word) && words[index + 1] === \"build\"\n );\n return at === -1 ? [] : words.slice(at + 2);\n });\n\n/** The slice of package.json the script check reads. */\nconst scriptsSchema = z.looseObject({\n scripts: z.record(z.string(), z.string()).default({}),\n});\n\nconst lineOf = (text: string, index: number): number =>\n text.slice(0, index).split(\"\\n\").length;\n\n/**\n * A finding for each package.json script that still passes a removed flag to\n * `blume build` — it would otherwise build a static site without a word.\n */\nconst scriptFindings = async (root: string): Promise<Diagnostic[]> => {\n const path = join(root, \"package.json\");\n if (!existsSync(path)) {\n return [];\n }\n const text = await readFile(path, \"utf-8\");\n const pkg = scriptsSchema.safeParse(JSON.parse(text));\n const scripts = pkg.success ? pkg.data.scripts : {};\n const scriptsAt = text.indexOf('\"scripts\"');\n return Object.entries(scripts).flatMap(([name, script]) => {\n const flags = removedBuildFlags(buildArgs(script));\n if (flags.length === 0) {\n return [];\n }\n const at = text.indexOf(JSON.stringify(name), scriptsAt);\n return [\n {\n code: \"BLUME_BUILD_FLAG_REMOVED\",\n file: path,\n line: at === -1 ? undefined : lineOf(text, at),\n message: `The \"${name}\" script passes ${flagList(flags)} to \\`blume build\\`, which Blume 2 removed.`,\n severity: \"error\",\n suggestion: removedBuildFlagsAdvice(flags),\n } satisfies Diagnostic,\n ];\n });\n};\n\n// ---------------------------------------------------------------------------\n// Removed front matter fields\n// ---------------------------------------------------------------------------\n\n/** Where a project's local content lives, as its config names it. */\ninterface ContentGlobs {\n exclude: string[];\n include: string[];\n root: string;\n}\n\nconst globFields = {\n exclude: z.array(z.string()).optional(),\n include: z.array(z.string()).optional(),\n root: z.string().optional(),\n};\n\n/**\n * A filesystem source in either form a config being upgraded may hold: the\n * `filesystem()` descriptor, or Blume 1's `{ type: \"filesystem\" }` object.\n */\nconst rawFilesystemSourceSchema = z.union([\n z\n .looseObject({\n kind: z.literal(\"filesystem\"),\n options: z.looseObject(globFields).default({}),\n })\n .transform((source) => source.options),\n z.looseObject({ type: z.literal(\"filesystem\"), ...globFields }),\n]);\n\n/** The `content` slice of a config as written, before validation. */\nconst rawContentSchema = z.looseObject({\n content: z\n .looseObject({ ...globFields, sources: z.array(z.unknown()).optional() })\n .optional(),\n});\n\n/**\n * The content roots to scan, read from the config as written rather than the\n * validated config: the upgrade runs on configs that don't validate yet. Its\n * filesystem sources when it lists any, else the `content.root` shorthand,\n * else the zero-config `docs` folder.\n */\nexport const contentGlobs = ({\n content,\n}: z.infer<typeof rawContentSchema>): ContentGlobs[] => {\n const sources = (content?.sources ?? []).flatMap((source) => {\n const filesystem = rawFilesystemSourceSchema.safeParse(source);\n return filesystem.success ? [filesystem.data] : [];\n });\n return (sources.length > 0 ? sources : [content ?? {}]).map((globs) => ({\n exclude: globs.exclude ?? DEFAULT_CONTENT_EXCLUDE,\n include: globs.include ?? DEFAULT_CONTENT_INCLUDE,\n root: globs.root ?? \"docs\",\n }));\n};\n\n/** The content roots the project's config names, loaded as written. */\nconst configContentGlobs = async (root: string): Promise<ContentGlobs[]> => {\n const file = findConfigFile(root);\n let raw: z.infer<typeof rawContentSchema> = {};\n if (file) {\n try {\n const parsed = rawContentSchema.safeParse(\n await createModuleLoader()(file)\n );\n raw = parsed.success ? parsed.data : {};\n } catch {\n // A config that won't load is already reported by the config check;\n // its content still gets the zero-config scan.\n }\n }\n return contentGlobs(raw);\n};\n\nconst isPathKey = (segment: PropertyKey): segment is number | string =>\n typeof segment !== \"symbol\";\n\n// Zod's own wording for an unknown key; a removed field's hint replaces it.\nconst PLAIN_UNKNOWN_KEY = \"Unrecognized key\";\n\n/**\n * The removed fields among one page's front matter issues, each at its own\n * line. Only issues the page schema words as a removed-field hint count: a\n * key a project declares through `frontmatter.extend` is unknown to the bare\n * schema, and any other invalid value was already an error on Blume 1.\n */\nconst removedFieldFindings = (\n file: string,\n text: string,\n issues: readonly z.core.$ZodIssue[]\n): Diagnostic[] =>\n issues.flatMap((issue) => {\n if (\n issue.code !== \"unrecognized_keys\" ||\n issue.message.startsWith(PLAIN_UNKNOWN_KEY)\n ) {\n return [];\n }\n const path = issue.path.filter(isPathKey);\n const position =\n locateFrontmatterKey(text, [...path, ...issue.keys.slice(0, 1)]) ??\n locateFrontmatterKey(text, path);\n return [\n {\n code: \"BLUME_FRONTMATTER_INVALID\",\n column: position?.column,\n file,\n line: position?.line,\n message: issue.message,\n severity: \"error\",\n } satisfies Diagnostic,\n ];\n });\n\n/** The removed front matter fields in one page, or none it can't parse. */\nconst pageFindings = async (file: string): Promise<Diagnostic[]> => {\n const text = await readFile(file, \"utf-8\");\n let parsed: ReturnType<typeof pageMetaSchema.safeParse>;\n try {\n parsed = pageMetaSchema.safeParse(matter(text).data);\n } catch {\n // Unparseable front matter fails the build with its own diagnostic.\n return [];\n }\n return parsed.success\n ? []\n : removedFieldFindings(file, text, parsed.error.issues);\n};\n\n/**\n * A finding for each front matter field Blume 2 removed, across the project's\n * local content. A page with one fails validation and drops out of the build,\n * which the config and components checks alone would report as ready.\n */\nconst frontmatterFindings = async (root: string): Promise<Diagnostic[]> => {\n const roots = await configContentGlobs(root);\n const matches = await Promise.all(\n roots.map((globs) => {\n const cwd = resolve(root, globs.root);\n return existsSync(cwd)\n ? glob(globs.include, {\n absolute: true,\n cwd,\n ignore: [...globs.exclude, ...baselineScanIgnore()],\n onlyFiles: true,\n })\n : [];\n })\n );\n // Two sources can share a folder; each page is checked once.\n const files = [...new Set(matches.flat())].toSorted();\n const findings = await Promise.all(files.map(pageFindings));\n return findings.flat();\n};\n\n/**\n * Run one check, turning the {@link BlumeError} it throws into its findings:\n * each config issue or `components.ts` entry on its own, or the one diagnostic\n * any other check has.\n */\nconst findingsOf = async (\n check: () => Promise<void>\n): Promise<Diagnostic[]> => {\n try {\n await check();\n return [];\n } catch (error) {\n if (\n error instanceof ConfigValidationError ||\n error instanceof ComponentOverridesError\n ) {\n return error.issues;\n }\n if (error instanceof BlumeError) {\n return [error.diagnostic];\n }\n throw error;\n }\n};\n\n/**\n * Check a project against this version of Blume: its config through the\n * loader (every invalid field, each with its line and replacement), its\n * `components.ts` through the static override planner (each entry at its\n * line), its pages' front matter for removed fields, and its package.json\n * scripts for `blume build` flags that no longer exist. Returns the failures;\n * an empty list means the project is ready.\n */\nexport const collectUpgradeFindings = async (\n root: string\n): Promise<Diagnostic[]> => {\n const componentsFile = findComponentsFile(root);\n return [\n ...(await findingsOf(async () => {\n await loadConfig(root);\n })),\n ...(componentsFile\n ? await findingsOf(async () => {\n analyzeComponentOverrides(\n await readFile(componentsFile, \"utf-8\"),\n componentsFile\n );\n })\n : []),\n ...(await frontmatterFindings(root)),\n ...(await scriptFindings(root)),\n ];\n};\n\n/** A finding as plain text for the agent: where, what, and the fix. */\nconst plainFinding = (finding: Diagnostic, root: string): string => {\n const where = finding.file\n ? `${relative(root, finding.file)}${finding.line === undefined ? \"\" : `:${finding.line}`}`\n : \"blume.config.ts\";\n const fix = finding.suggestion ? `\\n Fix: ${finding.suggestion}` : \"\";\n return `- ${where} (${finding.code})\\n ${finding.message.replaceAll(\"\\n\", \"\\n \")}${fix}`;\n};\n\n/**\n * The handoff prompt: the findings inline (they're few, and each already\n * names its replacement), where the guide is, and the ground rules — change\n * the config's shape, never the site's behavior, and verify with the site's\n * own checks, run through the project's package runner (`npx`, `pnpm exec`),\n * since a local install puts no `blume` on PATH.\n */\nexport const upgradePrompt = (options: {\n findings: Diagnostic[];\n guidePath: string;\n root: string;\n runner: string;\n version: string;\n}): string =>\n `Upgrade this Blume project to Blume ${options.version}. \\`blume upgrade\\` has bumped the \\`blume\\` dependency in package.json where it could; what's left is the config.\n\nThe upgrade guide is at ${options.guidePath} (online at ${UPGRADE_GUIDE_URL}). It covers every change between Blume 1 and 2 with before-and-after examples: search, deployment, content sources, API references, analytics, and Ask AI become adapters imported from \\`blume/*\\` subpaths; the machine-readable settings move from \\`ai\\` to \\`agents\\`; and \\`components.ts\\` entries must be one of the static forms.\n\n\\`blume upgrade\\` found:\n\n${options.findings.map((finding) => plainFinding(finding, options.root)).join(\"\\n\\n\")}\n\nWork through every finding:\n1. Read the guide section for each change before editing.\n2. Rewrite the config to the Blume 2 form, adding the adapter imports it needs. Keep the site's behavior the same: the same content, routes, search backend, deployment target, analytics, and credentials (keep reading secrets from the same environment variables).\n3. The config loader reports every invalid field at once, but some changes only surface once earlier ones are fixed, so rerun \\`${options.runner} blume doctor\\` after each round of edits.\n4. Never delete content, or remove a setting only to silence an error; if something needs a human decision, leave it and say so in your summary.\n\nWhen \\`${options.runner} blume doctor\\` reports no errors, run \\`${options.runner} blume build\\` and fix anything it reports until it succeeds.`;\n"
6
- ],
7
- "mappings": "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",
8
- "debugId": "BBD151C21AC6C49164756E2164756E21",
9
- "names": []
10
- }
@@ -1,10 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/cli/eject-scripts.ts"],
4
- "sourcesContent": [
5
- "import { existsSync, readFileSync } from \"node:fs\";\nimport { readFile, writeFile } from \"node:fs/promises\";\n\nimport { join } from \"pathe\";\n\nimport { EJECTED_CONFIG_HEADER } from \"../astro/templates.ts\";\nimport { packageRoot } from \"../core/package-root.ts\";\nimport { commandsFor, detectProjectPackageManager } from \"./init/scaffold.ts\";\nimport { logger } from \"./log.ts\";\n\n/** A JSON value, as `JSON.parse` of a manifest can return. */\ntype JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonValue[]\n | { [key: string]: JsonValue };\n\n/** The slice of package.json the rewrite touches; the rest rides along. */\ninterface PackageManifest {\n [key: string]: JsonValue | undefined;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n optionalDependencies?: Record<string, string>;\n peerDependencies?: Record<string, string>;\n scripts?: Record<string, string>;\n}\n\n/** Package name → the semver range to depend on it at. */\nexport interface DependencyRanges {\n [name: string]: string;\n}\n\n/**\n * The version range Blume itself uses for each package it depends on or\n * accepts as a peer, plus `blume` at the installed version — what an ejected\n * app should pin so it keeps building against the same majors.\n */\nexport const blumeDependencyRanges = (): DependencyRanges => {\n // SAFETY: blume's own package.json, which declares its version and both\n // dependency maps; nothing else is read.\n const manifest = JSON.parse(\n readFileSync(join(packageRoot(), \"package.json\"), \"utf-8\")\n ) as {\n dependencies: Record<string, string>;\n peerDependencies: Record<string, string>;\n version: string;\n };\n return {\n ...manifest.peerDependencies,\n ...manifest.dependencies,\n blume: `^${manifest.version}`,\n };\n};\n\n/** Whether the manifest already lists `name` in any dependency field. */\nconst declares = (pkg: PackageManifest, name: string): boolean =>\n [\n pkg.dependencies,\n pkg.devDependencies,\n pkg.optionalDependencies,\n pkg.peerDependencies,\n ].some((field) => field !== undefined && Object.hasOwn(field, name));\n\n/**\n * Point the project's package.json at the ejected app. Its scripts run Astro\n * directly: after an eject the Blume CLI no longer manages the runtime, so\n * scaffolded scripts like `\"dev\": \"blume dev\"` would rebuild the removed\n * `.blume` tree instead of serving the ejected app. And every package the\n * ejected app imports by bare name (`dependencies`, from `eject()`) that the\n * project doesn't list yet becomes a dependency at the range Blume itself\n * uses (`ranges`).\n *\n * Returns the names it added, so the caller can ask for an install. A missing\n * or unreadable package.json is left alone.\n */\nexport const updatePackageScripts = async (\n root: string,\n dependencies: string[] = [],\n ranges: DependencyRanges = blumeDependencyRanges()\n): Promise<string[]> => {\n const pkgPath = join(root, \"package.json\");\n let pkg: PackageManifest;\n try {\n pkg = JSON.parse(await readFile(pkgPath, \"utf-8\"));\n } catch {\n return [];\n }\n const scripts = pkg.scripts ?? {};\n pkg.scripts = {\n ...scripts,\n build: \"astro build\",\n dev: \"astro dev\",\n preview: \"astro preview\",\n };\n const added = dependencies.filter((name) => !declares(pkg, name));\n if (added.length > 0) {\n // Sorted, the way package managers write the field.\n pkg.dependencies = Object.fromEntries(\n Object.entries({\n ...pkg.dependencies,\n ...Object.fromEntries(added.map((name) => [name, ranges[name] ?? \"*\"])),\n }).toSorted(([a], [b]) => (a < b ? -1 : 1))\n );\n }\n await writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`, \"utf-8\");\n return added;\n};\n\n/**\n * The first line Blume 1's `blume eject` wrote into the ejected\n * `astro.config.mjs` — the hidden runtime's own header, carried over. Blume 2's\n * hidden runtime keeps it under `.blume/`, never at the project root, so a root\n * config that opens with it is an app Blume 1 ejected.\n */\nconst BLUME_1_EJECTED_CONFIG_HEADER =\n \"// Generated by Blume. Do not edit; this file is recreated on each run.\";\n\n/**\n * Whether `root` holds an app `blume eject` already wrote: a root\n * `astro.config.mjs` whose first line is eject's header, or the header Blume 1\n * ejected with. The header is the only signal: a Blume project may keep its own\n * `astro.config.mjs` and a codegen `src/generated/` folder beside its content,\n * and neither makes it an ejected app.\n */\nexport const isEjectedProject = (root: string): boolean => {\n const config = join(root, \"astro.config.mjs\");\n if (!existsSync(config)) {\n return false;\n }\n const source = readFileSync(config, \"utf-8\");\n return (\n source.startsWith(EJECTED_CONFIG_HEADER) ||\n source.startsWith(BLUME_1_EJECTED_CONFIG_HEADER)\n );\n};\n\n/**\n * Stop `blume dev`/`blume build` in an ejected app. Either would regenerate\n * `.blume/` from `blume.config.ts` and run that hidden copy, ignoring every\n * edit made to the app since eject, so point at the app's own script under\n * the project's package manager instead.\n */\nexport const refuseIfEjected = async (\n root: string,\n script: \"build\" | \"dev\"\n): Promise<void> => {\n if (!isEjectedProject(root)) {\n return;\n }\n const commands = commandsFor(await detectProjectPackageManager(root));\n logger.error(\n `This project was ejected to a standalone Astro app, so \\`blume ${script}\\` would run a regenerated copy of the site instead of it. Run \\`${commands[script]}\\` instead.`\n );\n process.exit(1);\n};\n"
6
- ],
7
- "mappings": ";;;;;;;;;;;;;;AAAA;AACA;AAEA;AAoCO,IAAM,wBAAwB,MAAwB;AAAA,EAG3D,MAAM,WAAW,KAAK,MACpB,aAAa,KAAK,YAAY,GAAG,cAAc,GAAG,OAAO,CAC3D;AAAA,EAKA,OAAO;AAAA,OACF,SAAS;AAAA,OACT,SAAS;AAAA,IACZ,OAAO,IAAI,SAAS;AAAA,EACtB;AAAA;AAIF,IAAM,WAAW,CAAC,KAAsB,SACtC;AAAA,EACE,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN,EAAE,KAAK,CAAC,UAAU,UAAU,aAAa,OAAO,OAAO,OAAO,IAAI,CAAC;AAc9D,IAAM,uBAAuB,OAClC,MACA,eAAyB,CAAC,GAC1B,SAA2B,sBAAsB,MAC3B;AAAA,EACtB,MAAM,UAAU,KAAK,MAAM,cAAc;AAAA,EACzC,IAAI;AAAA,EACJ,IAAI;AAAA,IACF,MAAM,KAAK,MAAM,MAAM,SAAS,SAAS,OAAO,CAAC;AAAA,IACjD,MAAM;AAAA,IACN,OAAO,CAAC;AAAA;AAAA,EAEV,MAAM,UAAU,IAAI,WAAW,CAAC;AAAA,EAChC,IAAI,UAAU;AAAA,OACT;AAAA,IACH,OAAO;AAAA,IACP,KAAK;AAAA,IACL,SAAS;AAAA,EACX;AAAA,EACA,MAAM,QAAQ,aAAa,OAAO,CAAC,SAAS,CAAC,SAAS,KAAK,IAAI,CAAC;AAAA,EAChE,IAAI,MAAM,SAAS,GAAG;AAAA,IAEpB,IAAI,eAAe,OAAO,YACxB,OAAO,QAAQ;AAAA,SACV,IAAI;AAAA,SACJ,OAAO,YAAY,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,OAAO,SAAS,GAAG,CAAC,CAAC;AAAA,IACxE,CAAC,EAAE,SAAS,EAAE,KAAK,OAAQ,IAAI,IAAI,KAAK,CAAE,CAC5C;AAAA,EACF;AAAA,EACA,MAAM,UAAU,SAAS,GAAG,KAAK,UAAU,KAAK,MAAM,CAAC;AAAA,GAAO,OAAO;AAAA,EACrE,OAAO;AAAA;AAST,IAAM,gCACJ;AASK,IAAM,mBAAmB,CAAC,SAA0B;AAAA,EACzD,MAAM,SAAS,KAAK,MAAM,kBAAkB;AAAA,EAC5C,IAAI,CAAC,WAAW,MAAM,GAAG;AAAA,IACvB,OAAO;AAAA,EACT;AAAA,EACA,MAAM,SAAS,aAAa,QAAQ,OAAO;AAAA,EAC3C,OACE,OAAO,WAAW,qBAAqB,KACvC,OAAO,WAAW,6BAA6B;AAAA;AAU5C,IAAM,kBAAkB,OAC7B,MACA,WACkB;AAAA,EAClB,IAAI,CAAC,iBAAiB,IAAI,GAAG;AAAA,IAC3B;AAAA,EACF;AAAA,EACA,MAAM,WAAW,YAAY,MAAM,4BAA4B,IAAI,CAAC;AAAA,EACpE,OAAO,MACL,kEAAkE,0EAA0E,SAAS,oBACvJ;AAAA,EACA,QAAQ,KAAK,CAAC;AAAA;",
8
- "debugId": "5D0834602D1CB35B64756E2164756E21",
9
- "names": []
10
- }
@@ -1,11 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/cli/report-format.ts", "../src/eval/agents.ts"],
4
- "sourcesContent": [
5
- "/**\n * Number formatting shared by the translate and eval report renderers. The\n * `1.5s` / `4m 12s` shapes are asserted in both suites — change them in both\n * minds at once.\n */\n\n/** Milliseconds as `1.5s`. */\nexport const seconds = (ms: number): string => `${(ms / 1000).toFixed(1)}s`;\n\n/** A dollar cost as `$1.23`, or nothing when the run reported none. */\nexport const money = (cost: number | undefined): string =>\n cost === undefined ? \"\" : `$${cost.toFixed(2)}`;\n\n/** Milliseconds as `12.3s` under a minute, `4m 12s` from there up. */\nexport const duration = (ms: number): string => {\n if (ms < 60_000) {\n return seconds(ms);\n }\n const minutes = Math.floor(ms / 60_000);\n const rest = Math.round((ms % 60_000) / 1000);\n return `${minutes}m ${rest}s`;\n};\n",
6
- "import { readFile, writeFile } from \"node:fs/promises\";\n\nimport spawn from \"cross-spawn\";\nimport { join } from \"pathe\";\nimport { z } from \"zod\";\n\nimport type { AgentKind } from \"../audit/agent.ts\";\n\n/** How long the SIGTERM on timeout gets to work before SIGKILL follows. */\nconst KILL_GRACE_MS = 5000;\n\n/** The MCP tools a reader run may use — nothing else. */\nexport const MCP_TOOL_NAMES = [\n \"search_docs\",\n \"get_page\",\n \"list_pages\",\n \"get_navigation\",\n] as const;\n\n/** The MCP server name in the generated config; tool ids derive from it. */\nconst MCP_SERVER_NAME = \"docs\";\n\n/**\n * Claude Code built-ins that would let the agent escape the docs-only\n * sandbox: the reader must not read the repo, run commands, or search the\n * web — it sees the documentation the way a stranger does, through MCP.\n */\nexport const DISALLOWED_TOOLS = [\n \"Bash\",\n \"Read\",\n \"Glob\",\n \"Grep\",\n \"Write\",\n \"Edit\",\n \"NotebookEdit\",\n \"WebFetch\",\n \"WebSearch\",\n \"Task\",\n];\n\n/** The captured outcome of one headless agent invocation. */\nexport interface HeadlessResult {\n code: number;\n stderr: string;\n stdout: string;\n timedOut: boolean;\n}\n\nexport interface HeadlessOptions {\n cwd: string;\n /** How long the SIGTERM gets before SIGKILL follows (default 5s). */\n killGraceMs?: number;\n prompt: string;\n timeoutMs: number;\n}\n\n/**\n * Run an agent CLI headlessly: prompt over stdin (dodging argv limits and\n * cmd.exe newline quoting alike), stdout and stderr captured, SIGTERM at the\n * deadline with a SIGKILL follow-up. Resolves with the captured result;\n * rejects only when the executable cannot be spawned at all (ENOENT).\n *\n * Spawned through cross-spawn, which runs npm's `.cmd` shims on Windows with\n * its own cmd.exe escaping — under the old `shell: true` launch, cmd.exe was\n * free to mangle the JSON carried by codex's `-c` arguments, and a missing\n * executable surfaced as exit code 9009 instead of an ENOENT rejection.\n * execa was evaluated and rejected here: it waits for the stdio streams to\n * close, and the `exit`-beats-`close` resolution below exists precisely\n * because a killed agent's own children (an MCP server, a shell) hold the\n * pipes open past the SIGTERM.\n */\nexport const runAgentHeadless = (\n bin: string,\n args: string[],\n options: HeadlessOptions\n): Promise<HeadlessResult> =>\n // oxlint-disable-next-line promise/avoid-new -- adapt spawn's event callbacks\n new Promise((resolve, reject) => {\n const child = spawn(bin, args, {\n cwd: options.cwd,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n let timedOut = false;\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n stdout += chunk.toString(\"utf-8\");\n });\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n stderr += chunk.toString(\"utf-8\");\n });\n\n let hardKill: ReturnType<typeof setTimeout> | undefined;\n const deadline = setTimeout(() => {\n timedOut = true;\n child.kill(\"SIGTERM\");\n hardKill = setTimeout(\n () => child.kill(\"SIGKILL\"),\n options.killGraceMs ?? KILL_GRACE_MS\n );\n hardKill.unref();\n }, options.timeoutMs);\n deadline.unref();\n\n child.once(\"error\", (error) => {\n clearTimeout(deadline);\n reject(error);\n });\n child.once(\"close\", (code) => {\n clearTimeout(deadline);\n resolve({ code: code ?? 1, stderr, stdout, timedOut });\n });\n // `close` waits for the stdio pipes, which a killed agent's own children\n // (an MCP server, a shell) can hold open past the SIGTERM. A timed-out\n // run's output is discarded anyway, so the process dying is enough.\n child.once(\"exit\", (code) => {\n // Once the process is gone the SIGKILL follow-up has nothing to do, and\n // firing it later could signal an unrelated process that reused the pid.\n clearTimeout(hardKill);\n if (timedOut) {\n clearTimeout(deadline);\n resolve({ code: code ?? 1, stderr, stdout, timedOut });\n }\n });\n\n child.stdin?.end(options.prompt);\n });\n\n/** The spawn signature `runEval` accepts, injectable for tests. */\nexport type HeadlessRunner = typeof runAgentHeadless;\n\n/** How the eval reaches the MCP stdio bridge from a spawned agent. */\nexport interface McpLaunch {\n /** The generated MCP config file (claude's `--mcp-config`). */\n configPath: string;\n /** argv for the bridge process (codex's `-c mcp_servers` override). */\n serverArgs: string[];\n /** The executable launching the bridge. */\n serverCommand: string;\n}\n\n/**\n * Write the MCP config a reader run points its agent CLI at. The bridge is\n * this same CLI relaunched (`blume mcp-stdio`), which resolves correctly from\n * both a source checkout (bun + src/cli/index.ts) and an installed package\n * (node + bin/blume.mjs).\n */\nexport const writeMcpConfig = async (\n dir: string,\n snapshotPath: string,\n launcher?: { args: string[]; command: string }\n): Promise<McpLaunch> => {\n const resolved = launcher ?? {\n args: [process.argv[1] ?? \"\", \"mcp-stdio\", \"--data\", snapshotPath],\n command: process.execPath,\n };\n const configPath = join(dir, \"mcp-config.json\");\n const config = {\n mcpServers: {\n [MCP_SERVER_NAME]: { args: resolved.args, command: resolved.command },\n },\n };\n await writeFile(configPath, JSON.stringify(config, null, 2));\n return {\n configPath,\n serverArgs: resolved.args,\n serverCommand: resolved.command,\n };\n};\n\nexport interface InvocationContext {\n /** Where codex writes its final message; unused by claude. */\n lastMessagePath: string;\n /** The MCP bridge for reader runs; omitted for the judge. */\n mcp?: McpLaunch;\n}\n\nconst CLAUDE_READER_MAX_TURNS = \"25\";\nconst CLAUDE_JUDGE_MAX_TURNS = \"1\";\n\nconst claudeArgs = (context: InvocationContext): string[] => {\n const base = [\"-p\", \"--output-format\", \"json\", \"--strict-mcp-config\"];\n if (context.mcp) {\n const allowed = MCP_TOOL_NAMES.map(\n (tool) => `mcp__${MCP_SERVER_NAME}__${tool}`\n ).join(\",\");\n return [\n ...base,\n \"--mcp-config\",\n context.mcp.configPath,\n \"--allowedTools\",\n allowed,\n \"--disallowedTools\",\n DISALLOWED_TOOLS.join(\",\"),\n \"--max-turns\",\n CLAUDE_READER_MAX_TURNS,\n ];\n }\n return [\n ...base,\n \"--disallowedTools\",\n DISALLOWED_TOOLS.join(\",\"),\n \"--max-turns\",\n CLAUDE_JUDGE_MAX_TURNS,\n ];\n};\n\n/**\n * Codex config overrides that leave an eval run nothing but its prompt and the\n * docs MCP tools. `--sandbox read-only` blocks writes and the network but still\n * lets the shell tool read any file the user can, and the docs a reader\n * searches — or the answer a judge grades — can come from a remote source, so\n * an instruction planted there could otherwise pull a local secret into the\n * transcript sent to the model provider. With both command tools and the\n * local-image tool off, and any subprocess started with no inherited\n * environment, there is nothing on the machine for a run to read.\n */\nconst CODEX_LOCKDOWN = [\n \"-c\",\n \"features.shell_tool=false\",\n \"-c\",\n \"features.unified_exec=false\",\n \"-c\",\n \"tools.view_image=false\",\n \"-c\",\n 'shell_environment_policy.inherit=\"none\"',\n];\n\nconst codexArgs = (context: InvocationContext): string[] => {\n const base = [\n \"exec\",\n \"--skip-git-repo-check\",\n \"--ignore-user-config\",\n \"--ephemeral\",\n \"--sandbox\",\n \"read-only\",\n ...CODEX_LOCKDOWN,\n \"--output-last-message\",\n context.lastMessagePath,\n ];\n if (context.mcp) {\n // `-c` values parse as TOML; JSON string/array literals are valid TOML\n // values, so JSON.stringify produces exactly the quoting codex expects.\n return [\n ...base,\n \"-c\",\n `mcp_servers.${MCP_SERVER_NAME}.command=${JSON.stringify(\n context.mcp.serverCommand\n )}`,\n \"-c\",\n `mcp_servers.${MCP_SERVER_NAME}.args=${JSON.stringify(\n context.mcp.serverArgs\n )}`,\n \"-\",\n ];\n }\n return [...base, \"-\"];\n};\n\n/** Build the argv for one headless run; pass `mcp` for the reader role. */\nexport const agentArgs = (\n kind: AgentKind,\n context: InvocationContext\n): string[] => (kind === \"claude\" ? claudeArgs(context) : codexArgs(context));\n\n/** What one headless run produced, normalized across agent CLIs. */\nexport interface AgentOutput {\n costUsd?: number;\n detail?: string;\n isError: boolean;\n text: string;\n}\n\nconst claudeResultSchema = z.object({\n is_error: z.boolean().default(false),\n result: z.string().default(\"\"),\n total_cost_usd: z.number().optional(),\n});\n\nconst tail = (value: string, max = 300): string => {\n const trimmed = value.trim();\n return trimmed.length > max ? trimmed.slice(-max) : trimmed;\n};\n\n/**\n * Normalize a finished run into the answer text. Claude prints one JSON\n * object on stdout (`--output-format json`); codex writes the final message\n * to `--output-last-message` because its stdout interleaves progress.\n */\nexport const readAgentOutput = async (\n kind: AgentKind,\n result: HeadlessResult,\n lastMessagePath: string\n): Promise<AgentOutput> => {\n if (result.timedOut) {\n return { detail: \"timed out\", isError: true, text: \"\" };\n }\n if (result.code !== 0) {\n return {\n detail: tail(result.stderr) || `exited with code ${result.code}`,\n isError: true,\n text: \"\",\n };\n }\n\n if (kind === \"claude\") {\n let parsed: unknown;\n try {\n parsed = JSON.parse(result.stdout);\n } catch {\n return {\n detail: \"unparseable --output-format json payload\",\n isError: true,\n text: \"\",\n };\n }\n const payload = claudeResultSchema.safeParse(parsed);\n if (!payload.success) {\n return {\n detail: \"unexpected --output-format json shape\",\n isError: true,\n text: \"\",\n };\n }\n return {\n costUsd: payload.data.total_cost_usd,\n isError: payload.data.is_error,\n text: payload.data.result,\n };\n }\n\n let text: string;\n try {\n const raw = await readFile(lastMessagePath, \"utf-8\");\n text = raw.trim();\n } catch {\n return { detail: \"no last message written\", isError: true, text: \"\" };\n }\n if (text === \"\") {\n return { detail: \"empty last message\", isError: true, text: \"\" };\n }\n return { isError: false, text };\n};\n\nconst verdictSchema = z.object({\n missing: z.array(z.string()).default([]),\n notes: z.string().default(\"\"),\n pass: z.boolean(),\n score: z.number().min(0).max(1).optional(),\n});\n\n/** The judge's grade for one answer. */\nexport type Verdict = z.infer<typeof verdictSchema>;\n\n/**\n * Extract the verdict JSON from a judge reply. Tolerates markdown fences and\n * surrounding prose; returns undefined when no valid verdict can be found.\n */\nexport const parseVerdict = (text: string): Verdict | undefined => {\n const start = text.indexOf(\"{\");\n const end = text.lastIndexOf(\"}\");\n if (start === -1 || end <= start) {\n return;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(text.slice(start, end + 1));\n } catch {\n return;\n }\n const result = verdictSchema.safeParse(parsed);\n return result.success ? result.data : undefined;\n};\n"
7
- ],
8
- "mappings": ";;;AAOO,IAAM,UAAU,CAAC,OAAuB,IAAI,KAAK,MAAM,QAAQ,CAAC;AAGhE,IAAM,QAAQ,CAAC,SACpB,SAAS,YAAY,KAAK,IAAI,KAAK,QAAQ,CAAC;AAGvC,IAAM,WAAW,CAAC,OAAuB;AAAA,EAC9C,IAAI,KAAK,OAAQ;AAAA,IACf,OAAO,QAAQ,EAAE;AAAA,EACnB;AAAA,EACA,MAAM,UAAU,KAAK,MAAM,KAAK,KAAM;AAAA,EACtC,MAAM,OAAO,KAAK,MAAO,KAAK,QAAU,IAAI;AAAA,EAC5C,OAAO,GAAG,YAAY;AAAA;;;ACpBxB;AAEA;AACA;AACA;AAKA,IAAM,gBAAgB;AAGf,IAAM,iBAAiB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGA,IAAM,kBAAkB;AAOjB,IAAM,mBAAmB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAiCO,IAAM,mBAAmB,CAC9B,KACA,MACA,YAGA,IAAI,QAAQ,CAAC,SAAS,WAAW;AAAA,EAC/B,MAAM,QAAQ,MAAM,KAAK,MAAM;AAAA,IAC7B,KAAK,QAAQ;AAAA,IACb,OAAO,CAAC,QAAQ,QAAQ,MAAM;AAAA,EAChC,CAAC;AAAA,EAED,IAAI,SAAS;AAAA,EACb,IAAI,SAAS;AAAA,EACb,IAAI,WAAW;AAAA,EACf,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAkB;AAAA,IAC1C,UAAU,MAAM,SAAS,OAAO;AAAA,GACjC;AAAA,EACD,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAkB;AAAA,IAC1C,UAAU,MAAM,SAAS,OAAO;AAAA,GACjC;AAAA,EAED,IAAI;AAAA,EACJ,MAAM,WAAW,WAAW,MAAM;AAAA,IAChC,WAAW;AAAA,IACX,MAAM,KAAK,SAAS;AAAA,IACpB,WAAW,WACT,MAAM,MAAM,KAAK,SAAS,GAC1B,QAAQ,eAAe,aACzB;AAAA,IACA,SAAS,MAAM;AAAA,KACd,QAAQ,SAAS;AAAA,EACpB,SAAS,MAAM;AAAA,EAEf,MAAM,KAAK,SAAS,CAAC,UAAU;AAAA,IAC7B,aAAa,QAAQ;AAAA,IACrB,OAAO,KAAK;AAAA,GACb;AAAA,EACD,MAAM,KAAK,SAAS,CAAC,SAAS;AAAA,IAC5B,aAAa,QAAQ;AAAA,IACrB,QAAQ,EAAE,MAAM,QAAQ,GAAG,QAAQ,QAAQ,SAAS,CAAC;AAAA,GACtD;AAAA,EAID,MAAM,KAAK,QAAQ,CAAC,SAAS;AAAA,IAG3B,aAAa,QAAQ;AAAA,IACrB,IAAI,UAAU;AAAA,MACZ,aAAa,QAAQ;AAAA,MACrB,QAAQ,EAAE,MAAM,QAAQ,GAAG,QAAQ,QAAQ,SAAS,CAAC;AAAA,IACvD;AAAA,GACD;AAAA,EAED,MAAM,OAAO,IAAI,QAAQ,MAAM;AAAA,CAChC;AAqBI,IAAM,iBAAiB,OAC5B,KACA,cACA,aACuB;AAAA,EACvB,MAAM,WAAW,YAAY;AAAA,IAC3B,MAAM,CAAC,QAAQ,KAAK,MAAM,IAAI,aAAa,UAAU,YAAY;AAAA,IACjE,SAAS,QAAQ;AAAA,EACnB;AAAA,EACA,MAAM,aAAa,KAAK,KAAK,iBAAiB;AAAA,EAC9C,MAAM,SAAS;AAAA,IACb,YAAY;AAAA,OACT,kBAAkB,EAAE,MAAM,SAAS,MAAM,SAAS,SAAS,QAAQ;AAAA,IACtE;AAAA,EACF;AAAA,EACA,MAAM,UAAU,YAAY,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,EAC3D,OAAO;AAAA,IACL;AAAA,IACA,YAAY,SAAS;AAAA,IACrB,eAAe,SAAS;AAAA,EAC1B;AAAA;AAUF,IAAM,0BAA0B;AAChC,IAAM,yBAAyB;AAE/B,IAAM,aAAa,CAAC,YAAyC;AAAA,EAC3D,MAAM,OAAO,CAAC,MAAM,mBAAmB,QAAQ,qBAAqB;AAAA,EACpE,IAAI,QAAQ,KAAK;AAAA,IACf,MAAM,UAAU,eAAe,IAC7B,CAAC,SAAS,QAAQ,oBAAoB,MACxC,EAAE,KAAK,GAAG;AAAA,IACV,OAAO;AAAA,MACL,GAAG;AAAA,MACH;AAAA,MACA,QAAQ,IAAI;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA,iBAAiB,KAAK,GAAG;AAAA,MACzB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA,OAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,IACA,iBAAiB,KAAK,GAAG;AAAA,IACzB;AAAA,IACA;AAAA,EACF;AAAA;AAaF,IAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,YAAY,CAAC,YAAyC;AAAA,EAC1D,MAAM,OAAO;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH;AAAA,IACA,QAAQ;AAAA,EACV;AAAA,EACA,IAAI,QAAQ,KAAK;AAAA,IAGf,OAAO;AAAA,MACL,GAAG;AAAA,MACH;AAAA,MACA,eAAe,2BAA2B,KAAK,UAC7C,QAAQ,IAAI,aACd;AAAA,MACA;AAAA,MACA,eAAe,wBAAwB,KAAK,UAC1C,QAAQ,IAAI,UACd;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA,OAAO,CAAC,GAAG,MAAM,GAAG;AAAA;AAIf,IAAM,YAAY,CACvB,MACA,YACc,SAAS,WAAW,WAAW,OAAO,IAAI,UAAU,OAAO;AAU3E,IAAM,qBAAqB,EAAE,OAAO;AAAA,EAClC,UAAU,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,EACnC,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE;AAAA,EAC7B,gBAAgB,EAAE,OAAO,EAAE,SAAS;AACtC,CAAC;AAED,IAAM,OAAO,CAAC,OAAe,MAAM,QAAgB;AAAA,EACjD,MAAM,UAAU,MAAM,KAAK;AAAA,EAC3B,OAAO,QAAQ,SAAS,MAAM,QAAQ,MAAM,CAAC,GAAG,IAAI;AAAA;AAQ/C,IAAM,kBAAkB,OAC7B,MACA,QACA,oBACyB;AAAA,EACzB,IAAI,OAAO,UAAU;AAAA,IACnB,OAAO,EAAE,QAAQ,aAAa,SAAS,MAAM,MAAM,GAAG;AAAA,EACxD;AAAA,EACA,IAAI,OAAO,SAAS,GAAG;AAAA,IACrB,OAAO;AAAA,MACL,QAAQ,KAAK,OAAO,MAAM,KAAK,oBAAoB,OAAO;AAAA,MAC1D,SAAS;AAAA,MACT,MAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,IAAI,SAAS,UAAU;AAAA,IACrB,IAAI;AAAA,IACJ,IAAI;AAAA,MACF,SAAS,KAAK,MAAM,OAAO,MAAM;AAAA,MACjC,MAAM;AAAA,MACN,OAAO;AAAA,QACL,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,MAAM;AAAA,MACR;AAAA;AAAA,IAEF,MAAM,UAAU,mBAAmB,UAAU,MAAM;AAAA,IACnD,IAAI,CAAC,QAAQ,SAAS;AAAA,MACpB,OAAO;AAAA,QACL,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,OAAO;AAAA,MACL,SAAS,QAAQ,KAAK;AAAA,MACtB,SAAS,QAAQ,KAAK;AAAA,MACtB,MAAM,QAAQ,KAAK;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,IAAI;AAAA,EACJ,IAAI;AAAA,IACF,MAAM,MAAM,MAAM,SAAS,iBAAiB,OAAO;AAAA,IACnD,OAAO,IAAI,KAAK;AAAA,IAChB,MAAM;AAAA,IACN,OAAO,EAAE,QAAQ,2BAA2B,SAAS,MAAM,MAAM,GAAG;AAAA;AAAA,EAEtE,IAAI,SAAS,IAAI;AAAA,IACf,OAAO,EAAE,QAAQ,sBAAsB,SAAS,MAAM,MAAM,GAAG;AAAA,EACjE;AAAA,EACA,OAAO,EAAE,SAAS,OAAO,KAAK;AAAA;AAGhC,IAAM,gBAAgB,EAAE,OAAO;AAAA,EAC7B,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA,EACvC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE;AAAA,EAC5B,MAAM,EAAE,QAAQ;AAAA,EAChB,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,SAAS;AAC3C,CAAC;AASM,IAAM,eAAe,CAAC,SAAsC;AAAA,EACjE,MAAM,QAAQ,KAAK,QAAQ,GAAG;AAAA,EAC9B,MAAM,MAAM,KAAK,YAAY,GAAG;AAAA,EAChC,IAAI,UAAU,MAAM,OAAO,OAAO;AAAA,IAChC;AAAA,EACF;AAAA,EACA,IAAI;AAAA,EACJ,IAAI;AAAA,IACF,SAAS,KAAK,MAAM,KAAK,MAAM,OAAO,MAAM,CAAC,CAAC;AAAA,IAC9C,MAAM;AAAA,IACN;AAAA;AAAA,EAEF,MAAM,SAAS,cAAc,UAAU,MAAM;AAAA,EAC7C,OAAO,OAAO,UAAU,OAAO,OAAO;AAAA;",
9
- "debugId": "FE1BEE26F1BB6F4964756E2164756E21",
10
- "names": []
11
- }