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
@@ -21,17 +21,17 @@ Keep content where it is — set `content.root: "src/content/docs"`.
21
21
  | `logo` (`{ src }` or `{ light, dark, alt }`) | `logo` — move the referenced file into `public/` |
22
22
  | `logo.replacesTitle` | `logo: { text: "" }` (renders the mark alone) |
23
23
  | `favicon` | copy the file into `public/` (drop the config field — Blume auto-detects) |
24
- | `social` (array of `{ label, icon, href }`; pre-0.33 legacy: `{ github: url }` object) | derive **`github: { owner, repo }`** from the GitHub entry; other socials drop (report) |
24
+ | `social` (array of `{ label, icon, href }`; pre-0.33 legacy: `{ github: url }` object) | derive **`github: { owner, repo }`** from the GitHub entry; every other entry whose platform Blume knows → **`footer.socials`** (`{ discord: href, bluesky: href, … }`; the `x.com`/`twitter` icon → `x`; the platform list is under `footer` in `docs/configuration/index.mdx`); the rest (`mastodon`, `gitlab`, `rss`, …) drop (report) |
25
25
  | `editLink.baseUrl` (`…/edit/<branch>/<subdir?>`) | `github: { owner, repo, branch }` — **and any repo sub-path after the branch → `github.dir`** (a docs-in-subfolder repo breaks every edit link without it); **an origin other than `https://github.com` → `github.host`** (a GitHub Enterprise repo otherwise links to the public site) |
26
26
  | `sidebar` (array) | filesystem nav / `navigation.sidebar` (see below) |
27
27
  | `tableOfContents` (`false` or `{ minHeadingLevel, maxHeadingLevel }`) | `toc` — identical shape, 1:1 |
28
28
  | `markdown.headingLinks: false` | `markdown.headingAnchors: false` |
29
29
  | `expressiveCode.themes` | `markdown.code.theme: { light, dark }` — EC assigns by theme _type_, so match each theme by its darkness, not by array position |
30
30
  | `expressiveCode.styleOverrides` | drop → restyle via a root `theme.css` file |
31
- | `head` | **drop and report** (there is no `seo.metatags`); analytics `<script>` entries → one `script()` adapter each in the `analytics` list (from `blume/analytics`): `attrs.src` → `src`, an `async`/`defer` attr → `strategy`, every other `attrs` key → `attributes`, and an inline body (`content`) → `content` |
31
+ | `head` | per entry: a `meta` tag → **`seo.metatags`** (`{ [attrs.name ?? attrs.property]: attrs.content }` — verification tokens, `theme-color`, and the like), minus the tags Blume writes itself (`description`, `robots`, `og:title`/`og:description`/`og:image`/`og:url`/`og:type`/`og:site_name`, `twitter:*` card tags, `article:*` dates — the build refuses them and names the setting that owns each, e.g. `og:image` → `seo.og`, `twitter:site` → `seo.x.handle`); an analytics `<script>` → one `script()` adapter each in the `analytics` list (from `blume/analytics`): `attrs.src` → `src`, an `async`/`defer` attr → `strategy`, every other `attrs` key → `attributes`, and an inline body (`content`) → `content`; anything else (`link`, `style`, other scripts) → drop and report |
32
32
  | `lastUpdated: true` | `lastModified: "git"` |
33
33
  | `customCss` | drop → move into a root **`theme.css`** file (auto-picked-up; not a config field) |
34
- | `components` (overrides) | Blume's layout slots via `defineComponents({ layout: { Header, Search, Sidebar, TableOfContents, Footer, … } })` — a near-1:1 map; reach for `blume eject` only beyond those |
34
+ | `components` (overrides) | Blume's layout slots via `defineComponents({ layout: { … } })` in a root `components.ts`: `Header`, `Search`, `Sidebar`, `TableOfContents`, `Pagination` keep their names; `SiteTitle` → `Logo`; **`Footer` → `PageFooter`**, not Blume's `Footer` (that slot is the site footer; Starlight's `Footer` is the page-end last-updated date, pagination, and edit link, which Blume renders itself, so keep only what the override added). Other overrides (`Banner`, `PageTitle`, `Hero`, `EditLink`, `SocialIcons`, …) have no slot — fold their additions into `PageHeader`/`PageFooter`, or `blume eject`; a Starlight name left under `layout` matches no slot and does nothing. Rewrite reads of `Astro.locals.starlightRoute` against the slot's props |
35
35
  | `plugins` | **map, don't blanket-drop** — see Plugins below |
36
36
  | `pagination: false`, `pagefind`/Pagefind options, `titleDelimiter`, `credits`, `disable404Route`, `routeMiddleware` | drop (report) |
37
37
  | `locales` / `defaultLocale` | `i18n` (see below) |
@@ -40,7 +40,7 @@ Keep content where it is — set `content.root: "src/content/docs"`.
40
40
 
41
41
  Starlight's `sidebar` array → prefer letting Blume generate from the filesystem; use `navigation.sidebar` only for shapes files can't express:
42
42
 
43
- - `{ label, autogenerate: { directory: "d" } }` → **structure the folder and rely on filesystem nav** (label → the folder's `meta.ts` `title`). Do **not** map it to a sidebar item with `root:` — in an explicit Blume sidebar that renders a single page link, not the directory's children.
43
+ - `{ label, items: [{ autogenerate: { directory: "d" } }] }` (0.39 and later; before 0.39 the group held it directly, `{ label, autogenerate: { directory: "d" } }`) → **structure the folder and rely on filesystem nav** (label → the folder's `meta.ts` `title`; `autogenerate.collapsed` → `collapsed: true` in each subfolder's `meta.ts`). Do **not** map it to a sidebar item with `root:` — in an explicit Blume sidebar that renders a single page link, not the directory's children. Since 0.39 a group's `items` can mix manual entries with an `autogenerate` entry; move those manual pages into the folder (route change → `redirects`) so the folder holds the whole group.
44
44
  - string `"guides/intro"` → `"/guides/intro"`; `{ label?, slug }` → `/<slug>`.
45
45
  - `{ label, items: [...] }` → `{ label, items: [...] }` (recursive) — or better, a real folder.
46
46
  - `{ label?, link }` → `{ label, href: link }`.
@@ -59,7 +59,8 @@ Starlight's `sidebar` array → prefer letting Blume generate from the filesyste
59
59
  | `lastUpdated: false` (boolean) | **remove** (Blume's `lastModified` takes a date; a boolean is a build error) |
60
60
  | `prev: false` **and** `next: false` | `pagination: false` (hides both links) |
61
61
  | `prev` / `next` otherwise (one side, or a label/link) | **drop** (report — the links can only be hidden together, and their labels come from the linked pages) |
62
- | `template: splash` / `hero` | **no equivalent** — rebuild as a custom `.astro` page under `content.pages`; a `hero` on a normal page drops (report) |
62
+ | `template: splash` | `mode: center` (no sidebar, a wider centered column — the closest layout; report) |
63
+ | `hero` | **no key** — its tagline → a paragraph, its actions → `<Card>`s or links, its image → a Markdown image (report); for a designed landing page, rebuild it as a custom `.astro` page under `content.pages` (`pages/index.astro` on `PageLayout` wins over the content page) |
63
64
  | `banner`, `tableOfContents`, `editUrl`, `head` | drop (report) |
64
65
 
65
66
  ## Asides → directives (and the `.md` trap)
@@ -83,7 +84,8 @@ Starlight content is full of Expressive Code fence meta; Blume understands some
83
84
  - `title="file.js"` → works as-is (or use the space-title shorthand). Line ranges `{2-3}` → work as-is.
84
85
  - `ins=`/`del=` line marks → `// [!code ++]` / `// [!code --]` comments; `mark=` → `{ranges}` or `// [!code highlight]`.
85
86
  - `showLineNumbers` → `lineNumbers`.
86
- - **Drop:** `frame="terminal"`, `collapse=`, `wrap`, `"string"` and `/regex/` text markers (report if they carried meaning).
87
+ - `wrap` / `wrap=true` → a bare `wrap` (that block's long lines wrap); a site-wide `expressiveCode.defaultProps.wrap: true` → `markdown.code.wrap: true`.
88
+ - **Drop:** `frame="terminal"`, `collapse=`, `wrap=false`, `preserveIndent`/`hangingIndent`, `"string"` and `/regex/` text markers (report if they carried meaning).
87
89
  - ` ```diff lang="js" ` → a normal ` ```js ` fence with `[!code ++]`/`[!code --]` markers.
88
90
 
89
91
  ## Plugins — map, don't drop
@@ -114,4 +116,4 @@ Remove `@astrojs/starlight` (and plugin deps) from deps, delete the Starlight bi
114
116
 
115
117
  ## Dropped — report these
116
118
 
117
- Non-GitHub socials, badge variants, sidebar/item `attrs` + `translations`, `customCss` beyond `theme.css`, `head` entries, `routeMiddleware`, splash/hero pages (rebuild as custom pages), starlight-blog's tag and author pages, aside custom icons, EC frames/collapse/text markers, prev/next toggles, unmapped plugins, any `<Icon>` name with no Lucide equivalent.
119
+ Socials on a platform `footer.socials` lacks, badge variants, sidebar/item `attrs` + `translations`, `customCss` beyond `theme.css`, `head` entries other than meta tags and analytics scripts, a page's frontmatter `head`, `routeMiddleware`, splash layouts approximated with `mode: center` and hero blocks rebuilt as content, override components with no Blume slot, starlight-blog's tag and author pages, aside custom icons, EC frames/collapse/text markers, prev/next toggles, unmapped plugins, any `<Icon>` name with no Lucide equivalent.
@@ -622,10 +622,37 @@ export interface ReaderScope {
622
622
  filters: { locale: string | undefined; version: string | undefined };
623
623
  }
624
624
 
625
+ // The snapshot's locale codes, keyed lowercase, built once per snapshot.
626
+ const localeCodes = new WeakMap<AskData, Map<string, string>>();
627
+
628
+ /**
629
+ * The locale a path's first segment names (`/ja` or `/ja/pricing` → `ja`),
630
+ * when it's one the snapshot's documents carry: a page outside the docs,
631
+ * like a localized landing page, still says which language it's in.
632
+ */
633
+ const pathLocale = (data: AskData, path: string): string | undefined => {
634
+ let codes = localeCodes.get(data);
635
+ if (!codes) {
636
+ codes = new Map<string, string>();
637
+ for (const { locale } of data.documents) {
638
+ if (locale) {
639
+ codes.set(locale.toLowerCase(), locale);
640
+ }
641
+ }
642
+ localeCodes.set(data, codes);
643
+ }
644
+ const [, first = ""] = normalizeRoute(path).split("/");
645
+ return first ? codes.get(first.toLowerCase()) : undefined;
646
+ };
647
+
625
648
  /**
626
649
  * Where the reader is: their page, when it's in the snapshot, and the locale
627
- * and docs version retrieval keeps to. Without a page, a versioned site
628
- * grounds in the current docs rather than every archived copy of each page.
650
+ * and docs version retrieval keeps to. A page the snapshot doesn't have (the
651
+ * homepage, a custom page, or none at all from a cross-origin caller) keeps
652
+ * to the locale its path's first segment names, or the default locale, so
653
+ * translations of every page don't crowd each other out. Without a page, a
654
+ * versioned site grounds in the current docs rather than every archived copy
655
+ * of each page.
629
656
  */
630
657
  export const readerScope = (
631
658
  data: AskData,
@@ -635,10 +662,13 @@ export const readerScope = (
635
662
  const current = page?.path
636
663
  ? byRoute.get(normalizeRoute(page.path))
637
664
  : undefined;
665
+ const locale = current
666
+ ? current.locale
667
+ : (page?.path && pathLocale(data, page.path)) || data.defaultLocale;
638
668
  return {
639
669
  current,
640
670
  filters: {
641
- locale: current?.locale || undefined,
671
+ locale: locale || undefined,
642
672
  version: data.versioned ? (current?.version ?? "") : undefined,
643
673
  },
644
674
  };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * What one request to the assistant's generated route (`/api/ask`) may carry.
3
+ * The route answers a longer conversation with a `400` (see
4
+ * `askEndpointTemplate`), so the panel's client (`useAssistant`) sends only
5
+ * the latest turns that fit. The endpoint is unauthenticated, so the limits
6
+ * bound what one request can spend against the model.
7
+ */
8
+
9
+ /** The most messages, questions and answers together, one request sends. */
10
+ export const ASK_MAX_MESSAGES = 40;
11
+
12
+ /** The most characters the messages take as JSON (`JSON.stringify`). */
13
+ export const ASK_MAX_MESSAGES_CHARS = 24_000;
@@ -1,4 +1,5 @@
1
1
  import { mountBasePath, normalizeBasePath } from "../core/base-path.ts";
2
+ import { withChangelogIndexText } from "../core/changelog-index.ts";
2
3
  import { EN_UI, resolveUIStrings } from "../core/i18n-ui.ts";
3
4
  import type { BlumeProject } from "../core/project-graph.ts";
4
5
  import { absoluteUrl } from "../core/site-url.ts";
@@ -45,12 +46,16 @@ const rowLine = (project: BlumeProject, { date, page }: IndexRow): string => {
45
46
 
46
47
  export const buildChangelogIndexMarkdown = (project: BlumeProject): string => {
47
48
  const { i18n } = project.config;
48
- const ui = i18n
49
- ? resolveUIStrings(i18n.defaultLocale, {
50
- defaultLocale: i18n.defaultLocale,
51
- overrides: i18n.ui,
52
- })
53
- : EN_UI;
49
+ const ui = withChangelogIndexText(
50
+ project.config,
51
+ i18n
52
+ ? resolveUIStrings(i18n.defaultLocale, {
53
+ defaultLocale: i18n.defaultLocale,
54
+ overrides: i18n.ui,
55
+ })
56
+ : EN_UI,
57
+ i18n?.defaultLocale
58
+ );
54
59
  const rows: IndexRow[] = project.graph.pages
55
60
  .filter(
56
61
  (page) =>
@@ -584,7 +584,7 @@ export const inlineCode = (text: string): string => {
584
584
  };
585
585
 
586
586
  /** Fence `code` so its opening/closing run outlengths any backticks inside. */
587
- const fencedBlock = (lang: string, code: string): string => {
587
+ export const fencedBlock = (lang: string, code: string): string => {
588
588
  const trimmed = code.replace(/(?<!\n)\n+$/u, "");
589
589
  const runs = trimmed.match(/`+/gu);
590
590
  const longest = runs ? Math.max(...runs.map((run) => run.length)) : 0;
package/src/ai/cors.ts CHANGED
@@ -66,6 +66,13 @@ interface RequestContext {
66
66
  request: Request;
67
67
  }
68
68
 
69
+ /**
70
+ * The response headers a cross-origin caller may read beyond the safelisted
71
+ * ones: the rate limit's `Retry-After`, so a caller can tell a reader how
72
+ * long to wait after a `429`.
73
+ */
74
+ const EXPOSED_HEADERS = "retry-after";
75
+
69
76
  /** Stamp the CORS headers on every response `handler` returns. */
70
77
  export const withCors =
71
78
  (
@@ -74,9 +81,8 @@ export const withCors =
74
81
  ): ((context: RequestContext) => Promise<Response>) =>
75
82
  async (context) => {
76
83
  const response = await handler(context);
77
- for (const [key, value] of Object.entries(
78
- corsHeaders(context.request, allowed)
79
- )) {
84
+ const headers = corsHeaders(context.request, allowed);
85
+ for (const [key, value] of Object.entries(headers)) {
80
86
  // `Vary` accumulates (the handler may already vary on something), the
81
87
  // rest replace.
82
88
  if (key === "vary") {
@@ -85,5 +91,8 @@ export const withCors =
85
91
  response.headers.set(key, value);
86
92
  }
87
93
  }
94
+ if (headers["access-control-allow-origin"]) {
95
+ response.headers.set("access-control-expose-headers", EXPOSED_HEADERS);
96
+ }
88
97
  return response;
89
98
  };
@@ -53,20 +53,28 @@ const contentTypesField = z.preprocess((value) => {
53
53
  }, 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.'));
54
54
 
55
55
  /**
56
- * The optional facet filter `search_docs` and `list_pages` share. Only
57
- * string-valued entries survive; an empty `{}` means "no filter".
56
+ * The optional facet filter `search_docs` and `list_pages` share. A value
57
+ * is read the way `pageFacets` stores one: a string as-is, a number or
58
+ * boolean stringified (`{"priority": 1}` matches the facet `"1"`). Entries of
59
+ * any other shape are dropped; an empty `{}` means "no filter".
58
60
  */
59
- /** Accepts any plain object, so the string-valued entries can be sifted out. */
61
+ /** Accepts any plain object, so the usable entries can be sifted out. */
60
62
  const looseFacetObject = z.record(z.string(), z.unknown());
61
63
 
64
+ /** A filter value a facet can hold, stringified like `pageFacets` does. */
65
+ const facetFilterValue = z
66
+ .union([z.string(), z.number(), z.boolean()])
67
+ .transform(String);
68
+
62
69
  const filtersField = z.preprocess((value) => {
63
70
  const candidate = looseFacetObject.safeParse(value);
64
71
  if (!candidate.success) {
65
72
  return;
66
73
  }
67
- const entries = Object.entries(candidate.data).filter(
68
- (entry): entry is [string, string] => typeof entry[1] === "string"
69
- );
74
+ const entries = Object.entries(candidate.data).flatMap(([key, entry]) => {
75
+ const parsed = facetFilterValue.safeParse(entry);
76
+ return parsed.success ? [[key, parsed.data] as const] : [];
77
+ });
70
78
  return entries.length > 0 ? Object.fromEntries(entries) : undefined;
71
79
  }, 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.'));
72
80
 
@@ -1,13 +1,24 @@
1
+ import { customCodeSamples } from "../components/openapi/code-samples.ts";
1
2
  import { mountBasePath, normalizeBasePath } from "../core/base-path.ts";
2
3
  import type { ApiOperationRef, OpenApiData } from "../openapi/model.ts";
3
- import { operationOf, specAddresses, specOf } from "../openapi/model.ts";
4
+ import {
5
+ operationObject,
6
+ operationOf,
7
+ specAddresses,
8
+ specOf,
9
+ } from "../openapi/model.ts";
4
10
  import { asSentence } from "../openapi/sentence.ts";
5
11
  import { operationSignature } from "../openapi/signature.ts";
6
12
  import type {
7
13
  ComponentMarkdown,
8
14
  EvaluatedValue,
9
15
  } from "./component-markdown.ts";
10
- import { inlineCode, isString, linkDestination } from "./component-markdown.ts";
16
+ import {
17
+ fencedBlock,
18
+ inlineCode,
19
+ isString,
20
+ linkDestination,
21
+ } from "./component-markdown.ts";
11
22
 
12
23
  /**
13
24
  * Spec-authored prose as one line of inline Markdown. Unlike the props the
@@ -62,7 +73,10 @@ const listItem = (
62
73
  * section shows. Parameters, schemas and responses are deliberately left out:
63
74
  * those are `operation-model.ts` plus each component's own preparation, and a
64
75
  * second implementation here would be free to disagree with the page. The
65
- * endpoint and what it does is the part that was missing altogether.
76
+ * endpoint and what it does is the part that was missing altogether. The
77
+ * spec's own `x-codeSamples` do come along, as fenced code: they're text the
78
+ * spec wrote out (often an SDK call no agent could derive from the
79
+ * endpoint), read through the same `customCodeSamples` the page renders.
66
80
  *
67
81
  * `specs` is the parsed `blume:openapi` data — empty when the project has no
68
82
  * API reference, in which case every serializer declines. `deployBase` is the
@@ -142,7 +156,17 @@ export const openapiComponentSerializers = (
142
156
  : "",
143
157
  operation.deprecated ? "**Deprecated.**" : "",
144
158
  ].filter(Boolean);
145
- return [signature, ...notes].join("\n\n");
159
+ // The spec's own `x-codeSamples`, labeled as their tabs are.
160
+ const object =
161
+ data.kind === "openapi" ? operationObject(data, operation) : undefined;
162
+ const samples = customCodeSamples({
163
+ "x-code-samples": object?.["x-code-samples"],
164
+ "x-codeSamples": object?.["x-codeSamples"],
165
+ }).map(
166
+ (sample) =>
167
+ `**${inlineText(sample.label)}**\n\n${fencedBlock(sample.lang, sample.source)}`
168
+ );
169
+ return [signature, ...notes, ...samples].join("\n\n");
146
170
  },
147
171
  } satisfies Record<string, ComponentMarkdown>;
148
172
  };
@@ -7,8 +7,6 @@ import {
7
7
  withAuthoredBasePath,
8
8
  withBasePath,
9
9
  } from "../core/base-path.ts";
10
- import { nextFenceState } from "../core/code-fences.ts";
11
- import type { FenceState } from "../core/code-fences.ts";
12
10
  import { localePrefix } from "../core/i18n.ts";
13
11
  import {
14
12
  buildFileRouteIndex,
@@ -49,10 +47,6 @@ export interface LinkedPage {
49
47
  /** Rewrite a page's relative page links to the routes they mean. */
50
48
  export type RelativeLinkRewriter = (text: string, page: LinkedPage) => string;
51
49
 
52
- /** A reference definition's target: `[label]: ./install` or `<./install>`. */
53
- const DEFINITION =
54
- /^(?<lead> {0,3}\[(?:[^\]\\]|\\.)+\]:[\t ]*)(?:<(?<angled>[^>]*)>|(?<bare>\S+))/u;
55
-
56
50
  /**
57
51
  * Whether a text could hold a relative page link at all: an inline link, a
58
52
  * reference definition, or a component `href` whose target isn't a URL with
@@ -88,31 +82,13 @@ const spliceLine = (line: string, splices: readonly Splice[]): string => {
88
82
  return out;
89
83
  };
90
84
 
91
- /** One line's reference-definition splice, when it defines a routed target. */
92
- const definitionSplice = (
93
- line: string,
94
- routed: (target: string) => string | undefined
95
- ): Splice | undefined => {
96
- const groups = DEFINITION.exec(line)?.groups;
97
- const lead = groups?.lead;
98
- const target = groups?.angled ?? groups?.bare;
99
- const route = target === undefined ? undefined : routed(target);
100
- if (lead === undefined || target === undefined || route === undefined) {
101
- return undefined;
102
- }
103
- // An angle-bracketed target starts one past its `<`.
104
- const angled = groups?.angled === undefined ? 0 : 1;
105
- return { column: lead.length + angled, length: target.length, text: route };
106
- };
107
-
108
85
  /**
109
- * Every routed splice in `text`, by 0-based line: links and images, then
110
- * definitions. An image target goes through `imaged`, everything else through
111
- * `routed`.
86
+ * Every routed splice in `text`, by 0-based line: links, images, and
87
+ * reference definitions, as `extractLinks` finds them. An image target goes
88
+ * through `imaged`, everything else through `routed`.
112
89
  */
113
90
  const collectSplices = (
114
91
  text: string,
115
- lines: readonly string[],
116
92
  routed: (target: string) => string | undefined,
117
93
  imaged: (target: string) => string | undefined
118
94
  ): Map<number, Splice[]> => {
@@ -130,16 +106,6 @@ const collectSplices = (
130
106
  });
131
107
  }
132
108
  }
133
- let fence: FenceState = null;
134
- for (const [index, line] of lines.entries()) {
135
- const next = nextFenceState(line, fence);
136
- const inFence = fence !== null || next !== null;
137
- fence = next;
138
- const splice = inFence ? undefined : definitionSplice(line, routed);
139
- if (splice !== undefined) {
140
- add(index, splice);
141
- }
142
- }
143
109
  return splices;
144
110
  };
145
111
 
@@ -242,7 +208,7 @@ export const relativeLinkRewriter = (
242
208
  };
243
209
 
244
210
  const lines = text.split("\n");
245
- const splices = collectSplices(text, lines, routed, imaged);
211
+ const splices = collectSplices(text, routed, imaged);
246
212
 
247
213
  return lines
248
214
  .map((line, index) => {
@@ -34,7 +34,11 @@ import {
34
34
  navVariants,
35
35
  } from "../components/layout/nav-utils.ts";
36
36
  import { normalizeBasePath, withBasePath } from "../core/base-path.ts";
37
- import { validateUsedComponents } from "../core/component-diagnostics.ts";
37
+ import { withChangelogIndexText } from "../core/changelog-index.ts";
38
+ import {
39
+ missingExampleDiagnostics,
40
+ validateUsedComponents,
41
+ } from "../core/component-diagnostics.ts";
38
42
  import {
39
43
  analyzeComponentOverrides,
40
44
  emptyComponentOverrides,
@@ -93,13 +97,13 @@ import {
93
97
  builtinProxyReferences,
94
98
  hasScalarReferences,
95
99
  needsPlaygroundProxy,
96
- referenceRoutes,
97
100
  } from "../openapi/references.ts";
98
101
  import { buildReferenceFiles } from "../openapi/scalar.ts";
99
102
  import { isOpenApiSource } from "../openapi/source.ts";
100
103
  import { registry } from "../registry/registry.ts";
101
104
  import { buildSearchDocuments } from "../search/documents.ts";
102
105
  import { resolveSearchPopular } from "../search/popular.ts";
106
+ import { sourcePages } from "../search/source-pages.ts";
103
107
  import {
104
108
  examplesEntryTemplate,
105
109
  tailwindEntryTemplate,
@@ -125,6 +129,7 @@ import {
125
129
  customOgRoutes,
126
130
  discoverPages,
127
131
  hasGeneratedChangelog,
132
+ navTargetRoutes,
128
133
  routeIsTaken,
129
134
  } from "./pages.ts";
130
135
  import { candidateHolding } from "./render-deps.ts";
@@ -180,28 +185,35 @@ export type { PrerenderDepsPlugin } from "./render-deps.ts";
180
185
  const BLUME_SRC = join(packageRoot(), "src");
181
186
 
182
187
  /**
183
- * Absolute path to `babel-plugin-react-compiler`, resolved from Blume's own
184
- * package root (Blume ships it). Returns null when React or the compiler is off.
188
+ * Whether the generated `react()` call turns the React Compiler on: React and
189
+ * `react.compiler` are both on, and `oxc-transform-react` (Blume ships it)
190
+ * resolves. False when React or the compiler is off.
185
191
  *
186
- * The path must be absolute: @vitejs/plugin-react resolves babel plugins from
187
- * the *project* root, not `.blume/`, so a bare specifier fails in a user project
188
- * that never installed the plugin directly. Resolving from `packageRoot()` binds
189
- * to Blume's shipped copy regardless of the user's package manager or hoisting.
192
+ * The compiler is loaded by `@vitejs/plugin-react` with a bare
193
+ * `import("oxc-transform-react")` from its own directory, which fails the
194
+ * build when it throws, so the check walks the same chain — Blume →
195
+ * `@astrojs/react` → `@vitejs/plugin-react` — rather than resolving from
196
+ * Blume's root, which a strict package manager may not link the plugin to.
190
197
  */
191
198
  export const resolveReactCompiler = (
192
199
  config: ResolvedConfig,
193
200
  needsReact: boolean,
194
201
  pkgDir: string = packageRoot()
195
- ): string | null => {
202
+ ): boolean => {
196
203
  if (!(needsReact && config.react.compiler)) {
197
- return null;
204
+ return false;
198
205
  }
199
206
  try {
200
- return createRequire(pathToFileURL(join(pkgDir, "_.js")).href).resolve(
201
- "babel-plugin-react-compiler"
207
+ const astroReact = createRequire(
208
+ pathToFileURL(join(pkgDir, "_.js")).href
209
+ ).resolve("@astrojs/react");
210
+ const pluginReact = createRequire(astroReact).resolve(
211
+ "@vitejs/plugin-react"
202
212
  );
213
+ createRequire(pluginReact).resolve("oxc-transform-react");
214
+ return true;
203
215
  } catch {
204
- return null;
216
+ return false;
205
217
  }
206
218
  };
207
219
 
@@ -213,11 +225,11 @@ export const resolveReactCompiler = (
213
225
  export const reactCompilerWarnings = (
214
226
  config: ResolvedConfig,
215
227
  needsReact: boolean,
216
- compilerPath: string | null
228
+ compiler: boolean
217
229
  ): string[] =>
218
- needsReact && config.react.compiler && !compilerPath
230
+ needsReact && config.react.compiler && !compiler
219
231
  ? [
220
- "React Compiler is enabled but `babel-plugin-react-compiler` could not be resolved; falling back to an uncompiled build. Reinstall Blume, or set `react: { compiler: false }` to silence this.",
232
+ "React Compiler is enabled but `oxc-transform-react` could not be resolved; falling back to an uncompiled build. Reinstall Blume, or set `react: { compiler: false }` to silence this.",
221
233
  ]
222
234
  : [];
223
235
 
@@ -1142,23 +1154,32 @@ export const buildRuntimeData = (project: BlumeProject): string => {
1142
1154
 
1143
1155
  // Resolved UI dictionaries: one per locale under i18n, English baseline
1144
1156
  // otherwise. Threaded into chrome so the catch-all can pick the active locale.
1157
+ // The `changelog` config's title and description replace the dictionaries'.
1145
1158
  const uiByLocale = i18n
1146
1159
  ? Object.fromEntries(
1147
1160
  i18n.locales.map(({ code }) => [
1148
1161
  code,
1149
- resolveUIStrings(code, {
1150
- defaultLocale: i18n.defaultLocale,
1151
- overrides: i18n.ui,
1152
- }),
1162
+ withChangelogIndexText(
1163
+ config,
1164
+ resolveUIStrings(code, {
1165
+ defaultLocale: i18n.defaultLocale,
1166
+ overrides: i18n.ui,
1167
+ }),
1168
+ code
1169
+ ),
1153
1170
  ])
1154
1171
  )
1155
1172
  : {};
1156
- const defaultUi = i18n
1157
- ? resolveUIStrings(i18n.defaultLocale, {
1158
- defaultLocale: i18n.defaultLocale,
1159
- overrides: i18n.ui,
1160
- })
1161
- : EN_UI;
1173
+ const defaultUi = withChangelogIndexText(
1174
+ config,
1175
+ i18n
1176
+ ? resolveUIStrings(i18n.defaultLocale, {
1177
+ defaultLocale: i18n.defaultLocale,
1178
+ overrides: i18n.ui,
1179
+ })
1180
+ : EN_UI,
1181
+ i18n?.defaultLocale
1182
+ );
1162
1183
 
1163
1184
  const navigationByLocale = i18n
1164
1185
  ? Object.fromEntries(
@@ -1255,6 +1276,7 @@ export const buildRuntimeData = (project: BlumeProject): string => {
1255
1276
  openInChat: config.ai.openInChat,
1256
1277
  repoUrl,
1257
1278
  search: {
1279
+ analytics: config.search.analytics,
1258
1280
  enabled: config.search.provider.mode !== "none",
1259
1281
  popular: resolveSearchPopular(config.search.popular, config.basePath),
1260
1282
  provider: config.search.provider.kind,
@@ -2064,11 +2086,12 @@ export const generateRuntime = async (
2064
2086
  const needsVue = frameworks.has("vue");
2065
2087
  const needsSvelte = frameworks.has("svelte");
2066
2088
 
2067
- // Absolute path to the React Compiler babel plugin (null when off). Resolved
2068
- // here, Node-side, so the generated config points babel straight at Blume's
2069
- // shipped copy — see resolveReactCompiler. Any unresolved-but-requested
2070
- // warning is folded into `warnings` below (declared later).
2071
- const reactCompilerPath = resolveReactCompiler(config, needsReact);
2089
+ // Whether react() turns the React Compiler on (false when off). Checked here,
2090
+ // Node-side, so an unresolvable compiler degrades to an uncompiled build
2091
+ // instead of failing it — see resolveReactCompiler. Any
2092
+ // unresolved-but-requested warning is folded into `warnings` below (declared
2093
+ // later).
2094
+ const reactCompiler = resolveReactCompiler(config, needsReact);
2072
2095
 
2073
2096
  // Custom pages that should get a generated OG card (the home most of all).
2074
2097
  // Computed before the MCP `.well-known` routes are appended below — those are
@@ -2146,7 +2169,7 @@ export const generateRuntime = async (
2146
2169
  needsSvelte,
2147
2170
  needsVue,
2148
2171
  pages,
2149
- reactCompilerPath,
2172
+ reactCompiler,
2150
2173
  searchClientPath,
2151
2174
  themePath,
2152
2175
  })
@@ -2209,7 +2232,7 @@ export const generateRuntime = async (
2209
2232
  languageIcons,
2210
2233
  sources: [
2211
2234
  `${BLUME_SRC}/**/*.{astro,ts,tsx}`,
2212
- `${context.root}/**/*.{astro,mdx,ts,tsx}`,
2235
+ `${context.root}/**/*.{astro,jsx,mdx,ts,tsx}`,
2213
2236
  ],
2214
2237
  twoslashCss: twoslashCss(),
2215
2238
  userTheme,
@@ -2247,7 +2270,10 @@ export const generateRuntime = async (
2247
2270
  writeMcpFiles(mcp, write, agentData),
2248
2271
  writeApiFiles(project, api, write, agentData, mcp),
2249
2272
  playgroundProxy.enabled
2250
- ? write(playgroundProxy.entrypoint, playgroundProxyTemplate(proxyOrigins))
2273
+ ? write(
2274
+ playgroundProxy.entrypoint,
2275
+ playgroundProxyTemplate(proxyOrigins, config.rateLimit)
2276
+ )
2251
2277
  : Promise.resolve(false),
2252
2278
  ]);
2253
2279
 
@@ -2317,7 +2343,11 @@ export const generateRuntime = async (
2317
2343
  if (searchAdapter.kind === "mixedbread") {
2318
2344
  await write(
2319
2345
  join(srcDir, "pages", "api", "search.ts"),
2320
- mixedbreadSearchEndpointTemplate(searchAdapter.options)
2346
+ mixedbreadSearchEndpointTemplate(
2347
+ searchAdapter.options,
2348
+ sourcePages(project),
2349
+ config.rateLimit
2350
+ )
2321
2351
  );
2322
2352
  }
2323
2353
 
@@ -2372,7 +2402,7 @@ export const generateRuntime = async (
2372
2402
  ...(depsLinkWarning ? [depsLinkWarning] : []),
2373
2403
  ...proxyAllowlistWarnings(config, openApiData),
2374
2404
  ...apiPageProxyWarnings(project),
2375
- ...reactCompilerWarnings(config, needsReact, reactCompilerPath),
2405
+ ...reactCompilerWarnings(config, needsReact, reactCompiler),
2376
2406
  ...mcp.warnings,
2377
2407
  ...islandDiscovery.warnings,
2378
2408
  ...exampleDiscovery.warnings,
@@ -2380,24 +2410,18 @@ export const generateRuntime = async (
2380
2410
  ...overrideWarnings,
2381
2411
  ];
2382
2412
 
2383
- // Missing-navigation-target check, now that every servable route is known:
2384
- // content routes, custom `.astro` pages, the generated changelog, and any
2385
- // OpenAPI reference routes (so a tab an author points at one still validates).
2386
- const navTargetRoutes = new Set<string>([
2387
- ...project.graph.routes.keys(),
2388
- ...pages.map((page) => page.pattern),
2389
- ...referenceRoutes(config),
2390
- ]);
2391
- if (changelogIndex) {
2392
- navTargetRoutes.add("/changelog");
2393
- }
2413
+ // Missing-navigation-target check, now that every servable route is known
2414
+ // (see `navTargetRoutes`).
2394
2415
  // Curated `search.popular` icons live outside the navigation model, so they
2395
2416
  // miss `validateNavIcons` in the graph build — they're checked here too,
2396
2417
  // where the search config is known. A typo otherwise just renders the
2397
2418
  // default glyph.
2398
2419
  warnings.push(
2399
2420
  ...[
2400
- ...validateNavTargets(project.graph.navigation, navTargetRoutes),
2421
+ ...validateNavTargets(
2422
+ project.graph.navigation,
2423
+ navTargetRoutes(project, pages)
2424
+ ),
2401
2425
  ...validateSearchPopularIcons(config.search.popular),
2402
2426
  ].map(diagnosticWarning)
2403
2427
  );
@@ -2414,6 +2438,12 @@ export const generateRuntime = async (
2414
2438
  knownComponentTags,
2415
2439
  new Set(registry.map((item) => item.name))
2416
2440
  ).map(diagnosticWarning),
2441
+ ...missingExampleDiagnostics(
2442
+ project.graph.pages,
2443
+ exampleDiscovery,
2444
+ context.root,
2445
+ knownComponentTags
2446
+ ).map(diagnosticWarning),
2417
2447
  ...dependencyWarnings
2418
2448
  );
2419
2449
  if (hasScalarReferences(config)) {