blume 2.1.3 → 2.2.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/AGENTS.md +1 -1
  2. package/CHANGELOG.md +134 -0
  3. package/README.md +4 -4
  4. package/dist/cli/{chunk-y4msrwm9.js → chunk-080mfch6.js} +15 -15
  5. package/dist/cli/{chunk-gfc5fqvf.js → chunk-1ch3ct3r.js} +18 -18
  6. package/dist/cli/{chunk-gfc5fqvf.js.map → chunk-1ch3ct3r.js.map} +1 -1
  7. package/dist/cli/{chunk-32khwcb6.js → chunk-2ssh60qh.js} +42 -69
  8. package/dist/cli/{chunk-32khwcb6.js.map → chunk-2ssh60qh.js.map} +9 -10
  9. package/dist/cli/{chunk-9qzgg6mj.js → chunk-34wap3ed.js} +3 -3
  10. package/dist/cli/{chunk-k3gr1nm9.js → chunk-40vk8kgf.js} +13 -16
  11. package/dist/cli/{chunk-k3gr1nm9.js.map → chunk-40vk8kgf.js.map} +3 -3
  12. package/dist/cli/chunk-487b95qy.js +329 -0
  13. package/dist/cli/chunk-487b95qy.js.map +13 -0
  14. package/dist/cli/chunk-4zxh25hc.js +157 -0
  15. package/dist/cli/chunk-4zxh25hc.js.map +10 -0
  16. package/dist/cli/chunk-69rjf786.js +146 -0
  17. package/dist/cli/chunk-69rjf786.js.map +11 -0
  18. package/dist/cli/{chunk-dqhg54mr.js → chunk-9h8mh2tk.js} +3 -3
  19. package/dist/cli/{chunk-dqhg54mr.js.map → chunk-9h8mh2tk.js.map} +2 -2
  20. package/dist/cli/{chunk-bqxaqn5p.js → chunk-9jqmq5hv.js} +8 -8
  21. package/dist/cli/{chunk-bqxaqn5p.js.map → chunk-9jqmq5hv.js.map} +1 -1
  22. package/dist/cli/{chunk-dv2z0rzf.js → chunk-aafhwx74.js} +38 -13
  23. package/dist/cli/{chunk-dv2z0rzf.js.map → chunk-aafhwx74.js.map} +5 -4
  24. package/dist/cli/{chunk-desvm72z.js → chunk-b680sv30.js} +22 -26
  25. package/dist/cli/chunk-b680sv30.js.map +13 -0
  26. package/dist/cli/{chunk-0w6hs479.js → chunk-beb0z983.js} +30 -18
  27. package/dist/cli/chunk-beb0z983.js.map +10 -0
  28. package/dist/cli/{chunk-1f882900.js → chunk-c5bjxq8v.js} +2 -2
  29. package/dist/cli/chunk-e1n41w9m.js +155 -0
  30. package/dist/cli/chunk-e1n41w9m.js.map +11 -0
  31. package/dist/cli/{chunk-sc93apfs.js → chunk-etckvbrh.js} +17 -17
  32. package/dist/cli/{chunk-sc93apfs.js.map → chunk-etckvbrh.js.map} +2 -2
  33. package/dist/cli/{chunk-bxwhtvw8.js → chunk-f260jdrn.js} +5 -5
  34. package/dist/cli/{chunk-74wfhv79.js → chunk-fnxgtjeq.js} +1 -1
  35. package/dist/cli/{chunk-ms9z66nz.js → chunk-gwy55cx3.js} +2 -2
  36. package/dist/cli/{chunk-ms9z66nz.js.map → chunk-gwy55cx3.js.map} +2 -2
  37. package/dist/cli/{chunk-dhwtqnnd.js → chunk-gxhcf2hs.js} +2 -2
  38. package/dist/cli/{chunk-tfycddbt.js → chunk-h0qwdbem.js} +2 -2
  39. package/dist/cli/{chunk-nbyvgnnm.js → chunk-hktq4j5r.js} +109 -49
  40. package/dist/cli/chunk-hktq4j5r.js.map +36 -0
  41. package/dist/cli/{chunk-twy7psdz.js → chunk-hn3g2b89.js} +14 -10
  42. package/dist/cli/chunk-hn3g2b89.js.map +10 -0
  43. package/dist/cli/{chunk-j1x92mx9.js → chunk-j4nfgn2j.js} +1 -1
  44. package/dist/cli/{chunk-dpy8qdjf.js → chunk-k45sq16q.js} +74 -183
  45. package/dist/cli/chunk-k45sq16q.js.map +18 -0
  46. package/dist/cli/{chunk-5cmts6dv.js → chunk-kmswjxj9.js} +7 -7
  47. package/dist/cli/{chunk-5cmts6dv.js.map → chunk-kmswjxj9.js.map} +1 -1
  48. package/dist/cli/{chunk-36bxs9c7.js → chunk-nqna8dzp.js} +27 -23
  49. package/dist/cli/chunk-nqna8dzp.js.map +10 -0
  50. package/dist/cli/{chunk-a57yh0h9.js → chunk-p834rkcn.js} +7 -7
  51. package/dist/cli/{chunk-a57yh0h9.js.map → chunk-p834rkcn.js.map} +1 -1
  52. package/dist/cli/{chunk-60hz265r.js → chunk-ppa6mg4d.js} +24 -11
  53. package/dist/cli/chunk-ppa6mg4d.js.map +10 -0
  54. package/dist/cli/{chunk-s0bdnc7v.js → chunk-q1wwrxzw.js} +5 -4
  55. package/dist/cli/chunk-q1wwrxzw.js.map +10 -0
  56. package/dist/cli/{chunk-ave1wcbr.js → chunk-qcyxkj44.js} +1 -1
  57. package/dist/cli/{chunk-124fvmzq.js → chunk-r5nm9w0c.js} +11183 -8806
  58. package/dist/cli/{chunk-124fvmzq.js.map → chunk-r5nm9w0c.js.map} +82 -66
  59. package/dist/cli/{chunk-ckrcm08h.js → chunk-rtvgmg2j.js} +48 -62
  60. package/dist/cli/{chunk-ckrcm08h.js.map → chunk-rtvgmg2j.js.map} +4 -5
  61. package/dist/cli/{chunk-297ajdcv.js → chunk-t2h8pwy1.js} +6 -6
  62. package/dist/cli/{chunk-297ajdcv.js.map → chunk-t2h8pwy1.js.map} +3 -3
  63. package/dist/cli/{chunk-gxtg6ed2.js → chunk-x60e15st.js} +21 -21
  64. package/dist/cli/{chunk-gxtg6ed2.js.map → chunk-x60e15st.js.map} +3 -3
  65. package/dist/cli/{chunk-58bt3hag.js → chunk-x7bh6d9j.js} +4 -4
  66. package/dist/cli/{chunk-hvcxng5f.js → chunk-ydqzkhjn.js} +6 -6
  67. package/dist/cli/{chunk-kdpzw38a.js → chunk-yg8ckc3g.js} +95 -7
  68. package/dist/cli/chunk-yg8ckc3g.js.map +11 -0
  69. package/dist/cli/{chunk-0pmb4tr4.js → chunk-zxd5jrgx.js} +1205 -222
  70. package/dist/cli/chunk-zxd5jrgx.js.map +44 -0
  71. package/dist/cli/index.js +78 -42
  72. package/dist/cli/index.js.map +7 -6
  73. package/dist/types/ai/changelog-components.d.ts +18 -0
  74. package/dist/types/ai/changelog-markdown.d.ts +12 -0
  75. package/dist/types/ai/openapi-components.d.ts +7 -7
  76. package/dist/types/ai/operation-details.d.ts +39 -0
  77. package/dist/types/ai/relative-links.d.ts +3 -1
  78. package/dist/types/ai/serializers.d.ts +6 -4
  79. package/dist/types/analytics/head.d.ts +4 -2
  80. package/dist/types/analytics/script.d.ts +8 -2
  81. package/dist/types/components/content/youtube.d.ts +20 -0
  82. package/dist/types/components/layout/nav-utils.d.ts +8 -0
  83. package/dist/types/components/openapi/example-panels.d.ts +33 -0
  84. package/dist/types/components/openapi/helpers.d.ts +53 -11
  85. package/dist/types/components/openapi/snippets.d.ts +7 -1
  86. package/dist/types/core/alert-diagnostics.d.ts +10 -0
  87. package/dist/types/core/builtin-tags.d.ts +16 -0
  88. package/dist/types/core/changelog-list.d.ts +48 -0
  89. package/dist/types/core/closest-match.d.ts +6 -0
  90. package/dist/types/core/code-fence-diagnostics.d.ts +18 -0
  91. package/dist/types/core/code-fences.d.ts +8 -1
  92. package/dist/types/core/config-input.d.ts +38 -6
  93. package/dist/types/core/content-assets.d.ts +16 -18
  94. package/dist/types/core/data.d.ts +6 -2
  95. package/dist/types/core/diagnostics.d.ts +14 -0
  96. package/dist/types/core/directive-diagnostics.d.ts +22 -8
  97. package/dist/types/core/former-routes.d.ts +14 -0
  98. package/dist/types/core/github.d.ts +5 -4
  99. package/dist/types/core/heading-markers.d.ts +65 -2
  100. package/dist/types/core/hidden-pages.d.ts +3 -0
  101. package/dist/types/core/includes.d.ts +23 -0
  102. package/dist/types/core/last-modified.d.ts +18 -6
  103. package/dist/types/core/links.d.ts +42 -2
  104. package/dist/types/core/load-module.d.ts +19 -4
  105. package/dist/types/core/manifest.d.ts +14 -1
  106. package/dist/types/core/markdown-mirrors.d.ts +22 -0
  107. package/dist/types/core/mcp-clients.d.ts +47 -0
  108. package/dist/types/core/mdx-elements.d.ts +32 -0
  109. package/dist/types/core/mdx-syntax.d.ts +100 -0
  110. package/dist/types/core/meta-diagnostics.d.ts +7 -0
  111. package/dist/types/core/meta.d.ts +32 -5
  112. package/dist/types/core/nav-diagnostics.d.ts +16 -2
  113. package/dist/types/core/navigation.d.ts +13 -0
  114. package/dist/types/core/ordering-prefix.d.ts +6 -0
  115. package/dist/types/core/pinned-ids.d.ts +23 -0
  116. package/dist/types/core/project-graph.d.ts +9 -1
  117. package/dist/types/core/redirect-patterns.d.ts +18 -0
  118. package/dist/types/core/relative-files.d.ts +31 -0
  119. package/dist/types/core/schema.d.ts +322 -11
  120. package/dist/types/core/sources/normalize.d.ts +101 -9
  121. package/dist/types/core/sources/types.d.ts +16 -0
  122. package/dist/types/core/static-files.d.ts +10 -0
  123. package/dist/types/core/static-host.d.ts +42 -0
  124. package/dist/types/core/svg-images.d.ts +26 -0
  125. package/dist/types/core/syntax-diagnostics.d.ts +14 -0
  126. package/dist/types/core/types.d.ts +42 -1
  127. package/dist/types/deploy/generated-files.d.ts +41 -0
  128. package/dist/types/deploy/redirects.d.ts +21 -7
  129. package/dist/types/markdown/code-title.d.ts +9 -5
  130. package/dist/types/markdown/directives.d.ts +8 -0
  131. package/dist/types/markdown/fence-language.d.ts +51 -0
  132. package/dist/types/markdown/fence-meta.d.ts +5 -0
  133. package/dist/types/markdown/github-alerts.d.ts +35 -0
  134. package/dist/types/openapi/asyncapi.d.ts +6 -5
  135. package/dist/types/openapi/checks.d.ts +10 -5
  136. package/dist/types/openapi/model.d.ts +55 -8
  137. package/dist/types/openapi/parse.d.ts +4 -1
  138. package/dist/types/ratelimit/index.d.ts +2 -0
  139. package/dist/types/ratelimit/memory.d.ts +1 -1
  140. package/dist/types/ratelimit/schema.d.ts +40 -0
  141. package/dist/types/ratelimit/unkey.d.ts +48 -0
  142. package/dist/types/sources/filesystem.d.ts +14 -4
  143. package/dist/types/sources/mdx-remote.d.ts +6 -3
  144. package/dist/types/sources/registry.d.ts +3 -3
  145. package/dist/types/theme/color.d.ts +33 -0
  146. package/dist/types/theme/palette.d.ts +122 -0
  147. package/docs/01-quickstart.mdx +1 -1
  148. package/docs/02-deployment.mdx +24 -8
  149. package/docs/04-migrating.mdx +16 -4
  150. package/docs/advanced/changelog.mdx +27 -1
  151. package/docs/advanced/skills.mdx +1 -1
  152. package/docs/cli/audit.mdx +1 -1
  153. package/docs/cli/doctor.mdx +2 -2
  154. package/docs/cli/evals.mdx +1 -1
  155. package/docs/cli/index.mdx +4 -2
  156. package/docs/cli/validate.mdx +10 -7
  157. package/docs/configuration/analytics.mdx +2 -0
  158. package/docs/configuration/customization.mdx +3 -1
  159. package/docs/configuration/index.mdx +7 -6
  160. package/docs/configuration/rate-limiting.mdx +20 -1
  161. package/docs/configuration/theming.mdx +5 -1
  162. package/docs/content/components.mdx +26 -6
  163. package/docs/content/frontmatter.mdx +1 -1
  164. package/docs/content/i18n.mdx +1 -1
  165. package/docs/content/includes.mdx +6 -2
  166. package/docs/content/index.mdx +11 -1
  167. package/docs/content/islands.mdx +2 -0
  168. package/docs/content/meta.mdx +9 -3
  169. package/docs/content/navigation.mdx +14 -6
  170. package/docs/content/sources/obsidian.mdx +1 -1
  171. package/docs/content/sources/remote-mdx.mdx +3 -1
  172. package/docs/content/syntax.mdx +76 -8
  173. package/docs/content/variables.mdx +3 -1
  174. package/docs/discoverability/agent-discovery.mdx +1 -1
  175. package/docs/discoverability/json-api.mdx +1 -1
  176. package/docs/discoverability/llms-txt.mdx +1 -1
  177. package/docs/discoverability/markdown.mdx +2 -2
  178. package/docs/discoverability/mcp.mdx +40 -8
  179. package/docs/discoverability/open-graph.mdx +1 -1
  180. package/docs/discoverability/sitemap-and-robots.mdx +1 -1
  181. package/docs/references/graphql.mdx +2 -0
  182. package/docs/references/openapi.mdx +45 -7
  183. package/package.json +4 -4
  184. package/skills/blume/SKILL.md +2 -2
  185. package/skills/blume-migrate/SKILL.md +51 -29
  186. package/skills/blume-migrate/references/docsify.md +262 -0
  187. package/skills/blume-migrate/references/docus.md +298 -0
  188. package/skills/blume-migrate/references/docusaurus.md +5 -5
  189. package/skills/blume-migrate/references/fern.md +327 -0
  190. package/skills/blume-migrate/references/fumadocs.md +1 -1
  191. package/skills/blume-migrate/references/gitbook.md +267 -0
  192. package/skills/blume-migrate/references/github-wiki.md +259 -0
  193. package/skills/blume-migrate/references/jekyll.md +253 -0
  194. package/skills/blume-migrate/references/mdbook.md +256 -0
  195. package/skills/blume-migrate/references/mintlify.md +5 -5
  196. package/skills/blume-migrate/references/mkdocs.md +236 -0
  197. package/skills/blume-migrate/references/monorepo.md +20 -8
  198. package/skills/blume-migrate/references/nextra.md +5 -5
  199. package/skills/blume-migrate/references/readme.md +257 -0
  200. package/skills/blume-migrate/references/redocly.md +198 -0
  201. package/skills/blume-migrate/references/starlight.md +3 -3
  202. package/skills/blume-migrate/references/vitepress.md +204 -0
  203. package/skills/blume-migrate/references/vuepress.md +271 -0
  204. package/skills/blume-migrate/scripts/docsify-codemod.mjs +2387 -0
  205. package/skills/blume-migrate/scripts/docus-codemod.mjs +2542 -0
  206. package/skills/blume-migrate/scripts/fern-codemod.mjs +2633 -0
  207. package/skills/blume-migrate/scripts/github-wiki-codemod.mjs +2808 -0
  208. package/skills/blume-migrate/scripts/include-excerpts.mjs +649 -0
  209. package/skills/blume-migrate/scripts/jekyll-codemod.mjs +3649 -0
  210. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +17 -13
  211. package/skills/blume-migrate/scripts/mkdocs-codemod.mjs +2526 -0
  212. package/skills/blume-migrate/scripts/operation-routes.mjs +508 -0
  213. package/skills/blume-migrate/scripts/pin-heading-ids.mjs +1049 -0
  214. package/skills/blume-migrate/scripts/readme-codemod.mjs +4139 -0
  215. package/skills/blume-migrate/scripts/vitepress-codemod.mjs +1511 -0
  216. package/src/ai/changelog-components.ts +24 -0
  217. package/src/ai/changelog-markdown.ts +45 -41
  218. package/src/ai/component-markdown.ts +22 -9
  219. package/src/ai/llms.ts +36 -5
  220. package/src/ai/markdown.ts +2 -2
  221. package/src/ai/openapi-components.ts +25 -9
  222. package/src/ai/operation-details.ts +208 -0
  223. package/src/ai/relative-links.ts +48 -8
  224. package/src/ai/serializers.ts +7 -6
  225. package/src/ai/site-skill.ts +6 -2
  226. package/src/analytics/head.ts +11 -5
  227. package/src/analytics/script.ts +13 -3
  228. package/src/astro/generate.ts +47 -11
  229. package/src/astro/index.ts +1 -0
  230. package/src/astro/integration.ts +27 -1
  231. package/src/astro/mdx-source-errors.ts +46 -0
  232. package/src/astro/templates.ts +93 -189
  233. package/src/audit/checks/redirects.ts +5 -1
  234. package/src/audit/redirects.ts +36 -19
  235. package/src/audit/report.ts +22 -9
  236. package/src/audit/run.ts +12 -28
  237. package/src/audit/terms.ts +23 -0
  238. package/src/audit/url.ts +2 -2
  239. package/src/cli/build-failure.ts +256 -7
  240. package/src/cli/command-meta.ts +1 -1
  241. package/src/cli/commands/audit.ts +1 -1
  242. package/src/cli/commands/build.ts +12 -3
  243. package/src/cli/commands/check.ts +13 -2
  244. package/src/cli/commands/dev.ts +1 -1
  245. package/src/cli/commands/doctor.ts +4 -2
  246. package/src/cli/commands/init.ts +3 -3
  247. package/src/cli/commands/preview.ts +17 -1
  248. package/src/cli/commands/validate.ts +22 -10
  249. package/src/cli/prepare.ts +1 -1
  250. package/src/cli/preview-pages.ts +161 -0
  251. package/src/cli/required-secrets.ts +0 -1
  252. package/src/cli/unknown-flags.ts +1 -42
  253. package/src/components/code-copy.ts +51 -0
  254. package/src/components/code-language.ts +75 -0
  255. package/src/components/content/AccordionItem.astro +16 -1
  256. package/src/components/content/Callout.astro +11 -1
  257. package/src/components/content/Changelog.astro +189 -0
  258. package/src/components/content/CodeBlock.astro +8 -11
  259. package/src/components/content/Tabs.astro +16 -2
  260. package/src/components/content/YouTube.astro +6 -4
  261. package/src/components/content/code-block.ts +33 -0
  262. package/src/components/content/content-strings.ts +5 -1
  263. package/src/components/content/inline-markdown.ts +35 -8
  264. package/src/components/content/youtube.ts +49 -7
  265. package/src/components/layout/Analytics.astro +5 -1
  266. package/src/components/layout/NavTree.astro +8 -3
  267. package/src/components/layout/PageActions.astro +81 -23
  268. package/src/components/layout/RootLayout.astro +35 -32
  269. package/src/components/layout/SiteFooter.astro +31 -8
  270. package/src/components/layout/nav-utils.ts +14 -0
  271. package/src/components/layout/search/pagefind.ts +2 -1
  272. package/src/components/openapi/Operation.astro +4 -0
  273. package/src/components/openapi/PanelTabs.astro +9 -3
  274. package/src/components/openapi/RequestBody.astro +42 -8
  275. package/src/components/openapi/RequestPanel.astro +34 -53
  276. package/src/components/openapi/example-panels.ts +110 -0
  277. package/src/components/openapi/helpers.ts +195 -20
  278. package/src/components/openapi/operation-model.ts +63 -26
  279. package/src/components/openapi/request.ts +13 -0
  280. package/src/components/openapi/snippets.ts +14 -2
  281. package/src/core/alert-diagnostics.ts +89 -0
  282. package/src/core/builtin-tags.ts +76 -0
  283. package/src/core/changelog-list.ts +87 -0
  284. package/src/core/closest-match.ts +41 -0
  285. package/src/core/code-fence-diagnostics.ts +267 -0
  286. package/src/core/code-fences.ts +43 -7
  287. package/src/core/component-diagnostics.ts +65 -12
  288. package/src/core/config-input.ts +39 -5
  289. package/src/core/config.ts +46 -1
  290. package/src/core/content-assets.ts +64 -144
  291. package/src/core/data.ts +10 -3
  292. package/src/core/diagnostics.ts +36 -3
  293. package/src/core/directive-diagnostics.ts +265 -47
  294. package/src/core/former-routes.ts +46 -0
  295. package/src/core/git-ignored.ts +53 -0
  296. package/src/core/github.ts +5 -4
  297. package/src/core/heading-markers.ts +156 -2
  298. package/src/core/hidden-pages.ts +64 -0
  299. package/src/core/includes.ts +181 -41
  300. package/src/core/last-modified.ts +47 -10
  301. package/src/core/links.ts +438 -83
  302. package/src/core/load-module.ts +98 -9
  303. package/src/core/manifest.ts +12 -9
  304. package/src/core/markdown-mirrors.ts +52 -0
  305. package/src/core/mcp-clients.ts +87 -0
  306. package/src/core/mdx-elements.ts +153 -0
  307. package/src/core/mdx-syntax.ts +377 -0
  308. package/src/core/meta-diagnostics.ts +134 -0
  309. package/src/core/meta.ts +154 -55
  310. package/src/core/nav-diagnostics.ts +41 -4
  311. package/src/core/navigation.ts +70 -10
  312. package/src/core/ordering-prefix.ts +22 -2
  313. package/src/core/pinned-ids.ts +141 -0
  314. package/src/core/project-graph.ts +221 -29
  315. package/src/core/redirect-patterns.ts +41 -0
  316. package/src/core/relative-files.ts +145 -0
  317. package/src/core/schema.ts +110 -6
  318. package/src/core/sources/mdx-remote.ts +75 -14
  319. package/src/core/sources/normalize.ts +885 -179
  320. package/src/core/sources/notion.ts +5 -4
  321. package/src/core/sources/obsidian.ts +2 -5
  322. package/src/core/sources/types.ts +17 -0
  323. package/src/core/static-files.ts +40 -0
  324. package/src/core/static-host.ts +125 -0
  325. package/src/core/svg-images.ts +98 -0
  326. package/src/core/syntax-diagnostics.ts +491 -0
  327. package/src/core/types.ts +43 -1
  328. package/src/deploy/artifacts.ts +2 -28
  329. package/src/deploy/generated-files.ts +128 -0
  330. package/src/deploy/platforms/vercel.ts +25 -1
  331. package/src/deploy/redirects.ts +43 -16
  332. package/src/deploy/sitemap.ts +2 -1
  333. package/src/markdown/base-links.ts +87 -6
  334. package/src/markdown/code-title.ts +29 -13
  335. package/src/markdown/content-assets.ts +210 -0
  336. package/src/markdown/directives.ts +69 -5
  337. package/src/markdown/fence-language.ts +106 -0
  338. package/src/markdown/fence-meta.ts +42 -0
  339. package/src/markdown/github-alerts.ts +172 -0
  340. package/src/markdown/heading-anchors.ts +265 -56
  341. package/src/markdown/include.ts +30 -3
  342. package/src/markdown/index.ts +145 -40
  343. package/src/markdown/prompt-text.ts +95 -0
  344. package/src/markdown/relative-links.ts +37 -2
  345. package/src/markdown/route-snapshot.ts +22 -12
  346. package/src/markdown/svg-fallback.ts +51 -0
  347. package/src/markdown/title-heading.ts +134 -0
  348. package/src/migrate/migrate.ts +127 -2
  349. package/src/og/card.ts +14 -4
  350. package/src/openapi/asyncapi.ts +14 -6
  351. package/src/openapi/checks.ts +160 -15
  352. package/src/openapi/model.ts +172 -33
  353. package/src/openapi/parse.ts +13 -4
  354. package/src/openapi/render-mdx.ts +55 -9
  355. package/src/openapi/source.ts +87 -6
  356. package/src/ratelimit/index.ts +2 -0
  357. package/src/ratelimit/memory.ts +1 -1
  358. package/src/ratelimit/runtime.ts +81 -0
  359. package/src/ratelimit/schema.ts +2 -0
  360. package/src/ratelimit/unkey.ts +61 -0
  361. package/src/registry/eject.ts +0 -1
  362. package/src/registry/registry.ts +1 -1
  363. package/src/search/documents.ts +10 -2
  364. package/src/sources/filesystem.ts +28 -3
  365. package/src/sources/mdx-remote.ts +7 -15
  366. package/src/theme/color.ts +14 -0
  367. package/src/theme/entry.ts +22 -1
  368. package/src/theme/palette.ts +74 -1
  369. package/dist/cli/chunk-0pmb4tr4.js.map +0 -35
  370. package/dist/cli/chunk-0w6hs479.js.map +0 -10
  371. package/dist/cli/chunk-150jk7km.js +0 -52
  372. package/dist/cli/chunk-150jk7km.js.map +0 -10
  373. package/dist/cli/chunk-36bxs9c7.js.map +0 -10
  374. package/dist/cli/chunk-53jqw8z0.js +0 -89
  375. package/dist/cli/chunk-53jqw8z0.js.map +0 -10
  376. package/dist/cli/chunk-60hz265r.js.map +0 -10
  377. package/dist/cli/chunk-desvm72z.js.map +0 -14
  378. package/dist/cli/chunk-dpy8qdjf.js.map +0 -17
  379. package/dist/cli/chunk-es8h0v3j.js +0 -441
  380. package/dist/cli/chunk-es8h0v3j.js.map +0 -11
  381. package/dist/cli/chunk-kdpzw38a.js.map +0 -11
  382. package/dist/cli/chunk-nbyvgnnm.js.map +0 -36
  383. package/dist/cli/chunk-s0bdnc7v.js.map +0 -10
  384. package/dist/cli/chunk-twy7psdz.js.map +0 -10
  385. package/dist/cli/chunk-ydp3065p.js +0 -64
  386. package/dist/cli/chunk-ydp3065p.js.map +0 -10
  387. /package/dist/cli/{chunk-y4msrwm9.js.map → chunk-080mfch6.js.map} +0 -0
  388. /package/dist/cli/{chunk-9qzgg6mj.js.map → chunk-34wap3ed.js.map} +0 -0
  389. /package/dist/cli/{chunk-1f882900.js.map → chunk-c5bjxq8v.js.map} +0 -0
  390. /package/dist/cli/{chunk-bxwhtvw8.js.map → chunk-f260jdrn.js.map} +0 -0
  391. /package/dist/cli/{chunk-74wfhv79.js.map → chunk-fnxgtjeq.js.map} +0 -0
  392. /package/dist/cli/{chunk-dhwtqnnd.js.map → chunk-gxhcf2hs.js.map} +0 -0
  393. /package/dist/cli/{chunk-tfycddbt.js.map → chunk-h0qwdbem.js.map} +0 -0
  394. /package/dist/cli/{chunk-j1x92mx9.js.map → chunk-j4nfgn2j.js.map} +0 -0
  395. /package/dist/cli/{chunk-ave1wcbr.js.map → chunk-qcyxkj44.js.map} +0 -0
  396. /package/dist/cli/{chunk-58bt3hag.js.map → chunk-x7bh6d9j.js.map} +0 -0
  397. /package/dist/cli/{chunk-hvcxng5f.js.map → chunk-ydqzkhjn.js.map} +0 -0
@@ -22,6 +22,8 @@ import type { FontSlug } from "../theme/fonts.ts";
22
22
  import type { FooterSocial } from "./footer.ts";
23
23
  import type {
24
24
  blumeConfigSchema,
25
+ McpClient,
26
+ McpCustomClient,
25
27
  OpenInChatProvider,
26
28
  SidebarDisplay,
27
29
  SidebarItemConfig,
@@ -147,9 +149,15 @@ export interface FooterLink {
147
149
 
148
150
  /**
149
151
  * The site footer, below the content on every page: a row of links on one
150
- * side, and social profile icons on the other.
152
+ * side, social profile icons on the other, and a copyright line centered
153
+ * between them.
151
154
  */
152
155
  export interface FooterConfig {
156
+ /**
157
+ * A line of plain text centered between the links and the icons, like
158
+ * `© 2026 Acme, Inc.`, or a map of locale code to text.
159
+ */
160
+ copyright?: LocalizableLabel;
153
161
  /** Links in one row, left to right. */
154
162
  links?: FooterLink[];
155
163
  /**
@@ -181,7 +189,7 @@ export interface ChangelogConfig {
181
189
  export interface ContentConfig {
182
190
  /** Default page `type` for content that sets none. Defaults to `doc`. */
183
191
  defaultType?: string;
184
- /** Glob patterns to ignore. Defaults to `["**\/_*", "**\/.*"]`. Shorthand; not allowed beside `sources`. */
192
+ /** Glob patterns to ignore, on top of `["**\/_*", "**\/.*"]`; a `!pattern` entry stops ignoring that pattern, a default included. Shorthand; not allowed beside `sources`. */
185
193
  exclude?: string[];
186
194
  /** Glob patterns to include. Defaults to `["**\/*.{md,mdx}"]`. Shorthand; not allowed beside `sources`. */
187
195
  include?: string[];
@@ -719,6 +727,32 @@ export interface LlmsTxtConfig {
719
727
 
720
728
  /** Expose the docs as an MCP server for connecting agents. */
721
729
  export interface McpConfig {
730
+ /**
731
+ * The clients the "Connect to MCP" page action installs the server into,
732
+ * beside its "Copy server URL" row. Defaults to `true` (every built-in
733
+ * client). Set `false` to list none, or list built-in keys and your own
734
+ * clients, in order. A custom client's `command` is copied with `{name}`
735
+ * (the server name as an id) and `{url}` (the server's URL) filled in; its
736
+ * `label` localizes like a header link's, and `icon` defaults to `terminal`.
737
+ *
738
+ * ```ts
739
+ * agents: {
740
+ * mcp: {
741
+ * enabled: true,
742
+ * clients: [
743
+ * "claude-code",
744
+ * {
745
+ * label: "Copy Copilot CLI command",
746
+ * command: "copilot mcp add --transport http {name} {url}",
747
+ * icon: "terminal",
748
+ * },
749
+ * "vscode",
750
+ * ],
751
+ * },
752
+ * }
753
+ * ```
754
+ */
755
+ clients?: boolean | (McpClient | McpCustomClient)[];
722
756
  /** Turn the MCP server on. Defaults to `false`. */
723
757
  enabled?: boolean;
724
758
  /** Optional system hint passed to connecting agents. */
@@ -1567,8 +1601,8 @@ export interface BlumeConfig {
1567
1601
  */
1568
1602
  feedback?: boolean | FeedbackConfig;
1569
1603
  /**
1570
- * The site footer: social profile icons and up to four link columns. Unset,
1571
- * the site has no footer.
1604
+ * The site footer: a copyright line, a row of links, and social profile
1605
+ * icons. Unset, the site has no footer.
1572
1606
  */
1573
1607
  footer?: FooterConfig;
1574
1608
  /** Opt-in custom frontmatter keys, validated by schemas you supply. */
@@ -1597,7 +1631,7 @@ export interface BlumeConfig {
1597
1631
  * Limit how often one reader (by IP address) can call the server routes —
1598
1632
  * the assistant, the API playground proxy, server-side search — with an
1599
1633
  * adapter from `blume/ratelimit`: `memory()` (the default, 30 requests per
1600
- * 10 minutes), `upstash()`, or `cloudflare()`. `false` turns it off.
1634
+ * 10 minutes), `upstash()`, `cloudflare()`, or `unkey()`. `false` turns it off.
1601
1635
  */
1602
1636
  rateLimit?: false | RateLimitAdapter;
1603
1637
  /** React island behavior (compiler auto-memoization). */
@@ -3,6 +3,7 @@ import { existsSync, readFileSync } from "node:fs";
3
3
  import { basename } from "pathe";
4
4
  import { z } from "zod";
5
5
 
6
+ import { ACCENTS, invalidColorSettings } from "../theme/palette.ts";
6
7
  import type { BlumeConfig } from "./config-input.ts";
7
8
  import { applyDeploymentEnv } from "./deployment-env.ts";
8
9
  import {
@@ -261,6 +262,50 @@ const configIssues = (
261
262
  );
262
263
  };
263
264
 
265
+ const COLOR_FORMS =
266
+ "a hex value like #6340ac, rgb(), hsl(), oklch(), or a color name like rebeccapurple";
267
+
268
+ /**
269
+ * A warning for each configured color that isn't a CSS color, at the line
270
+ * that sets it. The schema has always taken any string, so this warns rather
271
+ * than failing a build that used to pass.
272
+ */
273
+ const colorDiagnostics = (
274
+ config: ResolvedConfig,
275
+ configFile: string | null
276
+ ): Diagnostic[] => {
277
+ const invalid = invalidColorSettings(config);
278
+ if (invalid.length === 0) {
279
+ return [];
280
+ }
281
+ const source =
282
+ configFile && existsSync(configFile)
283
+ ? readFileSync(configFile, "utf-8")
284
+ : undefined;
285
+ const located = diagnosticsFromIssues(
286
+ invalid.map((setting) => ({
287
+ message: `${JSON.stringify(setting.value)} isn't a CSS color, so ${
288
+ setting.path[0] === "seo"
289
+ ? "the social card renderer rejects it"
290
+ : "browsers ignore the styles that use it"
291
+ }.`,
292
+ path: setting.path,
293
+ })),
294
+ {
295
+ code: "BLUME_THEME_COLOR_INVALID",
296
+ file: configFile ?? undefined,
297
+ source,
298
+ }
299
+ );
300
+ return located.map((diagnostic, index) => ({
301
+ ...diagnostic,
302
+ severity: "warning",
303
+ suggestion: invalid[index]?.presets
304
+ ? `Use a preset (${Object.keys(ACCENTS).join(", ")}) or a CSS color: ${COLOR_FORMS}.`
305
+ : `Use a CSS color: ${COLOR_FORMS}.`,
306
+ }));
307
+ };
308
+
264
309
  /**
265
310
  * Load and validate the project config. When no config file exists, schema
266
311
  * defaults produce a fully resolved config so the zero-boilerplate path works.
@@ -364,7 +409,7 @@ export const loadConfig = async (
364
409
  seo: { ...config.seo, og: { ...config.seo.og, enabled: ogEnabled } },
365
410
  },
366
411
  configFile,
367
- diagnostics: [],
412
+ diagnostics: colorDiagnostics(config, configFile),
368
413
  themeFontsConfigured,
369
414
  };
370
415
  };
@@ -1,20 +1,19 @@
1
- import { readdirSync, statSync } from "node:fs";
2
1
  import { readFile } from "node:fs/promises";
3
2
 
4
- import {
5
- basename,
6
- dirname,
7
- extname,
8
- normalize,
9
- relative,
10
- resolve,
11
- } from "pathe";
3
+ import { dirname, extname, relative } from "pathe";
12
4
 
13
5
  import { normalizeBasePath } from "./base-path.ts";
14
6
  import { nextFenceState } from "./code-fences.ts";
15
7
  import type { FenceState } from "./code-fences.ts";
8
+ import { resolveRelativeFile, resolveRelativeImage } from "./relative-files.ts";
16
9
  import { hashText } from "./sources/cache.ts";
17
- import { INLINE_CODE, MD_IMAGE, targetOffsetIn } from "./sources/normalize.ts";
10
+ import {
11
+ isLinkElementUrl,
12
+ rewriteCardImages,
13
+ rewriteElementUrls,
14
+ rewriteImageTargets,
15
+ rewriteLinkTargets,
16
+ } from "./sources/normalize.ts";
18
17
  import { readExpandedEntryText } from "./sources/read.ts";
19
18
  import type { ContentSource } from "./sources/types.ts";
20
19
  import type { PageRecord } from "./types.ts";
@@ -29,31 +28,18 @@ import type { PageRecord } from "./types.ts";
29
28
  * `/blume-assets/content/<project-relative path>` by a generated endpoint (see
30
29
  * `contentAssetsEndpointTemplate`), and these helpers rewrite the relative
31
30
  * references in agent-facing output to that URL.
31
+ *
32
+ * Other files beside a page are published the same way: a link to one
33
+ * (`[spec](./spec.pdf)`, a reference definition), a raw HTML or `.mdx`
34
+ * element's `src` (`<img src="./diagram.png">`, `<video>`, `<source>`,
35
+ * `<audio>`), and an `<a>`'s or a component's `href`. Nothing else serves
36
+ * them, so the HTML render points those at the same URLs (see
37
+ * `markdown/content-assets.ts`).
32
38
  */
33
39
 
34
- /** The endpoint route prefix colocated content images are served under. */
40
+ /** The endpoint route prefix colocated content files are served under. */
35
41
  export const CONTENT_ASSETS_PREFIX = "/blume-assets/content";
36
42
 
37
- // The formats Astro's image pipeline accepts, plus the web-safe pass-throughs;
38
- // anything else referenced relatively (a `.pdf`, a source file) is left alone.
39
- const IMAGE_EXTENSIONS = new Set([
40
- ".apng",
41
- ".avif",
42
- ".bmp",
43
- ".gif",
44
- ".ico",
45
- ".jpeg",
46
- ".jpg",
47
- ".png",
48
- ".svg",
49
- ".tiff",
50
- ".webp",
51
- ]);
52
-
53
- /** Whether a link target is a relative filesystem path (not URL/absolute/hash). */
54
- const isRelativeTarget = (target: string): boolean =>
55
- !(target.startsWith("/") || target.startsWith("#")) && !URL.canParse(target);
56
-
57
43
  /**
58
44
  * The endpoint param a colocated image is served under: its project-relative
59
45
  * path (readable, collision-free — it mirrors the source tree). A file outside
@@ -71,118 +57,35 @@ export const contentAssetParam = (
71
57
  return rel;
72
58
  };
73
59
 
74
- /** Percent-decode an image target; malformed escapes stay verbatim. */
75
- const decodeTarget = (target: string): string => {
76
- try {
77
- return decodeURI(target);
78
- } catch {
79
- return target;
80
- }
81
- };
82
-
83
- /**
84
- * Whether a target is *shaped* like a colocated image reference: a relative
85
- * filesystem path with an image extension. Exported so link validation can
86
- * tell "not a colocated candidate" apart from "a candidate that resolves
87
- * nowhere" — the former falls through to the public-dir probe, the latter is
88
- * a broken reference beside the page source.
89
- */
90
- export const isRelativeImageTarget = (target: string): boolean =>
91
- isRelativeTarget(target) &&
92
- IMAGE_EXTENSIONS.has(extname(decodeTarget(target)).toLowerCase());
93
-
94
- /**
95
- * Whether `abs` is a file whose trailing `segments` names match the on-disk
96
- * entries exactly. A bare `existsSync` accepts `./Diagram.PNG` for
97
- * `diagram.png` (or a directory named like an image) on a case-insensitive
98
- * filesystem — the reference then validates and serves locally but breaks on
99
- * the case-sensitive Linux build.
100
- */
101
- const existsAsWritten = (abs: string, segments: number): boolean => {
102
- let current = abs;
103
- for (let i = 0; i < segments; i += 1) {
104
- const parent = dirname(current);
105
- let entries: string[];
106
- try {
107
- entries = readdirSync(parent);
108
- } catch {
109
- return false;
110
- }
111
- if (!entries.includes(basename(current))) {
112
- return false;
113
- }
114
- current = parent;
115
- }
116
- const stat = statSync(abs, { throwIfNoEntry: false });
117
- return stat !== undefined && stat.isFile();
118
- };
119
-
120
60
  /**
121
- * Resolve one image target against its page's directory. Returns the absolute
122
- * file path when the target is relative, is an image, and exists on disk —
123
- * anything else (remote URLs, `public/` absolutes, broken refs, code-block
124
- * examples that happen to look like paths) is null and left untouched. Shared
125
- * with link validation, so what counts as a colocated image is decided once.
61
+ * The URL a colocated image's endpoint param is served at, under
62
+ * `deployment.base` when set, encoded for a Markdown or HTML URL with its `/`
63
+ * separators kept.
126
64
  */
127
- export const resolveRelativeImage = (
128
- sourceDir: string,
129
- target: string
130
- ): string | null => {
131
- if (!isRelativeImageTarget(target)) {
132
- return null;
133
- }
134
- const decoded = decodeTarget(target);
135
- // Only the segments the author wrote are checked against on-disk names;
136
- // `sourceDir`'s own casing is the filesystem's business, not the target's.
137
- const segments = normalize(decoded)
65
+ export const contentAssetUrl = (param: string, deployBase?: string): string =>
66
+ `${normalizeBasePath(deployBase)}${CONTENT_ASSETS_PREFIX}/${param
138
67
  .split("/")
139
- .filter((part) => part !== "" && part !== "..").length;
140
- const abs = resolve(sourceDir, decoded);
141
- return existsAsWritten(abs, segments) ? abs : null;
142
- };
143
-
144
- /** Encode an endpoint param for use in a Markdown URL, keeping `/` separators. */
145
- const encodedParam = (param: string): string =>
146
- param.split("/").map(encodeURIComponent).join("/");
68
+ .map(encodeURIComponent)
69
+ .join("/")}`;
70
+
71
+ // Where a link (`[spec](./spec.pdf)`, `[spec]: ./spec.pdf`) or an element's
72
+ // `href`/`src` could name a file beside the page: a destination or value not
73
+ // written as a root path or a fragment, which `resolveRelativeFile` never
74
+ // resolves. They're read in the text as it stands, code included, so a page
75
+ // without one, like a page that links only to routes and anchors, has
76
+ // nothing to rewrite and skips the passes that mask its code to look.
77
+ const RELATIVE_DESTINATION = /\][(:][ \t]*(?![/#\s])/u;
78
+ const RELATIVE_ELEMENT_URL = /(?:href|src)=["'](?![/#])/u;
147
79
 
148
80
  /**
149
- * Rewrite one line's relative image targets. Matches run on a copy with inline
150
- * code blanked out (a `` `![x](./y.png)` `` span is syntax being *shown*, not
151
- * an image), while replacements splice into the real line by index — the mask
152
- * preserves length, so the indices line up.
153
- */
154
- const rewriteLine = (
155
- line: string,
156
- toUrl: (target: string) => string | null
157
- ): string => {
158
- const masked = line.replaceAll(INLINE_CODE, (span) =>
159
- " ".repeat(span.length)
160
- );
161
- let out = "";
162
- let cursor = 0;
163
- for (const match of masked.matchAll(MD_IMAGE)) {
164
- const target = match.groups?.target ?? "";
165
- const url = toUrl(target);
166
- if (url === null) {
167
- continue;
168
- }
169
- const offset =
170
- (match.index ?? 0) +
171
- targetOffsetIn(match[0], target, match.groups?.title);
172
- out += line.slice(cursor, offset) + url;
173
- cursor = offset + target.length;
174
- }
175
- return out + line.slice(cursor);
176
- };
177
-
178
- /**
179
- * Rewrite a page's relative image references to their served
81
+ * Rewrite a page's relative image references, and its links and element
82
+ * URLs naming other files beside it, to their served
180
83
  * `/blume-assets/content/…` URLs (under `deployment.base` when set). Fenced
181
84
  * code blocks and inline code are skipped; only references whose file actually
182
85
  * exists next to the source are touched. `register` observes each rewritten
183
86
  * asset so a caller can accumulate the files the endpoint must serve.
184
87
  */
185
- export const rewriteRelativeImages = (options: {
88
+ export const rewriteRelativeAssets = (options: {
186
89
  source: string;
187
90
  sourcePath: string;
188
91
  projectRoot: string;
@@ -191,15 +94,18 @@ export const rewriteRelativeImages = (options: {
191
94
  }): string => {
192
95
  const { source, sourcePath, projectRoot, deployBase, register } = options;
193
96
  const sourceDir = dirname(sourcePath);
194
- const prefix = `${normalizeBasePath(deployBase)}${CONTENT_ASSETS_PREFIX}`;
195
- const toUrl = (target: string): string | null => {
196
- const abs = resolveRelativeImage(sourceDir, target);
197
- if (abs === null) {
198
- return null;
199
- }
97
+ const served = (abs: string): string => {
200
98
  const param = contentAssetParam(projectRoot, abs);
201
99
  register?.(param, abs);
202
- return `${prefix}/${encodedParam(param)}`;
100
+ return contentAssetUrl(param, deployBase);
101
+ };
102
+ const toUrl = (target: string): string | null => {
103
+ const abs = resolveRelativeImage(sourceDir, target);
104
+ return abs === null ? null : served(abs);
105
+ };
106
+ const toFileUrl = (target: string): string | null => {
107
+ const file = resolveRelativeFile(sourceDir, target);
108
+ return file === null ? null : `${served(file.path)}${file.suffix}`;
203
109
  };
204
110
 
205
111
  let fence: FenceState = null;
@@ -207,13 +113,27 @@ export const rewriteRelativeImages = (options: {
207
113
  const next = nextFenceState(line, fence);
208
114
  const inFence = fence !== null || next !== null;
209
115
  fence = next;
210
- return inFence ? line : rewriteLine(line, toUrl);
116
+ // Inline code is skipped too: a `` `![x](./y.png)` `` span is syntax
117
+ // being shown, not an image.
118
+ return inFence ? line : rewriteImageTargets(line, toUrl);
211
119
  });
212
- return lines.join("\n");
120
+ // A `<Card img>` is an image embed too: its file is served and its value
121
+ // pointed there, which the HTML render reads back (see
122
+ // `markdown/content-assets.ts`), as are links and element URLs naming any
123
+ // other file beside the page.
124
+ const withCards = rewriteCardImages(lines.join("\n"), toUrl);
125
+ const withLinks = RELATIVE_DESTINATION.test(withCards)
126
+ ? rewriteLinkTargets(withCards, toFileUrl)
127
+ : withCards;
128
+ return RELATIVE_ELEMENT_URL.test(withLinks)
129
+ ? rewriteElementUrls(withLinks, (url) =>
130
+ isLinkElementUrl(url) ? toFileUrl(url.value) : null
131
+ )
132
+ : withLinks;
213
133
  };
214
134
 
215
135
  /**
216
- * Every colocated image the project's pages reference, keyed by endpoint param.
136
+ * Every colocated file the project's pages reference, keyed by endpoint param.
217
137
  * Serialized to `generated/content-assets.json`, which the
218
138
  * `/blume-assets/[...asset]` endpoint reads to serve the original files. Runs
219
139
  * the same rewrite the agent-Markdown builders apply — over the same
@@ -243,7 +163,7 @@ export const collectContentAssets = async (project: {
243
163
  } catch {
244
164
  return;
245
165
  }
246
- rewriteRelativeImages({
166
+ rewriteRelativeAssets({
247
167
  projectRoot: project.context.root,
248
168
  register: (param, abs) => {
249
169
  files[param] = abs;
package/src/core/data.ts CHANGED
@@ -187,7 +187,7 @@ export interface BlumeDataConfig {
187
187
  feedback: boolean;
188
188
  /** Whether the rating asks for a written comment after it (`feedback.comments`). */
189
189
  feedbackComments: boolean;
190
- /** `footer`: social profile icons and link columns, or `null` when unset. */
190
+ /** `footer`: the copyright line, links, and social profile icons, or `null` when unset. */
191
191
  footer: ResolvedConfig["footer"] | null;
192
192
  /**
193
193
  * Repo coordinates for content components that address the API or build
@@ -206,8 +206,15 @@ export interface BlumeDataConfig {
206
206
  /** `markdown.imageZoom`: click-to-zoom content images. */
207
207
  imageZoom: boolean;
208
208
  logo: BlumeLogo | null;
209
- /** Hosted MCP server, or `null` when MCP is off. */
210
- mcp: { name: string; route: string } | null;
209
+ /**
210
+ * Hosted MCP server, or `null` when MCP is off, with the clients its
211
+ * "Connect to MCP" page action lists (`agents.mcp.clients`), in order.
212
+ */
213
+ mcp: {
214
+ clients: ResolvedConfig["agents"]["mcp"]["clients"];
215
+ name: string;
216
+ route: string;
217
+ } | null;
211
218
  /** `seo.metatags`: the site's own tags for every page's head, or `null`. */
212
219
  metatags: Record<string, string> | null;
213
220
  /**
@@ -27,40 +27,65 @@ const DOCS_CONTENT_SOURCES = "/docs/content/sources";
27
27
  const DOCS_CONTENT_SOURCES_OBSIDIAN = "/docs/content/sources/obsidian";
28
28
  const DOCS_CONTENT_NAVIGATION = "/docs/content/navigation";
29
29
  const DOCS_CONTENT_INCLUDES = "/docs/content/includes";
30
+ const DOCS_OPENAPI_WARNINGS = "/docs/references/openapi#spec-warnings";
31
+ const DOCS_OTHER_TOOLS_SYNTAX = "/docs/content/syntax#syntax-from-other-tools";
32
+ const DOCS_MARKDOWN_AND_MDX = "/docs/content#markdown-and-mdx";
30
33
 
31
34
  /** Diagnostic code → the docs page that explains it. */
32
35
  const DOCS_PATHS = new Map(
33
36
  Object.entries({
34
37
  BLUME_ADAPTER_REQUIRED: DOCS_DEPLOYMENT,
35
- BLUME_ASSETS_UNCHECKED: DOCS_CLI_VALIDATE,
36
38
  BLUME_ASSET_FETCH_FAILED: DOCS_CONTENT_SOURCES,
37
39
  BLUME_BROKEN_ANCHOR: DOCS_CLI_VALIDATE,
38
40
  BLUME_BROKEN_ASSET: DOCS_CLI_VALIDATE,
39
41
  BLUME_BROKEN_LINK: DOCS_CLI_VALIDATE,
42
+ BLUME_BROKEN_REDIRECT: DOCS_CLI_VALIDATE,
43
+ BLUME_CODE_FENCE_OPTION: "/docs/content/syntax",
40
44
  BLUME_COMPONENTS_INVALID: "/docs/configuration/customization",
41
45
  BLUME_CONFIG_INVALID: "/docs/configuration",
42
46
  BLUME_CONFIG_LOAD_FAILED: "/docs/configuration",
43
47
  BLUME_CONTENT_ROOT_MISSING: DOCS_CONTENT_SOURCES,
44
48
  BLUME_DEAD_LINK: DOCS_CLI_VALIDATE,
45
49
  BLUME_DEPENDENCY_MISSING: "/docs/cli/doctor",
50
+ BLUME_DIRECTIVE_CLOSING_TEXT: "/docs/content/syntax",
51
+ BLUME_DIRECTIVE_OPENING_TEXT: "/docs/content/syntax",
52
+ BLUME_DIRECTIVE_SPACED_NAME: "/docs/content/syntax",
46
53
  BLUME_DUPLICATE_ROUTE: DOCS_CONTENT_NAVIGATION,
47
54
  BLUME_DUPLICATE_SIDEBAR_ORDER: DOCS_CONTENT_NAVIGATION,
48
55
  BLUME_EXAMPLE_NOT_FOUND: "/docs/content/components#component",
49
56
  BLUME_FRONTMATTER_INVALID: "/docs/content/frontmatter",
57
+ BLUME_GRAPHQL_UNKNOWN_CODE_SAMPLE: "/docs/references/graphql",
50
58
  BLUME_INCLUDE_CYCLE: DOCS_CONTENT_INCLUDES,
51
59
  BLUME_INCLUDE_MALFORMED: DOCS_CONTENT_INCLUDES,
52
60
  BLUME_INCLUDE_NOT_FOUND: DOCS_CONTENT_INCLUDES,
53
61
  BLUME_INCLUDE_OUTSIDE_ROOT: DOCS_CONTENT_INCLUDES,
62
+ BLUME_MDC_SYNTAX: DOCS_OTHER_TOOLS_SYNTAX,
63
+ BLUME_MDX_ATTRIBUTE_LIST: DOCS_OTHER_TOOLS_SYNTAX,
54
64
  BLUME_MDX_CURLY_ANCHOR: "/docs/content/syntax",
55
-
65
+ BLUME_MDX_EVENT_HANDLER: "/docs/content/islands",
66
+ BLUME_MDX_SYNTAX: DOCS_MARKDOWN_AND_MDX,
67
+ BLUME_MDX_UNCLOSED_ELEMENT: DOCS_OTHER_TOOLS_SYNTAX,
68
+ BLUME_MDX_UNDEFINED_NAME: DOCS_MARKDOWN_AND_MDX,
69
+ BLUME_MD_ATTRIBUTE_LIST: DOCS_OTHER_TOOLS_SYNTAX,
70
+ BLUME_MD_CURLY_ANCHOR: "/docs/content/syntax",
71
+ BLUME_MD_DIRECTIVE: "/docs/content/syntax",
72
+ BLUME_MD_GITHUB_ALERT: "/docs/content/syntax",
56
73
  BLUME_META_INVALID: "/docs/content/meta",
57
74
  BLUME_META_LOAD_FAILED: "/docs/content/meta",
75
+ BLUME_META_OUTSIDE_INCLUDE: "/docs/content/meta",
76
+ BLUME_META_UNKNOWN_PAGE: "/docs/content/meta",
58
77
  BLUME_MISSING_SECRET: DOCS_DEPLOYMENT,
59
78
  BLUME_NAV_DUPLICATE_LABEL: DOCS_CONTENT_NAVIGATION,
60
79
  BLUME_NAV_HIDDEN_IN_SIDEBAR: DOCS_CONTENT_NAVIGATION,
61
80
  BLUME_NAV_INDEX_TITLE_MISMATCH: DOCS_CONTENT_NAVIGATION,
62
81
  BLUME_NAV_MISSING_PAGE: DOCS_CONTENT_NAVIGATION,
63
82
  BLUME_NODE_VERSION: "/docs/quickstart",
83
+ BLUME_OPENAPI_DUPLICATE_OPERATION_ID: DOCS_OPENAPI_WARNINGS,
84
+ BLUME_OPENAPI_LOCAL_SERVER: DOCS_OPENAPI_WARNINGS,
85
+ BLUME_OPENAPI_NULLABLE: DOCS_OPENAPI_WARNINGS,
86
+ BLUME_OPENAPI_UNKNOWN_CODE_SAMPLE:
87
+ "/docs/references/openapi#code-samples-and-schemas",
88
+ BLUME_PUBLIC_OPENAPI_JSON: "/docs/discoverability/json-api",
64
89
  BLUME_SEARCH_SYNC_FAILED: "/docs/configuration/search",
65
90
  BLUME_SERVER_FEATURE_REQUIRED: DOCS_DEPLOYMENT,
66
91
  BLUME_SIDEBAR_DISPLAY_IGNORED: DOCS_CONTENT_NAVIGATION,
@@ -72,15 +97,23 @@ const DOCS_PATHS = new Map(
72
97
  BLUME_SOURCE_UNAVAILABLE: DOCS_CONTENT_SOURCES,
73
98
  BLUME_SOURCE_UNRESOLVED_LINK: "/docs/content/sources/contentful",
74
99
  BLUME_SOURCE_UNSUPPORTED_NODE: "/docs/content/sources/payload",
100
+ BLUME_SVG_UNOPTIMIZED: "/docs/content/syntax#links-and-images",
101
+ BLUME_TEMPLATE_TAG: DOCS_OTHER_TOOLS_SYNTAX,
102
+ BLUME_THEME_COLOR_INVALID: "/docs/configuration/theming",
75
103
  BLUME_TRANSLATE_MISSING: "/docs/cli/translate",
76
104
  BLUME_TRANSLATE_STALE: "/docs/cli/translate",
105
+ BLUME_TSCONFIG_EXTENDS: "/docs/cli#type-checking",
106
+ BLUME_UNKNOWN_CODE_LANGUAGE: "/docs/content/syntax",
77
107
  BLUME_UNKNOWN_COMPONENT: "/docs/configuration/customization",
78
108
  BLUME_UNKNOWN_DIRECTIVE: "/docs/content/syntax",
79
109
  BLUME_UNKNOWN_ICON: DOCS_CONTENT_NAVIGATION,
80
110
  BLUME_UNKNOWN_OPTION: "/docs/cli",
111
+ BLUME_UNKNOWN_PROP: "/docs/content/components",
81
112
  BLUME_UNLOADABLE_FILE_NAME: "/docs/content",
113
+ BLUME_UNSUPPORTED_LINK_SCHEME: DOCS_CLI_VALIDATE,
82
114
  BLUME_WIKILINK_AMBIGUOUS: DOCS_CONTENT_SOURCES_OBSIDIAN,
83
115
  BLUME_WIKILINK_UNRESOLVED: DOCS_CONTENT_SOURCES_OBSIDIAN,
116
+ BLUME_WIKILINK_UNSUPPORTED: DOCS_OTHER_TOOLS_SYNTAX,
84
117
  BLUME_YARN_PNP: "/docs/quickstart",
85
118
  })
86
119
  );
@@ -317,7 +350,7 @@ const findElement = (scope: Scope, position: number): Step | undefined => {
317
350
  * Returns 1-based line/column, or undefined when not even the first segment
318
351
  * matches.
319
352
  */
320
- const locatePath = (
353
+ export const locatePath = (
321
354
  source: string,
322
355
  path: readonly (string | number)[]
323
356
  ): { column: number; line: number } | undefined => {