blume 2.0.1 → 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 (410) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/dist/cli/{chunk-vtk4a6dg.js → chunk-00gs3wqs.js} +1 -1
  3. package/dist/cli/{chunk-fa25z98p.js → chunk-273ygyr4.js} +4 -3
  4. package/dist/cli/chunk-273ygyr4.js.map +11 -0
  5. package/dist/cli/{chunk-yw7dm696.js → chunk-3yce002v.js} +21 -8
  6. package/dist/cli/{chunk-yw7dm696.js.map → chunk-3yce002v.js.map} +3 -3
  7. package/dist/cli/{chunk-yt5n7ppj.js → chunk-4e9b9ra6.js} +17 -7
  8. package/dist/cli/chunk-4e9b9ra6.js.map +10 -0
  9. package/dist/cli/{chunk-fs23ddbb.js → chunk-5r8g91qn.js} +352 -676
  10. package/dist/cli/chunk-5r8g91qn.js.map +34 -0
  11. package/dist/cli/{chunk-6vm74dry.js → chunk-6dtt0zfn.js} +8 -8
  12. package/dist/cli/{chunk-qwsrynx5.js → chunk-6k4ftwze.js} +38 -19
  13. package/dist/cli/{chunk-qwsrynx5.js.map → chunk-6k4ftwze.js.map} +4 -4
  14. package/dist/cli/{chunk-qs4q5p4e.js → chunk-7mbqtmgb.js} +16 -7
  15. package/dist/cli/chunk-7mbqtmgb.js.map +10 -0
  16. package/dist/cli/{chunk-q5163e60.js → chunk-7vtckvaw.js} +21 -19
  17. package/dist/cli/chunk-7vtckvaw.js.map +11 -0
  18. package/dist/cli/{chunk-kdp5q7ke.js → chunk-8g8ytmgx.js} +17 -18
  19. package/dist/cli/{chunk-kdp5q7ke.js.map → chunk-8g8ytmgx.js.map} +2 -2
  20. package/dist/cli/{chunk-epjnccmv.js → chunk-91ws1n6j.js} +18 -15
  21. package/dist/cli/chunk-91ws1n6j.js.map +10 -0
  22. package/dist/cli/{chunk-fxypxtvm.js → chunk-bbnwccaz.js} +2 -2
  23. package/dist/cli/{chunk-m3vmjgmq.js → chunk-bfwp9vp6.js} +16 -8
  24. package/dist/cli/chunk-bfwp9vp6.js.map +10 -0
  25. package/dist/cli/{chunk-f2z5v128.js → chunk-d1v5rhy0.js} +14 -15
  26. package/dist/cli/{chunk-f2z5v128.js.map → chunk-d1v5rhy0.js.map} +2 -2
  27. package/dist/cli/{chunk-6crbhc3x.js → chunk-ddndchfr.js} +21 -6
  28. package/dist/cli/chunk-ddndchfr.js.map +14 -0
  29. package/dist/cli/{chunk-ce574jw2.js → chunk-esh98wmb.js} +1 -1
  30. package/dist/cli/{chunk-zxcczpyx.js → chunk-fsmrqk8a.js} +1 -1
  31. package/dist/cli/{chunk-hdpx1tax.js → chunk-g698a744.js} +5 -5
  32. package/dist/cli/{chunk-jts8mvcz.js → chunk-gs7r695n.js} +9 -3
  33. package/dist/cli/{chunk-jts8mvcz.js.map → chunk-gs7r695n.js.map} +3 -3
  34. package/dist/cli/{chunk-zxh4d9vy.js → chunk-h2ez8dzb.js} +4 -4
  35. package/dist/cli/{chunk-5shv93fd.js → chunk-h7k3nq3v.js} +2 -2
  36. package/dist/cli/{chunk-79jhk4py.js → chunk-hqp2ajnh.js} +251 -122
  37. package/dist/cli/chunk-hqp2ajnh.js.map +35 -0
  38. package/dist/cli/{chunk-2hn4b8z7.js → chunk-hr8ne106.js} +109 -40
  39. package/dist/cli/chunk-hr8ne106.js.map +13 -0
  40. package/dist/cli/{chunk-6hsn950k.js → chunk-j85scx15.js} +62 -18
  41. package/dist/cli/chunk-j85scx15.js.map +10 -0
  42. package/dist/cli/{chunk-s1p84fyh.js → chunk-k7pj68a8.js} +63 -22
  43. package/dist/cli/chunk-k7pj68a8.js.map +11 -0
  44. package/dist/cli/{chunk-ah61y8py.js → chunk-mqc662a6.js} +2 -2
  45. package/dist/cli/{chunk-ch6g3ar0.js → chunk-n1yg3tj3.js} +4 -4
  46. package/dist/cli/{chunk-mb2919y2.js → chunk-nfcyttvj.js} +17 -6
  47. package/dist/cli/chunk-nfcyttvj.js.map +10 -0
  48. package/dist/cli/{chunk-kpf8rrjc.js → chunk-pbg5a4s3.js} +56 -24
  49. package/dist/cli/chunk-pbg5a4s3.js.map +19 -0
  50. package/dist/cli/{chunk-jwyddg7y.js → chunk-ppzjqwx2.js} +21 -14
  51. package/dist/cli/{chunk-jwyddg7y.js.map → chunk-ppzjqwx2.js.map} +4 -4
  52. package/dist/cli/{chunk-dh8cwk36.js → chunk-qkb5a8sa.js} +24 -9
  53. package/dist/cli/chunk-qkb5a8sa.js.map +10 -0
  54. package/dist/cli/{chunk-wm7js3j9.js → chunk-sqw4ekg1.js} +2 -2
  55. package/dist/cli/{chunk-qkqwkpte.js → chunk-v6ya5kcb.js} +3083 -953
  56. package/dist/cli/chunk-v6ya5kcb.js.map +189 -0
  57. package/dist/cli/{chunk-fz5wtpmh.js → chunk-w4bxdvsa.js} +18 -15
  58. package/dist/cli/chunk-w4bxdvsa.js.map +10 -0
  59. package/dist/cli/{chunk-27g6wdth.js → chunk-wdrt2k2v.js} +2 -2
  60. package/dist/cli/{chunk-wgm7m9qk.js → chunk-xh43dwgw.js} +158 -43
  61. package/dist/cli/chunk-xh43dwgw.js.map +36 -0
  62. package/dist/cli/{chunk-s6jhgk0q.js → chunk-zp79m0ts.js} +2 -2
  63. package/dist/cli/index.js +160 -35
  64. package/dist/cli/index.js.map +4 -4
  65. package/dist/types/ai/agent-surface.d.ts +32 -0
  66. package/dist/types/ai/api-catalog.d.ts +7 -1
  67. package/dist/types/ai/ask-context.d.ts +7 -0
  68. package/dist/types/ai/component-markdown.d.ts +4 -4
  69. package/dist/types/ai/relative-links.d.ts +9 -4
  70. package/dist/types/ai/static-expression.d.ts +28 -0
  71. package/dist/types/analytics/databuddy.d.ts +43 -0
  72. package/dist/types/analytics/index.d.ts +2 -0
  73. package/dist/types/analytics/schema.d.ts +14 -0
  74. package/dist/types/cli/env.d.ts +5 -0
  75. package/dist/types/cli/init/scaffold.d.ts +19 -3
  76. package/dist/types/cli/init/starter-spec.d.ts +11 -0
  77. package/dist/types/core/base-path.d.ts +9 -0
  78. package/dist/types/core/config-input.d.ts +4 -4
  79. package/dist/types/core/config.d.ts +2 -2
  80. package/dist/types/core/graph.d.ts +2 -0
  81. package/dist/types/core/i18n-ui.d.ts +31 -0
  82. package/dist/types/core/i18n.d.ts +7 -1
  83. package/dist/types/core/links.d.ts +3 -1
  84. package/dist/types/core/load-module.d.ts +10 -0
  85. package/dist/types/core/locale-links.d.ts +12 -2
  86. package/dist/types/core/meta.d.ts +5 -1
  87. package/dist/types/core/nav-diagnostics.d.ts +10 -0
  88. package/dist/types/core/navigation.d.ts +38 -0
  89. package/dist/types/core/ordering-prefix.d.ts +4 -0
  90. package/dist/types/core/safe-links.d.ts +3 -1
  91. package/dist/types/core/schema.d.ts +52 -13
  92. package/dist/types/core/sources/github-releases.d.ts +5 -0
  93. package/dist/types/core/sources/lower.d.ts +14 -2
  94. package/dist/types/core/sources/normalize.d.ts +10 -1
  95. package/dist/types/core/sources/remote.d.ts +11 -1
  96. package/dist/types/core/sources/resolve.d.ts +12 -0
  97. package/dist/types/core/sources/types.d.ts +31 -0
  98. package/dist/types/core/types.d.ts +9 -0
  99. package/dist/types/deploy/adapters/node.d.ts +5 -2
  100. package/dist/types/deploy/adapters/types.d.ts +7 -0
  101. package/dist/types/deploy/cloudflare-negotiation.d.ts +3 -2
  102. package/dist/types/deploy/headers.d.ts +31 -7
  103. package/dist/types/deploy/node-headers.d.ts +43 -8
  104. package/dist/types/deploy/platforms/netlify.d.ts +27 -2
  105. package/dist/types/deploy/platforms/node.d.ts +6 -5
  106. package/dist/types/deploy/platforms/types.d.ts +7 -0
  107. package/dist/types/deploy/platforms/vercel.d.ts +3 -2
  108. package/dist/types/deploy/redirects.d.ts +7 -2
  109. package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
  110. package/dist/types/openapi/model.d.ts +20 -6
  111. package/dist/types/search/sync/algolia.d.ts +3 -1
  112. package/docs/01-quickstart.mdx +3 -2
  113. package/docs/02-deployment.mdx +17 -8
  114. package/docs/08-faq.mdx +9 -2
  115. package/docs/advanced/changelog.mdx +1 -1
  116. package/docs/advanced/custom-pages.mdx +4 -2
  117. package/docs/cli/audit.mdx +2 -2
  118. package/docs/cli/doctor.mdx +2 -2
  119. package/docs/cli/evals.mdx +2 -2
  120. package/docs/cli/index.mdx +4 -1
  121. package/docs/cli/translate.mdx +1 -1
  122. package/docs/configuration/analytics.mdx +23 -2
  123. package/docs/configuration/assistant.mdx +1 -1
  124. package/docs/configuration/customization.mdx +4 -3
  125. package/docs/configuration/index.mdx +5 -3
  126. package/docs/configuration/search.mdx +1 -1
  127. package/docs/content/components.mdx +1 -1
  128. package/docs/content/frontmatter.mdx +5 -1
  129. package/docs/content/i18n.mdx +1 -1
  130. package/docs/content/index.mdx +4 -2
  131. package/docs/content/meta.mdx +5 -3
  132. package/docs/content/navigation.mdx +29 -4
  133. package/docs/content/sources.mdx +18 -12
  134. package/docs/content/versioning.mdx +1 -0
  135. package/docs/discoverability/agent-discovery.mdx +21 -7
  136. package/docs/discoverability/index.mdx +2 -2
  137. package/docs/discoverability/llms-txt.mdx +2 -5
  138. package/docs/discoverability/markdown.mdx +5 -3
  139. package/docs/discoverability/mcp.mdx +4 -0
  140. package/docs/discoverability/metadata.mdx +3 -2
  141. package/docs/discoverability/open-graph.mdx +6 -4
  142. package/docs/discoverability/rss.mdx +3 -1
  143. package/docs/references/asyncapi.mdx +1 -1
  144. package/docs/references/graphql.mdx +2 -2
  145. package/docs/references/openapi.mdx +3 -3
  146. package/package.json +1 -1
  147. package/skills/blume-migrate/SKILL.md +6 -6
  148. package/skills/blume-migrate/references/docusaurus.md +6 -6
  149. package/skills/blume-migrate/references/mintlify.md +3 -3
  150. package/skills/blume-migrate/references/monorepo.md +1 -1
  151. package/skills/blume-migrate/references/nextra.md +1 -1
  152. package/skills/blume-migrate/references/starlight.md +5 -5
  153. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +2 -2
  154. package/skills/blume-update-docs/references/audit-checklist.md +1 -1
  155. package/src/ai/agent-surface.ts +56 -0
  156. package/src/ai/api/spec.ts +15 -2
  157. package/src/ai/api-catalog.ts +7 -1
  158. package/src/ai/ask-context.ts +14 -2
  159. package/src/ai/ask-data.ts +26 -12
  160. package/src/ai/component-markdown.ts +43 -37
  161. package/src/ai/link-headers.ts +8 -3
  162. package/src/ai/llms.ts +4 -3
  163. package/src/ai/markdown.ts +23 -11
  164. package/src/ai/mcp/server.ts +78 -7
  165. package/src/ai/relative-links.ts +78 -10
  166. package/src/ai/static-expression.ts +416 -0
  167. package/src/ai/visibility.ts +45 -14
  168. package/src/analytics/databuddy.ts +67 -0
  169. package/src/analytics/head.ts +4 -0
  170. package/src/analytics/index.ts +2 -0
  171. package/src/analytics/posthog.ts +21 -3
  172. package/src/analytics/schema.ts +2 -0
  173. package/src/astro/generate.ts +71 -27
  174. package/src/astro/templates.ts +61 -23
  175. package/src/audit/catalog.ts +2 -2
  176. package/src/audit/checks/assets.ts +25 -5
  177. package/src/audit/checks/content.ts +20 -2
  178. package/src/audit/checks/i18n.ts +46 -8
  179. package/src/audit/checks/indexability.ts +39 -19
  180. package/src/audit/checks/links.ts +14 -0
  181. package/src/audit/checks/llms.ts +6 -3
  182. package/src/audit/checks/network.ts +3 -1
  183. package/src/audit/checks/og-image.ts +10 -0
  184. package/src/audit/checks/robots.ts +6 -1
  185. package/src/audit/checks/sitemap.ts +56 -31
  186. package/src/audit/checks/social.ts +21 -2
  187. package/src/audit/crawl.ts +88 -15
  188. package/src/audit/report.ts +54 -17
  189. package/src/audit/run.ts +1 -0
  190. package/src/audit/types.ts +18 -0
  191. package/src/audit/url.ts +37 -6
  192. package/src/cli/build-failure.ts +50 -0
  193. package/src/cli/commands/audit.ts +7 -0
  194. package/src/cli/commands/build.ts +19 -5
  195. package/src/cli/commands/check.ts +2 -0
  196. package/src/cli/commands/eval.ts +1 -7
  197. package/src/cli/commands/init.ts +15 -5
  198. package/src/cli/commands/preview.ts +15 -0
  199. package/src/cli/commands/sync.ts +2 -0
  200. package/src/cli/commands/translate.ts +9 -6
  201. package/src/cli/commands/upgrade.ts +11 -0
  202. package/src/cli/eject-scripts.ts +32 -7
  203. package/src/cli/env.ts +12 -1
  204. package/src/cli/init/scaffold.ts +117 -11
  205. package/src/cli/init/starter-spec.ts +235 -0
  206. package/src/components/content/AccordionItem.astro +26 -22
  207. package/src/components/content/Badge.astro +2 -9
  208. package/src/components/content/Card.astro +2 -2
  209. package/src/components/content/Component.astro +9 -3
  210. package/src/components/content/Expandable.astro +5 -1
  211. package/src/components/content/Frame.astro +2 -7
  212. package/src/components/content/GithubInfo.astro +12 -2
  213. package/src/components/content/Prompt.astro +2 -7
  214. package/src/components/content/Tabs.astro +54 -5
  215. package/src/components/content/Tile.astro +1 -1
  216. package/src/components/content/Tooltip.astro +69 -7
  217. package/src/components/content/Tree.astro +7 -2
  218. package/src/components/content/TypeTable.astro +10 -5
  219. package/src/components/content/Update.astro +8 -2
  220. package/src/components/content/auto-type-table.ts +4 -1
  221. package/src/components/content/badge-color.ts +17 -0
  222. package/src/components/content/base-href.ts +18 -3
  223. package/src/components/content/inline-markdown.ts +27 -7
  224. package/src/components/copy-feedback.ts +35 -8
  225. package/src/components/islands/assistant.tsx +16 -2
  226. package/src/components/islands/hooks.ts +7 -2
  227. package/src/components/islands/webmcp.ts +12 -8
  228. package/src/components/layout/Banner.astro +23 -4
  229. package/src/components/layout/Header.astro +22 -9
  230. package/src/components/layout/Logo.astro +5 -0
  231. package/src/components/layout/NavSelector.astro +6 -1
  232. package/src/components/layout/NavTabMenu.astro +133 -0
  233. package/src/components/layout/NavTree.astro +15 -6
  234. package/src/components/layout/NavTreeCache.astro +5 -2
  235. package/src/components/layout/NavTreeScript.astro +45 -5
  236. package/src/components/layout/PageLayout.astro +22 -8
  237. package/src/components/layout/ReferenceLayout.astro +4 -1
  238. package/src/components/layout/RootLayout.astro +21 -8
  239. package/src/components/layout/Search.astro +5 -1
  240. package/src/components/layout/analytics-client.ts +2 -0
  241. package/src/components/openapi/GraphqlType.astro +11 -3
  242. package/src/components/openapi/MessageComposer.astro +1 -1
  243. package/src/components/openapi/Operation.astro +21 -5
  244. package/src/components/openapi/PanelTabs.astro +4 -1
  245. package/src/components/openapi/Playground.astro +8 -2
  246. package/src/components/openapi/RequestPanel.astro +9 -5
  247. package/src/components/openapi/SchemaProperty.astro +11 -33
  248. package/src/components/openapi/SchemaTable.astro +27 -52
  249. package/src/components/openapi/helpers.ts +90 -12
  250. package/src/components/openapi/message-composer.ts +8 -0
  251. package/src/components/openapi/message-model.ts +12 -2
  252. package/src/components/openapi/message.ts +4 -1
  253. package/src/components/openapi/operation-model.ts +40 -8
  254. package/src/components/openapi/panel.ts +29 -5
  255. package/src/components/openapi/playground-client.ts +37 -7
  256. package/src/components/openapi/playground-schema.ts +25 -6
  257. package/src/components/openapi/request.ts +2 -0
  258. package/src/components/openapi/schema-tree.ts +203 -0
  259. package/src/components/openapi/snippets.ts +30 -15
  260. package/src/components/openapi/validate-json.ts +1 -1
  261. package/src/core/base-path.ts +15 -0
  262. package/src/core/config-input.ts +4 -4
  263. package/src/core/config.ts +18 -4
  264. package/src/core/diagnostics.ts +217 -30
  265. package/src/core/graph.ts +120 -11
  266. package/src/core/i18n-ui.ts +39 -2
  267. package/src/core/i18n.ts +13 -2
  268. package/src/core/links.ts +7 -4
  269. package/src/core/load-module.ts +20 -0
  270. package/src/core/locale-links.ts +17 -17
  271. package/src/core/manifest.ts +3 -2
  272. package/src/core/meta.ts +69 -6
  273. package/src/core/nav-diagnostics.ts +56 -1
  274. package/src/core/navigation.ts +245 -76
  275. package/src/core/ordering-prefix.ts +27 -0
  276. package/src/core/project-graph.ts +23 -1
  277. package/src/core/safe-href.ts +53 -1
  278. package/src/core/safe-links.ts +11 -2
  279. package/src/core/schema.ts +70 -14
  280. package/src/core/sources/assets.ts +83 -41
  281. package/src/core/sources/contentful-rich-text.ts +3 -2
  282. package/src/core/sources/contentful.ts +25 -12
  283. package/src/core/sources/filesystem.ts +5 -1
  284. package/src/core/sources/github-releases.ts +106 -5
  285. package/src/core/sources/lexical.ts +9 -4
  286. package/src/core/sources/lower.ts +79 -26
  287. package/src/core/sources/mdx-remote.ts +1 -0
  288. package/src/core/sources/normalize.ts +132 -30
  289. package/src/core/sources/notion.ts +26 -10
  290. package/src/core/sources/obsidian.ts +17 -3
  291. package/src/core/sources/payload.ts +1 -0
  292. package/src/core/sources/portable-text.ts +63 -44
  293. package/src/core/sources/remote.ts +18 -2
  294. package/src/core/sources/resolve.ts +52 -33
  295. package/src/core/sources/sanity.ts +1 -0
  296. package/src/core/sources/strapi-blocks.ts +9 -4
  297. package/src/core/sources/strapi.ts +1 -0
  298. package/src/core/sources/types.ts +31 -0
  299. package/src/core/types.ts +9 -0
  300. package/src/core/ui-packs/ar.ts +13 -0
  301. package/src/core/ui-packs/bg.ts +13 -0
  302. package/src/core/ui-packs/bn.ts +13 -0
  303. package/src/core/ui-packs/ca.ts +13 -0
  304. package/src/core/ui-packs/cs.ts +13 -0
  305. package/src/core/ui-packs/da.ts +13 -0
  306. package/src/core/ui-packs/de.ts +13 -0
  307. package/src/core/ui-packs/el.ts +13 -0
  308. package/src/core/ui-packs/es.ts +13 -0
  309. package/src/core/ui-packs/fa.ts +13 -0
  310. package/src/core/ui-packs/fi.ts +13 -0
  311. package/src/core/ui-packs/fr.ts +13 -0
  312. package/src/core/ui-packs/he.ts +13 -0
  313. package/src/core/ui-packs/hi.ts +13 -0
  314. package/src/core/ui-packs/hr.ts +13 -0
  315. package/src/core/ui-packs/hu.ts +13 -0
  316. package/src/core/ui-packs/id.ts +13 -0
  317. package/src/core/ui-packs/it.ts +13 -0
  318. package/src/core/ui-packs/ja.ts +13 -0
  319. package/src/core/ui-packs/ko.ts +13 -0
  320. package/src/core/ui-packs/nl.ts +13 -0
  321. package/src/core/ui-packs/no.ts +13 -0
  322. package/src/core/ui-packs/pl.ts +13 -0
  323. package/src/core/ui-packs/pt-br.ts +13 -0
  324. package/src/core/ui-packs/pt.ts +13 -0
  325. package/src/core/ui-packs/ro.ts +13 -0
  326. package/src/core/ui-packs/ru.ts +13 -0
  327. package/src/core/ui-packs/sk.ts +13 -0
  328. package/src/core/ui-packs/sr.ts +13 -0
  329. package/src/core/ui-packs/sv.ts +13 -0
  330. package/src/core/ui-packs/th.ts +13 -0
  331. package/src/core/ui-packs/tr.ts +13 -0
  332. package/src/core/ui-packs/uk.ts +13 -0
  333. package/src/core/ui-packs/vi.ts +13 -0
  334. package/src/core/ui-packs/zh-tw.ts +13 -0
  335. package/src/core/ui-packs/zh.ts +13 -0
  336. package/src/core/version-cut.ts +17 -2
  337. package/src/core/versions.ts +4 -1
  338. package/src/deploy/adapters/node.ts +5 -2
  339. package/src/deploy/adapters/registry.ts +2 -1
  340. package/src/deploy/adapters/types.ts +13 -1
  341. package/src/deploy/artifacts.ts +25 -5
  342. package/src/deploy/cloudflare-negotiation.ts +23 -4
  343. package/src/deploy/headers.ts +67 -51
  344. package/src/deploy/node-headers.ts +148 -27
  345. package/src/deploy/platforms/cloudflare.ts +1 -0
  346. package/src/deploy/platforms/netlify.ts +82 -5
  347. package/src/deploy/platforms/node.ts +7 -5
  348. package/src/deploy/platforms/static.ts +1 -0
  349. package/src/deploy/platforms/types.ts +7 -0
  350. package/src/deploy/platforms/vercel.ts +9 -3
  351. package/src/deploy/redirects.ts +14 -3
  352. package/src/deploy/vercel-negotiation.ts +35 -3
  353. package/src/eval/agents.ts +10 -2
  354. package/src/eval/run.ts +25 -0
  355. package/src/markdown/base-links.ts +55 -8
  356. package/src/markdown/index.ts +6 -3
  357. package/src/markdown/relative-links.ts +3 -23
  358. package/src/markdown/route-snapshot.ts +37 -0
  359. package/src/og/card.ts +20 -4
  360. package/src/og/derive.ts +145 -4
  361. package/src/openapi/graphql-build.ts +28 -2
  362. package/src/openapi/model.ts +77 -13
  363. package/src/openapi/render-mdx.ts +10 -1
  364. package/src/registry/eject.ts +119 -22
  365. package/src/search/documents.ts +22 -1
  366. package/src/search/sync/algolia.ts +36 -2
  367. package/src/sources/registry.ts +5 -0
  368. package/src/theme/entry.ts +11 -4
  369. package/src/translate/agents.ts +6 -1
  370. package/src/translate/ledger.ts +26 -3
  371. package/src/translate/meta.ts +11 -3
  372. package/src/translate/run.ts +11 -5
  373. package/src/translate/validate.ts +10 -1
  374. package/src/translate/work-list.ts +36 -3
  375. package/src/upgrade/upgrade.ts +36 -4
  376. package/dist/cli/chunk-2hn4b8z7.js.map +0 -12
  377. package/dist/cli/chunk-6crbhc3x.js.map +0 -14
  378. package/dist/cli/chunk-6hsn950k.js.map +0 -10
  379. package/dist/cli/chunk-79jhk4py.js.map +0 -35
  380. package/dist/cli/chunk-82bbrxdn.js +0 -51
  381. package/dist/cli/chunk-82bbrxdn.js.map +0 -10
  382. package/dist/cli/chunk-abh8yjkn.js +0 -31
  383. package/dist/cli/chunk-abh8yjkn.js.map +0 -10
  384. package/dist/cli/chunk-dh8cwk36.js.map +0 -10
  385. package/dist/cli/chunk-epjnccmv.js.map +0 -10
  386. package/dist/cli/chunk-fa25z98p.js.map +0 -11
  387. package/dist/cli/chunk-fs23ddbb.js.map +0 -35
  388. package/dist/cli/chunk-fz5wtpmh.js.map +0 -10
  389. package/dist/cli/chunk-kpf8rrjc.js.map +0 -19
  390. package/dist/cli/chunk-m3vmjgmq.js.map +0 -10
  391. package/dist/cli/chunk-mb2919y2.js.map +0 -10
  392. package/dist/cli/chunk-q5163e60.js.map +0 -11
  393. package/dist/cli/chunk-qkqwkpte.js.map +0 -182
  394. package/dist/cli/chunk-qs4q5p4e.js.map +0 -10
  395. package/dist/cli/chunk-s1p84fyh.js.map +0 -10
  396. package/dist/cli/chunk-wgm7m9qk.js.map +0 -36
  397. package/dist/cli/chunk-yt5n7ppj.js.map +0 -10
  398. /package/dist/cli/{chunk-vtk4a6dg.js.map → chunk-00gs3wqs.js.map} +0 -0
  399. /package/dist/cli/{chunk-6vm74dry.js.map → chunk-6dtt0zfn.js.map} +0 -0
  400. /package/dist/cli/{chunk-fxypxtvm.js.map → chunk-bbnwccaz.js.map} +0 -0
  401. /package/dist/cli/{chunk-ce574jw2.js.map → chunk-esh98wmb.js.map} +0 -0
  402. /package/dist/cli/{chunk-zxcczpyx.js.map → chunk-fsmrqk8a.js.map} +0 -0
  403. /package/dist/cli/{chunk-hdpx1tax.js.map → chunk-g698a744.js.map} +0 -0
  404. /package/dist/cli/{chunk-zxh4d9vy.js.map → chunk-h2ez8dzb.js.map} +0 -0
  405. /package/dist/cli/{chunk-5shv93fd.js.map → chunk-h7k3nq3v.js.map} +0 -0
  406. /package/dist/cli/{chunk-ah61y8py.js.map → chunk-mqc662a6.js.map} +0 -0
  407. /package/dist/cli/{chunk-ch6g3ar0.js.map → chunk-n1yg3tj3.js.map} +0 -0
  408. /package/dist/cli/{chunk-wm7js3j9.js.map → chunk-sqw4ekg1.js.map} +0 -0
  409. /package/dist/cli/{chunk-27g6wdth.js.map → chunk-wdrt2k2v.js.map} +0 -0
  410. /package/dist/cli/{chunk-s6jhgk0q.js.map → chunk-zp79m0ts.js.map} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,87 @@
1
1
  # blume
2
2
 
3
+ ## 2.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - e80b141: Only a reader opening an `<AccordionItem>` or `<Expandable>` writes its id to the URL hash. A `defaultOpen` item on the page a client-side navigation lands on no longer replaces the hash the reader arrived with.
8
+ - 4f9d1bf: `llms.txt`, `agent-readability.json`, the API and AI catalogs, and the `_headers` and `vercel.json` rules now list the agent skills index only when the build published a skill, and the MCP server only when it was generated, rather than whenever `agents.skills` or `agents.mcp` is set. The API catalog is now served as `application/linkset+json` with the RFC 9727 `profile` parameter, in its `Content-Type` and in the homepage `Link` header.
9
+ - 4f9d1bf: Root-relative links in the agent Markdown (`.md` and `.mdx` mirrors, `llms-full.txt`, MCP `get_page`) now match the rendered page: `[Install](/guides/install)` and `<Card href="/guides/config">` gain the site's `deployment.base` and `basePath`, and on a translated page they point at that locale's copy of the page when it exists. A paragraph written straight after a converted `<Callout>`, `<Steps>`, or `<Prompt>` now stays its own paragraph instead of joining the quote or list, and `<Visibility>` markup inside inline code or an indented code block (in a tab or step) is left as written.
10
+ - a70a0f1: GraphQL references build from schemas that use directives they don't declare, such as Apollo Federation subgraphs (`@key`, `@link`) and AppSync schemas (`@aws_*`); syntax errors and unknown types still fail the build. A custom scalar's `@specifiedBy` URL is a link only when it's a safe web address, so a `javascript:` URL from a remote schema shows as text. OpenAPI overview pages list tag sections in the order the spec's `tags` declares them, code spans in description lines that start with HTML keep their braces, and the build warns that OpenAPI 3.1 `webhooks` aren't rendered instead of dropping them silently.
11
+ - f2ebe6f: `blume init --template api` now writes a small example spec, `openapi.json`, beside `blume.config.ts` and points `openapi()` at it, so a new project's first build no longer depends on the public Petstore server. Its operation pages carry descriptions that a fresh `blume audit` accepts.
12
+ - 591c39e: The assistant shows its error message when an answer comes back empty, which is how a provider failure after the response starts (a bad key, a rate limit, an unknown model) arrives, instead of leaving a pulsing placeholder that never resolves. `useAssistant` from `blume/hooks` does the same.
13
+ - 4f9d1bf: On a versioned site, the assistant now grounds its answers in the docs version the reader is viewing, the current docs unless they're on an archived page, instead of retrieving from every archived copy of each page.
14
+ - 0befec9: `blume audit` passes on Blume's own output for multilingual and versioned sites. The hreflang checks and the non-canonical page check skip i18n fallback copies, which canonicalize to the page they copy on purpose; the hreflang checks also accept archived pages that canonicalize to the latest docs, and match non-ASCII slugs. The sitemap and llms.txt checks no longer flag pages those files leave out on purpose: fallback copies, archived pages that canonicalize elsewhere, and pages with `ai: { exclude: true }`, which the llms.txt fix now names. A sitemap index, which Blume writes past 50,000 URLs, is read through its child sitemaps, and the size limits apply to each file.
15
+
16
+ Other checks no longer report what the config asked for. Noindex pages aren't asked for a canonical, robots.txt isn't expected to name a sitemap when `seo.sitemap` is off or to be reachable when `seo.robots` is off, `/_vercel/image` URLs aren't reported as broken images, and a locale prefix such as `pt-BR` doesn't count as an uppercase URL.
17
+ - 0befec9: The `blume audit` report labels each check with the number of distinct pages it affects and previews each page once, while `--verbose` still lists every finding. A check's glyph follows its findings' actual severity, so an external link downgraded to a warning no longer shows as an error. Each distinct fix in a group is printed, and length findings on pages generated from an API spec point at the spec's summary or description instead of front matter.
18
+
19
+ On a site with `deployment.base`, links, assets, and `og:image` URLs that leave out the base are reported, since the deployed site doesn't serve them. `blume audit --url` rejects a value with no scheme, such as `example.com`, with a message suggesting the `https://` form instead of failing with an internal error.
20
+ - e80b141: `<AutoTypeTable>` shows the right type for properties inherited from an interface in another file (`interface Props extends Base`, with `Base` imported). Those rows read their type from the file that declares them, where they used to show a slice of unrelated text from the documented file.
21
+ - e80b141: A `<Badge>` with a `variant` it doesn't recognize, such as `info`, renders as the default gray badge instead of failing the page build.
22
+ - 591c39e: The banner's link is written as if the site were mounted at root, like a featured link: with `basePath` set it lands under the base, and on a multi-locale site it points at the reader's locale when that locale serves the page. A link to a route served outside the docs (a custom page, the generated changelog) keeps its own path. A `<TypeTable>` entry's `typeDescriptionLink` gets the base too.
23
+ - 351d6ab: `blume build` reports a page Astro can't compile or render (MDX that doesn't parse, say) as a `BLUME_BUILD_FAILED` error at that file and position, instead of an internal error asking you to report a Blume bug. After a `vercel()` or `netlify()` server build, `blume preview` explains that the adapter has no local preview server and suggests `blume dev` or the host's preview deploy, and in an ejected app `blume check`, `blume sync`, and `blume preview` now stop like `dev` and `build` do, naming the app's own command to run instead.
24
+ - 3b5e90a: A `notion()` source saves a downloaded asset only when the server reports an image or video type, or when the response names no type and the URL has an image or video extension. An "image" that links to an HTML page keeps its original URL with a warning instead of being served as a page from `/blume-assets/`, and an image whose caption holds a bracket or whose URL holds spaces or parentheses is now downloaded too, instead of keeping a signed Notion URL that expires. `/blume-assets/` publishes only the images and videos a source downloaded, so a file an earlier build saved under another extension is no longer served, and a downloaded video carries its video type in dev.
25
+ - 3b5e90a: Content imported from a CMS or GitHub releases can no longer run script on the docs site. A Sanity code span keeps its text as code however many backticks or braces it holds, and a `javascript:` link keeps only its label wherever it comes from: a Sanity link, a scheme hidden behind an escape (`java&#115;cript:`, `javascript&colon;`), a release note's footnote, or a Contentful, Payload, or Strapi Markdown field. A link's URL can't close it early to start a second link, a code block whose language holds a backtick still opens its fence, and an unsupported block's type can't end its comment. Lowered rich text also keeps `## text`, indented text, and an `import` followed by code in the next run as prose, instead of rendering a heading or code block or failing the MDX page.
26
+ - 3b5e90a: Sanity bold and italic spans keep their edge spaces outside the markers, so `Hello ` in bold before `world` renders as bold text instead of literal asterisks, and nested list items indent to their parent item's text, so an item under a numbered one nests instead of joining the parent's line. Notion video captions show `<` and `>` as typed rather than as `&lt;` and `&gt;`, and a rate-limited Notion request waits for the `Retry-After` the API sends.
27
+ - f2ebe6f: A config error now points at the line that holds the problem: an invalid `redirects[2].status` at the third redirect rather than the first, and a missing key such as `github.repo` or `i18n.locales[1].label` at its parent object or array entry rather than at a same-named key later in the file, so issues also list in the order they appear. A `blume.config.ts` without a default export (a bare `defineConfig({ … })` call, `export const config`, or `export default null`) now fails with one diagnostic naming the file and the missing `export default`, where it used to build a default site or fail with an unrelated error. The `defineConfig` hover docs no longer list `markdown.math` or inline-highlighting options the config doesn't accept.
28
+ - f2ebe6f: Config paths and URLs are checked up front. `deployment.site` must be an absolute `http://` or `https://` URL, so `localhost:4321`, `mailto:`, and `javascript:` values fail with a hint instead of prefixing every sitemap entry and canonical link. Redirects need a leading slash on `from` and on a relative `to`, since a path without one never gained the base path. `basePath` rejects a full URL, query, or fragment. A navigation tab's `path` is normalized to one leading slash and no trailing slash, so `/guides/` marks its tab and scopes the sidebar, and `api` links to `/api`.
29
+ - 5f5c72a: With `basePath: "/docs"`, a content folder named `docs/` now publishes beneath the base (`docs/guide.md` at `/docs/docs/guide`) instead of colliding with the root pages. Folder `meta.ts` discovery now honors the content source's `include` and `exclude` globs, so an unrelated `src/lib/meta.ts` in a `.`-rooted project is no longer imported, and meta in a lowercase locale folder (`pt-br/`) now applies to its configured locale (`pt-BR`).
30
+ - abb982a: Only file and folder names lose an ordering prefix (`01-intro.mdx` → `/intro`, and an Obsidian note `01 Intro` → `/intro`). A frontmatter `slug`, a GitHub release tag, and a CMS slug keep their leading numbers, so releases `1.0.0` and `2.0.0` publish at `/changelog/1-0-0` and `/changelog/2-0-0` instead of colliding on `/changelog/0-0`. A version (`1.2.0.md`) or an ISO date (`2024-01-05-first-post.md`) is part of a name rather than an order, which also stops posts from the same year raising a duplicate sidebar order warning. A source `prefix` written with slashes (`sanity({ prefix: "/guides" })`) now names the source `guides`, so its pages no longer 404 and its asset URLs no longer carry a double slash.
31
+ - abb982a: Remote sources keep one snapshot per preview mode and set of options under `.blume/cache/<source>/`. A plain `blume dev` after `blume dev --preview` no longer serves unpublished drafts, a build whose fetch fails no longer falls back to them, `--preview` fetches drafts even when a published snapshot is cached, and editing a source's `query` or `fields` refetches in dev. Contentful's `--preview` without a Preview API token now fails with a clear error instead of serving the cached snapshot with a warning, and a source passed to `custom()` from one of Blume's engine factories (`sanitySource`, `contentfulSource`, …) now honors `--preview` and caches in the runtime directory like the built-in adapter.
32
+ - a6401cf: Add a `databuddy()` analytics adapter. It renders Databuddy's tracker tag with `clientId` as `data-client-id` and forwards any other option as a `data-` attribute, and page feedback events reach `window.databuddy.track`.
33
+ - f2ebe6f: A `dateFormat` that sets only `timeZone`, `calendar`, or `numberingSystem` keeps the long date form (`July 21, 2026`) instead of falling back to a bare numeric date (`7/21/2026`). Setting `dateStyle` or a component field such as `year` still picks the shape.
34
+ - 8d88600: `blume eject` now adds `tailwindcss` and `@tailwindcss/typography` to the project's dependencies, so the ejected stylesheets resolve under pnpm and other strict linkers, and the ejected `features.ts` names `epub-gen-memory` only when EPUB export is on. The ejected app also serves colocated images from its raw Markdown at `/blume-assets/content/…`, and renders the Open Graph card its `/changelog` page points at. With `playground: { proxy: true }` on a reference, the ejected app serves the API playground's built-in proxy at `{basePath}/_api-proxy`, with the same origin allowlist, instead of every Send returning a 404.
35
+ - 5f5c72a: English pages (`en`, `en-GB`, and other `en-*` codes) on a site whose default locale isn't English now show Blume's built-in English interface strings, plus their own `i18n.ui` overrides, instead of the default locale's translation.
36
+ - 351d6ab: Blume no longer takes `NODE_ENV` from `.env` files, so a monorepo root `.env` that sets it for another app can't turn `blume build` into a development build or `blume dev` into a production one. Set it in the shell when you need to.
37
+ - 0befec9: A `severity: warning` question in `blume eval` now warns instead of failing CI, as documented: its miss doesn't count against the gate or `--threshold`, so the exit code agrees with the JSON summary's error count. When an agent CLI exits before reading its prompt, `blume eval` and `blume translate` report the agent's exit code and stderr for that item instead of aborting the whole run with an internal error.
38
+ - aa325a8: A header tab's `icon` now shows beside its label in the header, on a dropdown tab, and in the mobile navigation drawer. Before, `navigation.tabs[].icon` was accepted but never rendered.
39
+ - aa325a8: On a multilingual site, a sidebar group with no `meta.ts` in a locale now mirrors the fallback locale's: that folder's `meta.ts` (title, order, `collapsed`, `display`) and the `sidebar.display` its index page sets. Groups set to collapsible no longer render flat on pages that fall back to the default language.
40
+ - 351d6ab: `blume init` inside a pnpm workspace that doesn't list the new folder under `packages` now skips the install (pnpm would exit without installing Blume) and tells you to add the folder, instead of saying the project joined the workspace. The `cd` line in its next steps is quoted when the directory needs it (`cd "my docs"`), and when `init --eject` can't eject after an install that ran, it reports the actual error rather than blaming missing dependencies.
41
+ - aa325a8: A `<Frame>` caption and a `<Prompt>` description keep code spans (`` `Array<string>` ``) and autolinks (`<https://…>`) intact, while raw HTML in them still shows as text. Before, their `<` and `>` rendered as `&lt;`/`&gt;` in code and as stray brackets around links.
42
+ - 5f5c72a: Links now resolve the same way in the built site and in `blume validate`: relative links on a page named `Index.md` resolve beside it, as its `/…/Index` route does, and links to dotted page routes such as `/releases/v1.2` move into the reader's locale and under `basePath`, in content and in component `href`s alike; only a link no page is served at counts as a public asset. `blume version` also rewrites unprefixed links (`/guides/setup`) into the snapshot when the default locale keeps its URL prefix, and for pages that exist only in another locale.
43
+ - 591c39e: More of the reader-facing chrome follows the page's language: the `<TypeTable>` column and row labels, `<Component>`'s Preview and Code tabs, untitled `<Tabs>` panels and the tab picker, `<Expandable>`'s default toggle, `<Update>`'s default heading, `<GithubInfo>`'s count labels, the search dialog's "latest" version tag, and the header on API reference pages. Each has a new `i18n.ui` key with a translation in every built-in locale. In right-to-left locales the sidebar's indents and badges, the assistant's close button, and the arrows on cards, tiles, and the banner link now mirror.
44
+ - 65edd91: With a `basePath`, the `_headers` and `vercel.json` rules that pin `charset=utf-8` on raw Markdown now cover every mirror, not only those under `basePath`: the section home's `/<basePath>.md` and the generated `/index.md`, `/changelog.md`, and `/404.md` went out without a charset, so browsers showed their non-ASCII text as mojibake.
45
+ - 4f9d1bf: The MCP endpoint now reads a request body only up to 64 KB and answers anything larger with `413`. A call to a tool that doesn't exist, or with `arguments` that aren't an object, now gets the JSON-RPC Invalid params error (`-32602`) with a short message. The `/mcp` operation in `openapi.json` now lists `text/event-stream` beside `application/json`, so a generated client sends the `Accept` header the endpoint requires.
46
+ - 5f5c72a: Header and sidebar links now land where they should across versions, locales, and base paths:
47
+
48
+ - On an archived version's pages, a tab links into that version's section (`/v1.0/guides/…`) instead of the current docs' path, which 404ed when the section had no index page.
49
+ - On a non-default locale, a `/changelog` tab opens the changelog timeline instead of the newest entry, and a tab whose path is a custom page opens that page.
50
+ - With a `basePath`, the logo in a non-default locale links inside the base (`/docs/fr`) instead of to `/fr`.
51
+ - Internal `href` links in an explicit `navigation.sidebar` move into the reader's locale, as featured links do.
52
+ - An explicit `navigation.sidebar` item that can't render as written now reports a warning: a route that matches no page, a `root` that matches no page, or an item with no route, `href`, `root`, or `items`.
53
+ - A featured or header link with a query or fragment (`/guides?tab=cli`) is no longer reported as `BLUME_NAV_MISSING_PAGE` when its page exists.
54
+ - aa325a8: A `netlify()` server build now serves the header rules a static build puts in `_headers` — the homepage `Link` header, `charset=utf-8` on the raw Markdown and text files, and the `.well-known` discovery files' media types and CORS header — by writing them into Netlify's `.netlify/v1/config.json`, which a server build reads instead of `_headers`.
55
+ - 8d88600: The default 404 page now switches to the reader's locale under a `deployment.base` or `basePath` too, and its localized home link includes the `basePath`. `<Component />` preview frames now follow the OS color scheme when browser storage is blocked, instead of rendering without a theme.
56
+ - abb982a: Broken-link diagnostics in an Obsidian vault note now name the line in the note itself, rather than a line counted from the frontmatter Blume rewrites when it drops Obsidian-only properties.
57
+ - f2ebe6f: Open Graph cards for non-Latin locales now render their text instead of empty boxes. Each configured locale whose script the card's built-in font can't draw adds a Google Noto fallback for it (`Noto Sans JP` for `ja`, `Noto Sans Devanagari` for `hi`, `Noto Sans` for Cyrillic, Greek, and accented Latin, and so on), with no config needed. Latin text keeps the built-in font, so English cards look the same, and an explicit `seo.og.fonts` still replaces the whole list.
58
+ - a70a0f1: API reference samples now use the examples an OpenAPI 3.0 or Swagger 2.0 spec declares on parameters, request bodies, and responses, which the 3.1 upgrade had moved out of reach, so a path parameter with `example: "pet_123"` renders `/pets/pet_123` instead of `/pets/string`. Response examples keep `readOnly` fields and leave out `writeOnly` ones, and the curl, JavaScript, and Python samples quote header values, URLs, and bodies for their language, so an `If-Match: "33a64df5"` header or a `$` in a token comes through intact. Schema rows label a nullable `$ref` as `Pet | null` rather than `Pet | any`, and a type array like `["string", "integer"]` as `string | integer`.
59
+ - a70a0f1: API reference schema tables stay a manageable size for specs whose schemas reference each other densely. A named schema expands wherever it appears in the first two levels of nesting, as before, and below that only where it first appears; later mentions show its name. A request body of twelve schemas that each reference four others went from tens of thousands of nested tables to a few dozen at most.
60
+ - a70a0f1: API reference code samples, the Try it panel, and the AsyncAPI composer fill server `{variables}` with their declared defaults, so `https://{region}.api.example.com` becomes a real URL, and the built-in playground proxy allows the resulting origin instead of refusing it with a 403. An operation's own `servers`, or its path's, now replace the document's servers in its samples and panel, and the proxy allows those origins too.
61
+ - 5f5c72a: A heading inside a multi-line HTML (`<!-- … -->`) or MDX (`{/* … */}`) comment no longer becomes the page title, a table of contents entry, or a link anchor. Titles derived from file names spell acronyms the way sidebar groups do (`faq.md` is "FAQ"), and an untitled folder `index` page takes its folder's label instead of "Index". A root file named like a version (`v3-migration.md`) no longer triggers the unconfigured-version warning, and the missing content root hint now names the `filesystem()` source's `root` for projects that list `content.sources`.
62
+ - 8d88600: The API playground's built-in proxy (`playground: { proxy: true }`) now works under a `basePath` or a `deployment.base`: the endpoint mounts under `basePath` at `{basePath}/_api-proxy`, and the Send button targets it with the deployment base included.
63
+ - a70a0f1: The Try it panel no longer blocks Send on valid request bodies: it doesn't ask for `readOnly` properties the spec marks required, accepts `null` in nullable fields, and doesn't check one type of a field that allows several. The panel also works when the browser blocks storage (Safari's "Block All Cookies", sandboxed iframes), where it now simply doesn't remember credentials, and the AsyncAPI composer closes its WebSocket when a client-side navigation leaves the page. The request and response tabs move with the arrow keys, Home, and End, and the reference's spacing follows right-to-left layouts.
64
+ - 0befec9: The `posthog()` analytics adapter only sends its own `$pageview` on client-router navigations while PostHog captures page loads alone. With `capture_pageview: "history_change"` or a `defaults` date such as `"2025-05-24"`, PostHog tracks navigations itself and each one is no longer counted twice, and `capture_pageview: false` now means no pageviews at all.
65
+ - 591c39e: Export → PDF (and any print of a page) keeps callouts, panels, and changelog update headings. The print stylesheet now hides only the site chrome (banner, header, sidebar, table of contents, assistant panel) instead of every `<header>` and `<aside>` on the page.
66
+ - 591c39e: Screen readers get a name for every piece of header and content chrome. An image-only logo (`logo: { image, text: "" }`) names the home link after the image's `alt`, or the site title when it has none. A navigation selector's accessible name includes the option it shows ("Version: v2"), matching the language switcher. `<GithubInfo>` labels its star and fork counts. The first "Copied" after each page load is announced reliably, since the status region now registers before its first message.
67
+ - 65edd91: A `node()` server build answers a configured `302` or `307` redirect with that status, as the Cloudflare Worker already does, instead of a permanent `301` or `308` that browsers cache. A `netlify()` static build forces its `_redirects` rules (`/old /new 301!`), so Netlify issues the redirect instead of serving the redirect page at the old path with a `200`; the `_redirects` for `cloudflare()` and for a build with no named host stays unforced, since Cloudflare rejects the flag.
68
+ - 3b5e90a: A `githubReleases()` source rewrites links in release notes that point at the site's own `deployment.site` to root-relative paths, so changelog pages follow preview deploys and the deployment base and no longer trip `blume audit`'s "Internal link hardcodes the site's own origin" check.
69
+ - e80b141: The Left and Right arrow keys follow the reading direction in `<Tabs>` and `<Tree>`: on a right-to-left page, Left moves to the next tab and expands a tree folder, and Right moves to the previous tab and collapses a folder.
70
+ - 65edd91: SVG images a content source downloads are now served with `Content-Security-Policy: sandbox` and `X-Content-Type-Options: nosniff` by `vercel()`, `netlify()`, and `cloudflare()` server builds too, each through its host's own mechanism: a route in Vercel's routing config, a header rule in Netlify's `.netlify/v1/config.json`, and the Worker Blume puts in front of Astro's on Cloudflare. The `/blume-assets` route prerenders, so its own headers never reached a deployed site. A `node()` server now matches those SVGs by the file it serves rather than the raw URL, so an encoded spelling (`abc.sv%67`, `abc%2Esvg`) or a path outside `deployment.base` no longer skips the sandbox, and static builds add `nosniff` beside it.
71
+ - 4f9d1bf: The Algolia sync now declares `locale` and `version` for faceting (as `filterOnly`), keeping any facets you set yourself, so search on a multi-language or versioned site finds results. The search dialog's section for pages outside every sidebar group is now labeled in the reader's language through the new `search.docs` UI string, and the WebMCP `search_docs` tool returns plain text, with characters like `<` and `&` no longer HTML-escaped.
72
+ - 591c39e: Sidebar sections loaded on first open show their icons on every page, where an icon could come out blank on a page that hadn't used it itself. A drill-in panel loaded on first open keeps its Back button and section title. Drilling into a panel or going back moves keyboard focus into the panel that slides in (its Back button, or the row that opened the panel you left), and each drill-in row reports whether its panel is open with `aria-expanded` and `aria-controls`.
73
+ - 5f5c72a: A sidebar group's path now comes from its folder, so a page with a frontmatter `slug` no longer moves its whole folder out of its tab (a `guides/` page with `slug: install` left every page under a `/guides` tab with an empty sidebar). A folder `meta.ts` `pages` list now wins over a listed subfolder's own `order`, as it already did over a listed page's `sidebar.order`, and a listed position no longer reports a `BLUME_DUPLICATE_SIDEBAR_ORDER` warning against an unlisted sibling's numeric prefix or `sidebar.order`.
74
+ - 4f9d1bf: The agent Markdown (`.md` mirrors, `llms-full.txt`, MCP `get_page`) and the search index now read component props like `title={...}` as literal data and never run them, on `.mdx` and plain `.md` pages and on content from CMS and GitHub release sources alike. Strings, numbers, booleans, arrays, objects, template strings, and `frontmatter.*` references resolve as before; any other expression, such as a function call, leaves the component as written.
75
+ - 591c39e: A header tab with `items` renders as a dropdown: its items open in a menu from the header, and expand in place under the tab in the mobile navigation drawer. The tab's `path` still scopes the sidebar and marks it as current. Before, `navigation.tabs[].items` was accepted but every tab rendered as a plain link.
76
+ - e80b141: `<Tabs>` deep links (`?install=windows`, `#windows`) now open the linked tab when the reader arrives through a client-side navigation, not only on a full page load, and a tab no longer opens because the previous page's URL carried a matching hash. A `ts2js` fence inside a `<CodeGroup>` now becomes one of the group's tabs, labeled with the fence title, instead of showing its TypeScript and JavaScript pair under every tab.
77
+ - e80b141: A `<Tooltip>` stays open while the pointer moves from its trigger up to the panel, so a mouse can reach the panel's link. The panel now shifts to stay inside the viewport near either edge of the screen, and hidden tooltips no longer widen the page, which let a whole page scroll sideways on phones.
78
+ - 0befec9: `blume translate --codex` runs Codex with the same lockdown as `blume eval`: no shell, exec, or local-image tool, and no inherited environment. Before, `--sandbox read-only` still let the translator's shell read any file the user could.
79
+ - abb982a: `blume translate` writes into a locale folder that already exists in another casing (`pt-br/` for a configured `pt-BR`) instead of creating a second one beside it, which on a case-sensitive filesystem published every page twice.
80
+ - 0befec9: `blume translate` leaves archived-version folders' `meta.ts` files alone, like the rest of a frozen snapshot. Drafts count as content: a hand-written translation marked `draft: true` is adopted instead of overwritten, and a source page that is a draft for a while keeps its ledger entries, so its outdated translations still read as stale. A partial that is one code block validates without its own fences being stripped, and folder titles from two sources with the same folder name each get their own translation.
81
+
82
+ A `blume.translations.json` with unresolved merge-conflict markers now fails with a clear error instead of reading as empty, which let `--check` pass over outdated translations. Line endings are normalized before hashing, so a CRLF checkout doesn't look like an edit to every page.
83
+ - 351d6ab: Config validation reports `content.root` (or `include`/`exclude`) beside `content.sources` even while a source entry is still in the Blume 1 `{ type }` form, so `blume upgrade` and `blume doctor` list both on the first run. A `blume` range `blume upgrade` can't bump, such as `catalog:` or an `npm:` alias, is reported with what to change by hand (the catalog entry in `pnpm-workspace.yaml`, or the alias's version) instead of as ready.
84
+
3
85
  ## 2.0.1
4
86
 
5
87
  ### Patch Changes
@@ -371,4 +371,4 @@ var analyzeComponentOverrides = (source, filePath) => {
371
371
  export { emptyComponentOverrides, ComponentOverridesError, analyzeComponentOverrides };
372
372
 
373
373
  //# debugId=D84ED7D5A5A4CC7064756E2164756E21
374
- //# sourceMappingURL=chunk-vtk4a6dg.js.map
374
+ //# sourceMappingURL=chunk-00gs3wqs.js.map
@@ -74,6 +74,7 @@ var runAgentHeadless = (bin, args, options) => new Promise((resolve, reject) =>
74
74
  resolve({ code: code ?? 1, stderr, stdout, timedOut });
75
75
  }
76
76
  });
77
+ child.stdin?.on("error", () => {});
77
78
  child.stdin?.end(options.prompt);
78
79
  });
79
80
  var writeMcpConfig = async (dir, snapshotPath, launcher) => {
@@ -234,7 +235,7 @@ var parseVerdict = (text) => {
234
235
  return result.success ? result.data : undefined;
235
236
  };
236
237
 
237
- export { seconds, money, duration, DISALLOWED_TOOLS, runAgentHeadless, writeMcpConfig, agentArgs, readAgentOutput, parseVerdict };
238
+ export { seconds, money, duration, DISALLOWED_TOOLS, runAgentHeadless, writeMcpConfig, CODEX_LOCKDOWN, agentArgs, readAgentOutput, parseVerdict };
238
239
 
239
- //# debugId=FE1BEE26F1BB6F4964756E2164756E21
240
- //# sourceMappingURL=chunk-fa25z98p.js.map
240
+ //# debugId=CB0F69834F515D3B64756E2164756E21
241
+ //# sourceMappingURL=chunk-273ygyr4.js.map
@@ -0,0 +1,11 @@
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 // An agent that exits before reading the whole prompt (it rejected a flag,\n // say) closes the pipe mid-write, and the resulting EPIPE would otherwise\n // be an uncaught exception that aborts the run. The exit itself is the\n // outcome: `close` resolves with the agent's code and stderr, which\n // `readAgentOutput` reports.\n child.stdin?.on(\"error\", () => {\n // Deliberately ignored — see above.\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 (and a `blume translate` run nothing but its prompt). `--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 */\nexport const 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": "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",
9
+ "debugId": "CB0F69834F515D3B64756E2164756E21",
10
+ "names": []
11
+ }
@@ -6,7 +6,7 @@ import {
6
6
  stripBasePath,
7
7
  absoluteUrl2,
8
8
  MCP_TOOLS
9
- } from "./chunk-jts8mvcz.js";
9
+ } from "./chunk-gs7r695n.js";
10
10
  import {
11
11
  commandMeta
12
12
  } from "./index.js";
@@ -23,7 +23,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
23
23
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
24
24
  import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
25
25
  import {
26
- CallToolRequestSchema,
26
+ ErrorCode,
27
27
  ListResourcesRequestSchema,
28
28
  ListToolsRequestSchema,
29
29
  McpError,
@@ -355,6 +355,10 @@ var TOOL_DEFINITIONS = MCP_TOOLS.map((tool) => ({
355
355
  }));
356
356
  var resourceUri = (route, data) => data.site ? urlFor(route, data) : `${LOCAL_RESOURCE_SCHEME}${withBasePath(data.base, route)}`;
357
357
  var resourceRoute = (uri, data) => normalizeRoute2(uri.startsWith(LOCAL_RESOURCE_SCHEME) ? uri.slice(LOCAL_RESOURCE_SCHEME.length) : uri, data);
358
+ var TOOL_CALL_PARAMS = z2.object({
359
+ arguments: z2.record(z2.string(), z2.unknown()).optional(),
360
+ name: z2.string()
361
+ });
358
362
  var text = (value, isError = false) => {
359
363
  const content = [{ text: value, type: "text" }];
360
364
  return isError ? { content, isError: true } : { content };
@@ -390,8 +394,7 @@ var buildServer = (data, index) => {
390
394
  contents: [{ mimeType: RESOURCE_MIME_TYPE, text: markdown, uri }]
391
395
  };
392
396
  });
393
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
394
- const { arguments: args = {}, name } = request.params;
397
+ const callTool = async (name, args) => {
395
398
  if (name === "search_docs") {
396
399
  const results = await searchDocs(data, index, TOOL_INPUTS.search_docs.parse(args));
397
400
  return text(JSON.stringify(results, null, 2));
@@ -416,8 +419,18 @@ var buildServer = (data, index) => {
416
419
  }
417
420
  return text(JSON.stringify(result.navigation, null, 2));
418
421
  }
419
- return text(`Unknown tool: ${name}`, true);
420
- });
422
+ throw new McpError(ErrorCode.InvalidParams, `Unknown tool: ${name}`);
423
+ };
424
+ server.fallbackRequestHandler = async (request) => {
425
+ if (request.method !== "tools/call") {
426
+ throw new McpError(ErrorCode.MethodNotFound, "Method not found");
427
+ }
428
+ const params = TOOL_CALL_PARAMS.safeParse(request.params);
429
+ if (!params.success) {
430
+ throw new McpError(ErrorCode.InvalidParams, "Invalid tools/call params: `name` must be a string and `arguments`, when given, an object.");
431
+ }
432
+ return await callTool(params.data.name, params.data.arguments ?? {});
433
+ };
421
434
  return server;
422
435
  };
423
436
 
@@ -459,5 +472,5 @@ export {
459
472
  mcpStdioCommand
460
473
  };
461
474
 
462
- //# debugId=F74FD7C10F87037364756E2164756E21
463
- //# sourceMappingURL=chunk-yw7dm696.js.map
475
+ //# debugId=E29C9C03F27EAC9C64756E2164756E21
476
+ //# sourceMappingURL=chunk-3yce002v.js.map
@@ -4,11 +4,11 @@
4
4
  "sourcesContent": [
5
5
  "import { readFile } from \"node:fs/promises\";\n\nimport { defineCommand } from \"citty\";\n\nimport type { McpData } from \"../../ai/mcp/data.ts\";\nimport { serveMcpStdio } from \"../../ai/mcp/stdio.ts\";\nimport { commandMeta } from \"../command-meta.ts\";\n\nexport const mcpStdioCommand = defineCommand({\n args: {\n data: {\n description: \"Path to a serialized MCP data snapshot (JSON).\",\n required: true,\n type: \"string\",\n },\n },\n meta: commandMeta[\"mcp-stdio\"],\n async run({ args }) {\n // stdout belongs to the JSON-RPC transport from here on; every diagnostic\n // must go to stderr or the MCP client chokes on the stray line.\n let data: McpData;\n try {\n // SAFETY: the snapshot is written by `blume eval` itself as\n // JSON.stringify of an McpData; a hand-mangled file fails the parse and\n // lands in the catch below.\n data = JSON.parse(await readFile(args.data, \"utf-8\")) as McpData;\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n process.stderr.write(\n `blume mcp-stdio: cannot load the snapshot at ${args.data}: ${detail}\\n`\n );\n process.exit(1);\n }\n await serveMcpStdio(data);\n },\n});\n",
6
6
  "import { once } from \"node:events\";\nimport type { Readable, Writable } from \"node:stream\";\n\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\n\nimport type { McpData } from \"./data.ts\";\nimport { buildServer, createIndexProvider } from \"./server.ts\";\n\n/** Streams for `serveMcpStdio`, injectable so tests can use in-memory pipes. */\nexport interface McpStdioStreams {\n stdin?: Readable;\n stdout?: Writable;\n}\n\n/**\n * Serve a precomputed MCP data snapshot over a stdio transport until the\n * client hangs up.\n *\n * The SDK transport reads its input stream but never watches for its end, so\n * a client that simply closes the pipe — which is how every MCP host shuts a\n * stdio server down — would leave the process running forever. EOF on the\n * input stream therefore ends the serve loop explicitly.\n */\nexport const serveMcpStdio = async (\n data: McpData,\n streams: McpStdioStreams = {}\n): Promise<void> => {\n const stdin = streams.stdin ?? process.stdin;\n const stdout = streams.stdout ?? process.stdout;\n const transport = new StdioServerTransport(stdin, stdout);\n const server = buildServer(\n data,\n createIndexProvider(data.documents, data.defaultLocale)\n );\n await server.connect(transport);\n await once(stdin, \"end\");\n await transport.close();\n};\n",
7
- "import { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport type { ServerOptions } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { WebStandardStreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js\";\nimport {\n CallToolRequestSchema,\n ListResourcesRequestSchema,\n ListToolsRequestSchema,\n McpError,\n ReadResourceRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { z } from \"zod\";\n\nimport { withBasePath } from \"../../core/base-path.ts\";\nimport type { McpData } from \"./data.ts\";\nimport {\n createIndexProvider,\n getNavigation,\n getPageMarkdown,\n listPages,\n normalizeRoute,\n searchDocs,\n TOOL_INPUTS,\n urlFor,\n} from \"./query.ts\";\nimport type { OramaIndexProvider } from \"./query.ts\";\nimport { MCP_TOOLS } from \"./tools.ts\";\n\nexport { createIndexProvider } from \"./query.ts\";\nexport type { OramaIndexProvider } from \"./query.ts\";\n\n/**\n * The low-level SDK `Server` is used (rather than the high-level `McpServer`)\n * because the latter's `registerTool` is generic over the caller's Zod instance;\n * Blume's zod and the SDK's may resolve to different copies, whose types don't\n * unify. The operations themselves live in `query.ts`, shared with the JSON\n * docs API; this module is the MCP transport over them.\n */\n\n/** Every page resource is the page's agent Markdown. */\nconst RESOURCE_MIME_TYPE = \"text/markdown\";\n/** The MCP spec's JSON-RPC code for an unknown resource URI. */\nconst RESOURCE_NOT_FOUND = -32_002;\n/** URI scheme for page resources when no `deployment.site` is configured. */\nconst LOCAL_RESOURCE_SCHEME = \"blume:\";\n\nconst CORS_HEADERS = {\n \"Access-Control-Allow-Headers\":\n \"Content-Type, Mcp-Session-Id, Mcp-Protocol-Version\",\n \"Access-Control-Allow-Methods\": \"GET, POST, OPTIONS\",\n \"Access-Control-Allow-Origin\": \"*\",\n \"Access-Control-Expose-Headers\": \"Mcp-Session-Id\",\n};\n\n/**\n * A tool's advertised JSON Schema. The dialect key is dropped (noise in a\n * tools/list payload), as is the root `additionalProperties: false` — the\n * runtime strips unknown keys rather than rejecting them, and the advertised\n * schema shouldn't promise stricter validation than the server performs.\n */\nconst inputSchemaFor = (schema: z.ZodType) => {\n const {\n $schema: _dialect,\n additionalProperties: _closed,\n ...rest\n } = z.toJSONSchema(schema);\n return rest;\n};\n\n/** The `tools/list` payload, derived from shared metadata + input schemas. */\nconst TOOL_DEFINITIONS = MCP_TOOLS.map((tool) => ({\n annotations: tool.annotations,\n description: tool.description,\n inputSchema: inputSchemaFor(\n // SAFETY: TOOL_INPUTS declares a schema for every MCP_TOOLS name; the two\n // lists are maintained together so names and descriptions never drift.\n TOOL_INPUTS[tool.name as keyof typeof TOOL_INPUTS]\n ),\n name: tool.name,\n title: tool.title,\n}));\n\n/** One `resources/list` entry: a page served as `text/markdown`. */\ninterface PageResource {\n description?: string;\n mimeType: string;\n name: string;\n title: string;\n uri: string;\n}\n\n/**\n * A page's resource URI. Resource URIs must be absolute, so this is the page's\n * served URL when a site is configured (the same URL `search_docs` and\n * `list_pages` emit, so an agent can hand either back to `resources/read`),\n * and a `blume:` URI carrying the based route otherwise.\n */\nconst resourceUri = (route: string, data: McpData): string =>\n data.site\n ? urlFor(route, data)\n : `${LOCAL_RESOURCE_SCHEME}${withBasePath(data.base, route)}`;\n\n/** The `pages` key a resource URI (either form, or a bare route) names. */\nconst resourceRoute = (uri: string, data: McpData): string =>\n normalizeRoute(\n uri.startsWith(LOCAL_RESOURCE_SCHEME)\n ? uri.slice(LOCAL_RESOURCE_SCHEME.length)\n : uri,\n data\n );\n\n/** A tool call's text result, marked as an error when `isError` is set. */\nconst text = (value: string, isError = false) => {\n const content = [{ text: value, type: \"text\" as const }];\n return isError ? { content, isError: true } : { content };\n};\n\n/** Construct a fresh MCP server with Blume's read-only docs tools registered. */\nexport const buildServer = (\n data: McpData,\n index: OramaIndexProvider\n): Server => {\n const capabilities = { resources: {}, tools: {} };\n const serverOptions: ServerOptions = data.instructions\n ? { capabilities, instructions: data.instructions }\n : { capabilities };\n const server = new Server(\n { name: data.name, version: data.version },\n serverOptions\n );\n\n server.setRequestHandler(ListToolsRequestSchema, () => ({\n tools: TOOL_DEFINITIONS,\n }));\n\n // Every page doubles as a resource, so a client that attaches context by\n // URI (rather than calling tools) can browse and read the docs too. The\n // list is the same route set `list_pages` returns; reading one serves the\n // same agent Markdown `get_page` does.\n server.setRequestHandler(ListResourcesRequestSchema, () => ({\n resources: data.routes.map((route) => {\n const resource: PageResource = {\n mimeType: RESOURCE_MIME_TYPE,\n name: route.title,\n title: route.title,\n uri: resourceUri(route.route, data),\n };\n if (route.description) {\n resource.description = route.description;\n }\n return resource;\n }),\n }));\n\n server.setRequestHandler(ReadResourceRequestSchema, (request) => {\n const { uri } = request.params;\n const markdown = getPageMarkdown(data, resourceRoute(uri, data));\n if (markdown === undefined) {\n throw new McpError(\n RESOURCE_NOT_FOUND,\n `No page found at \"${uri}\". Use resources/list or list_pages to find valid URIs.`\n );\n }\n return {\n contents: [{ mimeType: RESOURCE_MIME_TYPE, text: markdown, uri }],\n };\n });\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { arguments: args = {}, name } = request.params;\n\n if (name === \"search_docs\") {\n const results = await searchDocs(\n data,\n index,\n TOOL_INPUTS.search_docs.parse(args)\n );\n return text(JSON.stringify(results, null, 2));\n }\n\n if (name === \"get_page\") {\n const input = TOOL_INPUTS.get_page.parse(args);\n const key = normalizeRoute(input.route, data);\n const markdown = getPageMarkdown(data, key);\n if (markdown === undefined) {\n return text(\n `No page found at \"${key}\". Use list_pages or search_docs to find valid routes.`,\n true\n );\n }\n return text(markdown);\n }\n\n if (name === \"list_pages\") {\n const listing = listPages(data, TOOL_INPUTS.list_pages.parse(args));\n return text(JSON.stringify(listing, null, 2));\n }\n\n if (name === \"get_navigation\") {\n const result = getNavigation(\n data,\n TOOL_INPUTS.get_navigation.parse(args)\n );\n if (\"error\" in result) {\n return text(result.error, true);\n }\n return text(JSON.stringify(result.navigation, null, 2));\n }\n\n return text(`Unknown tool: ${name}`, true);\n });\n\n return server;\n};\n\n/**\n * Build a stateless Streamable-HTTP MCP request handler from a data snapshot.\n *\n * The Orama index is built once and reused; a fresh `Server` and transport are\n * created per request (required by the SDK's stateless mode, which skips session\n * tracking). `enableJsonResponse` makes each call a plain request/response — no\n * SSE — which suits read-only docs tools and runs on any adapter (Node, Vercel,\n * Netlify, Cloudflare). CORS is added so browser-based connectors (e.g.\n * claude.ai) can reach the endpoint.\n */\nexport const createMcpFetchHandler = (\n data: McpData\n): ((request: Request) => Promise<Response>) => {\n const index = createIndexProvider(data.documents, data.defaultLocale);\n\n return async (request: Request): Promise<Response> => {\n if (request.method === \"OPTIONS\") {\n return new Response(null, { headers: CORS_HEADERS, status: 204 });\n }\n if (request.method === \"GET\") {\n // No server-initiated streams are needed for read-only tools.\n return new Response(\"Method Not Allowed\", {\n headers: { ...CORS_HEADERS, Allow: \"POST, OPTIONS\" },\n status: 405,\n });\n }\n\n const server = buildServer(data, index);\n const transport = new WebStandardStreamableHTTPServerTransport({\n enableJsonResponse: true,\n // The SDK enables stateless mode only when this is `undefined`; `null` is\n // not an accepted value for the `(() => string) | undefined` option.\n // oxlint-disable-next-line sonarjs/no-undefined-assignment\n sessionIdGenerator: undefined,\n });\n await server.connect(transport);\n const response = await transport.handleRequest(request);\n\n const headers = new Headers(response.headers);\n for (const [key, value] of Object.entries(CORS_HEADERS)) {\n headers.set(key, value);\n }\n return new Response(response.body, {\n headers,\n status: response.status,\n statusText: response.statusText,\n });\n };\n};\n",
7
+ "import { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport type { ServerOptions } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { WebStandardStreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js\";\nimport {\n ErrorCode,\n ListResourcesRequestSchema,\n ListToolsRequestSchema,\n McpError,\n ReadResourceRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { z } from \"zod\";\n\nimport { withBasePath } from \"../../core/base-path.ts\";\nimport { readCappedText } from \"../../core/request-body.ts\";\nimport type { McpData } from \"./data.ts\";\nimport {\n createIndexProvider,\n getNavigation,\n getPageMarkdown,\n listPages,\n normalizeRoute,\n searchDocs,\n TOOL_INPUTS,\n urlFor,\n} from \"./query.ts\";\nimport type { OramaIndexProvider } from \"./query.ts\";\nimport { MCP_TOOLS } from \"./tools.ts\";\n\nexport { createIndexProvider } from \"./query.ts\";\nexport type { OramaIndexProvider } from \"./query.ts\";\n\n/**\n * The low-level SDK `Server` is used (rather than the high-level `McpServer`)\n * because the latter's `registerTool` is generic over the caller's Zod instance;\n * Blume's zod and the SDK's may resolve to different copies, whose types don't\n * unify. The operations themselves live in `query.ts`, shared with the JSON\n * docs API; this module is the MCP transport over them.\n */\n\n/** Every page resource is the page's agent Markdown. */\nconst RESOURCE_MIME_TYPE = \"text/markdown\";\n/** The MCP spec's JSON-RPC code for an unknown resource URI. */\nconst RESOURCE_NOT_FOUND = -32_002;\n/** URI scheme for page resources when no `deployment.site` is configured. */\nconst LOCAL_RESOURCE_SCHEME = \"blume:\";\n/**\n * The largest request body the HTTP endpoint reads. A JSON-RPC call to these\n * tools is a few hundred bytes; the cap keeps one oversized POST from being\n * buffered whole, as the assistant route's does.\n */\nconst MCP_BODY_LIMIT_BYTES = 65_536;\n\nconst CORS_HEADERS = {\n \"Access-Control-Allow-Headers\":\n \"Content-Type, Mcp-Session-Id, Mcp-Protocol-Version\",\n \"Access-Control-Allow-Methods\": \"GET, POST, OPTIONS\",\n \"Access-Control-Allow-Origin\": \"*\",\n \"Access-Control-Expose-Headers\": \"Mcp-Session-Id\",\n};\n\n/**\n * A tool's advertised JSON Schema. The dialect key is dropped (noise in a\n * tools/list payload), as is the root `additionalProperties: false` — the\n * runtime strips unknown keys rather than rejecting them, and the advertised\n * schema shouldn't promise stricter validation than the server performs.\n */\nconst inputSchemaFor = (schema: z.ZodType) => {\n const {\n $schema: _dialect,\n additionalProperties: _closed,\n ...rest\n } = z.toJSONSchema(schema);\n return rest;\n};\n\n/** The `tools/list` payload, derived from shared metadata + input schemas. */\nconst TOOL_DEFINITIONS = MCP_TOOLS.map((tool) => ({\n annotations: tool.annotations,\n description: tool.description,\n inputSchema: inputSchemaFor(\n // SAFETY: TOOL_INPUTS declares a schema for every MCP_TOOLS name; the two\n // lists are maintained together so names and descriptions never drift.\n TOOL_INPUTS[tool.name as keyof typeof TOOL_INPUTS]\n ),\n name: tool.name,\n title: tool.title,\n}));\n\n/** One `resources/list` entry: a page served as `text/markdown`. */\ninterface PageResource {\n description?: string;\n mimeType: string;\n name: string;\n title: string;\n uri: string;\n}\n\n/**\n * A page's resource URI. Resource URIs must be absolute, so this is the page's\n * served URL when a site is configured (the same URL `search_docs` and\n * `list_pages` emit, so an agent can hand either back to `resources/read`),\n * and a `blume:` URI carrying the based route otherwise.\n */\nconst resourceUri = (route: string, data: McpData): string =>\n data.site\n ? urlFor(route, data)\n : `${LOCAL_RESOURCE_SCHEME}${withBasePath(data.base, route)}`;\n\n/** The `pages` key a resource URI (either form, or a bare route) names. */\nconst resourceRoute = (uri: string, data: McpData): string =>\n normalizeRoute(\n uri.startsWith(LOCAL_RESOURCE_SCHEME)\n ? uri.slice(LOCAL_RESOURCE_SCHEME.length)\n : uri,\n data\n );\n\n/**\n * The `tools/call` params, as far as the server reads them before dispatch:\n * each tool validates its own `arguments` against `TOOL_INPUTS`.\n */\nconst TOOL_CALL_PARAMS = z.object({\n arguments: z.record(z.string(), z.unknown()).optional(),\n name: z.string(),\n});\n\n/** A tool call's `arguments` object, before the tool's own validation. */\ntype ToolArguments = NonNullable<\n z.output<typeof TOOL_CALL_PARAMS>[\"arguments\"]\n>;\n\n/** A tool call's text result, marked as an error when `isError` is set. */\nconst text = (value: string, isError = false) => {\n const content = [{ text: value, type: \"text\" as const }];\n return isError ? { content, isError: true } : { content };\n};\n\n/** Construct a fresh MCP server with Blume's read-only docs tools registered. */\nexport const buildServer = (\n data: McpData,\n index: OramaIndexProvider\n): Server => {\n const capabilities = { resources: {}, tools: {} };\n const serverOptions: ServerOptions = data.instructions\n ? { capabilities, instructions: data.instructions }\n : { capabilities };\n const server = new Server(\n { name: data.name, version: data.version },\n serverOptions\n );\n\n server.setRequestHandler(ListToolsRequestSchema, () => ({\n tools: TOOL_DEFINITIONS,\n }));\n\n // Every page doubles as a resource, so a client that attaches context by\n // URI (rather than calling tools) can browse and read the docs too. The\n // list is the same route set `list_pages` returns; reading one serves the\n // same agent Markdown `get_page` does.\n server.setRequestHandler(ListResourcesRequestSchema, () => ({\n resources: data.routes.map((route) => {\n const resource: PageResource = {\n mimeType: RESOURCE_MIME_TYPE,\n name: route.title,\n title: route.title,\n uri: resourceUri(route.route, data),\n };\n if (route.description) {\n resource.description = route.description;\n }\n return resource;\n }),\n }));\n\n server.setRequestHandler(ReadResourceRequestSchema, (request) => {\n const { uri } = request.params;\n const markdown = getPageMarkdown(data, resourceRoute(uri, data));\n if (markdown === undefined) {\n throw new McpError(\n RESOURCE_NOT_FOUND,\n `No page found at \"${uri}\". Use resources/list or list_pages to find valid URIs.`\n );\n }\n return {\n contents: [{ mimeType: RESOURCE_MIME_TYPE, text: markdown, uri }],\n };\n });\n\n const callTool = async (name: string, args: ToolArguments) => {\n if (name === \"search_docs\") {\n const results = await searchDocs(\n data,\n index,\n TOOL_INPUTS.search_docs.parse(args)\n );\n return text(JSON.stringify(results, null, 2));\n }\n\n if (name === \"get_page\") {\n const input = TOOL_INPUTS.get_page.parse(args);\n const key = normalizeRoute(input.route, data);\n const markdown = getPageMarkdown(data, key);\n if (markdown === undefined) {\n return text(\n `No page found at \"${key}\". Use list_pages or search_docs to find valid routes.`,\n true\n );\n }\n return text(markdown);\n }\n\n if (name === \"list_pages\") {\n const listing = listPages(data, TOOL_INPUTS.list_pages.parse(args));\n return text(JSON.stringify(listing, null, 2));\n }\n\n if (name === \"get_navigation\") {\n const result = getNavigation(\n data,\n TOOL_INPUTS.get_navigation.parse(args)\n );\n if (\"error\" in result) {\n return text(result.error, true);\n }\n return text(JSON.stringify(result.navigation, null, 2));\n }\n\n // The MCP spec answers an unknown tool with a protocol error, not a\n // failed tool result.\n throw new McpError(ErrorCode.InvalidParams, `Unknown tool: ${name}`);\n };\n\n // `tools/call` goes through the fallback handler rather than a registered\n // one: the SDK parses a registered handler's request against its own\n // schema first and reports a malformed call (`arguments` that aren't an\n // object) as -32603 Internal error carrying a raw Zod dump. Reading the\n // params here answers it as -32602 Invalid params with a short message.\n server.fallbackRequestHandler = async (request) => {\n if (request.method !== \"tools/call\") {\n throw new McpError(ErrorCode.MethodNotFound, \"Method not found\");\n }\n const params = TOOL_CALL_PARAMS.safeParse(request.params);\n if (!params.success) {\n throw new McpError(\n ErrorCode.InvalidParams,\n \"Invalid tools/call params: `name` must be a string and `arguments`, when given, an object.\"\n );\n }\n return await callTool(params.data.name, params.data.arguments ?? {});\n };\n\n return server;\n};\n\n/** A JSON-RPC error response the transport would send, for errors raised before it runs. */\nconst jsonRpcError = (\n status: number,\n code: number,\n message: string\n): Response =>\n Response.json(\n { error: { code, message }, id: null, jsonrpc: \"2.0\" },\n { headers: CORS_HEADERS, status }\n );\n\n/**\n * Build a stateless Streamable-HTTP MCP request handler from a data snapshot.\n *\n * The Orama index is built once and reused; a fresh `Server` and transport are\n * created per request (required by the SDK's stateless mode, which skips session\n * tracking). `enableJsonResponse` makes each call a plain request/response — no\n * SSE — which suits read-only docs tools and runs on any adapter (Node, Vercel,\n * Netlify, Cloudflare). CORS is added so browser-based connectors (e.g.\n * claude.ai) can reach the endpoint.\n */\nexport const createMcpFetchHandler = (\n data: McpData\n): ((request: Request) => Promise<Response>) => {\n const index = createIndexProvider(data.documents, data.defaultLocale);\n\n return async (request: Request): Promise<Response> => {\n if (request.method === \"OPTIONS\") {\n return new Response(null, { headers: CORS_HEADERS, status: 204 });\n }\n if (request.method === \"GET\") {\n // No server-initiated streams are needed for read-only tools.\n return new Response(\"Method Not Allowed\", {\n headers: { ...CORS_HEADERS, Allow: \"POST, OPTIONS\" },\n status: 405,\n });\n }\n\n // The transport would read the body with `request.json()`, buffering\n // whatever a client sends; read it under the cap first and hand the\n // transport a copy.\n let forwarded = request;\n if (request.method === \"POST\") {\n const body = await readCappedText(request, MCP_BODY_LIMIT_BYTES);\n if (body === undefined) {\n return jsonRpcError(\n 413,\n ErrorCode.InvalidRequest,\n \"Request too large: the body must be at most 64 KB.\"\n );\n }\n forwarded = new Request(request.url, {\n body,\n headers: request.headers,\n method: \"POST\",\n });\n }\n\n const server = buildServer(data, index);\n const transport = new WebStandardStreamableHTTPServerTransport({\n enableJsonResponse: true,\n // The SDK enables stateless mode only when this is `undefined`; `null` is\n // not an accepted value for the `(() => string) | undefined` option.\n // oxlint-disable-next-line sonarjs/no-undefined-assignment\n sessionIdGenerator: undefined,\n });\n await server.connect(transport);\n const response = await transport.handleRequest(forwarded);\n\n const headers = new Headers(response.headers);\n for (const [key, value] of Object.entries(CORS_HEADERS)) {\n headers.set(key, value);\n }\n return new Response(response.body, {\n headers,\n status: response.status,\n statusText: response.statusText,\n });\n };\n};\n",
8
8
  "import { z } from \"zod\";\n\nimport {\n normalizeRoute as normalizePageRoute,\n stripBasePath,\n withBasePath,\n} from \"../../core/base-path.ts\";\nimport { absoluteUrl } from \"../../core/site-url.ts\";\nimport { trimEnd } from \"../../core/trim.ts\";\nimport type { Navigation } from \"../../core/types.ts\";\nimport { buildOramaIndex, queryOramaIndex } from \"../../search/orama-index.ts\";\nimport type { OramaDoc } from \"../../search/orama-index.ts\";\nimport type { McpData } from \"./data.ts\";\n\n/**\n * The read-only docs operations behind the agent-facing surfaces — search,\n * page lookup, the page listing, and the navigation tree — over an\n * {@link McpData} snapshot. The MCP server (`server.ts`) and the JSON docs API\n * (`ai/api/handlers.ts`) are both thin transports over these, so a tool call\n * and its REST twin can never answer differently. SDK-free on purpose: the\n * prerendered API endpoints import this module without pulling the MCP SDK\n * into their bundle.\n *\n * Each input is defined once in Zod: the runtime parse and the JSON Schema\n * advertised by `tools/list` (via `z.toJSONSchema`) derive from the same\n * definition, so they cannot drift.\n */\n\n/** Default and maximum number of hits returned by a search. */\nexport const DEFAULT_SEARCH_LIMIT = 8;\nexport const MAX_SEARCH_LIMIT = 20;\n/** Excerpt length when a page has no description. */\nconst EXCERPT_LENGTH = 200;\n\n// Each field is a preprocess pipe: the input side accepts the sloppy shapes\n// LLM callers actually send (a bare string for an array field, `[]`/`{}`\n// meaning \"no filter\", out-of-range limits clamped rather than rejected), and\n// the pipe's *output* side is the clean shape — which is exactly what\n// `z.toJSONSchema` emits for `tools/list`. No coercion can ever fail, so a\n// tool call is never rejected on argument shape, matching the previous\n// hand-rolled coercions.\n\n/**\n * The optional content-type filter `search_docs` and `list_pages` share.\n * `[]` or no usable strings mean \"no filter\", not \"match nothing\"; a bare\n * string is accepted as a one-element list.\n */\nconst contentTypesField = z.preprocess((value) => {\n const list = (Array.isArray(value) ? value : [value]).filter(\n (entry): entry is string => typeof entry === \"string\"\n );\n return list.length > 0 ? list : undefined;\n}, z.array(z.string()).optional().describe('Only include pages of these content types (frontmatter `type`, e.g. `[\"doc\", \"rfc\"]`). `list_pages` shows each page\\'s type. Omit to include every type.'));\n\n/**\n * The optional facet filter `search_docs` and `list_pages` share. Only\n * string-valued entries survive; an empty `{}` means \"no filter\".\n */\n/** Accepts any plain object, so the string-valued entries can be sifted out. */\nconst looseFacetObject = z.record(z.string(), z.unknown());\n\nconst filtersField = z.preprocess((value) => {\n const candidate = looseFacetObject.safeParse(value);\n if (!candidate.success) {\n return;\n }\n const entries = Object.entries(candidate.data).filter(\n (entry): entry is [string, string] => typeof entry[1] === \"string\"\n );\n return entries.length > 0 ? Object.fromEntries(entries) : undefined;\n}, z.record(z.string(), z.string()).optional().describe('Only include pages matching every facet, key → required value (e.g. `{\"status\": \"enforced\"}`). Facets are metadata the site declares per content type; `list_pages` shows each page\\'s facet values. Omit for no facet filtering.'));\n\n/** Clamped into range rather than rejected; non-numeric means the default. */\nconst limitField = z.preprocess(\n (value) => {\n // `Number` is the identity on numbers, so one conversion covers both the\n // well-typed call and a numeric string.\n const num = Number(value);\n return Number.isFinite(num)\n ? Math.min(Math.max(Math.trunc(num), 1), MAX_SEARCH_LIMIT)\n : undefined;\n },\n z\n .int()\n .min(1)\n .max(MAX_SEARCH_LIMIT)\n .optional()\n .describe(`Maximum hits to return (default ${DEFAULT_SEARCH_LIMIT}).`)\n);\n\n/** A required text field; a missing or non-string value coerces to \"\". */\nconst textField = (description: string) =>\n z.preprocess((value) => {\n const parsed = z.string().safeParse(value);\n return parsed.success ? parsed.data : \"\";\n }, z.string().describe(description));\n\n/** An optional trimmed text field; blank or non-string means \"absent\". */\nconst optionalTextField = (description: string) =>\n z.preprocess((value) => {\n const parsed = z.string().safeParse(value);\n const trimmed = parsed.success ? parsed.data.trim() : \"\";\n return trimmed || undefined;\n }, z.string().optional().describe(description));\n\n/** The optional locale filter `search_docs` and `list_pages` share. */\nconst localeField = optionalTextField(\n \"Only include pages in this locale (e.g. `fr`). Omit for every language.\"\n);\n\n/** The optional docs-version scope `search_docs` and `list_pages` share. */\nconst versionField = optionalTextField(\n 'Docs version to scope to on a versioned site: `\"latest\"` (the default — current docs only), `\"all\"` (every version), or an archived version id (e.g. `\"v1.0\"`). Ignored when the site is unversioned.'\n);\n\n/** Every tool's input schema — the runtime parse and tools/list source. */\nexport const TOOL_INPUTS = {\n get_navigation: z.object({\n locale: optionalTextField(\n \"Locale whose navigation tree to return (defaults to the default locale).\"\n ),\n version: optionalTextField(\n \"Archived version id whose tree to return (defaults to the current docs).\"\n ),\n }),\n get_page: z.object({\n route: textField(\"The page route, e.g. `/guides/install`.\"),\n }),\n list_pages: z.object({\n contentTypes: contentTypesField,\n filters: filtersField,\n locale: localeField,\n version: versionField,\n }),\n search_docs: z.object({\n contentTypes: contentTypesField,\n filters: filtersField,\n limit: limitField,\n locale: localeField,\n query: textField(\"The search query.\"),\n version: versionField,\n }),\n};\n\n/** One `search_docs` result entry; `version` only appears on versioned sites. */\nexport interface SearchHitPayload {\n contentType: string | undefined;\n excerpt: string;\n facets: Record<string, string> | undefined;\n route: string;\n title: string;\n url: string;\n version?: string;\n}\n\n/** One `list_pages` entry; `version` only appears on versioned sites. */\nexport interface PageListingPayload {\n contentType: string;\n description: string | undefined;\n facets: Record<string, string> | undefined;\n lastModified: string | null;\n route: string;\n title: string;\n url: string;\n version?: string;\n}\n\n/** Whether a page's facet values satisfy every requested filter entry. */\nconst matchesFacets = (\n facets: Record<string, string> | undefined,\n filters: Record<string, string>\n): boolean =>\n Object.entries(filters).every(([key, value]) => facets?.[key] === value);\n\n/**\n * Resolve the `version` scope on a versioned site: `undefined` disables the\n * filter (`\"all\"`), `\"\"` is the current docs (the default — agents almost\n * always want the live documentation), and anything else is an archived id\n * (an unknown id simply matches nothing). On an unversioned site the input is\n * ignored entirely. The input arrives pre-trimmed (blank coerced to absent)\n * from the tool's input schema.\n */\nconst asVersionScope = (\n value: string | undefined,\n data: McpData\n): string | undefined => {\n if (!data.archivedVersions) {\n return;\n }\n if (value === \"all\") {\n return;\n }\n if (value === undefined || value === \"latest\" || value === \"current\") {\n return \"\";\n }\n return value;\n};\n\n/**\n * Error message for a `get_navigation` version id that isn't a configured\n * archived version, or `null` when the id is valid (or the site is\n * unversioned, where the id is ignored like the other tools' scopes). Unlike\n * `asVersionScope`'s match-nothing filters, a bad id here would otherwise\n * silently return the *current* tree posing as the requested snapshot.\n */\nconst unknownVersionError = (\n versionId: string | undefined,\n data: McpData\n): string | null =>\n versionId &&\n data.archivedVersions &&\n !data.archivedVersions.includes(versionId)\n ? `Unknown version \"${versionId}\". Archived versions: ${data.archivedVersions.join(\", \")}.`\n : null;\n\n/**\n * Normalize a user-supplied route to a `pages` key (`/`, `/a/b`, no suffix).\n * Accepts a full URL too — `search_docs` hits and llms.txt entries carry\n * `site` + `deployment.base`, and an agent following \"pass a route from\n * `search_docs`\" will hand one straight back — reducing it to its base-less,\n * percent-decoded path.\n */\nexport const normalizeRoute = (input: string, data: McpData): string => {\n let value = input.trim();\n if (/^https?:\\/\\//iu.test(value)) {\n try {\n value = new URL(value).pathname;\n } catch {\n // Not parseable as a URL after all; treat it as a path.\n }\n }\n try {\n value = decodeURI(value);\n } catch {\n // Malformed percent sequence — compare it as written.\n }\n // Trailing slashes come off before the suffix so `/a/b.md/` still loses its\n // `.md`; normalizePageRoute then settles the leading slash.\n const noSuffix = trimEnd(value, \"/\").replace(/\\.mdx?$/u, \"\");\n return stripBasePath(data.base, normalizePageRoute(noSuffix));\n};\n\n/** Build the absolute (or root-relative) URL for a route. */\nexport const urlFor = (route: string, data: McpData): string => {\n // Routes are base-less manifest paths; layer `deployment.base` on top so the\n // URL matches where the page is served (the sitemap/llms.txt convention).\n const path = withBasePath(data.base, route);\n // Concatenate rather than `new URL(path, site)` — a root-absolute path\n // would drop the base path of a subpath deployment (`acme.com/docs`).\n return data.site ? absoluteUrl(data.site, path) : path;\n};\n\n/** A hit's excerpt: its description, else the head of its content with an\n * ellipsis only when something was actually cut off. */\nconst excerptFor = (doc: OramaDoc): string => {\n if (doc.description) {\n return doc.description;\n }\n const head = doc.content.slice(0, EXCERPT_LENGTH).trim();\n return doc.content.length > EXCERPT_LENGTH ? `${head}…` : head;\n};\n\n/** Lazily builds the Orama index over a snapshot's documents, once. */\nexport type OramaIndexProvider = () => Promise<\n Awaited<ReturnType<typeof buildOramaIndex>>\n>;\n\n/**\n * Memoize the search index so every server built from a snapshot shares it.\n * `locale` is the snapshot's `defaultLocale`, forwarded so non-Latin scripts\n * (Japanese and Chinese, but equally Cyrillic, Greek, Hebrew, Devanagari…)\n * get a word-segmenting tokenizer.\n */\nexport const createIndexProvider = (\n documents: OramaDoc[],\n locale?: string\n): OramaIndexProvider => {\n let dbPromise: ReturnType<OramaIndexProvider> | null = null;\n return function provideIndex() {\n dbPromise ??= buildOramaIndex(documents, locale);\n return dbPromise;\n };\n};\n\n/** Full-text search over the snapshot; the `search_docs` operation. */\nexport const searchDocs = async (\n data: McpData,\n index: OramaIndexProvider,\n input: z.output<typeof TOOL_INPUTS.search_docs>\n): Promise<SearchHitPayload[]> => {\n const db = await index();\n const hits = await queryOramaIndex(\n db,\n input.query,\n input.limit ?? DEFAULT_SEARCH_LIMIT,\n {\n contentTypes: input.contentTypes,\n facets: input.filters,\n locale: input.locale,\n version: asVersionScope(input.version, data),\n }\n );\n // `route` is the key `get_page` takes (the tool descriptions promise\n // it); `url` is where the page is served.\n return hits.map((doc: OramaDoc) => {\n const hit: SearchHitPayload = {\n contentType: doc.contentType,\n excerpt: excerptFor(doc),\n facets: doc.facets,\n route: doc.route,\n title: doc.title,\n url: urlFor(doc.route, data),\n };\n if (data.archivedVersions) {\n hit.version = doc.version ?? \"\";\n }\n return hit;\n });\n};\n\n/** A page's agent Markdown, or `undefined` when no page has the route. */\nexport const getPageMarkdown = (\n data: McpData,\n route: string\n): string | undefined => data.pages[route];\n\n/** Every non-hidden route matching the filters; the `list_pages` operation. */\nexport const listPages = (\n data: McpData,\n input: z.output<typeof TOOL_INPUTS.list_pages>\n): PageListingPayload[] => {\n const { contentTypes, filters, locale } = input;\n const versionScope = asVersionScope(input.version, data);\n return data.routes\n .filter(\n (route) =>\n (!contentTypes || contentTypes.includes(route.contentType)) &&\n (!filters || matchesFacets(route.facets, filters)) &&\n (!locale || route.locale === locale) &&\n (versionScope === undefined || route.version === versionScope)\n )\n .map((route) => {\n const listing: PageListingPayload = {\n contentType: route.contentType,\n description: route.description,\n facets: route.facets,\n lastModified: route.lastModified,\n route: route.route,\n title: route.title,\n url: urlFor(route.route, data),\n };\n if (data.archivedVersions) {\n listing.version = route.version;\n }\n return listing;\n });\n};\n\n/**\n * The navigation tree for a locale and version; the `get_navigation`\n * operation. A version id selects the snapshot's tree; a locale selects its\n * language (falling back through the default locale to any tree the snapshot\n * has). Without a version, a locale selects the current docs' localized\n * tree. An unknown id on a versioned site is an error string — the current\n * tree would silently masquerade as the requested snapshot.\n */\nexport const getNavigation = (\n data: McpData,\n input: z.output<typeof TOOL_INPUTS.get_navigation>\n): { error: string } | { navigation: Navigation } => {\n const { locale, version: versionId } = input;\n const unknownVersion = unknownVersionError(versionId, data);\n if (unknownVersion) {\n return { error: unknownVersion };\n }\n let { navigation } = data;\n const byLocale = versionId\n ? data.navigationByVersion?.[versionId]\n : undefined;\n if (byLocale) {\n navigation =\n (locale ? byLocale[locale] : undefined) ??\n byLocale[data.defaultLocale ?? \"\"] ??\n Object.values(byLocale)[0] ??\n navigation;\n } else if (locale && data.navigationByLocale?.[locale]) {\n navigation = data.navigationByLocale[locale];\n }\n return { navigation };\n};\n",
9
9
  "import { create, insertMultiple, search } from \"@orama/orama\";\nimport type {\n AnyOrama,\n EnumArrComparisonOperator,\n EnumComparisonOperator,\n Tokenizer,\n} from \"@orama/orama\";\n\n/**\n * The minimal document shape both the client-side search dialog and the\n * server-side MCP `search_docs` tool index. Mirrors the `blume-search.json`\n * entries built by `buildSearchDocuments`.\n */\nexport interface OramaDoc {\n content: string;\n description: string;\n route: string;\n title: string;\n /** Locale code; indexed as an enum so queries can filter to one language. */\n locale?: string;\n /**\n * Docs version; indexed as an enum so queries can filter to one version.\n * The current docs carry `\"\"`, which the enum stores and matches exactly.\n */\n version?: string;\n /** Resolved page `type`; indexed as an enum so queries can filter by type. */\n contentType?: string;\n /** Declared facet values (`content.types.<type>.facets`), key → value. */\n facets?: Record<string, string>;\n /** Carried through for the search dialog's breadcrumb + filter pills. Stored\n * but not indexed, so they ride along on the returned document untouched. */\n breadcrumb?: string[];\n section?: string;\n}\n\nconst SCHEMA = {\n content: \"string\",\n // Enums (not full-text \"string\") so `where` does an exact-match filter.\n contentType: \"enum\",\n description: \"string\",\n // Facets, flattened to `key:value` terms — enum[] so one static schema\n // serves every project's facet keys, with `containsAll` matching a filter\n // set. Derived from `facets` at insert time.\n facetTerms: \"enum[]\",\n locale: \"enum\",\n route: \"string\",\n title: \"string\",\n version: \"enum\",\n} as const;\n\n/** Flatten a facet map to the `key:value` terms the `facetTerms` enum holds. */\nconst toFacetTerms = (facets: Record<string, string>): string[] =>\n Object.entries(facets).map(([key, value]) => `${key}:${value}`);\n\n/** Title and description outrank body text, matching the search dialog. */\nconst BOOST = { description: 2, title: 3 };\n\n/**\n * The script whose text Orama's default tokenizer keeps mostly intact. Its\n * delimiter class is `/[^A-Za-zàèéìòóù0-9_'-]+/`, so every character outside\n * that set counts as a separator: text in any other script collapses to zero\n * tokens and every query silently returns no hits. Unspaced scripts are the\n * best-known casualty, but the failure is not about spacing — Russian, Greek,\n * Hebrew and Hindi lose their tokens the same way Japanese does.\n */\nconst LATIN_SCRIPT = \"Latn\";\n\n/**\n * Parse a locale tag, tolerating the legacy forms that reach here from\n * hand-written config: underscores (`ru_RU`), POSIX suffixes (`ja_JP.UTF-8`,\n * `zh_TW@Big5`), and extlang tags ICU rejects (`zh-cmn-Hans`). The POSIX\n * codeset/modifier suffix is dropped first — it carries no BCP 47 meaning,\n * and dropping it keeps an explicit script subtag in play: `az_Cyrl.UTF-8`\n * must resolve to `Cyrl`, not fall back to `az` and maximize to Latin.\n * `Intl.Locale` still throws on extlang forms, so a failed parse retries with\n * the primary language subtag alone — enough to resolve the script, which is\n * all that gates the tokenizer. Returns `undefined` when even that subtag is\n * unparseable.\n */\nconst parseLocale = (tag: string): Intl.Locale | undefined => {\n const hyphenated = tag.replace(/[.@].*$/u, \"\").replaceAll(\"_\", \"-\");\n try {\n return new Intl.Locale(hyphenated);\n } catch {\n // Retry below with the primary subtag.\n }\n const [primary = \"\"] = hyphenated.split(\"-\");\n try {\n return new Intl.Locale(primary);\n } catch {\n return undefined;\n }\n};\n\n/**\n * The maximized locale — script and language filled in from CLDR likely-subtags\n * and alias data, so `ru` resolves to `Cyrl`, `cmn` to the canonical `zh`, and\n * an explicit script subtag is honored: `sr-Latn` reports `Latn` while\n * `az-Cyrl` reports `Cyrl`. `undefined` for tags {@link parseLocale} cannot\n * salvage; a well-formed tag ICU knows nothing about keeps an `undefined`\n * script instead.\n */\nconst resolveLocale = (tag: string): Intl.Locale | undefined =>\n parseLocale(tag)?.maximize();\n\n/**\n * Indexes whose language maximizes to one of these scripts are built as\n * character bigrams rather than whole segments, and queried to match\n * accordingly. Japanese and Chinese write compounds in {@link BIGRAM_SCRIPTS}\n * without delimiters, and the writing system — not the language subtag — is\n * what carries that convention: `yue` (Cantonese) maximizes to `Hant` and\n * needs bigrams the same way `zh` does. Korean maximizes to `Kore` and Thai\n * to `Thai`; Korean separates words with spaces and Thai has no comparable\n * bigram convention, so both keep the plain segmented tokens even where a\n * page mixes in Han or kana.\n *\n * Dictionary segmentation alone drops the adjacency that makes a compound term\n * distinctive: 資金決済法 becomes 資金 / 決済 / 法, and because Orama scores a\n * bag of words, a page that merely mentions each fragment somewhere outranks\n * the page about the law itself. Bigrams put that adjacency back as index\n * terms, and dropping the whole-segment tokens keeps a fragment as common as\n * 法 from matching on its own.\n */\nconst BIGRAM_INDEX_SCRIPTS = new Set([\"Hans\", \"Hant\", \"Jpan\"]);\n\n/**\n * Whether the index keyed to this tokenizer language is built from bigrams.\n * {@link segmentingTokenizer} stores the canonical maximized language, so the\n * builder and the strict pass in {@link queryOramaIndex} share this predicate\n * — an index is never built from bigrams that the query side then matches\n * loosely, or vice versa.\n */\nconst isBigramLanguage = (language: string): boolean => {\n const script = resolveLocale(language)?.script;\n return script !== undefined && BIGRAM_INDEX_SCRIPTS.has(script);\n};\n\n/**\n * Segments written entirely in these scripts are the ones re-cut into bigrams,\n * matching the scripts Lucene's CJK analyzer bigrams. Property escapes rather\n * than ranges, so ideographs outside the basic plane are covered as well —\n * 𠮟, the 常用漢字表 form of しかる, is one. The literals that follow belong to\n * no script of their own but appear only inside such words: the iteration\n * marks, the prolonged sound mark, and the halfwidth voiced sound marks.\n */\nconst BIGRAM_SCRIPTS =\n /^[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}々〆〇ー゙゚]+$/u;\n\n/**\n * One index term inside a word-like segment. Being word-like does not make a\n * segment all letters: UAX #29 keeps connector punctuation, format characters\n * and mid-number punctuation *within* a word, so `Intl.Segmenter` reports\n * スネーク_ケース and robots.txt as one segment each. A term is a run of\n * letters, combining marks and digits. Marks are spelling, not punctuation —\n * Thai writes vowels and tones as combining marks, so dropping them leaves\n * consonant skeletons that collapse distinct words (เสื้อ, shirt, and เสือ,\n * tiger, differ by one mark). Two separators stay where the surrounding text\n * makes them part of the word: an apostrophe followed by a letter (don't),\n * and a decimal point or thousands separator flanked by digits (1.0.3,\n * 1,000) — split, either would leave one-letter and one-digit fragments that\n * co-occur on unrelated pages.\n */\nconst TERM =\n /[\\p{L}\\p{M}\\p{N}]+(?:(?:['’](?=\\p{L})|(?<=\\p{N})[.,](?=\\p{N}))[\\p{L}\\p{M}\\p{N}]+)*/gu;\n\n/**\n * A term written entirely in Latin script (plus digits and the separators\n * {@link TERM} keeps within a word). Orama's default tokenizer folds the\n * accented vowels it recognizes (café → cafe), so a segmented index folds\n * Latin terms too — otherwise switching a Cyrillic- or Greek-default site to\n * the segmenting tokenizer would silently drop the unaccented-query matches\n * the default tokenizer provided. Only all-Latin terms fold: marks are\n * spelling elsewhere (Thai vowels and tones; the breve that separates\n * Cyrillic й from и), so a term carrying any other script keeps its marks.\n */\nconst LATIN_TERM = /^[\\p{Script=Latin}\\p{N}'’.,]+$/u;\n\nconst MARKS = /\\p{M}+/gu;\n\nconst foldDiacritics = (term: string): string =>\n LATIN_TERM.test(term) ? term.normalize(\"NFD\").replace(MARKS, \"\") : term;\n\n/**\n * Emit every overlapping 2-character window of `run`, or the lone character.\n * Windows are cut by code point: an ideograph outside the basic plane is a\n * surrogate pair, and slicing by code unit would split it into halves that\n * match nothing.\n *\n * Dropping the whole-segment tokens means a single-character query reaches\n * only pages where the character opens a bigram: a run-final 法 sits in 示法,\n * which the query 法 does not prefix-match. Lucene's CJK analyzer shares this\n * property; indexing lone characters alongside the bigrams would reinvite the\n * fragment noise this file exists to remove.\n */\nconst addBigrams = (run: string, tokens: Set<string>): void => {\n const characters = [...run];\n if (characters.length === 1) {\n tokens.add(run);\n return;\n }\n let previous = \"\";\n for (const character of characters) {\n if (previous) {\n tokens.add(previous + character);\n }\n previous = character;\n }\n};\n\n/**\n * `Intl.Segmenter` is missing on some runtimes even though the lib type\n * declares it, so the constructor's presence is probed before use.\n */\nconst hasSegmenter = (\n segmenter: typeof Intl.Segmenter | undefined\n): segmenter is typeof Intl.Segmenter => typeof segmenter === \"function\";\n\n/**\n * A word-segmenting tokenizer for languages the default splitter can't handle,\n * built on `Intl.Segmenter` (the same engine `@orama/tokenizers` wraps).\n * Input is NFC-normalized and lowercased before segmenting — unlike the\n * upstream tokenizers — so decomposed text (macOS filenames, some CMS\n * pipelines) indexes the same terms a composed query produces, and Latin\n * terms (\"GDPR\", English pages on a mixed-locale site) still match\n * case-insensitively, with their diacritics folded by\n * {@link foldDiacritics}. Returns `undefined` for scripts the default\n * tokenizer already serves ({@link resolveLocale} decides, so `sr-Latn` keeps\n * the default while `az-Cyrl` is segmented), and on runtimes without\n * `Intl.Segmenter`, where the caller falls back to Orama's default.\n *\n * On a {@link BIGRAM_INDEX_SCRIPTS} index, runs of adjacent\n * {@link BIGRAM_SCRIPTS} segments are joined and re-cut into character\n * bigrams; everything else (Latin, digits, and every segment on a Korean or\n * Thai index) is emitted one {@link TERM} at a time. Separators end a run\n * either way — whether they stand between segments, as 「クーリング・オフ」\n * does, or inside one.\n */\nconst segmentingTokenizer = (locale?: string): Tokenizer | undefined => {\n const resolved = resolveLocale(locale ?? \"\");\n if (!resolved?.script || resolved.script === LATIN_SCRIPT) {\n return;\n }\n if (!hasSegmenter(Intl.Segmenter)) {\n return;\n }\n // The canonical maximized language (`cmn` → `zh`), so the bigram predicate\n // here and the strict query pass read the same name.\n const { language } = resolved;\n const segmenter = new Intl.Segmenter(language, { granularity: \"word\" });\n const bigram = isBigramLanguage(language);\n return {\n language,\n normalizationCache: new Map(),\n tokenize: (raw: string): string[] => {\n const tokens = new Set<string>();\n let run = \"\";\n const flush = (): void => {\n if (run) {\n addBigrams(run, tokens);\n run = \"\";\n }\n };\n const take = (term: string): void => {\n if (bigram && BIGRAM_SCRIPTS.test(term)) {\n run += term;\n return;\n }\n flush();\n tokens.add(foldDiacritics(term));\n };\n for (const segment of segmenter.segment(\n raw.normalize(\"NFC\").toLowerCase()\n )) {\n if (!segment.isWordLike) {\n flush();\n continue;\n }\n let end = 0;\n for (const match of segment.segment.matchAll(TERM)) {\n // A gap means punctuation stood there, which ends the run as surely\n // as a non-word-like segment would: スネーク_ケース pairs either side\n // of the connector, never across it.\n if (match.index > end) {\n flush();\n }\n take(match[0]);\n end = match.index + match[0].length;\n }\n if (end < segment.segment.length) {\n flush();\n }\n }\n flush();\n return [...tokens];\n },\n };\n};\n\n/**\n * Build an in-memory Orama full-text index from search documents. Shared by the\n * Orama client loader (browser), the MCP server, and assistant grounding (Node),\n * so ranking is identical wherever docs are queried. `locale` — the site's\n * `i18n.defaultLocale` — swaps in a word-segmenting tokenizer for every\n * non-Latin script, all of which Orama's default tokenizer reduces to zero\n * tokens; the tokenizer belongs to the database, so on a mixed-locale site it\n * applies to every document. That is safe in one direction only: Latin words\n * survive segmentation intact, so English pages on a segmented index stay\n * searchable, but non-Latin translations on a Latin-default index still\n * collapse to zero tokens.\n */\nexport const buildOramaIndex = async (\n documents: OramaDoc[],\n locale?: string\n): Promise<AnyOrama> => {\n const tokenizer = segmentingTokenizer(locale);\n const db = tokenizer\n ? create({ components: { tokenizer }, schema: SCHEMA })\n : create({ schema: SCHEMA });\n await insertMultiple(\n db,\n documents.map((doc) =>\n doc.facets ? { ...doc, facetTerms: toFacetTerms(doc.facets) } : doc\n )\n );\n return db;\n};\n\n/** Orama keeps only documents matching every token at a threshold of 0. */\nconst ALL_TOKENS = 0;\n\n/** Optional exact-match filters applied to a query via Orama's `where`. */\nexport interface OramaQueryFilters {\n /** Keep only documents whose `contentType` is in this list. */\n contentTypes?: string[];\n /**\n * Keep only documents matching every facet, key → required value. Facet\n * keys and values come from the `facets` field on the indexed documents.\n */\n facets?: Record<string, string>;\n /** Keep only documents in this locale. */\n locale?: string;\n /**\n * Keep only documents of this docs version (`\"\"` is the current docs — a\n * meaningful filter value, so absence alone disables version filtering).\n */\n version?: string;\n}\n\n/**\n * The exact-match `where` clause the filters compile to. Orama types `where`\n * openly (any schema property to an operator), mirrored here by the index\n * signature; this module only ever emits the two enum operators.\n */\ninterface OramaWhereClause {\n [property: string]:\n | EnumArrComparisonOperator\n | EnumComparisonOperator\n | undefined;\n}\n\n/**\n * Query the index, returning the matching documents (highest-ranked first).\n * `filters` narrows results by exact `where` matches on the enum fields:\n * `locale` to one language, `contentTypes` to a set of page types, `facets`\n * to documents carrying every requested `key:value` term.\n *\n * On a bigrammed index the strict pass runs first: a term is only meant to\n * match where its bigrams sit together, and scoring them independently lets a\n * page sharing a couple of windows outrank the page the term is about. Terms\n * spanning several words rarely appear in full on one page, so an empty strict\n * result falls back to the default pass rather than reporting no matches.\n */\nexport const queryOramaIndex = async (\n db: AnyOrama,\n term: string,\n limit: number,\n filters?: OramaQueryFilters\n): Promise<OramaDoc[]> => {\n const facetTerms = filters?.facets ? toFacetTerms(filters.facets) : [];\n const where: OramaWhereClause = {};\n if (filters?.locale) {\n where.locale = { eq: filters.locale };\n }\n // `\"\"` (the current docs) is a real filter value, so test for presence.\n if (filters?.version !== undefined) {\n where.version = { eq: filters.version };\n }\n if (filters?.contentTypes && filters.contentTypes.length > 0) {\n where.contentType = { in: filters.contentTypes };\n }\n if (facetTerms.length > 0) {\n where.facetTerms = { containsAll: facetTerms };\n }\n const unfiltered = {\n boost: BOOST,\n limit,\n properties: [\"title\", \"description\", \"content\"],\n term,\n };\n const params =\n Object.keys(where).length > 0 ? { ...unfiltered, where } : unfiltered;\n const bigrammed = isBigramLanguage(db.tokenizer?.language ?? \"\");\n // The result-document generic is OramaDoc because `buildOramaIndex` is the\n // only writer to this database and inserts OramaDoc records (plus the\n // derived `facetTerms`).\n const strict = bigrammed\n ? await search<AnyOrama, OramaDoc>(db, { ...params, threshold: ALL_TOKENS })\n : undefined;\n const found =\n strict && strict.hits.length > 0\n ? strict\n : await search<AnyOrama, OramaDoc>(db, params);\n return found.hits.map((hit) => hit.document);\n};\n"
10
10
  ],
11
- "mappings": ";;;;;;;;;;;;;;AAAA;AAEA;;;ACFA;AAGA;;;ACHA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,cAAS;;;ACVT;;;ACAA;AAmCA,IAAM,SAAS;AAAA,EACb,SAAS;AAAA,EAET,aAAa;AAAA,EACb,aAAa;AAAA,EAIb,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,OAAO;AAAA,EACP,SAAS;AACX;AAGA,IAAM,eAAe,CAAC,WACpB,OAAO,QAAQ,MAAM,EAAE,IAAI,EAAE,KAAK,WAAW,GAAG,OAAO,OAAO;AAGhE,IAAM,QAAQ,EAAE,aAAa,GAAG,OAAO,EAAE;AAUzC,IAAM,eAAe;AAcrB,IAAM,cAAc,CAAC,QAAyC;AAAA,EAC5D,MAAM,aAAa,IAAI,QAAQ,YAAY,EAAE,EAAE,WAAW,KAAK,GAAG;AAAA,EAClE,IAAI;AAAA,IACF,OAAO,IAAI,KAAK,OAAO,UAAU;AAAA,IACjC,MAAM;AAAA,EAGR,OAAO,UAAU,MAAM,WAAW,MAAM,GAAG;AAAA,EAC3C,IAAI;AAAA,IACF,OAAO,IAAI,KAAK,OAAO,OAAO;AAAA,IAC9B,MAAM;AAAA,IACN;AAAA;AAAA;AAYJ,IAAM,gBAAgB,CAAC,QACrB,YAAY,GAAG,GAAG,SAAS;AAoB7B,IAAM,uBAAuB,IAAI,IAAI,CAAC,QAAQ,QAAQ,MAAM,CAAC;AAS7D,IAAM,mBAAmB,CAAC,aAA8B;AAAA,EACtD,MAAM,SAAS,cAAc,QAAQ,GAAG;AAAA,EACxC,OAAO,WAAW,aAAa,qBAAqB,IAAI,MAAM;AAAA;AAWhE,IAAM,iBACJ;AAgBF,IAAM,OACJ;AAYF,IAAM,aAAa;AAEnB,IAAM,QAAQ;AAEd,IAAM,iBAAiB,CAAC,SACtB,WAAW,KAAK,IAAI,IAAI,KAAK,UAAU,KAAK,EAAE,QAAQ,OAAO,EAAE,IAAI;AAcrE,IAAM,aAAa,CAAC,KAAa,WAA8B;AAAA,EAC7D,MAAM,aAAa,CAAC,GAAG,GAAG;AAAA,EAC1B,IAAI,WAAW,WAAW,GAAG;AAAA,IAC3B,OAAO,IAAI,GAAG;AAAA,IACd;AAAA,EACF;AAAA,EACA,IAAI,WAAW;AAAA,EACf,WAAW,aAAa,YAAY;AAAA,IAClC,IAAI,UAAU;AAAA,MACZ,OAAO,IAAI,WAAW,SAAS;AAAA,IACjC;AAAA,IACA,WAAW;AAAA,EACb;AAAA;AAOF,IAAM,eAAe,CACnB,cACuC,OAAO,cAAc;AAsB9D,IAAM,sBAAsB,CAAC,WAA2C;AAAA,EACtE,MAAM,WAAW,cAAc,UAAU,EAAE;AAAA,EAC3C,IAAI,CAAC,UAAU,UAAU,SAAS,WAAW,cAAc;AAAA,IACzD;AAAA,EACF;AAAA,EACA,IAAI,CAAC,aAAa,KAAK,SAAS,GAAG;AAAA,IACjC;AAAA,EACF;AAAA,EAGA,QAAQ,aAAa;AAAA,EACrB,MAAM,YAAY,IAAI,KAAK,UAAU,UAAU,EAAE,aAAa,OAAO,CAAC;AAAA,EACtE,MAAM,SAAS,iBAAiB,QAAQ;AAAA,EACxC,OAAO;AAAA,IACL;AAAA,IACA,oBAAoB,IAAI;AAAA,IACxB,UAAU,CAAC,QAA0B;AAAA,MACnC,MAAM,SAAS,IAAI;AAAA,MACnB,IAAI,MAAM;AAAA,MACV,MAAM,QAAQ,MAAY;AAAA,QACxB,IAAI,KAAK;AAAA,UACP,WAAW,KAAK,MAAM;AAAA,UACtB,MAAM;AAAA,QACR;AAAA;AAAA,MAEF,MAAM,OAAO,CAAC,SAAuB;AAAA,QACnC,IAAI,UAAU,eAAe,KAAK,IAAI,GAAG;AAAA,UACvC,OAAO;AAAA,UACP;AAAA,QACF;AAAA,QACA,MAAM;AAAA,QACN,OAAO,IAAI,eAAe,IAAI,CAAC;AAAA;AAAA,MAEjC,WAAW,WAAW,UAAU,QAC9B,IAAI,UAAU,KAAK,EAAE,YAAY,CACnC,GAAG;AAAA,QACD,IAAI,CAAC,QAAQ,YAAY;AAAA,UACvB,MAAM;AAAA,UACN;AAAA,QACF;AAAA,QACA,IAAI,MAAM;AAAA,QACV,WAAW,SAAS,QAAQ,QAAQ,SAAS,IAAI,GAAG;AAAA,UAIlD,IAAI,MAAM,QAAQ,KAAK;AAAA,YACrB,MAAM;AAAA,UACR;AAAA,UACA,KAAK,MAAM,EAAE;AAAA,UACb,MAAM,MAAM,QAAQ,MAAM,GAAG;AAAA,QAC/B;AAAA,QACA,IAAI,MAAM,QAAQ,QAAQ,QAAQ;AAAA,UAChC,MAAM;AAAA,QACR;AAAA,MACF;AAAA,MACA,MAAM;AAAA,MACN,OAAO,CAAC,GAAG,MAAM;AAAA;AAAA,EAErB;AAAA;AAeK,IAAM,kBAAkB,OAC7B,WACA,WACsB;AAAA,EACtB,MAAM,YAAY,oBAAoB,MAAM;AAAA,EAC5C,MAAM,KAAK,YACP,OAAO,EAAE,YAAY,EAAE,UAAU,GAAG,QAAQ,OAAO,CAAC,IACpD,OAAO,EAAE,QAAQ,OAAO,CAAC;AAAA,EAC7B,MAAM,eACJ,IACA,UAAU,IAAI,CAAC,QACb,IAAI,SAAS,KAAK,KAAK,YAAY,aAAa,IAAI,MAAM,EAAE,IAAI,GAClE,CACF;AAAA,EACA,OAAO;AAAA;AAIT,IAAM,aAAa;AA4CZ,IAAM,kBAAkB,OAC7B,IACA,MACA,OACA,YACwB;AAAA,EACxB,MAAM,aAAa,SAAS,SAAS,aAAa,QAAQ,MAAM,IAAI,CAAC;AAAA,EACrE,MAAM,QAA0B,CAAC;AAAA,EACjC,IAAI,SAAS,QAAQ;AAAA,IACnB,MAAM,SAAS,EAAE,IAAI,QAAQ,OAAO;AAAA,EACtC;AAAA,EAEA,IAAI,SAAS,YAAY,WAAW;AAAA,IAClC,MAAM,UAAU,EAAE,IAAI,QAAQ,QAAQ;AAAA,EACxC;AAAA,EACA,IAAI,SAAS,gBAAgB,QAAQ,aAAa,SAAS,GAAG;AAAA,IAC5D,MAAM,cAAc,EAAE,IAAI,QAAQ,aAAa;AAAA,EACjD;AAAA,EACA,IAAI,WAAW,SAAS,GAAG;AAAA,IACzB,MAAM,aAAa,EAAE,aAAa,WAAW;AAAA,EAC/C;AAAA,EACA,MAAM,aAAa;AAAA,IACjB,OAAO;AAAA,IACP;AAAA,IACA,YAAY,CAAC,SAAS,eAAe,SAAS;AAAA,IAC9C;AAAA,EACF;AAAA,EACA,MAAM,SACJ,OAAO,KAAK,KAAK,EAAE,SAAS,IAAI,KAAK,YAAY,MAAM,IAAI;AAAA,EAC7D,MAAM,YAAY,iBAAiB,GAAG,WAAW,YAAY,EAAE;AAAA,EAI/D,MAAM,SAAS,YACX,MAAM,OAA2B,IAAI,KAAK,QAAQ,WAAW,WAAW,CAAC,IACzE;AAAA,EACJ,MAAM,QACJ,UAAU,OAAO,KAAK,SAAS,IAC3B,SACA,MAAM,OAA2B,IAAI,MAAM;AAAA,EACjD,OAAO,MAAM,KAAK,IAAI,CAAC,QAAQ,IAAI,QAAQ;AAAA;;;AD/XtC,IAAM,uBAAuB;AAC7B,IAAM,mBAAmB;AAEhC,IAAM,iBAAiB;AAevB,IAAM,oBAAoB,EAAE,WAAW,CAAC,UAAU;AAAA,EAChD,MAAM,QAAQ,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,GAAG,OACpD,CAAC,UAA2B,OAAO,UAAU,QAC/C;AAAA,EACA,OAAO,KAAK,SAAS,IAAI,OAAO;AAAA,GAC/B,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,0JAA0J,CAAC;AAOtM,IAAM,mBAAmB,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC;AAEzD,IAAM,eAAe,EAAE,WAAW,CAAC,UAAU;AAAA,EAC3C,MAAM,YAAY,iBAAiB,UAAU,KAAK;AAAA,EAClD,IAAI,CAAC,UAAU,SAAS;AAAA,IACtB;AAAA,EACF;AAAA,EACA,MAAM,UAAU,OAAO,QAAQ,UAAU,IAAI,EAAE,OAC7C,CAAC,UAAqC,OAAO,MAAM,OAAO,QAC5D;AAAA,EACA,OAAO,QAAQ,SAAS,IAAI,OAAO,YAAY,OAAO,IAAI;AAAA,GACzD,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,mOAAmO,CAAC;AAG5R,IAAM,aAAa,EAAE,WACnB,CAAC,UAAU;AAAA,EAGT,MAAM,MAAM,OAAO,KAAK;AAAA,EACxB,OAAO,OAAO,SAAS,GAAG,IACtB,KAAK,IAAI,KAAK,IAAI,KAAK,MAAM,GAAG,GAAG,CAAC,GAAG,gBAAgB,IACvD;AAAA,GAEN,EACG,IAAI,EACJ,IAAI,CAAC,EACL,IAAI,gBAAgB,EACpB,SAAS,EACT,SAAS,mCAAmC,wBAAwB,CACzE;AAGA,IAAM,YAAY,CAAC,gBACjB,EAAE,WAAW,CAAC,UAAU;AAAA,EACtB,MAAM,SAAS,EAAE,OAAO,EAAE,UAAU,KAAK;AAAA,EACzC,OAAO,OAAO,UAAU,OAAO,OAAO;AAAA,GACrC,EAAE,OAAO,EAAE,SAAS,WAAW,CAAC;AAGrC,IAAM,oBAAoB,CAAC,gBACzB,EAAE,WAAW,CAAC,UAAU;AAAA,EACtB,MAAM,SAAS,EAAE,OAAO,EAAE,UAAU,KAAK;AAAA,EACzC,MAAM,UAAU,OAAO,UAAU,OAAO,KAAK,KAAK,IAAI;AAAA,EACtD,OAAO,WAAW;AAAA,GACjB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,WAAW,CAAC;AAGhD,IAAM,cAAc,kBAClB,yEACF;AAGA,IAAM,eAAe,kBACnB,uMACF;AAGO,IAAM,cAAc;AAAA,EACzB,gBAAgB,EAAE,OAAO;AAAA,IACvB,QAAQ,kBACN,0EACF;AAAA,IACA,SAAS,kBACP,0EACF;AAAA,EACF,CAAC;AAAA,EACD,UAAU,EAAE,OAAO;AAAA,IACjB,OAAO,UAAU,yCAAyC;AAAA,EAC5D,CAAC;AAAA,EACD,YAAY,EAAE,OAAO;AAAA,IACnB,cAAc;AAAA,IACd,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,SAAS;AAAA,EACX,CAAC;AAAA,EACD,aAAa,EAAE,OAAO;AAAA,IACpB,cAAc;AAAA,IACd,SAAS;AAAA,IACT,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,OAAO,UAAU,mBAAmB;AAAA,IACpC,SAAS;AAAA,EACX,CAAC;AACH;AA0BA,IAAM,gBAAgB,CACpB,QACA,YAEA,OAAO,QAAQ,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,SAAS,SAAS,KAAK;AAUzE,IAAM,iBAAiB,CACrB,OACA,SACuB;AAAA,EACvB,IAAI,CAAC,KAAK,kBAAkB;AAAA,IAC1B;AAAA,EACF;AAAA,EACA,IAAI,UAAU,OAAO;AAAA,IACnB;AAAA,EACF;AAAA,EACA,IAAI,UAAU,aAAa,UAAU,YAAY,UAAU,WAAW;AAAA,IACpE,OAAO;AAAA,EACT;AAAA,EACA,OAAO;AAAA;AAUT,IAAM,sBAAsB,CAC1B,WACA,SAEA,aACA,KAAK,oBACL,CAAC,KAAK,iBAAiB,SAAS,SAAS,IACrC,oBAAoB,kCAAkC,KAAK,iBAAiB,KAAK,IAAI,OACrF;AASC,IAAM,kBAAiB,CAAC,OAAe,SAA0B;AAAA,EACtE,IAAI,QAAQ,MAAM,KAAK;AAAA,EACvB,IAAI,iBAAiB,KAAK,KAAK,GAAG;AAAA,IAChC,IAAI;AAAA,MACF,QAAQ,IAAI,IAAI,KAAK,EAAE;AAAA,MACvB,MAAM;AAAA,EAGV;AAAA,EACA,IAAI;AAAA,IACF,QAAQ,UAAU,KAAK;AAAA,IACvB,MAAM;AAAA,EAKR,MAAM,WAAW,QAAQ,OAAO,GAAG,EAAE,QAAQ,YAAY,EAAE;AAAA,EAC3D,OAAO,cAAc,KAAK,MAAM,eAAmB,QAAQ,CAAC;AAAA;AAIvD,IAAM,SAAS,CAAC,OAAe,SAA0B;AAAA,EAG9D,MAAM,OAAO,aAAa,KAAK,MAAM,KAAK;AAAA,EAG1C,OAAO,KAAK,OAAO,aAAY,KAAK,MAAM,IAAI,IAAI;AAAA;AAKpD,IAAM,aAAa,CAAC,QAA0B;AAAA,EAC5C,IAAI,IAAI,aAAa;AAAA,IACnB,OAAO,IAAI;AAAA,EACb;AAAA,EACA,MAAM,OAAO,IAAI,QAAQ,MAAM,GAAG,cAAc,EAAE,KAAK;AAAA,EACvD,OAAO,IAAI,QAAQ,SAAS,iBAAiB,GAAG,UAAU;AAAA;AAcrD,IAAM,sBAAsB,CACjC,WACA,WACuB;AAAA,EACvB,IAAI,YAAmD;AAAA,EACvD,OAAO,SAAS,YAAY,GAAG;AAAA,IAC7B,cAAc,gBAAgB,WAAW,MAAM;AAAA,IAC/C,OAAO;AAAA;AAAA;AAKJ,IAAM,aAAa,OACxB,MACA,OACA,UACgC;AAAA,EAChC,MAAM,KAAK,MAAM,MAAM;AAAA,EACvB,MAAM,OAAO,MAAM,gBACjB,IACA,MAAM,OACN,MAAM,SAAS,sBACf;AAAA,IACE,cAAc,MAAM;AAAA,IACpB,QAAQ,MAAM;AAAA,IACd,QAAQ,MAAM;AAAA,IACd,SAAS,eAAe,MAAM,SAAS,IAAI;AAAA,EAC7C,CACF;AAAA,EAGA,OAAO,KAAK,IAAI,CAAC,QAAkB;AAAA,IACjC,MAAM,MAAwB;AAAA,MAC5B,aAAa,IAAI;AAAA,MACjB,SAAS,WAAW,GAAG;AAAA,MACvB,QAAQ,IAAI;AAAA,MACZ,OAAO,IAAI;AAAA,MACX,OAAO,IAAI;AAAA,MACX,KAAK,OAAO,IAAI,OAAO,IAAI;AAAA,IAC7B;AAAA,IACA,IAAI,KAAK,kBAAkB;AAAA,MACzB,IAAI,UAAU,IAAI,WAAW;AAAA,IAC/B;AAAA,IACA,OAAO;AAAA,GACR;AAAA;AAII,IAAM,kBAAkB,CAC7B,MACA,UACuB,KAAK,MAAM;AAG7B,IAAM,YAAY,CACvB,MACA,UACyB;AAAA,EACzB,QAAQ,cAAc,SAAS,WAAW;AAAA,EAC1C,MAAM,eAAe,eAAe,MAAM,SAAS,IAAI;AAAA,EACvD,OAAO,KAAK,OACT,OACC,CAAC,WACE,CAAC,gBAAgB,aAAa,SAAS,MAAM,WAAW,OACxD,CAAC,WAAW,cAAc,MAAM,QAAQ,OAAO,OAC/C,CAAC,UAAU,MAAM,WAAW,YAC5B,iBAAiB,aAAa,MAAM,YAAY,aACrD,EACC,IAAI,CAAC,UAAU;AAAA,IACd,MAAM,UAA8B;AAAA,MAClC,aAAa,MAAM;AAAA,MACnB,aAAa,MAAM;AAAA,MACnB,QAAQ,MAAM;AAAA,MACd,cAAc,MAAM;AAAA,MACpB,OAAO,MAAM;AAAA,MACb,OAAO,MAAM;AAAA,MACb,KAAK,OAAO,MAAM,OAAO,IAAI;AAAA,IAC/B;AAAA,IACA,IAAI,KAAK,kBAAkB;AAAA,MACzB,QAAQ,UAAU,MAAM;AAAA,IAC1B;AAAA,IACA,OAAO;AAAA,GACR;AAAA;AAWE,IAAM,gBAAgB,CAC3B,MACA,UACmD;AAAA,EACnD,QAAQ,QAAQ,SAAS,cAAc;AAAA,EACvC,MAAM,iBAAiB,oBAAoB,WAAW,IAAI;AAAA,EAC1D,IAAI,gBAAgB;AAAA,IAClB,OAAO,EAAE,OAAO,eAAe;AAAA,EACjC;AAAA,EACA,MAAM,eAAe;AAAA,EACrB,MAAM,WAAW,YACb,KAAK,sBAAsB,aAC3B;AAAA,EACJ,IAAI,UAAU;AAAA,IACZ,cACG,SAAS,SAAS,UAAU,cAC7B,SAAS,KAAK,iBAAiB,OAC/B,OAAO,OAAO,QAAQ,EAAE,MACxB;AAAA,EACJ,EAAO,SAAI,UAAU,KAAK,qBAAqB,SAAS;AAAA,IACtD,aAAa,KAAK,mBAAmB;AAAA,EACvC;AAAA,EACA,OAAO,EAAE,WAAW;AAAA;;;AD7VtB,IAAM,qBAAqB;AAE3B,IAAM,qBAAqB;AAE3B,IAAM,wBAAwB;AAgB9B,IAAM,iBAAiB,CAAC,WAAsB;AAAA,EAC5C;AAAA,IACE,SAAS;AAAA,IACT,sBAAsB;AAAA,OACnB;AAAA,MACD,GAAE,aAAa,MAAM;AAAA,EACzB,OAAO;AAAA;AAIT,IAAM,mBAAmB,UAAU,IAAI,CAAC,UAAU;AAAA,EAChD,aAAa,KAAK;AAAA,EAClB,aAAa,KAAK;AAAA,EAClB,aAAa,eAGX,YAAY,KAAK,KACnB;AAAA,EACA,MAAM,KAAK;AAAA,EACX,OAAO,KAAK;AACd,EAAE;AAiBF,IAAM,cAAc,CAAC,OAAe,SAClC,KAAK,OACD,OAAO,OAAO,IAAI,IAClB,GAAG,wBAAwB,aAAa,KAAK,MAAM,KAAK;AAG9D,IAAM,gBAAgB,CAAC,KAAa,SAClC,gBACE,IAAI,WAAW,qBAAqB,IAChC,IAAI,MAAM,sBAAsB,MAAM,IACtC,KACJ,IACF;AAGF,IAAM,OAAO,CAAC,OAAe,UAAU,UAAU;AAAA,EAC/C,MAAM,UAAU,CAAC,EAAE,MAAM,OAAO,MAAM,OAAgB,CAAC;AAAA,EACvD,OAAO,UAAU,EAAE,SAAS,SAAS,KAAK,IAAI,EAAE,QAAQ;AAAA;AAInD,IAAM,cAAc,CACzB,MACA,UACW;AAAA,EACX,MAAM,eAAe,EAAE,WAAW,CAAC,GAAG,OAAO,CAAC,EAAE;AAAA,EAChD,MAAM,gBAA+B,KAAK,eACtC,EAAE,cAAc,cAAc,KAAK,aAAa,IAChD,EAAE,aAAa;AAAA,EACnB,MAAM,SAAS,IAAI,OACjB,EAAE,MAAM,KAAK,MAAM,SAAS,KAAK,QAAQ,GACzC,aACF;AAAA,EAEA,OAAO,kBAAkB,wBAAwB,OAAO;AAAA,IACtD,OAAO;AAAA,EACT,EAAE;AAAA,EAMF,OAAO,kBAAkB,4BAA4B,OAAO;AAAA,IAC1D,WAAW,KAAK,OAAO,IAAI,CAAC,UAAU;AAAA,MACpC,MAAM,WAAyB;AAAA,QAC7B,UAAU;AAAA,QACV,MAAM,MAAM;AAAA,QACZ,OAAO,MAAM;AAAA,QACb,KAAK,YAAY,MAAM,OAAO,IAAI;AAAA,MACpC;AAAA,MACA,IAAI,MAAM,aAAa;AAAA,QACrB,SAAS,cAAc,MAAM;AAAA,MAC/B;AAAA,MACA,OAAO;AAAA,KACR;AAAA,EACH,EAAE;AAAA,EAEF,OAAO,kBAAkB,2BAA2B,CAAC,YAAY;AAAA,IAC/D,QAAQ,QAAQ,QAAQ;AAAA,IACxB,MAAM,WAAW,gBAAgB,MAAM,cAAc,KAAK,IAAI,CAAC;AAAA,IAC/D,IAAI,aAAa,WAAW;AAAA,MAC1B,MAAM,IAAI,SACR,oBACA,qBAAqB,4DACvB;AAAA,IACF;AAAA,IACA,OAAO;AAAA,MACL,UAAU,CAAC,EAAE,UAAU,oBAAoB,MAAM,UAAU,IAAI,CAAC;AAAA,IAClE;AAAA,GACD;AAAA,EAED,OAAO,kBAAkB,uBAAuB,OAAO,YAAY;AAAA,IACjE,QAAQ,WAAW,OAAO,CAAC,GAAG,SAAS,QAAQ;AAAA,IAE/C,IAAI,SAAS,eAAe;AAAA,MAC1B,MAAM,UAAU,MAAM,WACpB,MACA,OACA,YAAY,YAAY,MAAM,IAAI,CACpC;AAAA,MACA,OAAO,KAAK,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAAA,IAC9C;AAAA,IAEA,IAAI,SAAS,YAAY;AAAA,MACvB,MAAM,QAAQ,YAAY,SAAS,MAAM,IAAI;AAAA,MAC7C,MAAM,MAAM,gBAAe,MAAM,OAAO,IAAI;AAAA,MAC5C,MAAM,WAAW,gBAAgB,MAAM,GAAG;AAAA,MAC1C,IAAI,aAAa,WAAW;AAAA,QAC1B,OAAO,KACL,qBAAqB,6DACrB,IACF;AAAA,MACF;AAAA,MACA,OAAO,KAAK,QAAQ;AAAA,IACtB;AAAA,IAEA,IAAI,SAAS,cAAc;AAAA,MACzB,MAAM,UAAU,UAAU,MAAM,YAAY,WAAW,MAAM,IAAI,CAAC;AAAA,MAClE,OAAO,KAAK,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAAA,IAC9C;AAAA,IAEA,IAAI,SAAS,kBAAkB;AAAA,MAC7B,MAAM,SAAS,cACb,MACA,YAAY,eAAe,MAAM,IAAI,CACvC;AAAA,MACA,IAAI,WAAW,QAAQ;AAAA,QACrB,OAAO,KAAK,OAAO,OAAO,IAAI;AAAA,MAChC;AAAA,MACA,OAAO,KAAK,KAAK,UAAU,OAAO,YAAY,MAAM,CAAC,CAAC;AAAA,IACxD;AAAA,IAEA,OAAO,KAAK,iBAAiB,QAAQ,IAAI;AAAA,GAC1C;AAAA,EAED,OAAO;AAAA;;;AD5LF,IAAM,gBAAgB,OAC3B,MACA,UAA2B,CAAC,MACV;AAAA,EAClB,MAAM,QAAQ,QAAQ,SAAS,QAAQ;AAAA,EACvC,MAAM,SAAS,QAAQ,UAAU,QAAQ;AAAA,EACzC,MAAM,YAAY,IAAI,qBAAqB,OAAO,MAAM;AAAA,EACxD,MAAM,SAAS,YACb,MACA,oBAAoB,KAAK,WAAW,KAAK,aAAa,CACxD;AAAA,EACA,MAAM,OAAO,QAAQ,SAAS;AAAA,EAC9B,MAAM,KAAK,OAAO,KAAK;AAAA,EACvB,MAAM,UAAU,MAAM;AAAA;;;AD5BjB,IAAM,kBAAkB,cAAc;AAAA,EAC3C,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,aAAa;AAAA,MACb,UAAU;AAAA,MACV,MAAM;AAAA,IACR;AAAA,EACF;AAAA,EACA,MAAM,YAAY;AAAA,OACZ,IAAG,GAAG,QAAQ;AAAA,IAGlB,IAAI;AAAA,IACJ,IAAI;AAAA,MAIF,OAAO,KAAK,MAAM,MAAM,SAAS,KAAK,MAAM,OAAO,CAAC;AAAA,MACpD,OAAO,OAAO;AAAA,MACd,MAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MACpE,QAAQ,OAAO,MACb,gDAAgD,KAAK,SAAS;AAAA,CAChE;AAAA,MACA,QAAQ,KAAK,CAAC;AAAA;AAAA,IAEhB,MAAM,cAAc,IAAI;AAAA;AAE5B,CAAC;",
12
- "debugId": "F74FD7C10F87037364756E2164756E21",
11
+ "mappings": "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",
12
+ "debugId": "E29C9C03F27EAC9C64756E2164756E21",
13
13
  "names": []
14
14
  }
@@ -2,14 +2,14 @@
2
2
  import {
3
3
  commandsFor,
4
4
  detectProjectPackageManager
5
- } from "./chunk-qkqwkpte.js";
5
+ } from "./chunk-v6ya5kcb.js";
6
6
  import {
7
7
  packageRoot,
8
8
  logger
9
9
  } from "./index.js";
10
10
  import {
11
11
  EJECTED_CONFIG_HEADER
12
- } from "./chunk-2hn4b8z7.js";
12
+ } from "./chunk-hr8ne106.js";
13
13
 
14
14
  // src/cli/eject-scripts.ts
15
15
  import { existsSync, readFileSync } from "node:fs";
@@ -64,16 +64,26 @@ var isEjectedProject = (root) => {
64
64
  const source = readFileSync(config, "utf-8");
65
65
  return source.startsWith(EJECTED_CONFIG_HEADER) || source.startsWith(BLUME_1_EJECTED_CONFIG_HEADER);
66
66
  };
67
- var refuseIfEjected = async (root, script) => {
67
+ var ejectedEquivalent = (pm, command) => {
68
+ const { build, dev, exec } = commandsFor(pm);
69
+ return {
70
+ build,
71
+ check: `${exec} astro check`,
72
+ dev,
73
+ preview: pm === "npm" ? "npm run preview" : `${pm} preview`,
74
+ sync: `${exec} astro sync`
75
+ }[command];
76
+ };
77
+ var refuseIfEjected = async (root, command) => {
68
78
  if (!isEjectedProject(root)) {
69
79
  return;
70
80
  }
71
- const commands = commandsFor(await detectProjectPackageManager(root));
72
- logger.error(`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.`);
81
+ const pm = await detectProjectPackageManager(root);
82
+ logger.error(`This project was ejected to a standalone Astro app, so \`blume ${command}\` would run a regenerated copy of the site instead of it. Run \`${ejectedEquivalent(pm, command)}\` instead.`);
73
83
  process.exit(1);
74
84
  };
75
85
 
76
86
  export { updatePackageScripts, isEjectedProject, refuseIfEjected };
77
87
 
78
- //# debugId=5D0834602D1CB35B64756E2164756E21
79
- //# sourceMappingURL=chunk-yt5n7ppj.js.map
88
+ //# debugId=F07EB9DD3957B57764756E2164756E21
89
+ //# sourceMappingURL=chunk-4e9b9ra6.js.map
@@ -0,0 +1,10 @@
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 type { PackageManager } 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/** The Blume commands that run the hidden `.blume/` copy of the site. */\nexport type RuntimeCommand = \"build\" | \"check\" | \"dev\" | \"preview\" | \"sync\";\n\n/**\n * What an ejected app runs in place of each {@link RuntimeCommand}: its own\n * `dev`/`build`/`preview` scripts (eject writes them), or Astro's CLI for the\n * check and the content sync.\n */\nconst ejectedEquivalent = (\n pm: PackageManager,\n command: RuntimeCommand\n): string => {\n const { build, dev, exec } = commandsFor(pm);\n return {\n build,\n check: `${exec} astro check`,\n dev,\n // Spelled like `dev`: npm runs a custom script only through `run`.\n preview: pm === \"npm\" ? \"npm run preview\" : `${pm} preview`,\n sync: `${exec} astro sync`,\n }[command];\n};\n\n/**\n * Stop a command that works on the hidden runtime in an ejected app. `dev`,\n * `build`, `check`, and `sync` would regenerate `.blume/` from\n * `blume.config.ts` and run that hidden copy, ignoring every edit made to the\n * app since eject, and `preview` would look for that copy's build; point at\n * the app's own command under the project's package manager instead.\n */\nexport const refuseIfEjected = async (\n root: string,\n command: RuntimeCommand\n): Promise<void> => {\n if (!isEjectedProject(root)) {\n return;\n }\n const pm = await detectProjectPackageManager(root);\n logger.error(\n `This project was ejected to a standalone Astro app, so \\`blume ${command}\\` would run a regenerated copy of the site instead of it. Run \\`${ejectedEquivalent(pm, command)}\\` instead.`\n );\n process.exit(1);\n};\n"
6
+ ],
7
+ "mappings": ";;;;;;;;;;;;;;AAAA;AACA;AAEA;AAqCO,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;AAYnD,IAAM,oBAAoB,CACxB,IACA,YACW;AAAA,EACX,QAAQ,OAAO,KAAK,SAAS,YAAY,EAAE;AAAA,EAC3C,OAAO;AAAA,IACL;AAAA,IACA,OAAO,GAAG;AAAA,IACV;AAAA,IAEA,SAAS,OAAO,QAAQ,oBAAoB,GAAG;AAAA,IAC/C,MAAM,GAAG;AAAA,EACX,EAAE;AAAA;AAUG,IAAM,kBAAkB,OAC7B,MACA,YACkB;AAAA,EAClB,IAAI,CAAC,iBAAiB,IAAI,GAAG;AAAA,IAC3B;AAAA,EACF;AAAA,EACA,MAAM,KAAK,MAAM,4BAA4B,IAAI;AAAA,EACjD,OAAO,MACL,kEAAkE,2EAA2E,kBAAkB,IAAI,OAAO,cAC5K;AAAA,EACA,QAAQ,KAAK,CAAC;AAAA;",
8
+ "debugId": "F07EB9DD3957B57764756E2164756E21",
9
+ "names": []
10
+ }