blume 2.1.0 → 2.1.1

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 (397) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/dist/cli/{chunk-5cg2rzgf.js → chunk-073rdvgb.js} +19 -48
  3. package/dist/cli/{chunk-5cg2rzgf.js.map → chunk-073rdvgb.js.map} +4 -5
  4. package/dist/cli/{chunk-ga0pf4aj.js → chunk-0n9xkp3d.js} +12 -3
  5. package/dist/cli/{chunk-ga0pf4aj.js.map → chunk-0n9xkp3d.js.map} +3 -3
  6. package/dist/cli/chunk-150jk7km.js +52 -0
  7. package/dist/cli/chunk-150jk7km.js.map +10 -0
  8. package/dist/cli/{chunk-rekqz4qg.js → chunk-313vnn40.js} +16 -15
  9. package/dist/cli/chunk-313vnn40.js.map +10 -0
  10. package/dist/cli/{chunk-gp84za0h.js → chunk-3v3ffbrj.js} +2 -2
  11. package/dist/cli/{chunk-9xr4dgee.js → chunk-3x6x3pph.js} +29 -16
  12. package/dist/cli/chunk-3x6x3pph.js.map +10 -0
  13. package/dist/cli/{chunk-0gz36akr.js → chunk-42t4jxdx.js} +21 -5
  14. package/dist/cli/chunk-42t4jxdx.js.map +12 -0
  15. package/dist/cli/{chunk-ta27feev.js → chunk-54ztw1nj.js} +21 -2
  16. package/dist/cli/chunk-54ztw1nj.js.map +10 -0
  17. package/dist/cli/{chunk-jnwj5kjj.js → chunk-5vjtgs6e.js} +20 -7
  18. package/dist/cli/{chunk-jnwj5kjj.js.map → chunk-5vjtgs6e.js.map} +3 -3
  19. package/dist/cli/chunk-66w9j7p6.js +322 -0
  20. package/dist/cli/chunk-66w9j7p6.js.map +14 -0
  21. package/dist/cli/{chunk-nd1821sk.js → chunk-7ppf1rva.js} +32 -18
  22. package/dist/cli/chunk-7ppf1rva.js.map +10 -0
  23. package/dist/cli/{chunk-p804f3pe.js → chunk-8y2kmjv8.js} +6 -6
  24. package/dist/cli/{chunk-2f7n4qzt.js → chunk-c1y538kd.js} +1 -1
  25. package/dist/cli/{chunk-1n228xqb.js → chunk-c887fj0s.js} +2 -2
  26. package/dist/cli/{chunk-y5h4r2y0.js → chunk-cfyg47zg.js} +15 -5
  27. package/dist/cli/chunk-cfyg47zg.js.map +10 -0
  28. package/dist/cli/{chunk-cmtcnh9f.js → chunk-dzwehpy7.js} +2 -2
  29. package/dist/cli/{chunk-h2a48yhd.js → chunk-e6atmn4h.js} +119 -15
  30. package/dist/cli/{chunk-h2a48yhd.js.map → chunk-e6atmn4h.js.map} +10 -10
  31. package/dist/cli/{chunk-5ffywsmb.js → chunk-e8xw9qkh.js} +8 -6
  32. package/dist/cli/{chunk-5ffywsmb.js.map → chunk-e8xw9qkh.js.map} +3 -3
  33. package/dist/cli/{chunk-ntv4qrt7.js → chunk-e9q7a2ak.js} +14 -14
  34. package/dist/cli/{chunk-xkz5xbtv.js → chunk-ehweg4fr.js} +2366 -1114
  35. package/dist/cli/chunk-ehweg4fr.js.map +218 -0
  36. package/dist/cli/{chunk-zf748avv.js → chunk-ep6nd0rn.js} +176 -281
  37. package/dist/cli/{chunk-zf748avv.js.map → chunk-ep6nd0rn.js.map} +11 -12
  38. package/dist/cli/{chunk-yxmk4pec.js → chunk-fdrkhpjy.js} +16 -15
  39. package/dist/cli/{chunk-yxmk4pec.js.map → chunk-fdrkhpjy.js.map} +3 -3
  40. package/dist/cli/{chunk-5wj2qwn2.js → chunk-ft9vfys6.js} +4 -4
  41. package/dist/cli/{chunk-4h8z9vv5.js → chunk-gb420vvz.js} +89 -46
  42. package/dist/cli/chunk-gb420vvz.js.map +17 -0
  43. package/dist/cli/{chunk-713mngdq.js → chunk-hebn0tpx.js} +76 -10
  44. package/dist/cli/chunk-hebn0tpx.js.map +14 -0
  45. package/dist/cli/{chunk-2n4ps1b3.js → chunk-htrwgabr.js} +40 -22
  46. package/dist/cli/chunk-htrwgabr.js.map +15 -0
  47. package/dist/cli/{chunk-zhvd6adm.js → chunk-hx3xqnk9.js} +13 -13
  48. package/dist/cli/chunk-j134vrp2.js +65 -0
  49. package/dist/cli/chunk-j134vrp2.js.map +10 -0
  50. package/dist/cli/{chunk-y4ykbark.js → chunk-kma9f6sf.js} +2 -2
  51. package/dist/cli/{chunk-py62fewh.js → chunk-mrcccgnh.js} +3 -3
  52. package/dist/cli/{chunk-2f3ndkpw.js → chunk-n0v7m8ry.js} +3 -3
  53. package/dist/cli/{chunk-43zcf4gz.js → chunk-ncj7w596.js} +522 -353
  54. package/dist/cli/chunk-ncj7w596.js.map +35 -0
  55. package/dist/cli/{chunk-9geymqns.js → chunk-nq1d20w2.js} +14 -14
  56. package/dist/cli/{chunk-g2z9x01v.js → chunk-nrns10mj.js} +3 -3
  57. package/dist/cli/{chunk-e7vvk5ya.js → chunk-sds923fa.js} +3 -3
  58. package/dist/cli/{chunk-cy5bm0j0.js → chunk-t2m5j4pr.js} +2 -2
  59. package/dist/cli/{chunk-cy5bm0j0.js.map → chunk-t2m5j4pr.js.map} +1 -1
  60. package/dist/cli/{chunk-yj17vs39.js → chunk-tdqhyxfz.js} +5 -5
  61. package/dist/cli/{chunk-q3wxxq39.js → chunk-tphac74f.js} +5 -5
  62. package/dist/cli/{chunk-6bm0xpgd.js → chunk-w1r4vase.js} +2 -2
  63. package/dist/cli/index.js +24 -19
  64. package/dist/cli/index.js.map +3 -3
  65. package/dist/types/ai/ask-context.d.ts +6 -2
  66. package/dist/types/ai/component-markdown.d.ts +2 -0
  67. package/dist/types/ai/openapi-components.d.ts +4 -1
  68. package/dist/types/astro/integration.d.ts +5 -4
  69. package/dist/types/astro/markdown-negotiation.d.ts +3 -0
  70. package/dist/types/astro/pages.d.ts +10 -0
  71. package/dist/types/cli/init/scaffold.d.ts +1 -1
  72. package/dist/types/components/content/api-field.d.ts +4 -1
  73. package/dist/types/components/content/api-page.d.ts +6 -1
  74. package/dist/types/components/openapi/code-samples.d.ts +29 -0
  75. package/dist/types/components/openapi/helpers.d.ts +22 -1
  76. package/dist/types/components/openapi/security.d.ts +10 -0
  77. package/dist/types/components/openapi/snippets.d.ts +60 -0
  78. package/dist/types/core/changelog-index.d.ts +8 -0
  79. package/dist/types/core/config-input.d.ts +30 -3
  80. package/dist/types/core/data.d.ts +4 -0
  81. package/dist/types/core/i18n-ui.d.ts +2 -0
  82. package/dist/types/core/links.d.ts +8 -0
  83. package/dist/types/core/probe.d.ts +2 -0
  84. package/dist/types/core/project-graph.d.ts +6 -0
  85. package/dist/types/core/redirect-patterns.d.ts +11 -3
  86. package/dist/types/core/schema.d.ts +119 -0
  87. package/dist/types/core/sources/cache.d.ts +3 -0
  88. package/dist/types/core/sources/contentful-rich-text.d.ts +11 -2
  89. package/dist/types/core/sources/contentful.d.ts +17 -1
  90. package/dist/types/core/sources/lexical.d.ts +5 -0
  91. package/dist/types/core/sources/normalize.d.ts +20 -1
  92. package/dist/types/core/sources/remote.d.ts +37 -10
  93. package/dist/types/core/sources/sanity.d.ts +1 -1
  94. package/dist/types/core/types.d.ts +17 -0
  95. package/dist/types/deploy/adapter-output.d.ts +2 -1
  96. package/dist/types/deploy/adapters/cloudflare.d.ts +7 -5
  97. package/dist/types/deploy/adapters/node.d.ts +58 -23
  98. package/dist/types/deploy/adapters/registry.d.ts +10 -0
  99. package/dist/types/deploy/adapters/types.d.ts +3 -2
  100. package/dist/types/deploy/platforms/cloudflare.d.ts +10 -7
  101. package/dist/types/deploy/platforms/node.d.ts +3 -1
  102. package/dist/types/deploy/platforms/types.d.ts +18 -7
  103. package/dist/types/deploy/platforms/vercel.d.ts +20 -1
  104. package/dist/types/deploy/redirects.d.ts +17 -1
  105. package/dist/types/deploy/sitemap.d.ts +2 -1
  106. package/dist/types/deploy/vercel-negotiation.d.ts +42 -21
  107. package/dist/types/openapi/checks.d.ts +27 -0
  108. package/dist/types/openapi/graphql.d.ts +6 -0
  109. package/dist/types/openapi/model.d.ts +15 -4
  110. package/dist/types/openapi/parse.d.ts +3 -0
  111. package/dist/types/openapi/references.d.ts +16 -0
  112. package/dist/types/ratelimit/index.d.ts +1 -1
  113. package/dist/types/ratelimit/schema.d.ts +8 -0
  114. package/dist/types/ratelimit/upstash.d.ts +39 -14
  115. package/dist/types/reference/graphql.d.ts +76 -1
  116. package/dist/types/reference/index.d.ts +1 -1
  117. package/dist/types/reference/openapi.d.ts +2 -2
  118. package/dist/types/reference/options.d.ts +37 -1
  119. package/dist/types/reference/schema.d.ts +84 -0
  120. package/dist/types/search/adapters/registry.d.ts +3 -0
  121. package/dist/types/search/adapters/typesense.d.ts +14 -4
  122. package/dist/types/search/build.d.ts +3 -1
  123. package/dist/types/search/orama-index.d.ts +9 -9
  124. package/dist/types/search/sync/algolia.d.ts +5 -2
  125. package/dist/types/search/sync/index.d.ts +3 -1
  126. package/dist/types/search/sync/typesense.d.ts +11 -7
  127. package/dist/types/sources/contentful.d.ts +16 -0
  128. package/dist/types/sources/github-releases.d.ts +8 -1
  129. package/dist/types/sources/payload.d.ts +2 -0
  130. package/dist/types/sources/registry.d.ts +54 -0
  131. package/dist/types/sources/shared.d.ts +3 -0
  132. package/dist/types/sources/strapi.d.ts +2 -0
  133. package/docs/01-quickstart.mdx +4 -2
  134. package/docs/02-deployment.mdx +31 -13
  135. package/docs/advanced/changelog.mdx +11 -0
  136. package/docs/advanced/skills.mdx +1 -1
  137. package/docs/cli/audit.mdx +30 -7
  138. package/docs/cli/doctor.mdx +5 -1
  139. package/docs/cli/evals.mdx +4 -4
  140. package/docs/cli/translate.mdx +1 -0
  141. package/docs/cli/validate.mdx +4 -3
  142. package/docs/configuration/analytics.mdx +1 -1
  143. package/docs/configuration/assistant.mdx +6 -2
  144. package/docs/configuration/customization.mdx +4 -2
  145. package/docs/configuration/export.mdx +5 -3
  146. package/docs/configuration/index.mdx +3 -2
  147. package/docs/configuration/rate-limiting.mdx +23 -2
  148. package/docs/configuration/search.mdx +37 -14
  149. package/docs/content/components.mdx +2 -2
  150. package/docs/content/frontmatter.mdx +5 -3
  151. package/docs/content/i18n.mdx +2 -2
  152. package/docs/content/includes.mdx +1 -1
  153. package/docs/content/navigation.mdx +4 -4
  154. package/docs/content/sources/contentful.mdx +7 -4
  155. package/docs/content/sources/custom.mdx +7 -3
  156. package/docs/content/sources/github-releases.mdx +10 -2
  157. package/docs/content/sources/index.mdx +3 -3
  158. package/docs/content/sources/notion.mdx +1 -1
  159. package/docs/content/sources/obsidian.mdx +1 -1
  160. package/docs/content/sources/payload.mdx +3 -2
  161. package/docs/content/sources/remote-mdx.mdx +4 -0
  162. package/docs/content/sources/sanity.mdx +2 -2
  163. package/docs/content/sources/strapi.mdx +3 -2
  164. package/docs/content/syntax.mdx +2 -2
  165. package/docs/discoverability/agent-discovery.mdx +1 -1
  166. package/docs/discoverability/index.mdx +1 -1
  167. package/docs/discoverability/json-api.mdx +2 -0
  168. package/docs/discoverability/llms-txt.mdx +1 -1
  169. package/docs/discoverability/markdown.mdx +3 -1
  170. package/docs/discoverability/mcp.mdx +4 -0
  171. package/docs/discoverability/sitemap-and-robots.mdx +1 -1
  172. package/docs/references/api-pages.mdx +1 -1
  173. package/docs/references/asyncapi.mdx +3 -1
  174. package/docs/references/graphql.mdx +13 -1
  175. package/docs/references/openapi.mdx +50 -8
  176. package/package.json +9 -9
  177. package/skills/blume-migrate/SKILL.md +1 -1
  178. package/skills/blume-migrate/references/docusaurus.md +4 -4
  179. package/skills/blume-migrate/references/fumadocs.md +17 -13
  180. package/skills/blume-migrate/references/nextra.md +8 -5
  181. package/skills/blume-migrate/references/starlight.md +9 -7
  182. package/src/ai/ask-context.ts +33 -3
  183. package/src/ai/ask-limits.ts +13 -0
  184. package/src/ai/changelog-markdown.ts +11 -6
  185. package/src/ai/component-markdown.ts +1 -1
  186. package/src/ai/cors.ts +12 -3
  187. package/src/ai/mcp/query.ts +14 -6
  188. package/src/ai/openapi-components.ts +28 -4
  189. package/src/ai/relative-links.ts +4 -38
  190. package/src/astro/generate.ts +78 -48
  191. package/src/astro/integration.ts +16 -13
  192. package/src/astro/markdown-negotiation.ts +3 -0
  193. package/src/astro/module-types.ts +3 -1
  194. package/src/astro/pages.ts +23 -0
  195. package/src/astro/templates.ts +125 -80
  196. package/src/audit/catalog.ts +19 -3
  197. package/src/audit/checks/links.ts +14 -1
  198. package/src/audit/checks/network.ts +110 -6
  199. package/src/audit/checks/redirects.ts +6 -4
  200. package/src/audit/checks/robots.ts +58 -2
  201. package/src/audit/run.ts +3 -0
  202. package/src/audit/types.ts +2 -0
  203. package/src/audit/url.ts +14 -5
  204. package/src/blume-modules.d.ts +5 -1
  205. package/src/cli/args.ts +51 -0
  206. package/src/cli/build-failure.ts +23 -0
  207. package/src/cli/commands/audit.ts +10 -1
  208. package/src/cli/commands/dev.ts +18 -2
  209. package/src/cli/commands/doctor.ts +45 -12
  210. package/src/cli/commands/eval.ts +36 -4
  211. package/src/cli/commands/init.ts +6 -4
  212. package/src/cli/commands/validate.ts +10 -1
  213. package/src/cli/init/scaffold.ts +3 -3
  214. package/src/cli/prepare.ts +6 -3
  215. package/src/cli/required-secrets.ts +1 -1
  216. package/src/components/content/ApiPlayground.astro +6 -2
  217. package/src/components/content/api-field.ts +4 -1
  218. package/src/components/content/api-page.ts +31 -7
  219. package/src/components/content/mermaid-element.ts +15 -1
  220. package/src/components/islands/hooks.ts +27 -1
  221. package/src/components/layout/PageActions.astro +139 -24
  222. package/src/components/layout/RootLayout.astro +10 -2
  223. package/src/components/layout/Search.astro +17 -3
  224. package/src/components/layout/TableOfContents.astro +24 -17
  225. package/src/components/layout/head-scripts.ts +6 -1
  226. package/src/components/layout/search/algolia.ts +10 -1
  227. package/src/components/layout/search/analytics.ts +42 -14
  228. package/src/components/layout/search/endpoint.ts +48 -5
  229. package/src/components/layout/search/pagefind.ts +114 -5
  230. package/src/components/layout/search/types.ts +12 -0
  231. package/src/components/layout/search/typesense.ts +18 -6
  232. package/src/components/openapi/AsyncApiOperation.astro +14 -7
  233. package/src/components/openapi/Authorization.astro +10 -0
  234. package/src/components/openapi/GraphqlOperation.astro +5 -0
  235. package/src/components/openapi/Operation.astro +24 -1
  236. package/src/components/openapi/ParametersTable.astro +8 -4
  237. package/src/components/openapi/Playground.astro +7 -1
  238. package/src/components/openapi/RequestBody.astro +19 -2
  239. package/src/components/openapi/Responses.astro +1 -0
  240. package/src/components/openapi/SchemaTable.astro +15 -3
  241. package/src/components/openapi/async-snippets.ts +61 -5
  242. package/src/components/openapi/async.ts +123 -29
  243. package/src/components/openapi/graphql-helpers.ts +35 -12
  244. package/src/components/openapi/helpers.ts +36 -2
  245. package/src/components/openapi/message-model.ts +54 -11
  246. package/src/components/openapi/message.ts +14 -4
  247. package/src/components/openapi/operation-model.ts +16 -7
  248. package/src/components/openapi/playground-client.ts +24 -5
  249. package/src/components/openapi/playground-schema.ts +1 -1
  250. package/src/components/openapi/schema-tree.ts +44 -14
  251. package/src/components/openapi/security.ts +10 -0
  252. package/src/components/openapi/snippets.ts +5 -3
  253. package/src/components/openapi/validate-json.ts +4 -3
  254. package/src/core/changelog-index.ts +31 -1
  255. package/src/core/component-diagnostics.ts +53 -0
  256. package/src/core/component-overrides.ts +32 -0
  257. package/src/core/config-input.ts +31 -3
  258. package/src/core/data.ts +2 -0
  259. package/src/core/diagnostics.ts +5 -0
  260. package/src/core/i18n-ui.ts +2 -0
  261. package/src/core/includes.ts +1 -1
  262. package/src/core/links.ts +47 -17
  263. package/src/core/probe.ts +26 -6
  264. package/src/core/project-graph.ts +59 -18
  265. package/src/core/redirect-patterns.ts +36 -7
  266. package/src/core/schema.ts +21 -2
  267. package/src/core/sources/cache.ts +6 -0
  268. package/src/core/sources/contentful-rich-text.ts +29 -6
  269. package/src/core/sources/contentful.ts +135 -25
  270. package/src/core/sources/filesystem.ts +26 -4
  271. package/src/core/sources/github-releases.ts +12 -1
  272. package/src/core/sources/lexical.ts +15 -10
  273. package/src/core/sources/mdx-remote.ts +62 -10
  274. package/src/core/sources/normalize.ts +227 -41
  275. package/src/core/sources/notion.ts +7 -1
  276. package/src/core/sources/obsidian.ts +16 -3
  277. package/src/core/sources/payload.ts +38 -15
  278. package/src/core/sources/remote.ts +72 -15
  279. package/src/core/sources/resolve.ts +11 -1
  280. package/src/core/sources/sanity.ts +43 -25
  281. package/src/core/sources/strapi.ts +2 -2
  282. package/src/core/types.ts +18 -0
  283. package/src/core/ui-packs/ar.ts +1 -0
  284. package/src/core/ui-packs/bg.ts +1 -0
  285. package/src/core/ui-packs/bn.ts +1 -0
  286. package/src/core/ui-packs/ca.ts +1 -0
  287. package/src/core/ui-packs/cs.ts +1 -0
  288. package/src/core/ui-packs/da.ts +1 -0
  289. package/src/core/ui-packs/de.ts +1 -0
  290. package/src/core/ui-packs/el.ts +1 -0
  291. package/src/core/ui-packs/es.ts +1 -0
  292. package/src/core/ui-packs/fa.ts +1 -0
  293. package/src/core/ui-packs/fi.ts +1 -0
  294. package/src/core/ui-packs/fr.ts +1 -0
  295. package/src/core/ui-packs/he.ts +1 -0
  296. package/src/core/ui-packs/hi.ts +1 -0
  297. package/src/core/ui-packs/hr.ts +1 -0
  298. package/src/core/ui-packs/hu.ts +1 -0
  299. package/src/core/ui-packs/id.ts +1 -0
  300. package/src/core/ui-packs/it.ts +1 -0
  301. package/src/core/ui-packs/ja.ts +1 -0
  302. package/src/core/ui-packs/ko.ts +1 -0
  303. package/src/core/ui-packs/nl.ts +1 -0
  304. package/src/core/ui-packs/no.ts +1 -0
  305. package/src/core/ui-packs/pl.ts +1 -0
  306. package/src/core/ui-packs/pt-br.ts +1 -0
  307. package/src/core/ui-packs/pt.ts +1 -0
  308. package/src/core/ui-packs/ro.ts +1 -0
  309. package/src/core/ui-packs/ru.ts +1 -0
  310. package/src/core/ui-packs/sk.ts +1 -0
  311. package/src/core/ui-packs/sr.ts +1 -0
  312. package/src/core/ui-packs/sv.ts +1 -0
  313. package/src/core/ui-packs/th.ts +1 -0
  314. package/src/core/ui-packs/tr.ts +1 -0
  315. package/src/core/ui-packs/uk.ts +1 -0
  316. package/src/core/ui-packs/vi.ts +1 -0
  317. package/src/core/ui-packs/zh-tw.ts +1 -0
  318. package/src/core/ui-packs/zh.ts +1 -0
  319. package/src/deploy/adapter-output.ts +7 -2
  320. package/src/deploy/adapters/cloudflare.ts +7 -5
  321. package/src/deploy/adapters/node.ts +47 -9
  322. package/src/deploy/adapters/types.ts +7 -3
  323. package/src/deploy/artifacts.ts +2 -1
  324. package/src/deploy/platforms/cloudflare.ts +13 -9
  325. package/src/deploy/platforms/netlify.ts +2 -1
  326. package/src/deploy/platforms/node.ts +12 -2
  327. package/src/deploy/platforms/types.ts +21 -7
  328. package/src/deploy/platforms/vercel.ts +113 -16
  329. package/src/deploy/redirects.ts +66 -6
  330. package/src/deploy/rss.ts +3 -1
  331. package/src/deploy/sitemap.ts +39 -5
  332. package/src/deploy/vercel-negotiation.ts +290 -102
  333. package/src/eval/agents.ts +18 -1
  334. package/src/eval/prompts.ts +6 -3
  335. package/src/eval/report.ts +34 -13
  336. package/src/eval/run.ts +5 -0
  337. package/src/markdown/relative-links.ts +3 -2
  338. package/src/openapi/checks.ts +273 -0
  339. package/src/openapi/graphql-build.ts +21 -0
  340. package/src/openapi/graphql.ts +11 -0
  341. package/src/openapi/model.ts +15 -3
  342. package/src/openapi/parse.ts +127 -2
  343. package/src/openapi/references.ts +43 -3
  344. package/src/openapi/source.ts +39 -14
  345. package/src/ratelimit/index.ts +1 -1
  346. package/src/ratelimit/runtime.ts +37 -19
  347. package/src/ratelimit/upstash.ts +40 -10
  348. package/src/reference/graphql.ts +15 -1
  349. package/src/reference/index.ts +1 -0
  350. package/src/reference/openapi.ts +2 -2
  351. package/src/reference/options.ts +29 -1
  352. package/src/registry/eject.ts +3 -1
  353. package/src/search/adapters/locale-scope.ts +30 -0
  354. package/src/search/adapters/typesense.ts +14 -4
  355. package/src/search/build.ts +21 -3
  356. package/src/search/orama-index.ts +163 -31
  357. package/src/search/source-pages.ts +29 -0
  358. package/src/search/sync/algolia.ts +99 -9
  359. package/src/search/sync/index.ts +52 -13
  360. package/src/search/sync/typesense.ts +72 -27
  361. package/src/sources/contentful.ts +12 -0
  362. package/src/sources/github-releases.ts +7 -1
  363. package/src/sources/shared.ts +3 -0
  364. package/src/theme/entry.ts +29 -3
  365. package/src/translate/validate.ts +23 -1
  366. package/dist/cli/chunk-0gz36akr.js.map +0 -12
  367. package/dist/cli/chunk-2n4ps1b3.js.map +0 -15
  368. package/dist/cli/chunk-43zcf4gz.js.map +0 -36
  369. package/dist/cli/chunk-4ejxbzee.js +0 -39
  370. package/dist/cli/chunk-4ejxbzee.js.map +0 -10
  371. package/dist/cli/chunk-4h8z9vv5.js.map +0 -16
  372. package/dist/cli/chunk-713mngdq.js.map +0 -14
  373. package/dist/cli/chunk-9xr4dgee.js.map +0 -10
  374. package/dist/cli/chunk-amrb6hgh.js +0 -145
  375. package/dist/cli/chunk-amrb6hgh.js.map +0 -11
  376. package/dist/cli/chunk-nd1821sk.js.map +0 -10
  377. package/dist/cli/chunk-rekqz4qg.js.map +0 -10
  378. package/dist/cli/chunk-ta27feev.js.map +0 -10
  379. package/dist/cli/chunk-xkz5xbtv.js.map +0 -213
  380. package/dist/cli/chunk-y5h4r2y0.js.map +0 -10
  381. /package/dist/cli/{chunk-gp84za0h.js.map → chunk-3v3ffbrj.js.map} +0 -0
  382. /package/dist/cli/{chunk-p804f3pe.js.map → chunk-8y2kmjv8.js.map} +0 -0
  383. /package/dist/cli/{chunk-2f7n4qzt.js.map → chunk-c1y538kd.js.map} +0 -0
  384. /package/dist/cli/{chunk-1n228xqb.js.map → chunk-c887fj0s.js.map} +0 -0
  385. /package/dist/cli/{chunk-cmtcnh9f.js.map → chunk-dzwehpy7.js.map} +0 -0
  386. /package/dist/cli/{chunk-ntv4qrt7.js.map → chunk-e9q7a2ak.js.map} +0 -0
  387. /package/dist/cli/{chunk-5wj2qwn2.js.map → chunk-ft9vfys6.js.map} +0 -0
  388. /package/dist/cli/{chunk-zhvd6adm.js.map → chunk-hx3xqnk9.js.map} +0 -0
  389. /package/dist/cli/{chunk-y4ykbark.js.map → chunk-kma9f6sf.js.map} +0 -0
  390. /package/dist/cli/{chunk-py62fewh.js.map → chunk-mrcccgnh.js.map} +0 -0
  391. /package/dist/cli/{chunk-2f3ndkpw.js.map → chunk-n0v7m8ry.js.map} +0 -0
  392. /package/dist/cli/{chunk-9geymqns.js.map → chunk-nq1d20w2.js.map} +0 -0
  393. /package/dist/cli/{chunk-g2z9x01v.js.map → chunk-nrns10mj.js.map} +0 -0
  394. /package/dist/cli/{chunk-e7vvk5ya.js.map → chunk-sds923fa.js.map} +0 -0
  395. /package/dist/cli/{chunk-yj17vs39.js.map → chunk-tdqhyxfz.js.map} +0 -0
  396. /package/dist/cli/{chunk-q3wxxq39.js.map → chunk-tphac74f.js.map} +0 -0
  397. /package/dist/cli/{chunk-6bm0xpgd.js.map → chunk-w1r4vase.js.map} +0 -0
@@ -1,14 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/cli/commands/mcp-stdio.ts", "../src/ai/mcp/stdio.ts", "../src/ai/mcp/server.ts", "../src/ai/mcp/query.ts", "../src/search/orama-index.ts"],
4
- "sourcesContent": [
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
- "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 ErrorCode,\n ListResourcesRequestSchema,\n ListToolsRequestSchema,\n McpError,\n ReadResourceRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { z } from \"zod\";\n\nimport { mountBasePath } 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}${mountBasePath(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
- "import { z } from \"zod\";\n\nimport {\n mountBasePath,\n normalizeRoute as normalizePageRoute,\n stripBasePath,\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 = mountBasePath(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
- "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 /** The page's relevance multiplier (`search.boost`); stored, not indexed. */\n boost?: number;\n /** Extra terms the page is found by (`search.keywords`). */\n keywords?: 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 keywords: \"string[]\",\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/**\n * Title and description outrank body text, matching the search dialog; a\n * page's keywords count as much as its title, since its author chose them.\n */\nconst BOOST = { description: 2, keywords: 3, title: 3 };\n\n/** Databases holding a boosted page, which rank by boosted score. */\nconst boostedDatabases = new WeakSet<AnyOrama>();\n\n/** A search match as Orama's sort function sees it: id, score, document. */\ntype RankedMatch = [unknown, number, OramaDoc];\n\n/** Rank matches by relevance times the page's `search.boost`. */\nconst byBoostedScore = (a: RankedMatch, b: RankedMatch): number =>\n b[1] * (b[2].boost ?? 1) - a[1] * (a[2].boost ?? 1);\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 if (documents.some((doc) => (doc.boost ?? 1) !== 1)) {\n boostedDatabases.add(db);\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 // Boosts multiply every match's score before the limit applies (Orama\n // sorts the whole match set with a sort function), so a boosted page can\n // rise past matches outside the first page.\n const unfiltered = {\n boost: BOOST,\n limit,\n properties: [\"title\", \"keywords\", \"description\", \"content\"],\n ...(boostedDatabases.has(db) && { sortBy: byBoostedScore }),\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
- ],
11
- "mappings": "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",
12
- "debugId": "19894529A226AE0264756E2164756E21",
13
- "names": []
14
- }
@@ -1,10 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/cli/commands/doctor.ts"],
4
- "sourcesContent": [
5
- "import { readFileSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\n\nimport { defineCommand } from \"citty\";\nimport { join } from \"pathe\";\nimport { satisfies } from \"semver\";\n\nimport { discoverIslands } from \"../../astro/islands.ts\";\nimport { missingDependencyDiagnostic } from \"../../astro/runtime-deps.ts\";\nimport {\n analyzeComponentOverrides,\n ComponentOverridesError,\n} from \"../../core/component-overrides.ts\";\nimport { BlumeError } from \"../../core/diagnostics.ts\";\nimport { packageRoot } from \"../../core/package-root.ts\";\nimport { scanProject } from \"../../core/project-graph.ts\";\nimport type { ResolvedConfig } from \"../../core/schema.ts\";\nimport { serverFeatures } from \"../../core/server-features.ts\";\nimport type { Diagnostic } from \"../../core/types.ts\";\nimport { unregisteredSnapshotDiagnostics } from \"../../core/version-cut.ts\";\nimport { commandMeta } from \"../command-meta.ts\";\nimport { loadEnvFiles } from \"../env.ts\";\nimport { reportInternalError } from \"../internal-error.ts\";\nimport {\n flushStdout,\n logger,\n reportDiagnostics,\n reportDiagnosticsJson,\n} from \"../log.ts\";\nimport { checkRequiredSecrets } from \"../required-secrets.ts\";\n\nconst FALLBACK_NODE_RANGE = \">=22.12.0\";\n\n/**\n * Plan `components.ts` the way `blume dev`/`build` do, reporting each override\n * that can't be planned — a non-static form, or an import of a file that\n * doesn't exist — with its own line. The scan alone never reads the file, so\n * without this doctor would pass a project whose build fails.\n */\nconst componentsDiagnostics = async (\n componentsFile: string | null\n): Promise<Diagnostic[]> => {\n if (!componentsFile) {\n return [];\n }\n try {\n analyzeComponentOverrides(\n await readFile(componentsFile, \"utf-8\"),\n componentsFile\n );\n return [];\n } catch (error) {\n if (error instanceof ComponentOverridesError) {\n return error.issues;\n }\n throw error;\n }\n};\n\n/** The supported Node range, read from the package's own `engines` field so\n * doctor can never drift from what the package actually declares. */\nconst supportedNodeRange = (): string => {\n try {\n // SAFETY: this parses blume's own package.json; the optional fields cover\n // an `engines` block going missing, and a bad read falls to the catch.\n const pkg = JSON.parse(\n readFileSync(join(packageRoot(), \"package.json\"), \"utf-8\")\n ) as { engines?: { node?: string } };\n return pkg.engines?.node || FALLBACK_NODE_RANGE;\n } catch {\n return FALLBACK_NODE_RANGE;\n }\n};\n\n/** The adapters a project runs, one line per kind, for the doctor summary. */\nconst integrationSummary = (config: ResolvedConfig): string[] => {\n const { assistant } = config.ai;\n let assistantSummary = \"off\";\n if (assistant?.enabled) {\n assistantSummary = assistant.endpoint\n ? \"external endpoint\"\n : assistant.provider.kind;\n }\n return [\n `References: ${config.reference.map((adapter) => adapter.kind).join(\", \") || \"none\"}`,\n `Analytics: ${config.analytics.map((adapter) => adapter.kind).join(\", \") || \"none\"}`,\n `Consent: ${config.consent?.kind ?? \"none\"}`,\n `Rate limiting: ${config.rateLimit?.kind ?? \"off\"}`,\n `Sources: ${config.content.sources.map((source) => source.kind).join(\", \")}`,\n `Assistant: ${assistantSummary}`,\n ];\n};\n\nexport const doctorCommand = defineCommand({\n args: {\n json: {\n description: \"Emit diagnostics as JSON on stdout (for CI/editors).\",\n type: \"boolean\",\n },\n },\n meta: commandMeta.doctor,\n async run({ args }) {\n const root = process.cwd();\n const diagnostics: Diagnostic[] = [];\n\n const nodeRange = supportedNodeRange();\n if (!satisfies(process.versions.node, nodeRange)) {\n diagnostics.push({\n code: \"BLUME_NODE_VERSION\",\n message: `Node ${process.versions.node} is outside the supported range (${nodeRange}).`,\n severity: \"warning\",\n });\n }\n\n // Read `.env` files first, as `blume dev`/`build` do: remote sources read\n // their tokens during the scan, and the secrets check below reads them too.\n loadEnvFiles(root);\n\n try {\n const project = await scanProject(root, { mode: \"build\" });\n diagnostics.push(\n ...project.diagnostics,\n ...unregisteredSnapshotDiagnostics(project)\n );\n\n const { config } = project;\n // The packages and secrets a build would need: an adapter's missing SDK\n // fails the build, and a missing secret fails at the first request.\n const { islands } = await discoverIslands(root);\n const dependencies = await missingDependencyDiagnostic(\n config,\n root,\n \"error\",\n islands.map((island) => island.framework)\n );\n if (dependencies) {\n diagnostics.push(dependencies);\n }\n diagnostics.push(\n ...(await componentsDiagnostics(project.context.componentsFile)),\n ...checkRequiredSecrets(config)\n );\n const features = serverFeatures(config);\n if (\n features.length > 0 &&\n config.deployment.options.output === \"static\"\n ) {\n // A host adapter already names the target, so only its\n // `output: \"static\"` stands in the way; otherwise name one.\n const { kind } = config.deployment;\n diagnostics.push({\n code: \"BLUME_SERVER_FEATURE_REQUIRED\",\n message: `${features.join(\", \")} ${features.length === 1 ? \"requires\" : \"require\"} server output.`,\n severity: \"error\",\n suggestion:\n kind === \"static\"\n ? 'Set deployment to a host adapter from \"blume/deploy\" (e.g. `deployment: vercel()`).'\n : `Drop \\`output: \"static\"\\` from \\`deployment: ${kind}()\\` to build for the server.`,\n });\n }\n\n if (!args.json) {\n logger.info(`Pages: ${project.graph.pages.length}`);\n logger.info(`Output: ${config.deployment.options.output}`);\n logger.info(`Adapter: ${config.deployment.kind}`);\n logger.info(`Search: ${config.search.provider.kind}`);\n for (const line of integrationSummary(config)) {\n logger.info(line);\n }\n }\n } catch (error) {\n if (error instanceof BlumeError) {\n diagnostics.push(error.diagnostic);\n } else {\n reportInternalError(error);\n process.exit(1);\n }\n }\n\n if (args.json) {\n // Drain stdout before exiting non-zero: `process.exit` would otherwise\n // truncate the JSON payload mid-write when stdout is a pipe — exactly how\n // `--json` is consumed in CI/editors.\n if (reportDiagnosticsJson(diagnostics, root)) {\n await flushStdout();\n process.exit(1);\n }\n return;\n }\n\n const hadErrors = reportDiagnostics(diagnostics, root);\n if (diagnostics.length === 0) {\n logger.success(\"No problems found.\");\n }\n if (hadErrors) {\n // Set the code and return rather than `process.exit`, which doesn't wait\n // for a piped stderr: a long diagnostic list would be cut off.\n process.exitCode = 1;\n }\n },\n});\n"
6
- ],
7
- "mappings": "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",
8
- "debugId": "F699BCD1CBA5F43264756E2164756E21",
9
- "names": []
10
- }
@@ -1,145 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- packageRoot
4
- } from "./index.js";
5
- import {
6
- commandsFor,
7
- detectProjectPackageManager
8
- } from "./chunk-xkz5xbtv.js";
9
-
10
- // src/astro/islands.ts
11
- import { readFile } from "node:fs/promises";
12
- import pMap from "p-map";
13
- import { basename, join } from "pathe";
14
- import { glob } from "tinyglobby";
15
- var DEFAULT_CLIENT = "visible";
16
- var READ_CONCURRENCY = 16;
17
- var VALID_MODES = new Set([
18
- "idle",
19
- "load",
20
- "only",
21
- "visible"
22
- ]);
23
- var isClientMode = (mode) => VALID_MODES.has(mode);
24
- var FRAMEWORK_BY_EXT = new Map([
25
- ["jsx", "react"],
26
- ["svelte", "svelte"],
27
- ["tsx", "react"],
28
- ["vue", "vue"]
29
- ]);
30
- var ISLAND_FILE = /\.(?<ext>jsx|svelte|tsx|vue)$/u;
31
- var CLIENT_EXPORT = /export\s+const\s+client\s*(?::[^=]+)?=\s*["'](?<mode>\w+)["']/u;
32
- var readClientMode = (source, file, warnings) => {
33
- const mode = source.match(CLIENT_EXPORT)?.groups?.mode;
34
- if (!mode) {
35
- return DEFAULT_CLIENT;
36
- }
37
- if (!isClientMode(mode)) {
38
- warnings.push(`Island "${file}" declares an unknown client mode "${mode}"; defaulting to "${DEFAULT_CLIENT}". Use "load", "idle", "visible", or "only".`);
39
- return DEFAULT_CLIENT;
40
- }
41
- return mode;
42
- };
43
- var discoverIslands = async (root) => {
44
- const dir = join(root, "islands");
45
- const matches = await glob(["**/*"], {
46
- absolute: true,
47
- cwd: dir,
48
- onlyFiles: true
49
- });
50
- const files = matches.toSorted();
51
- const sources = await pMap(files, (file) => readFile(file, "utf-8"), {
52
- concurrency: READ_CONCURRENCY
53
- });
54
- const islands = [];
55
- const warnings = [];
56
- const seen = new Map;
57
- const collectIsland = (file, source) => {
58
- const base = basename(file);
59
- const ext = base.match(ISLAND_FILE)?.groups?.ext;
60
- const framework = ext ? FRAMEWORK_BY_EXT.get(ext) : undefined;
61
- if (!framework) {
62
- return;
63
- }
64
- const name = base.replace(ISLAND_FILE, "");
65
- if (!/^[A-Z][A-Za-z0-9_]*$/u.test(name)) {
66
- warnings.push(`Island "${file}" must have a PascalCase identifier filename to be used in MDX (letters, digits, and underscores only, e.g. Counter.tsx → <Counter />); skipping it.`);
67
- return;
68
- }
69
- const existing = seen.get(name);
70
- if (existing) {
71
- warnings.push(`Two islands both resolve to <${name}> ("${existing}" and "${file}"); ignoring the second. Give them distinct filenames.`);
72
- return;
73
- }
74
- seen.set(name, file);
75
- islands.push({
76
- client: readClientMode(source, file, warnings),
77
- file,
78
- framework,
79
- name
80
- });
81
- };
82
- for (const [index, file] of files.entries()) {
83
- collectIsland(file, sources[index] ?? "");
84
- }
85
- return { islands, warnings };
86
- };
87
-
88
- // src/astro/runtime-deps.ts
89
- import { createRequire } from "node:module";
90
- import { pathToFileURL } from "node:url";
91
- import { join as join2 } from "pathe";
92
- var canResolveFrom = (fromDir, spec) => {
93
- try {
94
- createRequire(pathToFileURL(join2(fromDir, "_.js")).href).resolve(spec);
95
- return true;
96
- } catch {
97
- return false;
98
- }
99
- };
100
- var unresolved = (owner, deps, root, pkgDir) => deps.filter((dep) => !(canResolveFrom(root, dep) || canResolveFrom(pkgDir, dep))).map((dep) => ({ dep, install: [dep], owner }));
101
- var searchProviderDependencies = (provider, root, pkgDir = packageRoot()) => unresolved(`Search adapter "${provider.kind}"`, provider.runtimeDeps, root, pkgDir);
102
- var sourceAdapterDependencies = (sources, root, pkgDir = packageRoot()) => sources.flatMap((source) => unresolved(`Content source "${source.kind}"`, source.runtimeDeps, root, pkgDir));
103
- var assistantProviderDependencies = (ask, root, pkgDir = packageRoot()) => ask?.enabled && !ask.endpoint ? unresolved(`Assistant provider "${ask.provider.kind}"`, ask.provider.runtimeDeps, root, pkgDir) : [];
104
- var deploymentAdapterDependencies = (deployment, root, pkgDir = packageRoot()) => unresolved(`Deployment adapter "${deployment.kind}"`, deployment.runtimeDeps, root, pkgDir);
105
- var ISLAND_FRAMEWORK_DEPS = new Map([
106
- ["svelte", "@astrojs/svelte"],
107
- ["vue", "@astrojs/vue"]
108
- ]);
109
- var islandFrameworkDependencies = (frameworks, root) => [...frameworks].flatMap((framework) => {
110
- const dep = ISLAND_FRAMEWORK_DEPS.get(framework);
111
- return dep && !canResolveFrom(root, dep) ? [
112
- {
113
- dep,
114
- install: [dep, framework],
115
- owner: `Island framework "${framework}"`
116
- }
117
- ] : [];
118
- });
119
- var missingRuntimeDependencies = (config, root, frameworks = [], pkgDir = packageRoot()) => [
120
- ...searchProviderDependencies(config.search.provider, root, pkgDir),
121
- ...sourceAdapterDependencies(config.content.sources, root, pkgDir),
122
- ...assistantProviderDependencies(config.ai.assistant, root, pkgDir),
123
- ...deploymentAdapterDependencies(config.deployment, root, pkgDir),
124
- ...islandFrameworkDependencies(frameworks, root)
125
- ];
126
- var missingDependencyDiagnostic = async (config, root, severity, frameworks = [], pkgDir = packageRoot()) => {
127
- const missing = missingRuntimeDependencies(config, root, frameworks, pkgDir);
128
- if (missing.length === 0) {
129
- return;
130
- }
131
- const { add } = commandsFor(await detectProjectPackageManager(root));
132
- const needs = missing.map(({ dep, owner }) => `${owner} needs "${dep}"`);
133
- const packages = [...new Set(missing.flatMap(({ install }) => install))];
134
- return {
135
- code: "BLUME_DEPENDENCY_MISSING",
136
- message: missing.length === 1 ? `${needs[0]}, which isn't installed.` : `These packages aren't installed: ${needs.join("; ")}.`,
137
- severity,
138
- suggestion: `Install ${packages.length === 1 ? "it" : "them"}: \`${add} ${packages.join(" ")}\`.`
139
- };
140
- };
141
-
142
- export { readClientMode, discoverIslands, missingDependencyDiagnostic };
143
-
144
- //# debugId=137E521D7DE6F81064756E2164756E21
145
- //# sourceMappingURL=chunk-amrb6hgh.js.map
@@ -1,11 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/astro/islands.ts", "../src/astro/runtime-deps.ts"],
4
- "sourcesContent": [
5
- "import { readFile } from \"node:fs/promises\";\n\nimport pMap from \"p-map\";\nimport { basename, join } from \"pathe\";\nimport { glob } from \"tinyglobby\";\n\n/** Astro hydration directives Blume applies to a convention island. */\nexport type IslandClientMode = \"idle\" | \"load\" | \"only\" | \"visible\";\n\n/** Client framework an island is authored in, inferred from its extension. */\nexport type IslandFramework = \"react\" | \"svelte\" | \"vue\";\n\n/** A discovered `islands/` component, ready to wrap and expose to MDX. */\nexport interface IslandSpec {\n /** Hydration directive, from `export const client` or the default. */\n client: IslandClientMode;\n /** Absolute path to the island source file. */\n file: string;\n /** Astro client framework, used to enable the renderer and for `client:only`. */\n framework: IslandFramework;\n /** Component name used in MDX — the file's basename without extension. */\n name: string;\n}\n\nexport interface IslandDiscovery {\n islands: IslandSpec[];\n warnings: string[];\n}\n\n/** Hydration mode used when an island doesn't declare one. */\nconst DEFAULT_CLIENT: IslandClientMode = \"visible\";\n\n/** Ceiling on concurrent island-file reads; unbounded fan-out risks EMFILE. */\nconst READ_CONCURRENCY = 16;\n\nconst VALID_MODES = new Set<IslandClientMode>([\n \"idle\",\n \"load\",\n \"only\",\n \"visible\",\n]);\n\n/** Whether a declared client mode is one Astro's directives support. */\nconst isClientMode = (mode: string): mode is IslandClientMode =>\n // SAFETY: `Set.has` only compares identity at runtime; the assertion widens\n // the lookup key so the narrower-typed set accepts it, and the `has` result\n // is exactly the predicate being claimed.\n VALID_MODES.has(mode as IslandClientMode);\n\n/** Island extensions mapped to the Astro renderer that handles them. */\nconst FRAMEWORK_BY_EXT = new Map<string, IslandFramework>([\n [\"jsx\", \"react\"],\n [\"svelte\", \"svelte\"],\n [\"tsx\", \"react\"],\n [\"vue\", \"vue\"],\n]);\n\n// Captures the extension so we can both strip it from the name and pick the\n// framework. Kept in sync with the glob below.\nconst ISLAND_FILE = /\\.(?<ext>jsx|svelte|tsx|vue)$/u;\n\n// Matches `export const client = \"load\"` (with optional type annotation and\n// either quote style). Read statically so we never execute island code in Node.\nconst CLIENT_EXPORT =\n /export\\s+const\\s+client\\s*(?::[^=]+)?=\\s*[\"'](?<mode>\\w+)[\"']/u;\n\nexport const readClientMode = (\n source: string,\n file: string,\n warnings: string[]\n): IslandClientMode => {\n const mode = source.match(CLIENT_EXPORT)?.groups?.mode;\n if (!mode) {\n return DEFAULT_CLIENT;\n }\n if (!isClientMode(mode)) {\n warnings.push(\n `Island \"${file}\" declares an unknown client mode \"${mode}\"; defaulting to \"${DEFAULT_CLIENT}\". Use \"load\", \"idle\", \"visible\", or \"only\".`\n );\n return DEFAULT_CLIENT;\n }\n return mode;\n};\n\n/**\n * Discover convention islands under `<root>/islands`. Every `.tsx`/`.jsx`/\n * `.vue`/`.svelte` file becomes a globally-available, hydrated MDX component\n * named after the file (which must be PascalCase to be usable as a JSX tag).\n * Hydration defaults to `client:visible`; a file opts out with `export const\n * client = \"load\" | \"idle\" | \"only\"`. Discovery is path-based (a glob), so no\n * user code is executed.\n */\nexport const discoverIslands = async (\n root: string\n): Promise<IslandDiscovery> => {\n const dir = join(root, \"islands\");\n // Enumerate everything and let ISLAND_FILE do the filtering below, so\n // unknown extensions take the skip guard instead of relying on a glob\n // pattern staying in sync with the regex and framework map.\n const matches = await glob([\"**/*\"], {\n absolute: true,\n cwd: dir,\n onlyFiles: true,\n });\n const files = matches.toSorted();\n const sources = await pMap(files, (file) => readFile(file, \"utf-8\"), {\n concurrency: READ_CONCURRENCY,\n });\n\n const islands: IslandSpec[] = [];\n const warnings: string[] = [];\n const seen = new Map<string, string>();\n\n // Extracted so the skip paths become early `return`s (one `continue` budget\n // per loop under the lint rule) instead of `continue` statements.\n const collectIsland = (file: string, source: string): void => {\n const base = basename(file);\n const ext = base.match(ISLAND_FILE)?.groups?.ext;\n const framework = ext ? FRAMEWORK_BY_EXT.get(ext) : undefined;\n if (!framework) {\n return;\n }\n const name = base.replace(ISLAND_FILE, \"\");\n // The name is used verbatim as both an MDX tag and an unquoted object key\n // in the generated island map, so it must be a plain PascalCase identifier\n // — a `-`, `.`, or space (e.g. `Time-Picker.tsx`) would otherwise emit a\n // syntax-error module and fail the whole build with no pointer to the file.\n if (!/^[A-Z][A-Za-z0-9_]*$/u.test(name)) {\n warnings.push(\n `Island \"${file}\" must have a PascalCase identifier filename to be used in MDX (letters, digits, and underscores only, e.g. Counter.tsx → <Counter />); skipping it.`\n );\n return;\n }\n const existing = seen.get(name);\n if (existing) {\n warnings.push(\n `Two islands both resolve to <${name}> (\"${existing}\" and \"${file}\"); ignoring the second. Give them distinct filenames.`\n );\n return;\n }\n seen.set(name, file);\n islands.push({\n client: readClientMode(source, file, warnings),\n file,\n framework,\n name,\n });\n };\n\n for (const [index, file] of files.entries()) {\n collectIsland(file, sources[index] ?? \"\");\n }\n\n return { islands, warnings };\n};\n",
6
- "import { createRequire } from \"node:module\";\nimport { pathToFileURL } from \"node:url\";\n\nimport { join } from \"pathe\";\n\nimport {\n commandsFor,\n detectProjectPackageManager,\n} from \"../cli/init/scaffold.ts\";\nimport { packageRoot } from \"../core/package-root.ts\";\nimport type { ResolvedConfig } from \"../core/schema.ts\";\nimport type { Diagnostic } from \"../core/types.ts\";\n\n/** Whether a module specifier resolves from a directory via node resolution. */\nexport const canResolveFrom = (fromDir: string, spec: string): boolean => {\n try {\n createRequire(pathToFileURL(join(fromDir, \"_.js\")).href).resolve(spec);\n return true;\n } catch {\n return false;\n }\n};\n\n/** A package a configured feature imports that the project can't resolve. */\nexport interface MissingDependency {\n /** The package whose absence was detected. */\n dep: string;\n /** Every package to install for the feature (a framework and its integration). */\n install: string[];\n /** What needs it, as the message names it: `Search adapter \"algolia\"`. */\n owner: string;\n}\n\n/**\n * The declared `deps` of one feature that resolve neither from the project\n * root nor from the Blume package itself. A dep is available when the project\n * installed it OR Blume ships it (the same set the `.blume` deps link exposes\n * to the build). Resolving from the project root alone falsely flagged a\n * shipped SDK like Orama (the default provider) as missing whenever it wasn't\n * hoisted into the project, e.g. under isolated linkers. Each package is\n * resolved from its real location rather than through the `.blume` junction,\n * which can't be traversed reliably for store-symlinked deps.\n */\nconst unresolved = (\n owner: string,\n deps: readonly string[],\n root: string,\n pkgDir: string\n): MissingDependency[] =>\n deps\n .filter(\n (dep) => !(canResolveFrom(root, dep) || canResolveFrom(pkgDir, dep))\n )\n .map((dep) => ({ dep, install: [dep], owner }));\n\n/**\n * The search adapter's SDK, when missing. Provider SDKs are optional peers,\n * so a hosted adapter's client import fails in Vite without one.\n */\nexport const searchProviderDependencies = (\n provider: ResolvedConfig[\"search\"][\"provider\"],\n root: string,\n pkgDir: string = packageRoot()\n): MissingDependency[] =>\n unresolved(\n `Search adapter \"${provider.kind}\"`,\n provider.runtimeDeps,\n root,\n pkgDir\n );\n\n/** Content source SDKs (`@notionhq/client`, `@sanity/client`), when missing. */\nexport const sourceAdapterDependencies = (\n sources: ResolvedConfig[\"content\"][\"sources\"],\n root: string,\n pkgDir: string = packageRoot()\n): MissingDependency[] =>\n sources.flatMap((source) =>\n unresolved(\n `Content source \"${source.kind}\"`,\n source.runtimeDeps,\n root,\n pkgDir\n )\n );\n\n/**\n * The assistant adapter's provider SDK, when missing — only `gateway` needs\n * nothing beyond the core `ai` package Blume ships. An external `endpoint`\n * means no generated route, so no SDK is imported.\n */\nexport const assistantProviderDependencies = (\n ask: ResolvedConfig[\"ai\"][\"assistant\"],\n root: string,\n pkgDir: string = packageRoot()\n): MissingDependency[] =>\n ask?.enabled && !ask.endpoint\n ? unresolved(\n `Assistant provider \"${ask.provider.kind}\"`,\n ask.provider.runtimeDeps,\n root,\n pkgDir\n )\n : [];\n\n/**\n * The deployment adapter's package, when missing. Node and Vercel ship with\n * Blume, so theirs always resolve; Netlify and Cloudflare are optional peers.\n * The generated astro.config.mjs imports the package directly, so without it\n * the build dies with an opaque ERR_MODULE_NOT_FOUND from the hidden config.\n */\nexport const deploymentAdapterDependencies = (\n deployment: ResolvedConfig[\"deployment\"],\n root: string,\n pkgDir: string = packageRoot()\n): MissingDependency[] =>\n unresolved(\n `Deployment adapter \"${deployment.kind}\"`,\n deployment.runtimeDeps,\n root,\n pkgDir\n );\n\n/** Astro integration package each non-React island framework needs installed. */\nconst ISLAND_FRAMEWORK_DEPS = new Map([\n [\"svelte\", \"@astrojs/svelte\"],\n [\"vue\", \"@astrojs/vue\"],\n]);\n\n/**\n * A Vue/Svelte island's Astro integration, when missing — the generated config\n * imports it. React ships with Blume, so it never needs this. The framework\n * itself is installed alongside its integration.\n */\nexport const islandFrameworkDependencies = (\n frameworks: Iterable<string>,\n root: string\n): MissingDependency[] =>\n [...frameworks].flatMap((framework) => {\n const dep = ISLAND_FRAMEWORK_DEPS.get(framework);\n return dep && !canResolveFrom(root, dep)\n ? [\n {\n dep,\n install: [dep, framework],\n owner: `Island framework \"${framework}\"`,\n },\n ]\n : [];\n });\n\n/**\n * Every package the resolved config's adapters (and the given island\n * frameworks) import that isn't installed: the search SDK, content source\n * SDKs, the assistant provider SDK, the deployment adapter, and island\n * integrations. The adapters' `runtimeDeps` are the one place that knows.\n */\nexport const missingRuntimeDependencies = (\n config: ResolvedConfig,\n root: string,\n frameworks: Iterable<string> = [],\n pkgDir: string = packageRoot()\n): MissingDependency[] => [\n ...searchProviderDependencies(config.search.provider, root, pkgDir),\n ...sourceAdapterDependencies(config.content.sources, root, pkgDir),\n ...assistantProviderDependencies(config.ai.assistant, root, pkgDir),\n ...deploymentAdapterDependencies(config.deployment, root, pkgDir),\n ...islandFrameworkDependencies(frameworks, root),\n];\n\n/**\n * One diagnostic for every package the config imports that isn't installed,\n * with the command that installs them all through the project's package\n * manager (`npm install`, `pnpm add`, …), or undefined when nothing is\n * missing. `blume build` raises it as an error and `blume dev` as a warning;\n * `blume doctor` reports it before a build ever runs.\n */\nexport const missingDependencyDiagnostic = async (\n config: ResolvedConfig,\n root: string,\n severity: Diagnostic[\"severity\"],\n frameworks: Iterable<string> = [],\n pkgDir: string = packageRoot()\n): Promise<Diagnostic | undefined> => {\n const missing = missingRuntimeDependencies(config, root, frameworks, pkgDir);\n if (missing.length === 0) {\n return undefined;\n }\n const { add } = commandsFor(await detectProjectPackageManager(root));\n const needs = missing.map(({ dep, owner }) => `${owner} needs \"${dep}\"`);\n const packages = [...new Set(missing.flatMap(({ install }) => install))];\n return {\n code: \"BLUME_DEPENDENCY_MISSING\",\n message:\n missing.length === 1\n ? `${needs[0]}, which isn't installed.`\n : `These packages aren't installed: ${needs.join(\"; \")}.`,\n severity,\n suggestion: `Install ${packages.length === 1 ? \"it\" : \"them\"}: \\`${add} ${packages.join(\" \")}\\`.`,\n };\n};\n"
7
- ],
8
- "mappings": "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",
9
- "debugId": "137E521D7DE6F81064756E2164756E21",
10
- "names": []
11
- }
@@ -1,10 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/cli/commands/dev.ts"],
4
- "sourcesContent": [
5
- "import { dev } from \"astro\";\nimport { watch } from \"chokidar\";\nimport { defineCommand } from \"citty\";\nimport { debounce } from \"perfect-debounce\";\n\nimport { generateRuntime } from \"../../astro/generate.ts\";\nimport {\n refreshBlumeContent,\n showBlumeErrorOverlay,\n} from \"../../astro/integration.ts\";\nimport { BlumeError } from \"../../core/diagnostics.ts\";\nimport { scanProject } from \"../../core/project-graph.ts\";\nimport { resolveRuntimeDir } from \"../../core/project.ts\";\nimport { parsePort } from \"../args.ts\";\nimport { commandMeta } from \"../command-meta.ts\";\nimport {\n acquireDevLock,\n describeDevLock,\n DevLockHeldError,\n updateDevLockPort,\n} from \"../dev-lock.ts\";\nimport { refuseIfEjected } from \"../eject-scripts.ts\";\nimport { normalizeHost } from \"../host-args.ts\";\nimport { logger, reportDiagnostics } from \"../log.ts\";\nimport { prepareProject } from \"../prepare.ts\";\n\n/**\n * A fingerprint of the route set: the sorted `path entryId` pairs. It changes\n * when a page is added, removed, or renamed (a folder rename shifts many at\n * once) but stays equal across pure body edits — so the dev loop can tell a\n * \"structural\" change (needs a content re-sync) from a hot-reloadable one.\n */\nconst routeSignature = (\n routes: readonly { entryId: string; path: string }[]\n): string =>\n routes\n .map((route) => `${route.path} ${route.entryId}`)\n .toSorted()\n .join(\"\\n\");\n\n/**\n * The port the dev server actually bound. Astro snapshots `address` when the\n * server first listens, but a config restart that lands during startup leaves\n * that snapshot null: the `astro.config.mjs` written just before the server\n * started can reach Astro's watcher late, and Astro restarts in place before\n * it ever listens. The live resolved URLs follow the restarted server, so read\n * the port from those, then fall back to the one that was asked for.\n */\nconst boundPortOf = (\n server: Awaited<ReturnType<typeof dev>>,\n requested: number\n): number => {\n // SAFETY: Astro types `address` as always set, but it is null at runtime\n // when a restart replaced the server before its first listen (see above).\n const address = server.address as typeof server.address | null;\n if (address) {\n return address.port;\n }\n for (const url of server.resolvedUrls.local) {\n const port = Number(new URL(url).port);\n if (port > 0) {\n return port;\n }\n }\n return requested;\n};\n\nexport const devCommand = defineCommand({\n args: {\n \"content-dir\": {\n description: \"Content folder to scan, overriding config (content.root).\",\n type: \"string\",\n },\n debug: {\n description: \"Verbose Astro/Vite logging for troubleshooting.\",\n type: \"boolean\",\n },\n host: { description: \"Network host to bind.\", type: \"string\" },\n open: { description: \"Open the browser on start.\", type: \"boolean\" },\n port: { description: \"Port to listen on.\", type: \"string\" },\n preview: {\n description: \"Include drafts and unpublished CMS content.\",\n type: \"boolean\",\n },\n strict: { description: \"Fail on diagnostics.\", type: \"boolean\" },\n },\n meta: commandMeta.dev,\n async run({ args }) {\n const root = process.cwd();\n await refuseIfEjected(root, \"dev\");\n const preview = args.preview ?? false;\n const overrides = args[\"content-dir\"]\n ? { contentRoot: args[\"content-dir\"] }\n : undefined;\n // Astro's dev server defaults to 4321 when no port is passed. Feeding the\n // resolved URL in as the `deployment.site` fallback lets site-gated features\n // (OG images, canonicals, sitemap) work locally without configuring a site.\n const explicitPort = parsePort(args.port);\n const port = explicitPort ?? 4321;\n let devServerUrl = `http://localhost:${port}`;\n\n // Claim the shared `.blume` dir BEFORE preparing: `prepareProject`\n // regenerates the runtime, so even a refused second dev server would\n // otherwise clobber the running one's generated tree (with this\n // invocation's port baked in) on its way out. The claim is atomic, so two\n // simultaneous starts can't both win. Dev never relocates the runtime dir,\n // so the lock always lives at `<root>/.blume`.\n const outDir = resolveRuntimeDir(root);\n let releaseLock: () => void;\n try {\n releaseLock = acquireDevLock(outDir, port);\n } catch (error) {\n if (error instanceof DevLockHeldError) {\n logger.error(\n `A \\`blume dev\\` server is already running${describeDevLock(error.lock)} in this project. Reuse that server instead of starting a second one — two dev servers would corrupt the shared .blume dir. If it crashed, delete .blume/dev.lock.`\n );\n process.exit(1);\n }\n throw error;\n }\n process.on(\"exit\", releaseLock);\n\n const project = await prepareProject({\n devServerUrl,\n mode: \"dev\",\n overrides,\n preview,\n root,\n strict: args.strict,\n });\n\n // A factory so the regenerate loop can recreate the server when a\n // structural (route-set) change can't be re-synced in place (see below).\n // `open` is honored on first boot only — a restart must not reopen the\n // browser.\n const createServer = (listenPort: number | undefined, open: boolean) =>\n dev({\n logLevel: args.debug ? \"debug\" : \"info\",\n root: project.context.outDir,\n server: { host: normalizeHost(args.host), open, port: listenPort },\n });\n\n let server = await createServer(explicitPort, args.open ?? false);\n\n // Vite bumps to the next free port when the default is taken, so record\n // the port the server actually bound — the lock's URL is what a refused\n // second invocation tells its caller to reuse. The site fallback baked\n // into the runtime also carries the port, so it must follow suit (below,\n // once the regeneration closure exists).\n const boundPort = boundPortOf(server, port);\n if (boundPort !== port) {\n updateDevLockPort(outDir, boundPort);\n devServerUrl = `http://localhost:${boundPort}`;\n }\n\n // Mirror any initial diagnostics into the browser overlay now the server\n // (and its HMR channel) is up.\n showBlumeErrorOverlay(project.diagnostics);\n\n let lastSignature = routeSignature(project.manifest.routes);\n\n // Watch user inputs and regenerate the runtime data on change. A body edit\n // hot-reloads via Vite (fast path). A route-set change also needs Astro's\n // content store re-synced: its glob watcher misses directory renames, so a\n // renamed page would 404 (`getEntry` reads a stale store). Astro hands the\n // integration `refreshContent` for exactly that — a full loader run\n // against the live server, after which Astro's own store watcher clears\n // the route cache and reloads the browser. Only a server that registered\n // no refresh (none since Astro 5) falls back to a cold restart: stop,\n // then bring up a fresh container whose cold sync re-globs everything.\n // perfect-debounce both debounces the watch burst (80ms) and\n // single-flights the scan: a\n // trigger during a run never starts a second run, only marks one trailing\n // rerun after the current settles. Both halves are load-bearing — a plain\n // debounce once let bursts stack overlapping scans until the heap was\n // exhausted (observed as an OOM after minutes of looping). The contract is\n // pinned by test/dev-debounce.test.ts. The task must not reject (the\n // library re-invokes it from an unhandled .finally), so the body catches\n // its own errors and always resolves.\n const regenerate = debounce(async () => {\n try {\n const next = await scanProject(root, {\n devServerUrl,\n mode: \"dev\",\n overrides,\n preview,\n });\n const nextSignature = routeSignature(next.manifest.routes);\n const structural = nextSignature !== lastSignature;\n // Generate first: the new runtime data (and any staged remote content)\n // is on disk and published before the store re-syncs against it.\n await generateRuntime(next);\n if (structural && !(await refreshBlumeContent())) {\n await server.stop();\n server = await createServer(boundPort, false);\n }\n // Commit the signature only after the (re)generation succeeded. If the\n // re-sync or restart above throws mid-sequence, the signature stays\n // stale so the next watch event retries the structural path.\n lastSignature = nextSignature;\n // Surface any content/config errors in the terminal AND the browser\n // overlay. The terminal report is not redundant: the overlay only\n // shows once the browser has connected, and it clears on the next HMR\n // update.\n reportDiagnostics(next.diagnostics, root);\n showBlumeErrorOverlay(next.diagnostics);\n } catch (error) {\n // A config error the generator raised (an unplannable `components.ts`\n // override, a missing font file) is a diagnostic: report it in the\n // terminal and the browser overlay like a scan-time error, so the fix\n // is visible where the edit happened.\n if (error instanceof BlumeError) {\n reportDiagnostics([error.diagnostic], root);\n showBlumeErrorOverlay([error.diagnostic]);\n return;\n }\n // SAFETY: regeneration failures come from the generator and Astro's\n // server API, which raise Error instances; only the message is shown.\n logger.error(`Regeneration failed: ${(error as Error).message}`);\n }\n }, 80);\n\n // The runtime prepared above baked the *requested* port into the site\n // fallback; if Vite bumped it, regenerate so OG images, canonicals, and\n // other site-gated URLs point at the port actually serving.\n if (boundPort !== port) {\n void regenerate();\n }\n\n // Content is watched per source (filesystem uses fs.watch; remote sources\n // are frozen for the session). The remaining project inputs — user pages,\n // config, theme, and component overrides — are watched directly.\n const dirTargets = [project.context.pagesRoot].filter(\n (target) => target !== null\n );\n const fileTargets = [\n project.context.configFile,\n project.context.themeFile,\n project.context.componentsFile,\n ].filter((target) => target !== null);\n\n // chokidar handles what raw fs.watch made us hand-roll: recursive\n // directory watching on every platform, and single files surviving a\n // rename-replace save (vim and most \"atomic save\" editors), which orphans\n // an inode-tracking fs.watch watcher after the first write.\n const projectWatcher = watch([...dirTargets, ...fileTargets], {\n ignoreInitial: true,\n }).on(\"all\", regenerate);\n const disposers = [\n ...project.sources.map((source) => source.watch?.(regenerate)),\n () => {\n void projectWatcher.close();\n },\n ].filter((dispose) => dispose !== undefined);\n\n const shutdown = async () => {\n for (const dispose of disposers) {\n dispose();\n }\n releaseLock();\n await server.stop();\n process.exit(0);\n };\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n },\n});\n"
6
- ],
7
- "mappings": "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",
8
- "debugId": "B868244539C2BD5E64756E2164756E21",
9
- "names": []
10
- }
@@ -1,10 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/registry/eject.ts"],
4
- "sourcesContent": [
5
- "import { existsSync } from \"node:fs\";\nimport { cp, mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\n\nimport { join, relative } from \"pathe\";\n\nimport { OPENAPI_PATH } from \"../ai/api/paths.ts\";\nimport { buildApiSpec } from \"../ai/api/spec.ts\";\nimport { buildAskData } from \"../ai/ask-data.ts\";\nimport { resolveAskBackend } from \"../ai/ask.ts\";\nimport { buildRawMarkdown } from \"../ai/markdown.ts\";\nimport { buildMcpData } from \"../ai/mcp/data.ts\";\nimport type { McpData } from \"../ai/mcp/data.ts\";\nimport { buildMcpDiscovery, buildMcpServerCard } from \"../ai/mcp/discovery.ts\";\nimport { planComponentSlots } from \"../astro/component-slots.ts\";\nimport {\n EXAMPLE_SCAN_GLOB,\n discoverExamples,\n exampleMarkdownLookup,\n exampleScanRoots,\n} from \"../astro/examples.ts\";\nimport {\n analyzeComponentsFile,\n buildRuntimeData,\n clientFeaturesFor,\n collectStaged,\n detectNeedsReact,\n detectUsesMath,\n languageIconCssFor,\n PLAYGROUND_PROXY_ENTRY,\n playgroundProxyPattern,\n proxyAllowlistWarnings,\n specOrigins,\n} from \"../astro/generate.ts\";\nimport { discoverIslands } from \"../astro/islands.ts\";\nimport { customOgRoutes, discoverPages, routeIsTaken } from \"../astro/pages.ts\";\nimport {\n apiNavigationTemplate,\n apiNotFoundTemplate,\n apiPagesIndexTemplate,\n apiPageTemplate,\n apiSearchTemplate,\n askComponentTemplate,\n askEndpointTemplate,\n astroConfigTemplate,\n catchAllPageTemplate,\n changelogIndexTemplate,\n contentAssetsEndpointTemplate,\n contentConfigTemplate,\n exampleMapTemplate,\n exampleSlug,\n examplesPageTemplate,\n exampleWrapperTemplate,\n featuresTemplate,\n mcpEndpointTemplate,\n mcpPageFile,\n mixedbreadSearchEndpointTemplate,\n notFoundJsonTemplate,\n notFoundMarkdownTemplate,\n notFoundPageTemplate,\n ogEndpointTemplate,\n playgroundProxyTemplate,\n rawMarkdownEndpointTemplate,\n rssEndpointTemplate,\n runtimeDependencies,\n runtimeTsconfigTemplate,\n consentClientTemplate,\n searchClientTemplate,\n searchEndpointTemplate,\n staticJsonEndpointTemplate,\n} from \"../astro/templates.ts\";\nimport { collectContentAssets } from \"../core/content-assets.ts\";\nimport { buildIncludeGraph } from \"../core/includes.ts\";\nimport { packageRoot } from \"../core/package-root.ts\";\nimport { scanProject } from \"../core/project-graph.ts\";\nimport type { BlumeProject } from \"../core/project-graph.ts\";\nimport {\n resolveDocsCollection,\n sourcesOfKind,\n} from \"../core/sources/collection.ts\";\nimport type { ProjectContext } from \"../core/types.ts\";\nimport { buildRssFeeds, renderRssFeed } from \"../deploy/rss.ts\";\nimport { resolveOgFonts } from \"../og/derive.ts\";\nimport type { OpenApiData } from \"../openapi/model.ts\";\nimport {\n hasScalarReferences,\n needsPlaygroundProxy,\n} from \"../openapi/references.ts\";\nimport { buildReferenceFiles } from \"../openapi/scalar.ts\";\nimport { isOpenApiSource } from \"../openapi/source.ts\";\nimport { buildSearchDocuments } from \"../search/documents.ts\";\nimport {\n examplesEntryTemplate,\n tailwindEntryTemplate,\n} from \"../theme/entry.ts\";\nimport { fontLocaleCodes } from \"../theme/fonts.ts\";\nimport { buildThemeCss } from \"../theme/palette.ts\";\nimport { rebaseSourceDirectives } from \"../theme/sources.ts\";\nimport { twoslashCss } from \"../theme/twoslash.ts\";\n\nconst toPosix = (path: string): string => path.split(\"\\\\\").join(\"/\");\n\n/** The portable `@source` guess: blume in the project's own node_modules. */\nconst LOCAL_BLUME_SOURCE = \"../../node_modules/blume/src/**/*.{astro,ts,tsx}\";\n\n/**\n * The `@source` glob pointing Tailwind at Blume's own source, relative to the\n * ejected `src/generated/app.css`. The project-local `node_modules/blume` is\n * preferred (portable, and under pnpm the symlink survives version bumps), but\n * hoisted installs (npm/yarn workspaces lift blume into the workspace root's\n * node_modules) would make that guess match nothing and silently drop utility\n * classes — so fall back to the package's real installed location, and when\n * even that fails, warn instead of failing silently.\n *\n * Exported for testing.\n */\nexport const blumeSourceGlob = (\n root: string,\n genDir: string,\n resolveBlumeRoot: () => string = packageRoot\n): string => {\n if (existsSync(join(root, \"node_modules\", \"blume\"))) {\n return LOCAL_BLUME_SOURCE;\n }\n try {\n const src = join(resolveBlumeRoot(), \"src\");\n return `${toPosix(relative(genDir, src))}/**/*.{astro,ts,tsx}`;\n } catch {\n console.warn(\n 'blume: could not locate the installed blume package; src/generated/app.css keeps its default `@source \"../../node_modules/blume/...\"` glob. If blume is hoisted elsewhere, point that glob at its install location or Blume\\'s utility classes will be missing.'\n );\n return LOCAL_BLUME_SOURCE;\n }\n};\n\n/** The `blume:openapi` payload for the ejected app (`{}` when none). */\nconst ejectOpenApiData = (project: BlumeProject): OpenApiData => {\n const source = project.sources.find(isOpenApiSource);\n return source ? source.openApiData() : {};\n};\n\n/**\n * The assistant endpoint plus, unless the backend runs its own retrieval (Inkeep),\n * its grounding snapshot. Empty when the assistant is disabled.\n */\nconst askFiles = async (\n project: BlumeProject,\n srcDir: string,\n genDir: string\n): Promise<{ content: string; path: string }[]> => {\n const { assistant } = project.config.ai;\n if (!(assistant?.enabled && !assistant.endpoint)) {\n const endpointPath = join(srcDir, \"pages\", \"api\", \"ask.ts\");\n if (existsSync(endpointPath)) {\n const content = await readFile(endpointPath, \"utf-8\");\n if (content.startsWith(\"// Generated by Blume. Do not edit.\")) {\n await rm(endpointPath, { force: true });\n }\n }\n await rm(join(genDir, \"ask-data.json\"), { force: true });\n return [];\n }\n const backend = resolveAskBackend(assistant.provider);\n const files = [\n {\n content: askEndpointTemplate(backend, {\n captcha: assistant.captcha,\n cors: assistant.cors,\n instructions: assistant.instructions,\n rateLimit: project.config.rateLimit,\n retrieval: assistant.retrieval,\n tools: assistant.tools ?? backend.toolsByDefault,\n }),\n path: join(srcDir, \"pages\", \"api\", \"ask.ts\"),\n },\n ];\n if (backend.grounded) {\n files.push({\n content: `${JSON.stringify(await buildAskData(project))}\\n`,\n path: join(genDir, \"ask-data.json\"),\n });\n }\n return files;\n};\n\n/** Whether the ejected app hosts the MCP server (enabled and route free). */\nconst hostsMcp = (\n project: BlumeProject,\n userPages: { pattern: string }[]\n): boolean =>\n project.config.agents.mcp.enabled &&\n !routeIsTaken(\n userPages,\n project.graph.pages,\n project.config.agents.mcp.route\n );\n\n/**\n * The `.well-known` MCP discovery routes, injected as prerendered pages\n * alongside the user's own so the ejected Astro config wires them in. Empty\n * when the server is disabled or its route is already owned by a page.\n */\nconst mcpDiscoveryPages = (\n project: BlumeProject,\n userPages: { pattern: string }[]\n): { entrypoint: string; pattern: string }[] =>\n hostsMcp(project, userPages)\n ? [\n {\n entrypoint: \"src/blume-mcp/discovery.ts\",\n pattern: \"/.well-known/mcp.json\",\n },\n {\n entrypoint: \"src/blume-mcp/server-card.ts\",\n pattern: \"/.well-known/mcp/server-card.json\",\n },\n ]\n : [];\n\n/**\n * The playground's built-in CORS proxy route (`playground: { proxy: true }`),\n * mirroring `planPlaygroundProxy` in generate.ts: injected like the MCP\n * discovery routes, because Astro treats a `_`-prefixed page file as private,\n * and mounted under `basePath` at the URL the playground sends to. Empty when\n * no reference routes its playground through it.\n */\nconst playgroundProxyPages = (\n config: BlumeProject[\"config\"]\n): { entrypoint: string; pattern: string }[] =>\n needsPlaygroundProxy(config)\n ? [\n {\n entrypoint: toPosix(join(\"src\", PLAYGROUND_PROXY_ENTRY)),\n pattern: playgroundProxyPattern(config),\n },\n ]\n : [];\n\n/**\n * The proxy endpoint behind {@link playgroundProxyPages}, with the origin\n * allowlist baked in from the same parsed specs the reference pages render.\n */\nconst playgroundProxyFiles = (\n config: BlumeProject[\"config\"],\n openApiData: OpenApiData,\n srcDir: string\n): { content: string; path: string }[] =>\n needsPlaygroundProxy(config)\n ? [\n {\n content: playgroundProxyTemplate(\n specOrigins(openApiData),\n config.rateLimit\n ),\n path: join(srcDir, PLAYGROUND_PROXY_ENTRY),\n },\n ]\n : [];\n\n/**\n * The agent data snapshot (`blume:mcp-data`) behind the MCP server and the\n * JSON docs API, mirroring `publishAgentData` in generate.ts: built when\n * either is on, null when neither needs it.\n */\nconst ejectAgentData = async (\n project: BlumeProject,\n userPages: { pattern: string }[]\n): Promise<McpData | null> =>\n hostsMcp(project, userPages) || project.config.agents.api\n ? await buildMcpData(project)\n : null;\n\n/**\n * The MCP server endpoint and `.well-known` discovery documents, mirroring\n * `writeMcpFiles` in generate.ts. Empty when the server is disabled or its\n * route is already owned by a page.\n */\nconst mcpFiles = (\n project: BlumeProject,\n userPages: { pattern: string }[],\n srcDir: string,\n data: McpData | null\n): { content: string; path: string }[] => {\n if (!(data && hostsMcp(project, userPages))) {\n return [];\n }\n const { route } = project.config.agents.mcp;\n const discoveryInput = {\n base: data.base,\n name: data.name,\n route,\n site: data.site,\n version: data.version,\n };\n return [\n {\n content: mcpEndpointTemplate(),\n path: join(srcDir, \"pages\", mcpPageFile(route)),\n },\n {\n content: staticJsonEndpointTemplate(buildMcpDiscovery(discoveryInput)),\n path: join(srcDir, \"blume-mcp\", \"discovery.ts\"),\n },\n {\n content: staticJsonEndpointTemplate(buildMcpServerCard(discoveryInput)),\n path: join(srcDir, \"blume-mcp\", \"server-card.ts\"),\n },\n ];\n};\n\n/**\n * The JSON docs API, mirroring `planApi` and `writeApiFiles` in generate.ts:\n * the prerendered page index, per-page documents, and navigation; on server\n * output the search endpoint and, unless a user page or a content page already\n * lives under `/api/`, the JSON 404 catch-all; and `/openapi.json`, unless a\n * `public/openapi.json` or a page owns that route. The ejected `llms.txt`,\n * homepage `Link` header, and 404 page advertise these routes, so the ejected\n * app serves them. Empty when `agents.api` is off.\n */\nconst apiFiles = (\n project: BlumeProject,\n userPages: { pattern: string }[],\n root: string,\n srcDir: string,\n data: McpData | null\n): { content: string; path: string }[] => {\n const { config } = project;\n if (!(data && config.agents.api)) {\n return [];\n }\n const server = config.deployment.options.output === \"server\";\n const apiDir = join(srcDir, \"pages\", \"api\");\n const files = [\n {\n content: apiPagesIndexTemplate(),\n path: join(apiDir, \"docs\", \"pages.json.ts\"),\n },\n {\n content: apiPageTemplate(),\n path: join(apiDir, \"docs\", \"pages\", \"[...route].json.ts\"),\n },\n {\n content: apiNavigationTemplate(),\n path: join(apiDir, \"docs\", \"navigation.json.ts\"),\n },\n ];\n if (server) {\n files.push({\n content: apiSearchTemplate(),\n path: join(apiDir, \"docs\", \"search.ts\"),\n });\n }\n const apiOwned =\n userPages.some((page) => page.pattern.startsWith(\"/api/[\")) ||\n project.graph.pages.some(\n (page) => page.route === \"/api\" || page.route.startsWith(\"/api/\")\n );\n if (server && !apiOwned) {\n files.push({\n content: apiNotFoundTemplate({ base: data.base, site: data.site }),\n path: join(apiDir, \"[...path].ts\"),\n });\n }\n if (\n !(\n routeIsTaken(userPages, project.graph.pages, OPENAPI_PATH) ||\n existsSync(join(root, \"public\", \"openapi.json\"))\n )\n ) {\n files.push({\n content: staticJsonEndpointTemplate(\n buildApiSpec({\n agentReadability: config.agents.agentReadability,\n base: data.base,\n description: config.description,\n llmsTxt: config.agents.llmsTxt.enabled,\n mcpRoute: hostsMcp(project, userPages)\n ? config.agents.mcp.route\n : null,\n name: config.title,\n search: server,\n site: data.site,\n version: data.version,\n })\n ),\n path: join(srcDir, \"pages\", \"openapi.json.ts\"),\n });\n }\n return files;\n};\n\n/**\n * The `/changelog` index page, mirroring `shouldGenerateChangelog` in\n * generate.ts: emitted when `type: changelog` entries or a release-backed\n * changelog source exist, unless a user page already owns the route.\n */\nconst changelogFiles = (\n project: BlumeProject,\n userPages: { pattern: string }[],\n srcDir: string,\n options: Parameters<typeof changelogIndexTemplate>[0]\n): { content: string; path: string }[] => {\n const hasChangelog = project.graph.pages.some(\n (page) =>\n page.contentType === \"changelog\" &&\n !(page.meta.draft || page.meta.sidebar.hidden)\n );\n const hasChangelogSource =\n sourcesOfKind(project.config, \"github-releases\").length > 0;\n if (\n !(hasChangelog || hasChangelogSource) ||\n routeIsTaken(userPages, project.graph.pages, \"/changelog\")\n ) {\n return [];\n }\n return [\n {\n content: changelogIndexTemplate(options),\n path: join(srcDir, \"pages\", \"changelog.astro\"),\n },\n ];\n};\n\n/**\n * The colocated-image map and the `/blume-assets/[...asset]` endpoint that\n * serves it, mirroring generate.ts: the ejected raw Markdown points\n * `![x](./diagram.png)` at `/blume-assets/content/…`, and the config aliases\n * `blume:content-assets` to the map. Each original's path is written relative\n * to the project root (the directory `astro dev`/`astro build` run from), so\n * the ejected app builds from any checkout. Remote-source images need no\n * endpoint: eject copies them into the project's own `public/blume-assets`.\n */\nconst contentAssetFiles = async (\n project: BlumeProject,\n root: string,\n srcDir: string,\n genDir: string\n): Promise<{ content: string; path: string }[]> => {\n const assets = await collectContentAssets(project);\n const portable = Object.fromEntries(\n Object.entries(assets).map(([param, abs]) => [\n param,\n toPosix(relative(root, abs)),\n ])\n );\n return [\n {\n content: `${JSON.stringify(portable)}\\n`,\n path: join(genDir, \"content-assets.json\"),\n },\n {\n content: contentAssetsEndpointTemplate(null),\n path: join(srcDir, \"pages\", \"blume-assets\", \"[...asset].ts\"),\n },\n ];\n};\n\n/**\n * The partial → including-pages map with every path relative to the project\n * root, like every other path in the ejected app, so it holds in any checkout.\n * Its readers (`includeHmrPlugin`, `withIncludeRefresh`) resolve each path\n * against the app's root, which after eject is the project itself.\n */\nconst portableIncludeGraph = (\n project: BlumeProject,\n root: string\n): Record<string, string[]> =>\n Object.fromEntries(\n Object.entries(buildIncludeGraph(project.graph.pages)).map(\n ([partial, includers]) => [\n toPosix(relative(root, partial)),\n includers.map((page) => toPosix(relative(root, page))),\n ]\n )\n );\n\n/** Contents of the configured `examples.css`, or `\"\"` when unset/absent. */\n/**\n * Read a user stylesheet that eject inlines into a generated entry under\n * `genDir`, re-rooting its relative `@source` paths from the user's file.\n * Resolves to an empty string when the file is unset or absent.\n */\nconst readUserCss = async (\n file: string | null,\n genDir: string\n): Promise<string> => {\n if (!(file && existsSync(file))) {\n return \"\";\n }\n const css = await readFile(file, \"utf-8\");\n return rebaseSourceDirectives(css, { from: file, to: genDir });\n};\n\n/**\n * The per-example preview route `<Component />` iframes embed, nested under\n * `basePath` so it stays reachable behind a proxy that only forwards the\n * base. Empty when the project has no examples.\n */\nconst examplesPreviewFiles = (\n srcDir: string,\n basePath: string,\n hasExamples: boolean\n): { content: string; path: string }[] =>\n hasExamples\n ? [\n {\n content: examplesPageTemplate(),\n path: join(\n srcDir,\n \"pages\",\n ...basePath.split(\"/\").filter(Boolean),\n \"blume-examples\",\n \"[...path].astro\"\n ),\n },\n ]\n : [];\n\n/**\n * The packages the ejected app imports by bare name: Astro, Tailwind's Vite\n * plugin, Tailwind and its typography plugin (which the generated `app.css`\n * and `examples.css` import by name), `blume` itself, the integrations the\n * config wires in, whatever the configured adapters declare, and React when\n * islands, examples, or the assistant render with it (Blume ships React, so\n * projects rarely list it). They have to be the project's own dependencies\n * after eject — under a strict linker such as pnpm nothing else makes them\n * resolvable, and `astro build` fails. The hidden runtime reaches the same\n * packages through its `node_modules` junction into Blume's own, so only\n * eject declares the ones its generated files import directly: the AI SDK the\n * assistant route streams through (unless `ai.assistant.endpoint` points\n * elsewhere, when no route is written) and the EPUB generator's browser bundle\n * `features.ts` loads (only named there when `export.epub` is on).\n */\nconst ejectDependencies = (\n options: Parameters<typeof runtimeDependencies>[0]\n): string[] => {\n const { assistant } = options.config.ai;\n return [\n ...new Set([\n \"astro\",\n \"@tailwindcss/vite\",\n \"tailwindcss\",\n \"@tailwindcss/typography\",\n \"blume\",\n ...runtimeDependencies(options),\n ...(options.needsReact ? [\"react\", \"react-dom\"] : []),\n ...(assistant?.enabled && !assistant.endpoint ? [\"ai\"] : []),\n ...(options.config.export.epub ? [\"epub-gen-memory\"] : []),\n ]),\n ];\n};\n\nconst ejectIntegrationBridge = (\n config: BlumeProject[\"config\"],\n root: string,\n configFile: string | null\n): Parameters<typeof astroConfigTemplate>[0][\"integrationBridge\"] =>\n config.integrations.length > 0 && configFile\n ? { configFile: toPosix(relative(root, configFile)) }\n : undefined;\n\n/**\n * Promote the generated runtime into the project as an owned Astro app. After\n * eject the project has a normal `astro.config.mjs` and `src/`, the `blume` CLI\n * is no longer required, and the `blume` package remains importable.\n *\n * Returns the written files plus non-fatal warnings, mirroring the generated\n * runtime (e.g. a Scalar reference spec that wasn't found, or a reference\n * route colliding with a content page), and the packages the ejected app\n * imports by bare name, for the caller to add to package.json.\n */\nexport const eject = async (\n root: string\n): Promise<{ dependencies: string[]; files: string[]; warnings: string[] }> => {\n const project = await scanProject(root, { mode: \"build\" });\n const { context, config } = project;\n\n const srcDir = join(root, \"src\");\n const genDir = join(srcDir, \"generated\");\n const assistantEnabled = config.ai.assistant?.enabled ?? false;\n const exportPdf = config.export.pdf;\n const exportEpub = config.export.epub;\n\n // Discover examples first and expose them on the project, so the agent-facing\n // Markdown built below (raw `.md`, MCP) downlevels `<Component>` to its source.\n const examples = await discoverExamples(root, config.examples.source);\n project.examples = exampleMarkdownLookup(examples.examples);\n\n const [\n pages,\n needsReactRaw,\n usesMath,\n userTheme,\n userExamplesCss,\n rawMarkdown,\n islands,\n overrideAnalysis,\n ] = await Promise.all([\n context.pagesRoot ? discoverPages(context.pagesRoot) : Promise.resolve([]),\n detectNeedsReact(root),\n detectUsesMath(root),\n readUserCss(context.themeFile, genDir),\n readUserCss(\n config.examples.css ? join(root, config.examples.css) : null,\n genDir\n ),\n buildRawMarkdown(project),\n discoverIslands(root),\n analyzeComponentsFile(context.componentsFile),\n ]);\n // The `islands/` convention and `components.ts` share the same static plan\n // the CLI uses, with every import written relative to its generated file so\n // the ejected app builds from any checkout (like the example wrappers below).\n const slotPlan = planComponentSlots(islands.islands, overrideAnalysis, {\n relativeTo: genDir,\n });\n // Island/example/override frameworks drive which Astro renderers the ejected\n // config wires in; React also switches on for project `.tsx`/`.jsx` and the assistant.\n const frameworks = new Set<string>([\n ...islands.islands.map((island) => island.framework),\n ...examples.examples.map((example) => example.framework),\n ...slotPlan.frameworks,\n ]);\n const needsReact =\n needsReactRaw || assistantEnabled || frameworks.has(\"react\");\n const needsVue = frameworks.has(\"vue\");\n const needsSvelte = frameworks.has(\"svelte\");\n\n // A project-relative context so generated files use portable paths.\n const relContext: ProjectContext = {\n ...context,\n contentRoot: toPosix(relative(root, context.contentRoot)),\n distDir: \"./dist\",\n outDir: \".\",\n pagesRoot: context.pagesRoot\n ? `./${toPosix(relative(root, context.pagesRoot))}`\n : null,\n root: \".\",\n };\n // The `docs` collection resolves its base against the real project root and\n // is then relativized like everything else, so the ejected content config\n // never bakes in the absolute path `eject` happened to run from.\n const docsCollection = resolveDocsCollection(config, root);\n const relCollection = {\n ...docsCollection,\n base: toPosix(relative(root, docsCollection.base)),\n };\n\n const relPages = [\n ...pages.map((page) => ({\n entrypoint: toPosix(relative(root, page.entrypoint)),\n pattern: page.pattern,\n })),\n ...mcpDiscoveryPages(project, pages),\n ...playgroundProxyPages(config),\n ];\n const openApiData = ejectOpenApiData(project);\n\n // Non-filesystem sources eject their materialized MDX into `<root>/blume-staged`\n // (a dedicated dir so it never clashes with a content root literally named\n // `content`; the relative `staged` collection points there).\n const staged = collectStaged(project);\n const hasStaged = staged.size > 0;\n const stagedDir = \"blume-staged\";\n const features = await clientFeaturesFor(project);\n const languageIcons = await languageIconCssFor(project);\n\n const files: {\n path: string;\n content: string;\n /** Don't overwrite a file the user already owns (e.g. a tuned tsconfig). */\n skipIfExists?: boolean;\n }[] = [\n {\n content: astroConfigTemplate({\n askPath: \"./src/generated/Ask.astro\",\n config,\n consentClientPath: \"./src/generated/consent-client.ts\",\n contentRoot: relContext.contentRoot,\n contentRoutes: project.manifest.routes.map((route) => route.path),\n context: relContext,\n examplesPath: \"./src/generated/examples.ts\",\n examplesThemePath: \"./src/generated/examples.css\",\n features,\n featuresPath: \"./src/generated/features.ts\",\n // No CLI publishes the runtime data modules in memory after eject, so\n // the config aliases each to the JSON snapshot written below.\n generatedModulesDir: \"./src/generated\",\n integrationBridge: ejectIntegrationBridge(\n config,\n root,\n context.configFile\n ),\n needsReact,\n needsSvelte,\n needsVue,\n pages: relPages,\n searchClientPath: \"./src/generated/search-client.ts\",\n themePath: \"./src/generated/app.css\",\n }),\n path: join(root, \"astro.config.mjs\"),\n },\n {\n content: runtimeTsconfigTemplate(),\n path: join(root, \"tsconfig.json\"),\n // Never clobber a hand-tuned tsconfig; only write ours if none exists.\n skipIfExists: true,\n },\n {\n content: contentConfigTemplate({\n collection: relCollection,\n config,\n context: relContext,\n staged: hasStaged,\n stagedBase: stagedDir,\n }),\n path: join(srcDir, \"content.config.ts\"),\n },\n {\n content: catchAllPageTemplate({\n exportEpub,\n exportPdf,\n mathEnabled: usesMath,\n needsReact,\n }),\n path: join(srcDir, \"pages\", \"[...slug].astro\"),\n },\n {\n content: slotPlan.module,\n path: join(genDir, \"components.ts\"),\n },\n // The example map the catch-all imports; written even when empty so the\n // `blume:examples` alias always resolves.\n {\n content: exampleMapTemplate(examples.examples, config.basePath),\n path: join(genDir, \"examples.ts\"),\n },\n {\n // The isolated Tailwind entry for `<Component />` preview frames.\n // Relative sources keep the ejected app portable.\n content: examplesEntryTemplate({\n configTokens: buildThemeCss(config.theme),\n sources: exampleScanRoots(root, examples.dir).map(\n (dir) => `${relative(genDir, dir)}/${EXAMPLE_SCAN_GLOB}`\n ),\n userCss: userExamplesCss,\n }),\n path: join(genDir, \"examples.css\"),\n },\n {\n content: tailwindEntryTemplate({\n configTokens: buildThemeCss(config.theme),\n languageIcons,\n // Relative paths from src/generated/app.css keep the ejected app\n // portable; the blume glob resolves the real install location when\n // the package is hoisted out of the project's own node_modules.\n sources: [\n blumeSourceGlob(root, genDir),\n \"../../**/*.{astro,mdx,ts,tsx}\",\n ],\n twoslashCss: twoslashCss(),\n userTheme,\n }),\n path: join(genDir, \"app.css\"),\n },\n { content: buildRuntimeData(project), path: join(genDir, \"data.json\") },\n // The header's Ask trigger behind the `blume:ask` alias. Always written — it\n // renders nothing when Ask is off — so the alias always resolves.\n {\n content: askComponentTemplate(assistantEnabled),\n path: join(genDir, \"Ask.astro\"),\n },\n {\n content: `${JSON.stringify(openApiData)}\\n`,\n path: join(genDir, \"openapi.json\"),\n },\n {\n content: `${JSON.stringify(rawMarkdown)}\\n`,\n path: join(genDir, \"raw-markdown.json\"),\n },\n {\n // The partial → including-pages map behind `includeHmrPlugin`, which the\n // ejected astro.config wires at this exact path — without the file every\n // hot update's read would silently no-op and partial edits would serve\n // stale pages. A snapshot like the rest of `src/generated`: the ejected\n // app owns (and may regenerate or prune) it.\n content: `${JSON.stringify(portableIncludeGraph(project, root))}\\n`,\n path: join(genDir, \"includes.json\"),\n },\n {\n content: rawMarkdownEndpointTemplate(\"md\"),\n path: join(srcDir, \"pages\", \"[...slug].md.ts\"),\n },\n {\n content: rawMarkdownEndpointTemplate(\"mdx\"),\n path: join(srcDir, \"pages\", \"[...slug].mdx.ts\"),\n },\n ];\n\n files.push(...(await contentAssetFiles(project, root, srcDir, genDir)));\n\n if (assistantEnabled) {\n files.push(...(await askFiles(project, srcDir, genDir)));\n }\n\n // The `/changelog` index, mirrored from the generated runtime (`[]` when\n // the project has no changelog). The OG endpoint below adds its card.\n const changelog = changelogFiles(project, pages, srcDir, {\n exportEpub,\n exportPdf,\n needsReact,\n staged: hasStaged,\n });\n\n if (config.seo.og.enabled) {\n // The default script fallbacks ride along (Google family names, nothing\n // machine-specific), so a non-Latin card still renders after eject. An\n // explicit seo.og.fonts replaces them, and isn't carried over.\n const { fallbacks, families } = resolveOgFonts(\n {\n locales: fontLocaleCodes(config.i18n),\n ogFonts: config.seo.og.fonts,\n themeFonts: config.theme.fonts,\n themeFontsConfigured: false,\n },\n root\n );\n files.push({\n content: ogEndpointTemplate(\n customOgRoutes(pages, config.title, config.seo.og.titles),\n {\n fallbacks,\n families,\n pageDescriptions: config.seo.og.description !== false,\n },\n changelog.length > 0\n ),\n path: join(srcDir, \"pages\", \"og\", \"[...slug].png.ts\"),\n });\n }\n\n // The hosted MCP server, the JSON docs API, and the playground's built-in\n // proxy, mirrored from the generated runtime (`[]` when each is off). The\n // server and the API share one agent data snapshot.\n const agentData = await ejectAgentData(project, pages);\n if (agentData) {\n files.push({\n content: `${JSON.stringify(agentData)}\\n`,\n path: join(genDir, \"mcp-data.json\"),\n });\n }\n files.push(\n ...mcpFiles(project, pages, srcDir, agentData),\n ...apiFiles(project, pages, root, srcDir, agentData),\n ...changelog,\n ...playgroundProxyFiles(config, openApiData, srcDir)\n );\n\n // Default 404 page and its Markdown (`/404.md`) and JSON (`/404.json`)\n // twins, mirroring `writeNotFoundPage` in generate.ts: all three are skipped\n // when the project already owns `/404` (a custom `pages/404.astro` or a\n // `404.md` content page). The ejected project owns the files afterwards and\n // can edit or remove them.\n if (!routeIsTaken(pages, project.graph.pages, \"/404\")) {\n files.push(\n {\n content: notFoundPageTemplate(),\n path: join(srcDir, \"pages\", \"404.astro\"),\n },\n {\n content: notFoundMarkdownTemplate(),\n path: join(srcDir, \"pages\", \"404.md.ts\"),\n },\n {\n content: notFoundJsonTemplate(),\n path: join(srcDir, \"pages\", \"404.json.ts\"),\n }\n );\n }\n\n // The client-feature loaders behind the `blume:features` alias, the\n // provider-specific client loader behind `blume:search-client`, and the\n // consent adapter's browser module behind `blume:consent-client`.\n files.push(\n {\n content: featuresTemplate(features),\n path: join(genDir, \"features.ts\"),\n },\n {\n content: searchClientTemplate(config),\n path: join(genDir, \"search-client.ts\"),\n },\n {\n content: consentClientTemplate(config.consent),\n path: join(genDir, \"consent-client.ts\"),\n }\n );\n\n const searchAdapter = config.search.provider;\n if (searchAdapter.mode === \"static\") {\n const documents = await buildSearchDocuments(project);\n files.push(\n {\n content: `${JSON.stringify(documents)}\\n`,\n path: join(genDir, \"search.json\"),\n },\n {\n content: searchEndpointTemplate(),\n path: join(srcDir, \"pages\", \"blume-search.json.ts\"),\n }\n );\n }\n\n if (searchAdapter.kind === \"mixedbread\") {\n files.push({\n content: mixedbreadSearchEndpointTemplate(\n searchAdapter.options,\n config.rateLimit\n ),\n path: join(srcDir, \"pages\", \"api\", \"search.ts\"),\n });\n }\n\n const feeds = buildRssFeeds(project);\n if (feeds.length > 0) {\n const feedXml = Object.fromEntries(\n feeds.map((feed) => [feed.type, renderRssFeed(feed)])\n );\n files.push(\n {\n content: `${JSON.stringify(feedXml)}\\n`,\n path: join(genDir, \"rss.json\"),\n },\n {\n content: rssEndpointTemplate(),\n path: join(srcDir, \"pages\", \"[section]\", \"rss.xml.ts\"),\n }\n );\n }\n\n // Scalar API/AsyncAPI reference pages, mirrored from the generated runtime so\n // the ejected app keeps its reference routes — including the warnings (a\n // missing spec file, a route collision), which the caller surfaces exactly\n // like the generated-runtime path does.\n // A proxied spec with no origin to allow gets the generated runtime's\n // diagnostic too.\n const warnings: string[] = proxyAllowlistWarnings(config, openApiData);\n if (hasScalarReferences(config)) {\n const references = await buildReferenceFiles({\n config,\n contentRoutes: new Set(project.graph.pages.map((page) => page.route)),\n root,\n });\n warnings.push(...references.warnings);\n for (const file of references.files) {\n files.push({\n content: file.content,\n path: join(srcDir, \"pages\", file.pagePath),\n });\n }\n }\n\n // Per-override and per-example live wrappers referenced by the maps above.\n files.push(\n ...slotPlan.wrappers.map((wrapper) => ({\n content: wrapper.content,\n path: join(genDir, \"component-slots\", `${wrapper.name}.astro`),\n })),\n ...examples.examples.map((example) => ({\n content: exampleWrapperTemplate(example, join(genDir, \"examples\")),\n path: join(genDir, \"examples\", `${exampleSlug(example.path)}.astro`),\n })),\n ...examplesPreviewFiles(\n srcDir,\n config.basePath,\n examples.examples.length > 0\n )\n );\n\n // Materialize staged source bodies under `<root>/blume-staged/<source>/<ref>`,\n // matching the relative `staged` collection base in the ejected config.\n for (const [entryId, content] of staged) {\n files.push({ content, path: join(root, stagedDir, entryId) });\n }\n\n const written = files.filter(\n (file) => !(file.skipIfExists && existsSync(file.path))\n );\n await Promise.all(\n written.map(async (file) => {\n await mkdir(join(file.path, \"..\"), { recursive: true });\n await writeFile(file.path, file.content, \"utf-8\");\n })\n );\n\n // Materialized source assets (e.g. downloaded Notion images) live under the\n // hidden runtime's public dir; copy them into the owned project's `public/`\n // so the staged content's `/blume-assets/…` references still resolve.\n const assetsSrc = join(context.outDir, \"public\", \"blume-assets\");\n if (existsSync(assetsSrc)) {\n await cp(assetsSrc, join(root, \"public\", \"blume-assets\"), {\n recursive: true,\n });\n }\n\n // The hidden runtime is no longer the source of truth.\n await rm(context.outDir, { force: true, recursive: true });\n\n return {\n dependencies: ejectDependencies({\n config,\n needsReact,\n needsSvelte,\n needsVue,\n }),\n files: written.map((file) => file.path),\n warnings,\n };\n};\n"
6
- ],
7
- "mappings": "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",
8
- "debugId": "CE6B7910AE02D8C964756E2164756E21",
9
- "names": []
10
- }