blume 2.0.1 → 2.0.3

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 (492) hide show
  1. package/CHANGELOG.md +147 -0
  2. package/dist/cli/{chunk-qwsrynx5.js → chunk-1d7ve1dm.js} +38 -19
  3. package/dist/cli/{chunk-qwsrynx5.js.map → chunk-1d7ve1dm.js.map} +4 -4
  4. package/dist/cli/{chunk-s6jhgk0q.js → chunk-2eytanqx.js} +2 -2
  5. package/dist/cli/{chunk-kdp5q7ke.js → chunk-2hsdwb9n.js} +18 -19
  6. package/dist/cli/{chunk-kdp5q7ke.js.map → chunk-2hsdwb9n.js.map} +2 -2
  7. package/dist/cli/{chunk-hdpx1tax.js → chunk-2z928egk.js} +5 -5
  8. package/dist/cli/{chunk-jwyddg7y.js → chunk-35d4wj9f.js} +30 -24
  9. package/dist/cli/chunk-35d4wj9f.js.map +15 -0
  10. package/dist/cli/{chunk-ah61y8py.js → chunk-364znk6q.js} +2 -2
  11. package/dist/cli/{chunk-jts8mvcz.js → chunk-3em5wd2y.js} +25 -6
  12. package/dist/cli/{chunk-jts8mvcz.js.map → chunk-3em5wd2y.js.map} +3 -3
  13. package/dist/cli/{chunk-s1p84fyh.js → chunk-5f86nr5m.js} +63 -22
  14. package/dist/cli/chunk-5f86nr5m.js.map +11 -0
  15. package/dist/cli/{chunk-2hn4b8z7.js → chunk-5m5nmvyq.js} +170 -76
  16. package/dist/cli/chunk-5m5nmvyq.js.map +13 -0
  17. package/dist/cli/{chunk-epjnccmv.js → chunk-5xvm6tfj.js} +18 -15
  18. package/dist/cli/chunk-5xvm6tfj.js.map +10 -0
  19. package/dist/cli/{chunk-fz5wtpmh.js → chunk-6dsbexzp.js} +18 -15
  20. package/dist/cli/chunk-6dsbexzp.js.map +10 -0
  21. package/dist/cli/{chunk-vtk4a6dg.js → chunk-8ktnccpt.js} +1 -1
  22. package/dist/cli/{chunk-6hsn950k.js → chunk-9t7a85s3.js} +178 -48
  23. package/dist/cli/chunk-9t7a85s3.js.map +10 -0
  24. package/dist/cli/{chunk-fxypxtvm.js → chunk-a58773jm.js} +2 -2
  25. package/dist/cli/{chunk-mb2919y2.js → chunk-acanzt5p.js} +17 -6
  26. package/dist/cli/chunk-acanzt5p.js.map +10 -0
  27. package/dist/cli/{chunk-27g6wdth.js → chunk-akbpwfxc.js} +90 -26
  28. package/dist/cli/chunk-akbpwfxc.js.map +10 -0
  29. package/dist/cli/{chunk-wm7js3j9.js → chunk-b07cmahc.js} +2 -2
  30. package/dist/cli/{chunk-wgm7m9qk.js → chunk-c8chx29p.js} +179 -51
  31. package/dist/cli/chunk-c8chx29p.js.map +36 -0
  32. package/dist/cli/{chunk-qs4q5p4e.js → chunk-crgn1q09.js} +32 -14
  33. package/dist/cli/chunk-crgn1q09.js.map +10 -0
  34. package/dist/cli/chunk-e04dxsz1.js +39 -0
  35. package/dist/cli/chunk-e04dxsz1.js.map +10 -0
  36. package/dist/cli/{chunk-yt5n7ppj.js → chunk-ey84smr6.js} +17 -7
  37. package/dist/cli/chunk-ey84smr6.js.map +10 -0
  38. package/dist/cli/{chunk-f2z5v128.js → chunk-g4hq16wv.js} +14 -15
  39. package/dist/cli/{chunk-f2z5v128.js.map → chunk-g4hq16wv.js.map} +2 -2
  40. package/dist/cli/{chunk-fa25z98p.js → chunk-ga0pf4aj.js} +9 -8
  41. package/dist/cli/chunk-ga0pf4aj.js.map +11 -0
  42. package/dist/cli/{chunk-kpf8rrjc.js → chunk-hm3vjy5s.js} +109 -58
  43. package/dist/cli/chunk-hm3vjy5s.js.map +19 -0
  44. package/dist/cli/{chunk-fs23ddbb.js → chunk-j85vccga.js} +625 -750
  45. package/dist/cli/chunk-j85vccga.js.map +36 -0
  46. package/dist/cli/{chunk-ch6g3ar0.js → chunk-p3v96n38.js} +6 -6
  47. package/dist/cli/{chunk-ch6g3ar0.js.map → chunk-p3v96n38.js.map} +3 -3
  48. package/dist/cli/{chunk-q5163e60.js → chunk-p73c0m7w.js} +21 -19
  49. package/dist/cli/chunk-p73c0m7w.js.map +11 -0
  50. package/dist/cli/{chunk-dh8cwk36.js → chunk-pehfxfta.js} +24 -9
  51. package/dist/cli/chunk-pehfxfta.js.map +10 -0
  52. package/dist/cli/{chunk-qkqwkpte.js → chunk-pv29h0wf.js} +3688 -1313
  53. package/dist/cli/chunk-pv29h0wf.js.map +190 -0
  54. package/dist/cli/{chunk-6vm74dry.js → chunk-q58y5e6a.js} +10 -10
  55. package/dist/cli/{chunk-6vm74dry.js.map → chunk-q58y5e6a.js.map} +3 -3
  56. package/dist/cli/{chunk-zxcczpyx.js → chunk-r20tn01b.js} +1 -1
  57. package/dist/cli/{chunk-m3vmjgmq.js → chunk-r9rcc4w7.js} +16 -8
  58. package/dist/cli/chunk-r9rcc4w7.js.map +10 -0
  59. package/dist/cli/{chunk-5shv93fd.js → chunk-tkacnehg.js} +2 -2
  60. package/dist/cli/{chunk-zxh4d9vy.js → chunk-tzmab476.js} +4 -4
  61. package/dist/cli/{chunk-yw7dm696.js → chunk-vg9r4eb9.js} +24 -11
  62. package/dist/cli/chunk-vg9r4eb9.js.map +14 -0
  63. package/dist/cli/{chunk-79jhk4py.js → chunk-wrr3j9w9.js} +260 -132
  64. package/dist/cli/chunk-wrr3j9w9.js.map +35 -0
  65. package/dist/cli/{chunk-6crbhc3x.js → chunk-yfyb25rh.js} +54 -27
  66. package/dist/cli/chunk-yfyb25rh.js.map +14 -0
  67. package/dist/cli/index.js +162 -35
  68. package/dist/cli/index.js.map +4 -4
  69. package/dist/types/ai/agent-surface.d.ts +32 -0
  70. package/dist/types/ai/api-catalog.d.ts +7 -1
  71. package/dist/types/ai/ask-context.d.ts +7 -0
  72. package/dist/types/ai/component-markdown.d.ts +4 -4
  73. package/dist/types/ai/link-headers.d.ts +8 -1
  74. package/dist/types/ai/openapi-components.d.ts +5 -2
  75. package/dist/types/ai/relative-links.d.ts +11 -4
  76. package/dist/types/ai/skills.d.ts +4 -1
  77. package/dist/types/ai/static-expression.d.ts +28 -0
  78. package/dist/types/ai/tar.d.ts +1 -3
  79. package/dist/types/analytics/databuddy.d.ts +43 -0
  80. package/dist/types/analytics/index.d.ts +4 -0
  81. package/dist/types/analytics/one-dollar-stats.d.ts +48 -0
  82. package/dist/types/analytics/schema.d.ts +28 -0
  83. package/dist/types/astro/integration.d.ts +3 -2
  84. package/dist/types/cli/env.d.ts +5 -0
  85. package/dist/types/cli/init/scaffold.d.ts +19 -3
  86. package/dist/types/cli/init/starter-spec.d.ts +11 -0
  87. package/dist/types/core/base-path.d.ts +24 -1
  88. package/dist/types/core/config-input.d.ts +13 -11
  89. package/dist/types/core/config.d.ts +2 -2
  90. package/dist/types/core/directive-diagnostics.d.ts +12 -0
  91. package/dist/types/core/graph.d.ts +2 -0
  92. package/dist/types/core/heading-markers.d.ts +5 -7
  93. package/dist/types/core/i18n-ui.d.ts +31 -0
  94. package/dist/types/core/i18n.d.ts +9 -1
  95. package/dist/types/core/last-modified.d.ts +10 -0
  96. package/dist/types/core/links.d.ts +3 -1
  97. package/dist/types/core/load-module.d.ts +10 -0
  98. package/dist/types/core/locale-links.d.ts +12 -2
  99. package/dist/types/core/meta.d.ts +13 -1
  100. package/dist/types/core/nav-diagnostics.d.ts +10 -0
  101. package/dist/types/core/navigation.d.ts +38 -0
  102. package/dist/types/core/ordering-prefix.d.ts +4 -0
  103. package/dist/types/core/safe-links.d.ts +3 -1
  104. package/dist/types/core/schema.d.ts +59 -13
  105. package/dist/types/core/sources/github-releases.d.ts +5 -0
  106. package/dist/types/core/sources/lower.d.ts +38 -13
  107. package/dist/types/core/sources/normalize.d.ts +23 -2
  108. package/dist/types/core/sources/remote.d.ts +11 -1
  109. package/dist/types/core/sources/resolve.d.ts +12 -0
  110. package/dist/types/core/sources/types.d.ts +31 -0
  111. package/dist/types/core/sources/watch.d.ts +12 -5
  112. package/dist/types/core/standard-schema.d.ts +5 -0
  113. package/dist/types/core/types.d.ts +9 -0
  114. package/dist/types/deploy/adapters/node.d.ts +5 -2
  115. package/dist/types/deploy/adapters/types.d.ts +7 -0
  116. package/dist/types/deploy/artifacts.d.ts +6 -4
  117. package/dist/types/deploy/cloudflare-negotiation.d.ts +3 -2
  118. package/dist/types/deploy/headers.d.ts +36 -7
  119. package/dist/types/deploy/node-headers.d.ts +43 -8
  120. package/dist/types/deploy/platforms/netlify.d.ts +27 -2
  121. package/dist/types/deploy/platforms/node.d.ts +6 -5
  122. package/dist/types/deploy/platforms/types.d.ts +15 -0
  123. package/dist/types/deploy/platforms/vercel.d.ts +3 -2
  124. package/dist/types/deploy/redirects.d.ts +31 -15
  125. package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
  126. package/dist/types/markdown/directives.d.ts +62 -0
  127. package/dist/types/markdown/features.d.ts +21 -0
  128. package/dist/types/markdown/mdast.d.ts +63 -0
  129. package/dist/types/openapi/asyncapi.d.ts +4 -2
  130. package/dist/types/openapi/model.d.ts +20 -6
  131. package/dist/types/search/sync/algolia.d.ts +3 -1
  132. package/docs/01-quickstart.mdx +3 -2
  133. package/docs/02-deployment.mdx +20 -9
  134. package/docs/08-faq.mdx +10 -3
  135. package/docs/advanced/changelog.mdx +1 -1
  136. package/docs/advanced/custom-pages.mdx +7 -5
  137. package/docs/cli/audit.mdx +19 -3
  138. package/docs/cli/doctor.mdx +2 -2
  139. package/docs/cli/evals.mdx +4 -4
  140. package/docs/cli/index.mdx +4 -1
  141. package/docs/cli/translate.mdx +4 -4
  142. package/docs/cli/version.mdx +1 -1
  143. package/docs/configuration/analytics.mdx +42 -2
  144. package/docs/configuration/assistant.mdx +1 -1
  145. package/docs/configuration/customization.mdx +6 -3
  146. package/docs/configuration/index.mdx +5 -3
  147. package/docs/configuration/search.mdx +2 -2
  148. package/docs/content/components.mdx +2 -2
  149. package/docs/content/frontmatter.mdx +5 -1
  150. package/docs/content/i18n.mdx +3 -1
  151. package/docs/content/index.mdx +8 -4
  152. package/docs/content/islands.mdx +1 -1
  153. package/docs/content/meta.mdx +7 -3
  154. package/docs/content/navigation.mdx +29 -4
  155. package/docs/content/sources.mdx +18 -16
  156. package/docs/content/syntax.mdx +14 -0
  157. package/docs/content/versioning.mdx +2 -1
  158. package/docs/discoverability/agent-discovery.mdx +22 -8
  159. package/docs/discoverability/index.mdx +4 -3
  160. package/docs/discoverability/llms-txt.mdx +2 -5
  161. package/docs/discoverability/markdown.mdx +6 -4
  162. package/docs/discoverability/mcp.mdx +4 -0
  163. package/docs/discoverability/metadata.mdx +3 -2
  164. package/docs/discoverability/open-graph.mdx +8 -4
  165. package/docs/discoverability/rss.mdx +4 -2
  166. package/docs/references/asyncapi.mdx +2 -2
  167. package/docs/references/graphql.mdx +2 -2
  168. package/docs/references/openapi.mdx +6 -4
  169. package/package.json +1 -1
  170. package/skills/blume-migrate/SKILL.md +6 -6
  171. package/skills/blume-migrate/references/docusaurus.md +6 -6
  172. package/skills/blume-migrate/references/fumadocs.md +1 -1
  173. package/skills/blume-migrate/references/mintlify.md +3 -3
  174. package/skills/blume-migrate/references/monorepo.md +1 -1
  175. package/skills/blume-migrate/references/nextra.md +2 -2
  176. package/skills/blume-migrate/references/starlight.md +5 -5
  177. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +9 -4
  178. package/skills/blume-update-docs/references/audit-checklist.md +1 -1
  179. package/src/ai/agent-readability.ts +2 -2
  180. package/src/ai/agent-surface.ts +56 -0
  181. package/src/ai/ai-catalog.ts +6 -2
  182. package/src/ai/api/handlers.ts +2 -2
  183. package/src/ai/api/spec.ts +17 -4
  184. package/src/ai/api-catalog.ts +13 -3
  185. package/src/ai/ask-context.ts +14 -2
  186. package/src/ai/ask-data.ts +26 -12
  187. package/src/ai/changelog-markdown.ts +2 -2
  188. package/src/ai/component-markdown.ts +43 -37
  189. package/src/ai/link-headers.ts +22 -8
  190. package/src/ai/llms.ts +22 -10
  191. package/src/ai/markdown.ts +23 -11
  192. package/src/ai/mcp/discovery.ts +2 -2
  193. package/src/ai/mcp/query.ts +2 -2
  194. package/src/ai/mcp/server.ts +80 -9
  195. package/src/ai/openapi-components.ts +22 -6
  196. package/src/ai/relative-links.ts +99 -15
  197. package/src/ai/serializers.ts +2 -1
  198. package/src/ai/skills.ts +14 -1
  199. package/src/ai/static-expression.ts +416 -0
  200. package/src/ai/tar.ts +139 -12
  201. package/src/ai/visibility.ts +45 -14
  202. package/src/analytics/databuddy.ts +67 -0
  203. package/src/analytics/head.ts +8 -0
  204. package/src/analytics/index.ts +7 -0
  205. package/src/analytics/one-dollar-stats.ts +86 -0
  206. package/src/analytics/posthog.ts +21 -3
  207. package/src/analytics/schema.ts +4 -0
  208. package/src/astro/generate.ts +72 -28
  209. package/src/astro/include-hmr.ts +31 -12
  210. package/src/astro/include-refresh.ts +19 -8
  211. package/src/astro/integration.ts +108 -14
  212. package/src/astro/runtime-modules.ts +28 -13
  213. package/src/astro/templates.ts +139 -57
  214. package/src/audit/catalog.ts +2 -2
  215. package/src/audit/checks/assets.ts +25 -5
  216. package/src/audit/checks/content.ts +20 -2
  217. package/src/audit/checks/i18n.ts +46 -8
  218. package/src/audit/checks/indexability.ts +39 -19
  219. package/src/audit/checks/links.ts +22 -1
  220. package/src/audit/checks/llms.ts +6 -3
  221. package/src/audit/checks/network.ts +3 -1
  222. package/src/audit/checks/og-image.ts +10 -0
  223. package/src/audit/checks/robots.ts +6 -1
  224. package/src/audit/checks/sitemap.ts +56 -31
  225. package/src/audit/checks/social.ts +21 -2
  226. package/src/audit/crawl.ts +88 -15
  227. package/src/audit/graph.ts +3 -1
  228. package/src/audit/report.ts +54 -17
  229. package/src/audit/run.ts +13 -9
  230. package/src/audit/snapshot.ts +5 -0
  231. package/src/audit/types.ts +18 -0
  232. package/src/audit/url.ts +37 -6
  233. package/src/cli/args.ts +32 -0
  234. package/src/cli/build-failure.ts +50 -0
  235. package/src/cli/commands/audit.ts +11 -1
  236. package/src/cli/commands/build.ts +19 -5
  237. package/src/cli/commands/check.ts +2 -0
  238. package/src/cli/commands/doctor.ts +3 -1
  239. package/src/cli/commands/eval.ts +20 -18
  240. package/src/cli/commands/init.ts +15 -5
  241. package/src/cli/commands/preview.ts +15 -0
  242. package/src/cli/commands/sync.ts +2 -0
  243. package/src/cli/commands/translate.ts +18 -18
  244. package/src/cli/commands/upgrade.ts +11 -0
  245. package/src/cli/commands/validate.ts +3 -1
  246. package/src/cli/dev-lock.ts +157 -37
  247. package/src/cli/eject-scripts.ts +32 -7
  248. package/src/cli/env.ts +12 -1
  249. package/src/cli/init/scaffold.ts +117 -11
  250. package/src/cli/init/starter-spec.ts +235 -0
  251. package/src/cli/report-format.ts +11 -6
  252. package/src/components/colors.ts +19 -0
  253. package/src/components/content/AccordionItem.astro +26 -22
  254. package/src/components/content/Badge.astro +7 -12
  255. package/src/components/content/Card.astro +2 -2
  256. package/src/components/content/Component.astro +11 -5
  257. package/src/components/content/Expandable.astro +5 -1
  258. package/src/components/content/Frame.astro +2 -7
  259. package/src/components/content/GithubInfo.astro +12 -2
  260. package/src/components/content/Prompt.astro +2 -7
  261. package/src/components/content/Tab.astro +0 -1
  262. package/src/components/content/Tabs.astro +58 -5
  263. package/src/components/content/Tile.astro +1 -1
  264. package/src/components/content/Tooltip.astro +69 -7
  265. package/src/components/content/Tree.astro +7 -2
  266. package/src/components/content/TypeTable.astro +10 -5
  267. package/src/components/content/Update.astro +8 -2
  268. package/src/components/content/auto-type-table.ts +4 -1
  269. package/src/components/content/badge-color.ts +20 -0
  270. package/src/components/content/base-href.ts +14 -22
  271. package/src/components/content/inline-markdown.ts +27 -7
  272. package/src/components/copy-feedback.ts +35 -8
  273. package/src/components/islands/assistant.tsx +37 -6
  274. package/src/components/islands/base-path.ts +47 -10
  275. package/src/components/islands/hooks.ts +42 -21
  276. package/src/components/islands/webmcp.ts +16 -10
  277. package/src/components/layout/Banner.astro +23 -4
  278. package/src/components/layout/Breadcrumbs.astro +5 -2
  279. package/src/components/layout/DiscoveryLinks.astro +9 -5
  280. package/src/components/layout/Header.astro +23 -10
  281. package/src/components/layout/LanguageSwitcher.astro +2 -2
  282. package/src/components/layout/Logo.astro +5 -0
  283. package/src/components/layout/NavSelector.astro +8 -3
  284. package/src/components/layout/NavTabMenu.astro +133 -0
  285. package/src/components/layout/NavTree.astro +31 -12
  286. package/src/components/layout/NavTreeCache.astro +5 -2
  287. package/src/components/layout/NavTreeScript.astro +45 -5
  288. package/src/components/layout/PageActions.astro +4 -3
  289. package/src/components/layout/PageLayout.astro +28 -13
  290. package/src/components/layout/Pagination.astro +3 -3
  291. package/src/components/layout/ReferenceLayout.astro +4 -1
  292. package/src/components/layout/RootLayout.astro +25 -12
  293. package/src/components/layout/Search.astro +38 -14
  294. package/src/components/layout/VersionBanner.astro +2 -2
  295. package/src/components/layout/analytics-client.ts +14 -0
  296. package/src/components/layout/toc-active.ts +41 -0
  297. package/src/components/layout/toc-element.ts +8 -14
  298. package/src/components/openapi/ApiTagOperations.astro +2 -2
  299. package/src/components/openapi/AsyncApiOperation.astro +7 -4
  300. package/src/components/openapi/GraphqlChip.astro +2 -2
  301. package/src/components/openapi/GraphqlType.astro +11 -3
  302. package/src/components/openapi/MessageComposer.astro +1 -1
  303. package/src/components/openapi/Operation.astro +23 -5
  304. package/src/components/openapi/PanelTabs.astro +4 -1
  305. package/src/components/openapi/Playground.astro +8 -2
  306. package/src/components/openapi/RequestPanel.astro +13 -6
  307. package/src/components/openapi/SchemaProperty.astro +11 -33
  308. package/src/components/openapi/SchemaTable.astro +34 -52
  309. package/src/components/openapi/async.ts +38 -6
  310. package/src/components/openapi/helpers.ts +100 -12
  311. package/src/components/openapi/message-composer.ts +14 -1
  312. package/src/components/openapi/message-model.ts +12 -2
  313. package/src/components/openapi/message.ts +21 -3
  314. package/src/components/openapi/operation-model.ts +119 -17
  315. package/src/components/openapi/panel.ts +31 -5
  316. package/src/components/openapi/param-style.ts +181 -0
  317. package/src/components/openapi/playground-client.ts +110 -23
  318. package/src/components/openapi/playground-schema.ts +25 -6
  319. package/src/components/openapi/request.ts +192 -26
  320. package/src/components/openapi/schema-tree.ts +209 -0
  321. package/src/components/openapi/snippets.ts +113 -19
  322. package/src/components/openapi/validate-json.ts +1 -1
  323. package/src/components/openapi/ws-client.ts +18 -2
  324. package/src/core/base-path.ts +69 -8
  325. package/src/core/config-input.ts +13 -11
  326. package/src/core/config.ts +18 -4
  327. package/src/core/diagnostics.ts +219 -30
  328. package/src/core/directive-diagnostics.ts +99 -0
  329. package/src/core/frontmatter.ts +21 -18
  330. package/src/core/graph.ts +120 -11
  331. package/src/core/heading-markers.ts +5 -18
  332. package/src/core/i18n-ui.ts +39 -2
  333. package/src/core/i18n.ts +44 -5
  334. package/src/core/last-modified.ts +25 -3
  335. package/src/core/links.ts +7 -4
  336. package/src/core/load-module.ts +20 -0
  337. package/src/core/locale-links.ts +22 -18
  338. package/src/core/manifest.ts +3 -2
  339. package/src/core/meta.ts +89 -12
  340. package/src/core/nav-diagnostics.ts +56 -1
  341. package/src/core/navigation.ts +272 -83
  342. package/src/core/ordering-prefix.ts +27 -0
  343. package/src/core/project-graph.ts +50 -6
  344. package/src/core/safe-href.ts +53 -1
  345. package/src/core/safe-links.ts +11 -2
  346. package/src/core/schema.ts +94 -18
  347. package/src/core/sources/assets.ts +83 -41
  348. package/src/core/sources/contentful-rich-text.ts +28 -20
  349. package/src/core/sources/contentful.ts +25 -12
  350. package/src/core/sources/filesystem.ts +25 -3
  351. package/src/core/sources/github-releases.ts +131 -11
  352. package/src/core/sources/lexical.ts +30 -20
  353. package/src/core/sources/lower.ts +276 -56
  354. package/src/core/sources/mdx-remote.ts +52 -16
  355. package/src/core/sources/normalize.ts +456 -119
  356. package/src/core/sources/notion.ts +77 -34
  357. package/src/core/sources/obsidian.ts +40 -9
  358. package/src/core/sources/payload.ts +1 -0
  359. package/src/core/sources/portable-text.ts +85 -49
  360. package/src/core/sources/remote.ts +18 -2
  361. package/src/core/sources/resolve.ts +52 -33
  362. package/src/core/sources/sanity.ts +1 -0
  363. package/src/core/sources/strapi-blocks.ts +28 -19
  364. package/src/core/sources/strapi.ts +1 -0
  365. package/src/core/sources/types.ts +31 -0
  366. package/src/core/sources/watch.ts +20 -7
  367. package/src/core/standard-schema.ts +10 -6
  368. package/src/core/types.ts +9 -0
  369. package/src/core/ui-packs/ar.ts +13 -0
  370. package/src/core/ui-packs/bg.ts +13 -0
  371. package/src/core/ui-packs/bn.ts +13 -0
  372. package/src/core/ui-packs/ca.ts +13 -0
  373. package/src/core/ui-packs/cs.ts +13 -0
  374. package/src/core/ui-packs/da.ts +13 -0
  375. package/src/core/ui-packs/de.ts +13 -0
  376. package/src/core/ui-packs/el.ts +13 -0
  377. package/src/core/ui-packs/es.ts +13 -0
  378. package/src/core/ui-packs/fa.ts +13 -0
  379. package/src/core/ui-packs/fi.ts +13 -0
  380. package/src/core/ui-packs/fr.ts +13 -0
  381. package/src/core/ui-packs/he.ts +13 -0
  382. package/src/core/ui-packs/hi.ts +13 -0
  383. package/src/core/ui-packs/hr.ts +13 -0
  384. package/src/core/ui-packs/hu.ts +13 -0
  385. package/src/core/ui-packs/id.ts +13 -0
  386. package/src/core/ui-packs/it.ts +13 -0
  387. package/src/core/ui-packs/ja.ts +13 -0
  388. package/src/core/ui-packs/ko.ts +13 -0
  389. package/src/core/ui-packs/nl.ts +13 -0
  390. package/src/core/ui-packs/no.ts +13 -0
  391. package/src/core/ui-packs/pl.ts +13 -0
  392. package/src/core/ui-packs/pt-br.ts +13 -0
  393. package/src/core/ui-packs/pt.ts +13 -0
  394. package/src/core/ui-packs/ro.ts +13 -0
  395. package/src/core/ui-packs/ru.ts +13 -0
  396. package/src/core/ui-packs/sk.ts +13 -0
  397. package/src/core/ui-packs/sr.ts +13 -0
  398. package/src/core/ui-packs/sv.ts +13 -0
  399. package/src/core/ui-packs/th.ts +13 -0
  400. package/src/core/ui-packs/tr.ts +13 -0
  401. package/src/core/ui-packs/uk.ts +13 -0
  402. package/src/core/ui-packs/vi.ts +13 -0
  403. package/src/core/ui-packs/zh-tw.ts +13 -0
  404. package/src/core/ui-packs/zh.ts +13 -0
  405. package/src/core/version-cut.ts +69 -17
  406. package/src/core/versions.ts +4 -1
  407. package/src/deploy/adapters/node.ts +5 -2
  408. package/src/deploy/adapters/registry.ts +2 -1
  409. package/src/deploy/adapters/types.ts +13 -1
  410. package/src/deploy/artifacts.ts +52 -11
  411. package/src/deploy/cloudflare-negotiation.ts +27 -16
  412. package/src/deploy/headers.ts +75 -55
  413. package/src/deploy/node-headers.ts +148 -27
  414. package/src/deploy/platforms/cloudflare.ts +13 -3
  415. package/src/deploy/platforms/netlify.ts +83 -5
  416. package/src/deploy/platforms/node.ts +8 -5
  417. package/src/deploy/platforms/static.ts +2 -0
  418. package/src/deploy/platforms/types.ts +15 -0
  419. package/src/deploy/platforms/vercel.ts +10 -3
  420. package/src/deploy/redirects.ts +68 -21
  421. package/src/deploy/robots.ts +2 -2
  422. package/src/deploy/rss.ts +4 -3
  423. package/src/deploy/sitemap.ts +7 -5
  424. package/src/deploy/vercel-negotiation.ts +35 -3
  425. package/src/eval/agents.ts +10 -2
  426. package/src/eval/run.ts +25 -0
  427. package/src/markdown/base-links.ts +74 -29
  428. package/src/markdown/directives.ts +242 -36
  429. package/src/markdown/features.ts +17 -0
  430. package/src/markdown/index.ts +13 -9
  431. package/src/markdown/mdast.ts +5 -2
  432. package/src/markdown/relative-links.ts +15 -27
  433. package/src/markdown/route-snapshot.ts +37 -0
  434. package/src/og/card.ts +149 -9
  435. package/src/og/derive.ts +156 -5
  436. package/src/og/index.ts +1 -0
  437. package/src/openapi/asyncapi.ts +4 -2
  438. package/src/openapi/graphql-build.ts +28 -2
  439. package/src/openapi/model.ts +99 -27
  440. package/src/openapi/proxy.ts +63 -10
  441. package/src/openapi/render-mdx.ts +51 -3
  442. package/src/registry/eject.ts +297 -43
  443. package/src/search/adapters/version-scope.ts +30 -0
  444. package/src/search/documents.ts +89 -27
  445. package/src/search/popular.ts +2 -1
  446. package/src/search/sync/algolia.ts +36 -2
  447. package/src/seo/jsonld.ts +7 -3
  448. package/src/sources/registry.ts +5 -0
  449. package/src/theme/entry.ts +11 -4
  450. package/src/translate/agents.ts +6 -1
  451. package/src/translate/ledger.ts +26 -3
  452. package/src/translate/meta.ts +76 -24
  453. package/src/translate/run.ts +14 -8
  454. package/src/translate/validate.ts +14 -2
  455. package/src/translate/work-list.ts +91 -19
  456. package/src/upgrade/upgrade.ts +36 -4
  457. package/dist/cli/chunk-27g6wdth.js.map +0 -10
  458. package/dist/cli/chunk-2hn4b8z7.js.map +0 -12
  459. package/dist/cli/chunk-6crbhc3x.js.map +0 -14
  460. package/dist/cli/chunk-6hsn950k.js.map +0 -10
  461. package/dist/cli/chunk-79jhk4py.js.map +0 -35
  462. package/dist/cli/chunk-82bbrxdn.js +0 -51
  463. package/dist/cli/chunk-82bbrxdn.js.map +0 -10
  464. package/dist/cli/chunk-abh8yjkn.js +0 -31
  465. package/dist/cli/chunk-abh8yjkn.js.map +0 -10
  466. package/dist/cli/chunk-ce574jw2.js +0 -23
  467. package/dist/cli/chunk-ce574jw2.js.map +0 -10
  468. package/dist/cli/chunk-dh8cwk36.js.map +0 -10
  469. package/dist/cli/chunk-epjnccmv.js.map +0 -10
  470. package/dist/cli/chunk-fa25z98p.js.map +0 -11
  471. package/dist/cli/chunk-fs23ddbb.js.map +0 -35
  472. package/dist/cli/chunk-fz5wtpmh.js.map +0 -10
  473. package/dist/cli/chunk-jwyddg7y.js.map +0 -15
  474. package/dist/cli/chunk-kpf8rrjc.js.map +0 -19
  475. package/dist/cli/chunk-m3vmjgmq.js.map +0 -10
  476. package/dist/cli/chunk-mb2919y2.js.map +0 -10
  477. package/dist/cli/chunk-q5163e60.js.map +0 -11
  478. package/dist/cli/chunk-qkqwkpte.js.map +0 -182
  479. package/dist/cli/chunk-qs4q5p4e.js.map +0 -10
  480. package/dist/cli/chunk-s1p84fyh.js.map +0 -10
  481. package/dist/cli/chunk-wgm7m9qk.js.map +0 -36
  482. package/dist/cli/chunk-yt5n7ppj.js.map +0 -10
  483. package/dist/cli/chunk-yw7dm696.js.map +0 -14
  484. /package/dist/cli/{chunk-s6jhgk0q.js.map → chunk-2eytanqx.js.map} +0 -0
  485. /package/dist/cli/{chunk-hdpx1tax.js.map → chunk-2z928egk.js.map} +0 -0
  486. /package/dist/cli/{chunk-ah61y8py.js.map → chunk-364znk6q.js.map} +0 -0
  487. /package/dist/cli/{chunk-vtk4a6dg.js.map → chunk-8ktnccpt.js.map} +0 -0
  488. /package/dist/cli/{chunk-fxypxtvm.js.map → chunk-a58773jm.js.map} +0 -0
  489. /package/dist/cli/{chunk-wm7js3j9.js.map → chunk-b07cmahc.js.map} +0 -0
  490. /package/dist/cli/{chunk-zxcczpyx.js.map → chunk-r20tn01b.js.map} +0 -0
  491. /package/dist/cli/{chunk-5shv93fd.js.map → chunk-tkacnehg.js.map} +0 -0
  492. /package/dist/cli/{chunk-zxh4d9vy.js.map → chunk-tzmab476.js.map} +0 -0
@@ -31,7 +31,7 @@ import {
31
31
  hasDeferrableGroups,
32
32
  navVariants,
33
33
  } from "../components/layout/nav-utils.ts";
34
- import { normalizeBasePath } from "../core/base-path.ts";
34
+ import { normalizeBasePath, withBasePath } from "../core/base-path.ts";
35
35
  import { validateUsedComponents } from "../core/component-diagnostics.ts";
36
36
  import {
37
37
  analyzeComponentOverrides,
@@ -81,7 +81,9 @@ import { ogCacheDir } from "../og/cache.ts";
81
81
  import { missingFontFiles, resolveOgFonts } from "../og/derive.ts";
82
82
  import type { DerivedOgFonts } from "../og/derive.ts";
83
83
  import { resolveOgLogo } from "../og/logo.ts";
84
- import type { ApiSpecData, OpenApiData } from "../openapi/model.ts";
84
+ import type { AsyncApiSpecValue } from "../openapi/asyncapi.ts";
85
+ import type { ApiSpecData, HttpMethod, OpenApiData } from "../openapi/model.ts";
86
+ import { HTTP_METHODS, withServerDefaults } from "../openapi/model.ts";
85
87
  import {
86
88
  builtinProxyReferences,
87
89
  hasScalarReferences,
@@ -954,6 +956,8 @@ const resolveBanner = (config: ResolvedConfig): BlumeBanner | null => {
954
956
  if (isStringShorthand(banner)) {
955
957
  return { content: banner, dismissible: false, key: banner };
956
958
  }
959
+ // `link.href` stays as authored; the link the banner renders is the
960
+ // navigation's `bannerHref`, localized and mounted under `basePath`.
957
961
  return {
958
962
  content: banner.content,
959
963
  dismissible: banner.dismissible,
@@ -1489,28 +1493,41 @@ const writeApiFiles = async (
1489
1493
  await Promise.all(writes);
1490
1494
  };
1491
1495
 
1496
+ /** The proxy endpoint's file under the Astro `src/` dir (eject writes it too). */
1497
+ export const PLAYGROUND_PROXY_ENTRY = join("blume-openapi", "api-proxy.ts");
1498
+
1499
+ /** The URL the built-in proxy is injected at (see {@link planPlaygroundProxy}). */
1500
+ export const playgroundProxyPattern = (config: ResolvedConfig): string =>
1501
+ withBasePath(config.basePath, "/_api-proxy");
1502
+
1492
1503
  /**
1493
1504
  * Decide whether to generate the playground's built-in CORS proxy endpoint.
1494
1505
  * Only the Blume renderer's playground with `proxy: true` needs it — a proxy
1495
1506
  * URL string points at an external service, and `false` sends requests
1496
1507
  * directly. Injected at `/_api-proxy` (rather than written under `pages/`)
1497
1508
  * because Astro treats `_`-prefixed page files as private; the endpoint's own
1498
- * `prerender = false` export wins over the injection default.
1509
+ * `prerender = false` export wins over the injection default. Mounted under
1510
+ * the site `basePath`, at the URL the playground sends to (see
1511
+ * openapi/source.ts); Astro layers `deployment.base` onto both.
1499
1512
  */
1500
1513
  const planPlaygroundProxy = (config: ResolvedConfig, srcDir: string) => ({
1501
1514
  enabled: needsPlaygroundProxy(config),
1502
- entrypoint: join(srcDir, "blume-openapi", "api-proxy.ts"),
1503
- pattern: "/_api-proxy",
1515
+ entrypoint: join(srcDir, PLAYGROUND_PROXY_ENTRY),
1516
+ pattern: playgroundProxyPattern(config),
1504
1517
  });
1505
1518
 
1506
1519
  /**
1507
1520
  * The origins the built-in proxy is allowed to reach: one per absolute
1508
- * `servers[].url` across the parsed specs. This is the endpoint's whole trust
1509
- * boundary — the client sends the target as a query parameter, and a reader's
1510
- * custom base URL is not a documented server — so it is derived here, at build
1511
- * time, from the same documents the operation pages render.
1521
+ * `servers[].url` across the parsed specs — the document's, each path item's,
1522
+ * and each operation's, since an operation's playground sends to the most
1523
+ * specific of those. This is the endpoint's whole trust boundary — the client
1524
+ * sends the target as a query parameter, and a reader's custom base URL is not
1525
+ * a documented server — so it is derived here, at build time, from the same
1526
+ * documents the operation pages render.
1512
1527
  *
1513
- * Relative (`/v1`) and templated (`{env}.api.example.com`) server URLs carry no
1528
+ * A templated URL (`https://{env}.api.example.com`) is allowed as the
1529
+ * playground sends it, with each variable at its declared default. Relative
1530
+ * (`/v1`) URLs, and templates with a variable left undefaulted, carry no
1514
1531
  * origin to allow and are skipped; AsyncAPI documents declare `servers` as a
1515
1532
  * map and contribute nothing (the proxy is OpenAPI-only).
1516
1533
  */
@@ -1526,27 +1543,53 @@ const specOriginsOf = (spec: ApiSpecData): string[] => {
1526
1543
  }
1527
1544
  }
1528
1545
  // SAFETY: `document` is arbitrary parsed JSON; the assertion only names
1529
- // the optional `servers` shape, and every access below re-checks it —
1530
- // `Array.isArray(servers)` guards the list and `server.url ?? ""` the url.
1531
- const { servers } = spec.document as { servers?: { url?: string }[] };
1532
- for (const server of Array.isArray(servers) ? servers : []) {
1533
- const url = server.url ?? "";
1534
- // `new URL("https://{region}.api.example.com")` parses — the braces land
1535
- // in the hostname — so templated URLs need an explicit check or their
1536
- // junk literal becomes an allowlist entry no real request can match.
1537
- if (url.includes("{")) {
1538
- continue;
1546
+ // the optional `servers` and `paths` shapes, and every access below
1547
+ // re-checks them — `Array.isArray` guards each list, optional chaining each
1548
+ // path item and operation, and `server.url ?? ""` the url.
1549
+ const document = spec.document as {
1550
+ paths?: Record<string, DocumentPathItem | null | undefined>;
1551
+ servers?: DocumentServer[];
1552
+ };
1553
+ const lists = [document.servers];
1554
+ for (const item of Object.values(document.paths ?? {})) {
1555
+ lists.push(item?.servers);
1556
+ for (const method of HTTP_METHODS) {
1557
+ lists.push(item?.[method]?.servers);
1539
1558
  }
1540
- try {
1541
- origins.add(new URL(url).origin);
1542
- } catch {
1543
- // Not an absolute URL: nothing to allow.
1559
+ }
1560
+ for (const servers of lists) {
1561
+ for (const server of Array.isArray(servers) ? servers : []) {
1562
+ const url = withServerDefaults(server.url ?? "", server.variables);
1563
+ // `new URL("https://{region}.api.example.com")` parses — the braces land
1564
+ // in the hostname — so a variable with no default needs an explicit
1565
+ // check or its junk literal becomes an allowlist entry no real request
1566
+ // can match.
1567
+ if (url.includes("{")) {
1568
+ continue;
1569
+ }
1570
+ try {
1571
+ origins.add(new URL(url).origin);
1572
+ } catch {
1573
+ // Not an absolute URL: nothing to allow.
1574
+ }
1544
1575
  }
1545
1576
  }
1546
1577
  return [...origins];
1547
1578
  };
1548
1579
 
1549
- const specOrigins = (data: OpenApiData): string[] =>
1580
+ /** A `servers[]` entry as a parsed spec document declares it. */
1581
+ interface DocumentServer {
1582
+ url?: string;
1583
+ variables?: AsyncApiSpecValue;
1584
+ }
1585
+
1586
+ /** An OpenAPI path item: its own `servers` plus one per operation. */
1587
+ type DocumentPathItem = { servers?: DocumentServer[] } & Partial<
1588
+ Record<HttpMethod, { servers?: DocumentServer[] } | null>
1589
+ >;
1590
+
1591
+ /** The built-in proxy's allowlist: every spec's origins, deduped and sorted. */
1592
+ export const specOrigins = (data: OpenApiData): string[] =>
1550
1593
  [...new Set(Object.values(data).flatMap(specOriginsOf))].toSorted();
1551
1594
 
1552
1595
  /**
@@ -1558,7 +1601,7 @@ const specOrigins = (data: OpenApiData): string[] =>
1558
1601
  * whose own origins came out empty (no absolute `servers[].url`, no absolute
1559
1602
  * GraphQL `endpoint`) gets a warning naming it.
1560
1603
  */
1561
- const proxyAllowlistWarnings = (
1604
+ export const proxyAllowlistWarnings = (
1562
1605
  config: ResolvedConfig,
1563
1606
  data: OpenApiData
1564
1607
  ): string[] => {
@@ -1573,7 +1616,7 @@ const proxyAllowlistWarnings = (
1573
1616
  warnings.push(
1574
1617
  spec.kind === "graphql"
1575
1618
  ? `The "${spec.label}" GraphQL reference (${spec.route}) has playground.proxy: true, but no absolute endpoint is configured for it, so the built-in proxy has no origin to allow and will refuse every request its playground sends. Set \`endpoint\` on the graphql() adapter (or the source) to the live GraphQL URL, or point playground.proxy at an external proxy URL.`
1576
- : `The "${spec.label}" reference (${spec.route}) has playground.proxy: true, but its spec declares no absolute servers[].url (relative and templated URLs carry no origin), so the built-in proxy has no origin to allow and will refuse every request its playground sends. Add an absolute server URL to the spec, or point playground.proxy at an external proxy URL.`
1619
+ : `The "${spec.label}" reference (${spec.route}) has playground.proxy: true, but its spec declares no absolute servers[].url (relative URLs, and templated ones with a variable missing its default, carry no origin), so the built-in proxy has no origin to allow and will refuse every request its playground sends. Add an absolute server URL to the spec, or point playground.proxy at an external proxy URL.`
1577
1620
  );
1578
1621
  }
1579
1622
  return warnings;
@@ -1699,6 +1742,7 @@ export const analyzeComponentsFile = async (
1699
1742
  const projectOgFonts = (project: BlumeProject): DerivedOgFonts =>
1700
1743
  resolveOgFonts(
1701
1744
  {
1745
+ locales: fontLocaleCodes(project.config.i18n),
1702
1746
  ogFonts: project.config.seo.og.fonts,
1703
1747
  themeFonts: project.config.theme.fonts,
1704
1748
  themeFontsConfigured: project.themeFontsConfigured,
@@ -1843,7 +1887,7 @@ export const generateRuntime = async (
1843
1887
  // `astro.config.mjs` — which would restart the dev server in place.
1844
1888
  const contentRoutes = markdownRoutePaths(project);
1845
1889
  const homeLinkHeader =
1846
- buildHomeLinkHeader(config, contentRoutes) ?? undefined;
1890
+ buildHomeLinkHeader(config, contentRoutes, "dev") ?? undefined;
1847
1891
 
1848
1892
  const assistantEnabled = config.ai.assistant?.enabled ?? false;
1849
1893
  const exportPdf = config.export.pdf;
@@ -1,5 +1,7 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
 
3
+ import { resolve } from "pathe";
4
+
3
5
  import { refreshBlumeContent } from "./integration.ts";
4
6
 
5
7
  /**
@@ -18,12 +20,21 @@ import { refreshBlumeContent } from "./integration.ts";
18
20
  */
19
21
 
20
22
  /** The Vite module-graph slice the plugin touches (structurally typed, like
21
- * every Blume-authored Vite plugin — see `serverAppResolvePlugin`). */
23
+ * every Blume-authored Vite plugin — see `serverAppResolvePlugin`). Every
24
+ * environment keeps its own graph, and the server's own `moduleGraph` only
25
+ * covers `client` and `ssr`: with `@astrojs/cloudflare` the content pages
26
+ * render in the `prerender` environment instead. */
22
27
  interface IncludeHmrServer {
23
- moduleGraph: {
24
- getModulesByFile: (file: string) => Set<unknown> | undefined;
25
- invalidateModule: (mod: never) => void;
26
- };
28
+ config: { root: string };
29
+ environments: Record<
30
+ string,
31
+ {
32
+ moduleGraph: {
33
+ getModulesByFile: (file: string) => Set<unknown> | undefined;
34
+ invalidateModule: (mod: never) => void;
35
+ };
36
+ }
37
+ >;
27
38
  ws: { send: (payload: { type: "full-reload" }) => void };
28
39
  }
29
40
 
@@ -51,16 +62,24 @@ export const includeHmrPlugin = (graphPath: string): IncludeHmrPlugin => ({
51
62
  // No graph yet (first run) — nothing to invalidate.
52
63
  return;
53
64
  }
54
- const includers = graph[ctx.file];
65
+ // An ejected app's graph is relative to the project, its Vite root; the
66
+ // hidden runtime's is absolute, which resolving leaves as it is.
67
+ const { config, environments, ws } = ctx.server;
68
+ const includers = Object.entries(graph).find(
69
+ ([partial]) => resolve(config.root, partial) === ctx.file
70
+ )?.[1];
55
71
  if (!includers || includers.length === 0) {
56
72
  return;
57
73
  }
58
- const { moduleGraph, ws } = ctx.server;
59
- for (const includer of includers) {
60
- for (const mod of moduleGraph.getModulesByFile(includer) ?? []) {
61
- // SAFETY: the module came out of this module graph; `never` only
62
- // reflects that the structural slice doesn't model the node type.
63
- moduleGraph.invalidateModule(mod as never);
74
+ for (const { moduleGraph } of Object.values(environments)) {
75
+ for (const includer of includers) {
76
+ for (const mod of moduleGraph.getModulesByFile(
77
+ resolve(config.root, includer)
78
+ ) ?? []) {
79
+ // SAFETY: the module came out of this module graph; `never` only
80
+ // reflects that the structural slice doesn't model the node type.
81
+ moduleGraph.invalidateModule(mod as never);
82
+ }
64
83
  }
65
84
  }
66
85
  // Plain `.md` pages have no Vite module: their HTML lives in the
@@ -29,7 +29,10 @@ type StoreEntry = ReturnType<LoaderContext["store"]["entries"]>[number][1];
29
29
  /** Meta-store key holding the recorded partials digest of an includer. */
30
30
  const stateKey = (id: string): string => `blume:include-state:${id}`;
31
31
 
32
- /** The generated include graph: absolute partial → absolute including pages. */
32
+ /**
33
+ * The generated include graph: partial → including pages, absolute in the
34
+ * hidden runtime and relative to the project root in an ejected app.
35
+ */
33
36
  type IncludeGraph = Record<string, string[]>;
34
37
 
35
38
  /**
@@ -47,14 +50,22 @@ const readGraph = (graphPath: string): IncludeGraph => {
47
50
  }
48
51
  };
49
52
 
50
- /** Invert the graph: absolute including page → its partials, sorted. */
51
- const partialsByPage = (graph: IncludeGraph): Map<string, string[]> => {
53
+ /**
54
+ * Invert the graph: absolute including page → its absolute partials, sorted.
55
+ * Each path resolves against the Astro root, which leaves the hidden runtime's
56
+ * absolute paths as they are.
57
+ */
58
+ const partialsByPage = (
59
+ graph: IncludeGraph,
60
+ root: string
61
+ ): Map<string, string[]> => {
52
62
  const byPage = new Map<string, string[]>();
53
63
  for (const [partial, pages] of Object.entries(graph)) {
54
64
  for (const page of pages) {
55
- const partials = byPage.get(page) ?? [];
56
- partials.push(partial);
57
- byPage.set(page, partials);
65
+ const abs = resolve(root, page);
66
+ const partials = byPage.get(abs) ?? [];
67
+ partials.push(resolve(root, partial));
68
+ byPage.set(abs, partials);
58
69
  }
59
70
  }
60
71
  for (const partials of byPage.values()) {
@@ -123,11 +134,11 @@ export const evictStaleIncluders = (
123
134
  context: LoaderContext,
124
135
  graphPath: string
125
136
  ): string[] => {
126
- const byPage = partialsByPage(readGraph(graphPath));
137
+ const root = fileURLToPath(context.config.root);
138
+ const byPage = partialsByPage(readGraph(graphPath), root);
127
139
  if (byPage.size === 0) {
128
140
  return [];
129
141
  }
130
- const root = fileURLToPath(context.config.root);
131
142
  const evicted: string[] = [];
132
143
  for (const [id, entry] of context.store.entries()) {
133
144
  if (evictIfStale(context, byPage, root, id, entry)) {
@@ -28,13 +28,40 @@ interface OverlayErrorPayload {
28
28
 
29
29
  /** The dev server's HMR channel — either `.ws` (Vite ≤5) or `.hot` (Vite 6+). */
30
30
  interface OverlayChannel {
31
- send: (payload: OverlayErrorPayload) => void;
31
+ send: (payload: OverlayErrorPayload | { type: "full-reload" }) => void;
32
32
  }
33
- interface OverlayServer {
33
+
34
+ /** A node in a Vite environment's module graph; only its identity matters. */
35
+ interface DevModuleNode {
36
+ id: string | null;
37
+ }
38
+
39
+ /**
40
+ * The dev-server slice the integration keeps: the overlay channel, and every
41
+ * Vite environment's module graph for the content re-sync (structurally
42
+ * typed, like every Blume-authored Vite plugin).
43
+ */
44
+ interface DevServer {
45
+ environments: Record<
46
+ string,
47
+ {
48
+ moduleGraph: {
49
+ getModuleById: (id: string) => DevModuleNode | undefined;
50
+ invalidateModule: (mod: never) => void;
51
+ };
52
+ }
53
+ >;
34
54
  hot?: OverlayChannel;
35
55
  ws?: OverlayChannel;
36
56
  }
37
57
 
58
+ /** The dev server's file watcher, as far as the data-store watch uses it. */
59
+ interface WatchedServer {
60
+ watcher: {
61
+ on: (event: "add" | "change", listener: (path: string) => void) => void;
62
+ };
63
+ }
64
+
38
65
  /** The parameters Astro hands `astro:server:setup`. */
39
66
  type ServerSetupParams = Parameters<
40
67
  NonNullable<AstroIntegration["hooks"]["astro:server:setup"]>
@@ -74,7 +101,7 @@ interface DevNegotiation {
74
101
  interface DevServerRegistry {
75
102
  buildProject: BlumeProject | null;
76
103
  negotiation: DevNegotiation | null;
77
- overlay: OverlayServer | null;
104
+ overlay: DevServer | null;
78
105
  refreshContent: RefreshContent | null;
79
106
  }
80
107
 
@@ -155,6 +182,64 @@ export const publishDevNegotiation = (
155
182
  : null;
156
183
  };
157
184
 
185
+ /** A dev server's HMR channel: `.ws`, or `.hot` where only that exists. */
186
+ const overlayChannelOf = (
187
+ server: DevServer | null
188
+ ): OverlayChannel | undefined => server?.ws ?? server?.hot;
189
+
190
+ /** Astro's content-layer data store, a virtual module in each environment. */
191
+ const DATA_STORE_MODULE_ID = "\0astro:data-layer-content";
192
+
193
+ /**
194
+ * Invalidate Astro's data store in every Vite environment, then reload the
195
+ * browser. Astro invalidates it after a sync in the `ssr` environment only,
196
+ * which is where pages render on most adapters. With `@astrojs/cloudflare`
197
+ * the `ssr` environment runs in workerd and the prerendered pages (every
198
+ * content page) render in the `prerender` environment, whose copy of the
199
+ * store then stayed at its startup snapshot: a page added in dev resolved
200
+ * its route but `getEntry` still missed it, so it 404ed until a restart.
201
+ */
202
+ const invalidateDataStore = (server: DevServer): void => {
203
+ for (const { moduleGraph } of Object.values(server.environments)) {
204
+ const mod = moduleGraph.getModuleById(DATA_STORE_MODULE_ID);
205
+ if (mod) {
206
+ // SAFETY: the node came out of this same module graph; `never` only
207
+ // reflects that the structural slice doesn't model the node type.
208
+ moduleGraph.invalidateModule(mod as never);
209
+ }
210
+ }
211
+ // Astro reloaded the browser when the store was written, which can land
212
+ // before the invalidation above; reload again so no page renders the
213
+ // stale copy.
214
+ overlayChannelOf(server)?.send({ type: "full-reload" });
215
+ };
216
+
217
+ /**
218
+ * Where Astro keeps the dev content store: `data-store.json` in the root's
219
+ * `.astro/` directory. Astro reloads its virtual module from this file.
220
+ */
221
+ const devDataStoreFile = (root: URL): string =>
222
+ fileURLToPath(new URL(".astro/data-store.json", root));
223
+
224
+ /**
225
+ * Invalidate the data store in every environment whenever Astro rewrites it.
226
+ * Astro's own file watcher updates the store on a content edit — a `.md`
227
+ * page's rendered body lives in it — but invalidates the module in `ssr`
228
+ * only, so with `@astrojs/cloudflare` a body edit stayed stale in
229
+ * `prerender` until something structural re-synced it. Keyed on the store
230
+ * write rather than Blume's regeneration, which can finish first.
231
+ */
232
+ const watchDataStore = (server: DevServer & WatchedServer, root: URL): void => {
233
+ const file = devDataStoreFile(root);
234
+ const onWrite = (path: string): void => {
235
+ if (path === file) {
236
+ invalidateDataStore(server);
237
+ }
238
+ };
239
+ server.watcher.on("add", onWrite);
240
+ server.watcher.on("change", onWrite);
241
+ };
242
+
158
243
  /**
159
244
  * Re-run Astro's content-layer loaders against the live dev server. Returns
160
245
  * `false` when no server has registered one — before the first
@@ -165,20 +250,21 @@ export const publishDevNegotiation = (
165
250
  * stale store and the moved page 404s.
166
251
  */
167
252
  export const refreshBlumeContent = async (): Promise<boolean> => {
168
- const { refreshContent } = registry();
253
+ const { overlay, refreshContent } = registry();
169
254
  if (!refreshContent) {
170
255
  return false;
171
256
  }
172
257
  // No loader filter: every collection re-syncs (the docs glob and any
173
258
  // staged collection alike).
174
259
  await refreshContent({});
260
+ if (overlay) {
261
+ invalidateDataStore(overlay);
262
+ }
175
263
  return true;
176
264
  };
177
265
 
178
- const overlayChannel = (): OverlayChannel | undefined => {
179
- const { overlay } = registry();
180
- return overlay?.ws ?? overlay?.hot;
181
- };
266
+ const overlayChannel = (): OverlayChannel | undefined =>
267
+ overlayChannelOf(registry().overlay);
182
268
 
183
269
  /**
184
270
  * Surface Blume's own diagnostics (config/frontmatter/content errors) in the
@@ -237,8 +323,9 @@ export interface BlumeIntegrationOptions {
237
323
  /**
238
324
  * Homepage `Link` header value for agent discovery (see
239
325
  * `ai/link-headers.ts`); the dev-server counterpart of the `_headers` /
240
- * Vercel-config emission, so `curl -I` against `blume dev` shows what the
241
- * deployed site will send. Published the same way as `contentRoutes`.
326
+ * Vercel-config emission, so `curl -I` against `blume dev` shows it too —
327
+ * built for the `"dev"` surface, which leaves out the targets only a build
328
+ * writes. Published the same way as `contentRoutes`.
242
329
  */
243
330
  homeLinkHeader?: string;
244
331
  /**
@@ -279,7 +366,8 @@ const isHomeUrl = (rawUrl: string | undefined): boolean => {
279
366
  * variant falls back to the synthesized llms.txt mirror when it's a landing
280
367
  * page (see `markdownRoutePaths`). The same
281
368
  * middleware also stamps the homepage agent-discovery `Link` header, mirroring
282
- * what the deployed site sends via `_headers` / the Vercel routing config.
369
+ * what the deployed site sends via `_headers` / the Vercel routing config
370
+ * minus the targets the dev server doesn't serve.
283
371
  *
284
372
  * Request URLs arrive base-less: Astro unshifts its own dev middlewares (base,
285
373
  * trailing slash, route guard) ahead of this one from its post-`configureServer`
@@ -351,9 +439,11 @@ export const blumeIntegration = (
351
439
  return;
352
440
  }
353
441
  // `dir` is what Astro reports as the client output — `dist/`, or
354
- // `dist/client` for a server build — which is what the platform
355
- // serves (the Vercel adapter copies it into its Build Output static
356
- // tree in a later hook).
442
+ // `dist/client` for a server build (`dist/client/<base>/` once
443
+ // `@astrojs/cloudflare` moves it under the base, whose `_headers` the
444
+ // writer places at the root served above it) — which is what the
445
+ // platform serves (the Vercel adapter copies it into its Build Output
446
+ // static tree in a later hook).
357
447
  await publishBuildArtifacts(project, fileURLToPath(dir), logger);
358
448
  },
359
449
  "astro:config:done": ({ config, injectTypes }) => {
@@ -399,6 +489,10 @@ export const blumeIntegration = (
399
489
  const shared = registry();
400
490
  shared.overlay = server;
401
491
  shared.refreshContent = refreshContent ?? null;
492
+ // `astro:config:done` always runs first; the root locates the store.
493
+ if (astroRoot) {
494
+ watchDataStore(server, astroRoot);
495
+ }
402
496
  // Prepend so the rewrite happens before Astro's own request handler,
403
497
  // letting the rewritten URL resolve to the `.md` endpoint.
404
498
  server.middlewares.stack.unshift({
@@ -8,11 +8,11 @@
8
8
  * generated pages through aliases or relative paths. `generateRuntime` now
9
9
  * publishes them here and `runtimeModulesPlugin` serves them to Vite as
10
10
  * virtual modules, so a regeneration never round-trips the disk or waits on a
11
- * file watcher: publishing invalidates the changed modules in every live dev
12
- * server (Vite walks the importers, so the pages that render them re-evaluate
13
- * on the next request) and asks the browser to reload — the same effect a JSON
14
- * file change used to reach through the watcher, minus the write and the
15
- * watch debounce.
11
+ * file watcher: publishing invalidates the changed modules in every Vite
12
+ * environment of every live dev server (Vite walks the importers, so the
13
+ * pages that render them re-evaluate on the next request) and asks the
14
+ * browser to reload — the same effect a JSON file change used to reach
15
+ * through the watcher, minus the write and the watch debounce.
16
16
  *
17
17
  * The registry hangs off `globalThis`, not module state. On a published
18
18
  * install the CLI bundle (`dist/cli`) carries its own copy of this module,
@@ -61,18 +61,29 @@ interface RuntimeModuleNode {
61
61
  id: string | null;
62
62
  }
63
63
 
64
+ /** One Vite environment's module graph, as far as the registry touches it. */
65
+ interface RuntimeModuleGraph {
66
+ getModuleById: (id: string) => RuntimeModuleNode | undefined;
67
+ invalidateModule: (mod: RuntimeModuleNode) => void;
68
+ }
69
+
64
70
  /**
65
71
  * The Vite dev-server slice the registry touches (structurally typed, like
66
72
  * every Blume-authored Vite plugin — see `includeHmrPlugin`).
67
73
  */
68
74
  export interface RuntimeModuleServer {
75
+ /**
76
+ * Every Vite environment, each with its own module graph. `moduleGraph` on
77
+ * the server itself is Vite's backward-compatible view of only `client`
78
+ * and `ssr`, and those are not the only environments that render: with
79
+ * `@astrojs/cloudflare` the `ssr` environment runs in workerd, so Astro
80
+ * renders the prerendered pages (every content page) in its `prerender`
81
+ * environment instead.
82
+ */
83
+ environments: Record<string, { moduleGraph: RuntimeModuleGraph }>;
69
84
  httpServer?: {
70
85
  once: (event: "close", listener: () => void) => void;
71
86
  } | null;
72
- moduleGraph: {
73
- getModuleById: (id: string) => RuntimeModuleNode | undefined;
74
- invalidateModule: (mod: RuntimeModuleNode) => void;
75
- };
76
87
  ws: { send: (payload: { type: "full-reload" }) => void };
77
88
  }
78
89
 
@@ -131,10 +142,14 @@ export const publishRuntimeModules = (
131
142
  return changed;
132
143
  }
133
144
  for (const server of servers) {
134
- for (const id of changed) {
135
- const mod = server.moduleGraph.getModuleById(`${RESOLVED_PREFIX}${id}`);
136
- if (mod) {
137
- server.moduleGraph.invalidateModule(mod);
145
+ // Every environment that loaded a module holds its own copy, and any of
146
+ // them may be the one rendering pages on this adapter.
147
+ for (const { moduleGraph } of Object.values(server.environments)) {
148
+ for (const id of changed) {
149
+ const mod = moduleGraph.getModuleById(`${RESOLVED_PREFIX}${id}`);
150
+ if (mod) {
151
+ moduleGraph.invalidateModule(mod);
152
+ }
138
153
  }
139
154
  }
140
155
  server.ws.send({ type: "full-reload" });