blume 2.0.3 → 2.1.0

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 (455) hide show
  1. package/AGENTS.md +1 -0
  2. package/CHANGELOG.md +48 -0
  3. package/README.md +1 -1
  4. package/dist/cli/{chunk-1d7ve1dm.js → chunk-0gz36akr.js} +18 -2
  5. package/dist/cli/{chunk-1d7ve1dm.js.map → chunk-0gz36akr.js.map} +3 -3
  6. package/dist/cli/{chunk-2eytanqx.js → chunk-1n228xqb.js} +2 -2
  7. package/dist/cli/{chunk-yfyb25rh.js → chunk-2f3ndkpw.js} +24 -8
  8. package/dist/cli/chunk-2f3ndkpw.js.map +14 -0
  9. package/dist/cli/{chunk-r20tn01b.js → chunk-2f7n4qzt.js} +8 -2
  10. package/dist/cli/{chunk-r20tn01b.js.map → chunk-2f7n4qzt.js.map} +3 -3
  11. package/dist/cli/{chunk-35d4wj9f.js → chunk-2n4ps1b3.js} +7 -7
  12. package/dist/cli/{chunk-j85vccga.js → chunk-43zcf4gz.js} +97 -57
  13. package/dist/cli/{chunk-j85vccga.js.map → chunk-43zcf4gz.js.map} +6 -6
  14. package/dist/cli/{chunk-e04dxsz1.js → chunk-4ejxbzee.js} +1 -1
  15. package/dist/cli/{chunk-5m5nmvyq.js → chunk-4h8z9vv5.js} +233 -39
  16. package/dist/cli/chunk-4h8z9vv5.js.map +16 -0
  17. package/dist/cli/{chunk-5f86nr5m.js → chunk-5cg2rzgf.js} +18 -16
  18. package/dist/cli/{chunk-5f86nr5m.js.map → chunk-5cg2rzgf.js.map} +2 -2
  19. package/dist/cli/{chunk-tzmab476.js → chunk-5ffywsmb.js} +4 -4
  20. package/dist/cli/{chunk-2z928egk.js → chunk-5wj2qwn2.js} +5 -5
  21. package/dist/cli/chunk-6bm0xpgd.js +18 -0
  22. package/dist/cli/chunk-6bm0xpgd.js.map +10 -0
  23. package/dist/cli/{chunk-vg9r4eb9.js → chunk-713mngdq.js} +12 -5
  24. package/dist/cli/{chunk-vg9r4eb9.js.map → chunk-713mngdq.js.map} +3 -3
  25. package/dist/cli/{chunk-5xvm6tfj.js → chunk-9geymqns.js} +17 -15
  26. package/dist/cli/{chunk-5xvm6tfj.js.map → chunk-9geymqns.js.map} +2 -2
  27. package/dist/cli/{chunk-q58y5e6a.js → chunk-9xr4dgee.js} +26 -17
  28. package/dist/cli/chunk-9xr4dgee.js.map +10 -0
  29. package/dist/cli/{chunk-b07cmahc.js → chunk-amrb6hgh.js} +2 -2
  30. package/dist/cli/chunk-cm89scdv.js +443 -0
  31. package/dist/cli/chunk-cm89scdv.js.map +11 -0
  32. package/dist/cli/{chunk-akbpwfxc.js → chunk-cmtcnh9f.js} +2 -2
  33. package/dist/cli/chunk-cy5bm0j0.js +104 -0
  34. package/dist/cli/chunk-cy5bm0j0.js.map +11 -0
  35. package/dist/cli/{chunk-crgn1q09.js → chunk-e7vvk5ya.js} +4 -4
  36. package/dist/cli/{chunk-pehfxfta.js → chunk-g2z9x01v.js} +6 -98
  37. package/dist/cli/chunk-g2z9x01v.js.map +10 -0
  38. package/dist/cli/{chunk-tkacnehg.js → chunk-gp84za0h.js} +2 -2
  39. package/dist/cli/{chunk-wrr3j9w9.js → chunk-h2a48yhd.js} +186 -63
  40. package/dist/cli/chunk-h2a48yhd.js.map +36 -0
  41. package/dist/cli/{chunk-hm3vjy5s.js → chunk-jnwj5kjj.js} +14 -9
  42. package/dist/cli/{chunk-hm3vjy5s.js.map → chunk-jnwj5kjj.js.map} +4 -4
  43. package/dist/cli/chunk-kwrg63tm.js +31 -0
  44. package/dist/cli/chunk-kwrg63tm.js.map +10 -0
  45. package/dist/cli/{chunk-2hsdwb9n.js → chunk-nd1821sk.js} +18 -16
  46. package/dist/cli/{chunk-2hsdwb9n.js.map → chunk-nd1821sk.js.map} +2 -2
  47. package/dist/cli/{chunk-6dsbexzp.js → chunk-ntv4qrt7.js} +17 -15
  48. package/dist/cli/{chunk-6dsbexzp.js.map → chunk-ntv4qrt7.js.map} +2 -2
  49. package/dist/cli/{chunk-r9rcc4w7.js → chunk-p804f3pe.js} +7 -7
  50. package/dist/cli/{chunk-ey84smr6.js → chunk-py62fewh.js} +6 -6
  51. package/dist/cli/{chunk-ey84smr6.js.map → chunk-py62fewh.js.map} +1 -1
  52. package/dist/cli/chunk-q3wxxq39.js +126 -0
  53. package/dist/cli/chunk-q3wxxq39.js.map +11 -0
  54. package/dist/cli/{chunk-9t7a85s3.js → chunk-rekqz4qg.js} +23 -13
  55. package/dist/cli/chunk-rekqz4qg.js.map +10 -0
  56. package/dist/cli/{chunk-8ktnccpt.js → chunk-ta27feev.js} +1 -1
  57. package/dist/cli/{chunk-pv29h0wf.js → chunk-xkz5xbtv.js} +3476 -822
  58. package/dist/cli/chunk-xkz5xbtv.js.map +213 -0
  59. package/dist/cli/{chunk-a58773jm.js → chunk-y4ykbark.js} +2 -2
  60. package/dist/cli/{chunk-p3v96n38.js → chunk-y5h4r2y0.js} +4 -4
  61. package/dist/cli/{chunk-acanzt5p.js → chunk-yj17vs39.js} +6 -6
  62. package/dist/cli/{chunk-p73c0m7w.js → chunk-yxmk4pec.js} +16 -14
  63. package/dist/cli/{chunk-p73c0m7w.js.map → chunk-yxmk4pec.js.map} +2 -2
  64. package/dist/cli/{chunk-3em5wd2y.js → chunk-zccbjcj0.js} +4 -3
  65. package/dist/cli/{chunk-3em5wd2y.js.map → chunk-zccbjcj0.js.map} +2 -2
  66. package/dist/cli/{chunk-c8chx29p.js → chunk-zf748avv.js} +384 -207
  67. package/dist/cli/chunk-zf748avv.js.map +37 -0
  68. package/dist/cli/{chunk-g4hq16wv.js → chunk-zhvd6adm.js} +16 -14
  69. package/dist/cli/{chunk-g4hq16wv.js.map → chunk-zhvd6adm.js.map} +2 -2
  70. package/dist/cli/index.js +26 -20
  71. package/dist/cli/index.js.map +5 -5
  72. package/dist/types/ai/agent-surface.d.ts +8 -7
  73. package/dist/types/ai/ai-catalog.d.ts +7 -0
  74. package/dist/types/ai/ask-context.d.ts +22 -0
  75. package/dist/types/ai/ask.d.ts +271 -58
  76. package/dist/types/ai/index.d.ts +2 -2
  77. package/dist/types/ai/site-skill.d.ts +26 -0
  78. package/dist/types/ai/skills.d.ts +16 -0
  79. package/dist/types/astro/integration.d.ts +8 -0
  80. package/dist/types/captcha/hcaptcha.d.ts +27 -0
  81. package/dist/types/captcha/index.d.ts +28 -0
  82. package/dist/types/captcha/schema.d.ts +29 -0
  83. package/dist/types/captcha/turnstile.d.ts +28 -0
  84. package/dist/types/components/content/api-field.d.ts +59 -0
  85. package/dist/types/components/content/api-page.d.ts +95 -0
  86. package/dist/types/components/layout/nav-utils.d.ts +26 -0
  87. package/dist/types/components/openapi/helpers.d.ts +137 -0
  88. package/dist/types/components/openapi/security.d.ts +85 -0
  89. package/dist/types/consent/ethyca.d.ts +48 -0
  90. package/dist/types/consent/index.d.ts +37 -0
  91. package/dist/types/consent/native.d.ts +28 -0
  92. package/dist/types/consent/osano.d.ts +42 -0
  93. package/dist/types/consent/schema.d.ts +110 -0
  94. package/dist/types/core/config-input.d.ts +187 -20
  95. package/dist/types/core/data.d.ts +49 -4
  96. package/dist/types/core/footer.d.ts +8 -0
  97. package/dist/types/core/i18n-ui.d.ts +86 -0
  98. package/dist/types/core/includes.d.ts +8 -2
  99. package/dist/types/core/localizable.d.ts +7 -0
  100. package/dist/types/core/page-modes.d.ts +29 -0
  101. package/dist/types/core/redirect-patterns.d.ts +144 -0
  102. package/dist/types/core/related.d.ts +40 -0
  103. package/dist/types/core/schema.d.ts +551 -56
  104. package/dist/types/core/sources/read.d.ts +5 -0
  105. package/dist/types/core/types.d.ts +1 -1
  106. package/dist/types/core/variables.d.ts +42 -0
  107. package/dist/types/deploy/node-headers.d.ts +13 -2
  108. package/dist/types/deploy/platforms/cloudflare.d.ts +5 -0
  109. package/dist/types/deploy/platforms/netlify.d.ts +7 -0
  110. package/dist/types/deploy/platforms/vercel.d.ts +6 -0
  111. package/dist/types/deploy/redirects.d.ts +39 -11
  112. package/dist/types/deploy/vercel-negotiation.d.ts +9 -0
  113. package/dist/types/markdown/code-title.d.ts +17 -0
  114. package/dist/types/markdown/features.d.ts +3 -2
  115. package/dist/types/narration/build.d.ts +70 -0
  116. package/dist/types/narration/provider.d.ts +26 -0
  117. package/dist/types/narration/script.d.ts +154 -0
  118. package/dist/types/openapi/model.d.ts +12 -6
  119. package/dist/types/openapi/overlay.d.ts +54 -0
  120. package/dist/types/openapi/parse.d.ts +13 -0
  121. package/dist/types/openapi/references.d.ts +14 -4
  122. package/dist/types/ratelimit/cloudflare.d.ts +39 -0
  123. package/dist/types/ratelimit/index.d.ts +27 -0
  124. package/dist/types/ratelimit/memory.d.ts +36 -0
  125. package/dist/types/ratelimit/schema.d.ts +111 -0
  126. package/dist/types/ratelimit/upstash.d.ts +21 -0
  127. package/dist/types/ratelimit/wrangler.d.ts +23 -0
  128. package/dist/types/reference/asyncapi.d.ts +10 -10
  129. package/dist/types/reference/graphql.d.ts +13 -10
  130. package/dist/types/reference/openapi.d.ts +33 -12
  131. package/dist/types/reference/options.d.ts +45 -5
  132. package/dist/types/reference/scalar.d.ts +13 -0
  133. package/dist/types/reference/schema.d.ts +60 -36
  134. package/dist/types/search/documents.d.ts +12 -1
  135. package/dist/types/search/orama-index.d.ts +4 -0
  136. package/dist/types/seo/metatags.d.ts +20 -0
  137. package/docs/02-deployment.mdx +41 -5
  138. package/docs/03-upgrading.mdx +4 -4
  139. package/docs/04-migrating.mdx +1 -1
  140. package/docs/advanced/changelog.mdx +2 -2
  141. package/docs/advanced/skills.mdx +14 -0
  142. package/docs/cli/audit.mdx +21 -1
  143. package/docs/cli/index.mdx +1 -0
  144. package/docs/configuration/analytics.mdx +3 -1
  145. package/docs/configuration/assistant.mdx +188 -36
  146. package/docs/configuration/consent.mdx +100 -0
  147. package/docs/configuration/customization.mdx +4 -4
  148. package/docs/configuration/index.mdx +63 -5
  149. package/docs/configuration/meta.ts +3 -0
  150. package/docs/configuration/narration.mdx +119 -0
  151. package/docs/configuration/rate-limiting.mdx +69 -0
  152. package/docs/configuration/search.mdx +29 -1
  153. package/docs/configuration/theming.mdx +3 -1
  154. package/docs/content/components.mdx +106 -0
  155. package/docs/content/frontmatter.mdx +76 -0
  156. package/docs/content/i18n.mdx +38 -3
  157. package/docs/content/includes.mdx +16 -0
  158. package/docs/content/meta.mdx +1 -0
  159. package/docs/content/meta.ts +1 -0
  160. package/docs/content/navigation.mdx +28 -6
  161. package/docs/content/sources/contentful.mdx +32 -0
  162. package/docs/content/sources/custom.mdx +39 -0
  163. package/docs/content/sources/github-releases.mdx +31 -0
  164. package/docs/content/sources/index.mdx +91 -0
  165. package/docs/content/sources/meta.ts +16 -0
  166. package/docs/content/sources/notion.mdx +32 -0
  167. package/docs/content/sources/obsidian.mdx +37 -0
  168. package/docs/content/sources/payload.mdx +30 -0
  169. package/docs/content/sources/remote-mdx.mdx +40 -0
  170. package/docs/content/sources/sanity.mdx +29 -0
  171. package/docs/content/sources/strapi.mdx +31 -0
  172. package/docs/content/syntax.mdx +67 -2
  173. package/docs/content/variables.mdx +41 -0
  174. package/docs/discoverability/agent-discovery.mdx +29 -2
  175. package/docs/discoverability/index.mdx +1 -1
  176. package/docs/discoverability/llms-txt.mdx +1 -1
  177. package/docs/discoverability/metadata.mdx +17 -0
  178. package/docs/references/api-pages.mdx +74 -0
  179. package/docs/references/asyncapi.mdx +1 -1
  180. package/docs/references/graphql.mdx +2 -2
  181. package/docs/references/meta.ts +1 -1
  182. package/docs/references/openapi.mdx +102 -3
  183. package/docs/references/scalar.mdx +3 -1
  184. package/package.json +36 -1
  185. package/skills/blume/SKILL.md +3 -0
  186. package/skills/blume-migrate/SKILL.md +5 -5
  187. package/skills/blume-migrate/references/docusaurus.md +3 -3
  188. package/skills/blume-migrate/references/mintlify.md +37 -19
  189. package/skills/blume-migrate/references/nextra.md +2 -2
  190. package/skills/blume-migrate/references/starlight.md +2 -1
  191. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +60 -14
  192. package/skills/blume-write-skill/SKILL.md +82 -0
  193. package/src/ai/agent-readability.ts +2 -1
  194. package/src/ai/agent-surface.ts +9 -7
  195. package/src/ai/ai-catalog.ts +6 -3
  196. package/src/ai/ask-context.ts +73 -20
  197. package/src/ai/ask-data.ts +6 -0
  198. package/src/ai/ask-tools.ts +142 -0
  199. package/src/ai/ask.ts +354 -76
  200. package/src/ai/component-markdown.ts +75 -0
  201. package/src/ai/cors.ts +2 -0
  202. package/src/ai/index.ts +12 -0
  203. package/src/ai/llms.ts +6 -2
  204. package/src/ai/markdown.ts +2 -1
  205. package/src/ai/mcp/data.ts +6 -0
  206. package/src/ai/openapi-components.ts +9 -3
  207. package/src/ai/site-skill.ts +274 -0
  208. package/src/ai/skills.ts +35 -2
  209. package/src/ai/write-skill.ts +97 -0
  210. package/src/astro/generate.ts +132 -18
  211. package/src/astro/index.ts +1 -0
  212. package/src/astro/integration.ts +35 -0
  213. package/src/astro/module-types.ts +6 -0
  214. package/src/astro/templates.ts +283 -30
  215. package/src/astro/variables.ts +31 -0
  216. package/src/audit/catalog.ts +19 -0
  217. package/src/audit/checks/contrast.ts +183 -0
  218. package/src/audit/checks/i18n.ts +2 -3
  219. package/src/audit/checks/indexability.ts +2 -3
  220. package/src/audit/checks/links.ts +5 -12
  221. package/src/audit/checks/sitemap.ts +2 -3
  222. package/src/audit/redirects.ts +46 -15
  223. package/src/audit/run.ts +2 -0
  224. package/src/audit/types.ts +2 -1
  225. package/src/blume-modules.d.ts +7 -0
  226. package/src/captcha/client.ts +152 -0
  227. package/src/captcha/hcaptcha.ts +36 -0
  228. package/src/captcha/index.ts +28 -0
  229. package/src/captcha/schema.ts +32 -0
  230. package/src/captcha/turnstile.ts +38 -0
  231. package/src/captcha/verify.ts +71 -0
  232. package/src/cli/command-meta.ts +5 -0
  233. package/src/cli/commands/doctor.ts +22 -16
  234. package/src/cli/commands/skill.ts +96 -0
  235. package/src/cli/index.ts +5 -0
  236. package/src/cli/required-secrets.ts +71 -29
  237. package/src/components/BlumePage.astro +2 -0
  238. package/src/components/content/ApiEndpoint.astro +18 -0
  239. package/src/components/content/ApiField.astro +79 -0
  240. package/src/components/content/ApiPlayground.astro +62 -0
  241. package/src/components/content/ApiRail.astro +19 -0
  242. package/src/components/content/Color.astro +1 -0
  243. package/src/components/content/FileTree.astro +1 -0
  244. package/src/components/content/GithubInfo.astro +1 -0
  245. package/src/components/content/Panel.astro +1 -0
  246. package/src/components/content/ParamField.astro +25 -0
  247. package/src/components/content/RequestExample.astro +17 -0
  248. package/src/components/content/ResponseExample.astro +17 -0
  249. package/src/components/content/ResponseField.astro +32 -0
  250. package/src/components/content/Step.astro +1 -0
  251. package/src/components/content/Tile.astro +1 -0
  252. package/src/components/content/Tooltip.astro +1 -0
  253. package/src/components/content/Tree.astro +1 -0
  254. package/src/components/content/TypeTable.astro +4 -1
  255. package/src/components/content/View.astro +21 -0
  256. package/src/components/content/ViewSwitcher.astro +168 -0
  257. package/src/components/content/api-field.ts +100 -0
  258. package/src/components/content/api-page.ts +224 -0
  259. package/src/components/content/view-switcher.ts +71 -0
  260. package/src/components/index.ts +2 -0
  261. package/src/components/islands/Assistant.astro +10 -1
  262. package/src/components/islands/assistant.tsx +104 -14
  263. package/src/components/islands/code-question.ts +62 -0
  264. package/src/components/islands/hooks.ts +110 -21
  265. package/src/components/islands/support-link.ts +68 -0
  266. package/src/components/layout/Analytics.astro +11 -2
  267. package/src/components/layout/Banner.astro +14 -10
  268. package/src/components/layout/Breadcrumbs.astro +6 -2
  269. package/src/components/layout/ConsentBanner.astro +62 -0
  270. package/src/components/layout/ConsentHead.astro +38 -0
  271. package/src/components/layout/Directory.astro +94 -0
  272. package/src/components/layout/DrawerSwitchers.astro +51 -0
  273. package/src/components/layout/Empty.astro +2 -2
  274. package/src/components/layout/Header.astro +122 -80
  275. package/src/components/layout/LanguageSwitcher.astro +48 -15
  276. package/src/components/layout/NarrationPlayer.astro +1085 -0
  277. package/src/components/layout/NavSelector.astro +33 -7
  278. package/src/components/layout/PageFeedback.astro +54 -49
  279. package/src/components/layout/PageLayout.astro +71 -3
  280. package/src/components/layout/ReferenceLayout.astro +17 -6
  281. package/src/components/layout/RelatedPages.astro +42 -0
  282. package/src/components/layout/RootLayout.astro +308 -45
  283. package/src/components/layout/Search.astro +31 -17
  284. package/src/components/layout/SiteFooter.astro +124 -0
  285. package/src/components/layout/SiteMetatags.astro +16 -0
  286. package/src/components/layout/consent/native.ts +79 -0
  287. package/src/components/layout/consent/types.ts +37 -0
  288. package/src/components/layout/footer-links.ts +53 -0
  289. package/src/components/layout/footer-socials.ts +87 -0
  290. package/src/components/layout/locale-redirect.ts +107 -0
  291. package/src/components/layout/nav-utils.ts +83 -0
  292. package/src/components/layout/page-feedback.ts +96 -0
  293. package/src/components/layout/search/analytics.ts +98 -0
  294. package/src/components/layout/search/flexsearch.ts +17 -5
  295. package/src/components/layout/search/orama-cloud.ts +26 -14
  296. package/src/components/layout/search/types.ts +20 -0
  297. package/src/components/layout/search/typesense.ts +5 -1
  298. package/src/components/openapi/Operation.astro +161 -13
  299. package/src/components/openapi/RequestBody.astro +6 -4
  300. package/src/components/openapi/RequestPanel.astro +59 -7
  301. package/src/components/openapi/Responses.astro +6 -4
  302. package/src/components/openapi/async-snippets.ts +4 -4
  303. package/src/components/openapi/code-samples.ts +97 -0
  304. package/src/components/openapi/security.ts +6 -3
  305. package/src/components/openapi/snippets.ts +869 -8
  306. package/src/components/props.ts +6 -0
  307. package/src/consent/client.ts +95 -0
  308. package/src/consent/clients.ts +19 -0
  309. package/src/consent/ethyca.ts +71 -0
  310. package/src/consent/head.ts +24 -0
  311. package/src/consent/index.ts +37 -0
  312. package/src/consent/init.ts +29 -0
  313. package/src/consent/native.ts +36 -0
  314. package/src/consent/osano.ts +59 -0
  315. package/src/consent/schema.ts +30 -0
  316. package/src/core/builtin-tags.ts +5 -0
  317. package/src/core/client-address.ts +19 -0
  318. package/src/core/config-input.ts +194 -20
  319. package/src/core/data.ts +44 -4
  320. package/src/core/diagnostics.ts +3 -2
  321. package/src/core/footer.ts +26 -0
  322. package/src/core/graph.ts +39 -36
  323. package/src/core/i18n-ui.ts +82 -2
  324. package/src/core/includes.ts +20 -3
  325. package/src/core/links.ts +16 -2
  326. package/src/core/localizable.ts +26 -0
  327. package/src/core/navigation.ts +43 -12
  328. package/src/core/page-modes.ts +56 -0
  329. package/src/core/project-graph.ts +111 -5
  330. package/src/core/redirect-patterns.ts +491 -0
  331. package/src/core/related.ts +131 -0
  332. package/src/core/schema.ts +318 -64
  333. package/src/core/sources/normalize.ts +2 -1
  334. package/src/core/sources/read.ts +8 -3
  335. package/src/core/types.ts +1 -1
  336. package/src/core/ui-packs/ar.ts +43 -0
  337. package/src/core/ui-packs/bg.ts +45 -0
  338. package/src/core/ui-packs/bn.ts +43 -0
  339. package/src/core/ui-packs/ca.ts +46 -0
  340. package/src/core/ui-packs/cs.ts +44 -0
  341. package/src/core/ui-packs/da.ts +45 -0
  342. package/src/core/ui-packs/de.ts +46 -0
  343. package/src/core/ui-packs/el.ts +45 -0
  344. package/src/core/ui-packs/es.ts +46 -0
  345. package/src/core/ui-packs/fa.ts +45 -0
  346. package/src/core/ui-packs/fi.ts +45 -0
  347. package/src/core/ui-packs/fr.ts +46 -0
  348. package/src/core/ui-packs/he.ts +43 -0
  349. package/src/core/ui-packs/hi.ts +44 -0
  350. package/src/core/ui-packs/hr.ts +45 -0
  351. package/src/core/ui-packs/hu.ts +44 -0
  352. package/src/core/ui-packs/id.ts +45 -0
  353. package/src/core/ui-packs/it.ts +45 -0
  354. package/src/core/ui-packs/ja.ts +45 -0
  355. package/src/core/ui-packs/ko.ts +44 -0
  356. package/src/core/ui-packs/nl.ts +46 -0
  357. package/src/core/ui-packs/no.ts +44 -0
  358. package/src/core/ui-packs/pl.ts +45 -0
  359. package/src/core/ui-packs/pt-br.ts +46 -0
  360. package/src/core/ui-packs/pt.ts +45 -0
  361. package/src/core/ui-packs/ro.ts +45 -0
  362. package/src/core/ui-packs/ru.ts +45 -0
  363. package/src/core/ui-packs/sk.ts +44 -0
  364. package/src/core/ui-packs/sr.ts +45 -0
  365. package/src/core/ui-packs/sv.ts +44 -0
  366. package/src/core/ui-packs/th.ts +43 -0
  367. package/src/core/ui-packs/tr.ts +45 -0
  368. package/src/core/ui-packs/uk.ts +45 -0
  369. package/src/core/ui-packs/vi.ts +45 -0
  370. package/src/core/ui-packs/zh-tw.ts +43 -0
  371. package/src/core/ui-packs/zh.ts +43 -0
  372. package/src/core/variables.ts +103 -0
  373. package/src/deploy/artifacts.ts +77 -8
  374. package/src/deploy/cloudflare-negotiation.ts +35 -5
  375. package/src/deploy/headers.ts +2 -1
  376. package/src/deploy/node-headers.ts +63 -9
  377. package/src/deploy/platforms/cloudflare.ts +32 -0
  378. package/src/deploy/platforms/netlify.ts +33 -1
  379. package/src/deploy/platforms/vercel.ts +37 -2
  380. package/src/deploy/redirects.ts +91 -24
  381. package/src/deploy/vercel-negotiation.ts +36 -0
  382. package/src/markdown/api-rail.ts +259 -0
  383. package/src/markdown/code-title.ts +35 -9
  384. package/src/markdown/features.ts +3 -2
  385. package/src/markdown/fence-meta.ts +2 -0
  386. package/src/markdown/include.ts +11 -1
  387. package/src/markdown/index.ts +28 -5
  388. package/src/markdown/math.ts +4 -4
  389. package/src/markdown/variables.ts +56 -0
  390. package/src/markdown/views.ts +82 -0
  391. package/src/narration/build.ts +412 -0
  392. package/src/narration/player.ts +249 -0
  393. package/src/narration/provider.ts +29 -0
  394. package/src/narration/script.ts +592 -0
  395. package/src/og/card.ts +7 -6
  396. package/src/openapi/model.ts +57 -24
  397. package/src/openapi/overlay.ts +256 -0
  398. package/src/openapi/parse.ts +55 -2
  399. package/src/openapi/references.ts +21 -5
  400. package/src/openapi/render-mdx.ts +5 -1
  401. package/src/openapi/scalar.ts +40 -6
  402. package/src/openapi/source.ts +1 -1
  403. package/src/ratelimit/cloudflare.ts +54 -0
  404. package/src/ratelimit/index.ts +30 -0
  405. package/src/ratelimit/memory.ts +45 -0
  406. package/src/ratelimit/runtime.ts +229 -0
  407. package/src/ratelimit/schema.ts +28 -0
  408. package/src/ratelimit/upstash.ts +30 -0
  409. package/src/ratelimit/wrangler.ts +70 -0
  410. package/src/reference/asyncapi.ts +2 -2
  411. package/src/reference/graphql.ts +5 -2
  412. package/src/reference/openapi.ts +21 -7
  413. package/src/reference/options.ts +51 -7
  414. package/src/reference/scalar.ts +33 -8
  415. package/src/registry/eject.ts +20 -4
  416. package/src/registry/registry.ts +7 -0
  417. package/src/search/documents.ts +26 -2
  418. package/src/search/orama-index.ts +28 -3
  419. package/src/search/pagefind-ranking.ts +39 -0
  420. package/src/search/sync/algolia.ts +15 -4
  421. package/src/search/sync/orama-cloud.ts +3 -0
  422. package/src/search/sync/typesense.ts +5 -0
  423. package/src/seo/metatags.ts +67 -0
  424. package/src/theme/chrome-icons.ts +1 -0
  425. package/src/theme/color.ts +500 -0
  426. package/src/theme/entry.ts +56 -3
  427. package/src/theme/palette.ts +121 -45
  428. package/src/translate/prompts.ts +1 -0
  429. package/src/translate/validate.ts +8 -2
  430. package/src/upgrade/upgrade.ts +4 -171
  431. package/dist/cli/chunk-364znk6q.js +0 -75
  432. package/dist/cli/chunk-364znk6q.js.map +0 -11
  433. package/dist/cli/chunk-5m5nmvyq.js.map +0 -13
  434. package/dist/cli/chunk-9t7a85s3.js.map +0 -10
  435. package/dist/cli/chunk-c8chx29p.js.map +0 -36
  436. package/dist/cli/chunk-pehfxfta.js.map +0 -10
  437. package/dist/cli/chunk-pv29h0wf.js.map +0 -190
  438. package/dist/cli/chunk-q58y5e6a.js.map +0 -10
  439. package/dist/cli/chunk-wrr3j9w9.js.map +0 -35
  440. package/dist/cli/chunk-yfyb25rh.js.map +0 -14
  441. package/docs/content/sources.mdx +0 -334
  442. /package/dist/cli/{chunk-2eytanqx.js.map → chunk-1n228xqb.js.map} +0 -0
  443. /package/dist/cli/{chunk-35d4wj9f.js.map → chunk-2n4ps1b3.js.map} +0 -0
  444. /package/dist/cli/{chunk-e04dxsz1.js.map → chunk-4ejxbzee.js.map} +0 -0
  445. /package/dist/cli/{chunk-tzmab476.js.map → chunk-5ffywsmb.js.map} +0 -0
  446. /package/dist/cli/{chunk-2z928egk.js.map → chunk-5wj2qwn2.js.map} +0 -0
  447. /package/dist/cli/{chunk-b07cmahc.js.map → chunk-amrb6hgh.js.map} +0 -0
  448. /package/dist/cli/{chunk-akbpwfxc.js.map → chunk-cmtcnh9f.js.map} +0 -0
  449. /package/dist/cli/{chunk-crgn1q09.js.map → chunk-e7vvk5ya.js.map} +0 -0
  450. /package/dist/cli/{chunk-tkacnehg.js.map → chunk-gp84za0h.js.map} +0 -0
  451. /package/dist/cli/{chunk-r9rcc4w7.js.map → chunk-p804f3pe.js.map} +0 -0
  452. /package/dist/cli/{chunk-8ktnccpt.js.map → chunk-ta27feev.js.map} +0 -0
  453. /package/dist/cli/{chunk-a58773jm.js.map → chunk-y4ykbark.js.map} +0 -0
  454. /package/dist/cli/{chunk-p3v96n38.js.map → chunk-y5h4r2y0.js.map} +0 -0
  455. /package/dist/cli/{chunk-acanzt5p.js.map → chunk-yj17vs39.js.map} +0 -0
@@ -0,0 +1,40 @@
1
+ ---
2
+ title: Remote MDX
3
+ description: Fetch Markdown and MDX over HTTP from a GitHub repository or a raw URL with the mdxRemote() source, rendered with every Blume component.
4
+ ---
5
+
6
+ The built-in `mdxRemote()` adapter fetches raw `.md`/`.mdx` over HTTP. Enumerate files either from a GitHub repo subtree (`github`) or explicitly against a raw base URL (`url` + `files`):
7
+
8
+ ```ts blume.config.ts
9
+ import { defineConfig } from "blume";
10
+ import { filesystem, mdxRemote } from "blume/sources";
11
+
12
+ export default defineConfig({
13
+ content: {
14
+ sources: [
15
+ filesystem({ root: "docs" }),
16
+ // The docs/ subtree of acme/sdk, mounted under /sdk
17
+ mdxRemote({
18
+ prefix: "sdk",
19
+ github: { owner: "acme", repo: "sdk", ref: "main", path: "docs" },
20
+ }),
21
+ ],
22
+ },
23
+ });
24
+ ```
25
+
26
+ To list the files yourself, point `url` at a raw base URL:
27
+
28
+ ```ts blume.config.ts
29
+ mdxRemote({
30
+ prefix: "sdk",
31
+ url: "https://raw.githubusercontent.com/acme/sdk/main/docs",
32
+ files: ["intro.mdx", "guide.mdx"],
33
+ });
34
+ ```
35
+
36
+ A private repo's token is read from the `GITHUB_TOKEN` environment variable — the adapter declares it, so `blume dev` and `blume build` warn when it's unset. It is never inlined into your config or generated output, and it is only ever sent to GitHub's own hosts (`api.github.com`, `raw.githubusercontent.com`), never to a custom `url` base. A public repo works without it.
37
+
38
+ Remote pages are rendered with full MDX-plus-component fidelity: their bodies are materialized into a hidden staging directory and rendered through Astro alongside your local docs, so callouts, tabs, and every other Blume component keep working.
39
+
40
+ Like every remote source, fetched pages are cached for offline builds and refreshed with `blume sync` — see [Caching and offline builds](/docs/content/sources#caching-and-offline-builds).
@@ -0,0 +1,29 @@
1
+ ---
2
+ title: Sanity
3
+ description: Pull documents from a Sanity dataset with the sanity() source — a GROQ query picks the documents and their Portable Text bodies become Markdown.
4
+ ---
5
+
6
+ The built-in `sanity()` adapter runs a GROQ query and maps each document's fields to frontmatter and its Portable Text body to Markdown. The adapter declares `@sanity/client` as its runtime dependency — an optional peer, so install it only if you use this source.
7
+
8
+ ```ts blume.config.ts
9
+ import { defineConfig } from "blume";
10
+ import { filesystem, sanity } from "blume/sources";
11
+
12
+ export default defineConfig({
13
+ content: {
14
+ sources: [
15
+ filesystem({ root: "docs" }),
16
+ sanity({
17
+ prefix: "guides",
18
+ projectId: "abc123",
19
+ dataset: "production",
20
+ query: `*[_type == "guide"]`,
21
+ // Field paths default to title / slug.current / body / _updatedAt
22
+ fields: { slug: "slug.current", body: "content" },
23
+ }),
24
+ ],
25
+ },
26
+ });
27
+ ```
28
+
29
+ A read token for a private dataset comes from the `SANITY_TOKEN` environment variable, which the adapter declares. Custom Portable Text block types map to Blume components through the engine's `serializers` option, available when you construct `sanitySource` directly and pass it to [`custom()`](/docs/content/sources/custom). Setting `serializers` writes the source's pages as MDX, so a component or directive a serializer returns renders.
@@ -0,0 +1,31 @@
1
+ ---
2
+ title: Strapi
3
+ description: Read a Strapi content type through its REST API with the strapi() source, lowering Blocks bodies to Markdown on both Strapi 4 and Strapi 5.
4
+ ---
5
+
6
+ The built-in `strapi()` adapter reads a content type through the Strapi REST API (`/api/<pluralApiId>`) and lowers each entry's Blocks body to Markdown: paragraphs, headings, lists, quotes, code blocks, images, and links. A Markdown rich text field passes through as written, except that a link that isn't a web, mail, phone, or relative address keeps only its label. Strapi 5 responses are read as-is, and the Strapi 4 `attributes` envelope is flattened so the same field paths apply. Nothing to install.
7
+
8
+ ```ts blume.config.ts
9
+ import { defineConfig } from "blume";
10
+ import { filesystem, strapi } from "blume/sources";
11
+
12
+ export default defineConfig({
13
+ content: {
14
+ sources: [
15
+ filesystem({ root: "docs" }),
16
+ strapi({
17
+ prefix: "guides",
18
+ url: "https://cms.example.com",
19
+ contentType: "guides",
20
+ // locale: "en"
21
+ // Field paths default to title / description / slug / content / updatedAt
22
+ fields: { body: "body" },
23
+ // Extra query parameters: filters[...], sort
24
+ params: { "filters[section][$eq]": "sdk" },
25
+ }),
26
+ ],
27
+ },
28
+ });
29
+ ```
30
+
31
+ The API token comes from the `STRAPI_API_TOKEN` environment variable, sent as a bearer token. Entries are fetched with `populate=*` so images carry their URLs (set `populate` to narrow it), and a relative upload path (`/uploads/x.png`) resolves against `url`. Only published entries are imported — `--preview` requests drafts (`status=draft` on Strapi 5, `publicationState=preview` on Strapi 4) and stages each document that has never been published with `draft: true`; a published document previews its latest draft.
@@ -248,6 +248,65 @@ createServer().listen(3000);
248
248
  ```
249
249
  ````
250
250
 
251
+ ### Long lines and long blocks
252
+
253
+ Append `wrap` to wrap one block's long lines instead of scrolling them sideways:
254
+
255
+ ```ts client.ts wrap
256
+ const client = createClient({
257
+ baseUrl: "https://api.example.com/v1",
258
+ retries: 3,
259
+ timeout: 10_000,
260
+ headers: { "x-client": "docs" },
261
+ });
262
+ ```
263
+
264
+ ````md
265
+ ```ts client.ts wrap
266
+ const client = createClient({
267
+ baseUrl: "https://api.example.com/v1",
268
+ retries: 3,
269
+ timeout: 10_000,
270
+ headers: { "x-client": "docs" },
271
+ });
272
+ ```
273
+ ````
274
+
275
+ Append `expandable` to a long block to show its first lines with a **Show more** toggle under them. Opened, the block shows in full, with no scrolling inside it. A block under 16 lines renders as usual, since there's little to hide.
276
+
277
+ ```ts routes.ts expandable
278
+ import { defineRoutes } from "./router";
279
+
280
+ export default defineRoutes({
281
+ home: "/",
282
+ docs: "/docs",
283
+ guides: "/docs/guides",
284
+ reference: "/docs/reference",
285
+ changelog: "/changelog",
286
+ blog: "/blog",
287
+ pricing: "/pricing",
288
+ about: "/about",
289
+ careers: "/careers",
290
+ contact: "/contact",
291
+ privacy: "/legal/privacy",
292
+ terms: "/legal/terms",
293
+ security: "/security",
294
+ status: "https://status.example.com",
295
+ });
296
+ ```
297
+
298
+ ````md
299
+ ```ts routes.ts expandable
300
+ import { defineRoutes } from "./router";
301
+
302
+ export default defineRoutes({
303
+ // …every route
304
+ });
305
+ ```
306
+ ````
307
+
308
+ Both work alongside a title and `lineNumbers`. To wrap every block, set `markdown.code.wrap` instead.
309
+
251
310
  ### Highlighting
252
311
 
253
312
  Annotate code with GitHub-style comments to draw attention to lines, words, and changes. The comments are stripped from the rendered output, so the code stays copy-paste clean. All four are on by default — no configuration.
@@ -625,7 +684,7 @@ Commit `blume.config.ts`, not `.blume/`.
625
684
 
626
685
  ## Math
627
686
 
628
- Render LaTeX with KaTeX as centered blocks — useful for math-heavy or scientific docs. Wrap a formula in `$$…$$`:
687
+ Render LaTeX with KaTeX, useful for math-heavy or scientific docs. Put `$$…$$` on lines of its own for a centered block:
629
688
 
630
689
  $$
631
690
  \int_0^\infty e^{-x^2}\,dx = \frac{\sqrt{\pi}}{2}
@@ -637,8 +696,14 @@ a^2 + b^2 = c^2
637
696
  $$
638
697
  ```
639
698
 
699
+ Or write `$$…$$` inside a sentence for inline math, like Euler's identity $$e^{i\pi} + 1 = 0$$:
700
+
701
+ ```md
702
+ Like Euler's identity $$e^{i\pi} + 1 = 0$$.
703
+ ```
704
+
640
705
  :::note
641
- Math is block-only and on automatically — write `$$…$$` and it renders; write none and KaTeX's stylesheet never ships. There's no inline `$…$` math: a lone `$` (currency, shell variables, code) is always left as literal text, so there's no delimiter to escape and no setting to toggle. Math is an MDX-only feature. The class names inside the rendered markup (`.katex-html`, `.katex-base`, …) are KaTeX's own internals, not a styling contract Blume maintains — they can change when KaTeX upgrades, so target the `.katex-display` wrapper for custom styles.
706
+ Math is on automatically: write `$$…$$` and it renders; write none and KaTeX's stylesheet never ships. Both block and inline math use two dollar signs. A single `$` (currency, shell variables, code) is always literal text, so "$5 and $10" never turns into math, and there's no delimiter to escape and no setting to toggle. Math is an MDX-only feature. The class names inside the rendered markup (`.katex-html`, `.katex-base`, …) are KaTeX's own internals, not a styling contract Blume maintains — they can change when KaTeX upgrades, so target the `.katex-display` wrapper for custom styles.
642
707
  :::
643
708
 
644
709
  ## Smart punctuation
@@ -0,0 +1,41 @@
1
+ ---
2
+ title: Variables
3
+ description: Define a value once in blume.config.ts and write {{name}} anywhere a page needs it, in prose, code, links, and component props.
4
+ ---
5
+
6
+ Define a value once and use it across every page. A version number, an API host, or a product name lives in `blume.config.ts`, and `{{name}}` in a page reads its value:
7
+
8
+ ```ts blume.config.ts lineNumbers
9
+ export default defineConfig({
10
+ variables: {
11
+ version: "2.1.0",
12
+ "api-url": "https://api.example.com",
13
+ },
14
+ });
15
+ ```
16
+
17
+ ```mdx docs/quickstart.mdx
18
+ Install version {{version}}, then call {{api-url}}.
19
+
20
+ ## What's new in {{version}}
21
+
22
+ <Card title="Release {{version}}" href="{{api-url}}/changelog" />
23
+ ```
24
+
25
+ References work in prose, headings, links, code blocks, inline code, and component props, in `.md` and `.mdx` pages and in the files they [include](/docs/content/includes). Spaces inside the braces are fine (`{{ version }}`). Change the value in the config and every page picks it up on the next build.
26
+
27
+ Names take letters, digits, `_`, and `-`. Values are plain text on one line.
28
+
29
+ ## Where they apply
30
+
31
+ A variable is replaced before anything reads the page, so the rendered HTML, [search](/docs/configuration/search), the page's [`.md` mirror](/docs/discoverability/markdown), and `llms-full.txt` all show the value.
32
+
33
+ Front matter is left as written: a `title` or `description` with `{{version}}` in it shows the braces.
34
+
35
+ ## Undefined names
36
+
37
+ A `{{name}}` that isn't defined in prose is a build error, `BLUME_UNDEFINED_VARIABLE`, reported at its line, so a typo never reaches readers. Inside a code block or inline code, an undefined name is left as written, since templating examples like a Handlebars `{{title}}` are common there.
38
+
39
+ To show a reference in prose without defining it, put it in inline code: `` `{{name}}` ``.
40
+
41
+ A site that defines no variables is unaffected: `{{…}}` in its pages stays as written.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Agent discovery
3
- description: How agents find your machine-readable surface without guessing — the agent-readability manifest, Link headers, the RFC 9727 API catalog, WebMCP, published skills, DNS-based discovery, and Web Bot Auth keys.
3
+ description: How agents find your machine-readable surface without guessing — the agent-readability manifest, Link headers, the RFC 9727 API catalog, WebMCP, your site's generated skill and published skills, DNS-based discovery, and Web Bot Auth keys.
4
4
  ---
5
5
 
6
6
  Publishing `llms.txt`, Markdown mirrors, a JSON API, and an MCP server is only half the job — an agent still has to find them. Blume advertises the whole surface through the conventions agents actually probe: a manifest at the site root, `Link` headers and `<link>` tags, well-known files, and the browser's own model context. Everything here is on by default and derived from what you've already enabled.
@@ -228,6 +228,33 @@ agents: {
228
228
  }
229
229
  ```
230
230
 
231
+ ## Your site's skill
232
+
233
+ Every build writes an [agent skill](https://agentskills.io) for your docs, a `SKILL.md` a coding agent can install to work with your product. It's served at `/skill.md` and listed in the [skills index](#skills-discovery). Blume builds it from what it already knows about the site, without calling a model, so it costs nothing and changes whenever your docs do. It holds:
234
+
235
+ - the site's title and description, and when an agent should use the skill
236
+ - how to read any page as Markdown, plus `llms.txt`, `llms-full.txt`, the [MCP server](/docs/discoverability/mcp), the JSON API, and the changelog, when the site serves them
237
+ - your API references
238
+ - a map of the docs in sidebar order, linking each page's Markdown with its one-line description
239
+
240
+ The skill is named after your site's `title` (`Acme Docs` becomes `acme-docs`). It covers the current version in the default language, leaves out API operation pages and changelog entries (each has one line of its own above the map), and lists up to 200 pages, pointing at `llms.txt` for the rest. The links are absolute, so the skill needs [`deployment.site`](/docs/deployment); a build without one skips it.
241
+
242
+ The generated skill is a guide to your docs, not a summary of your product. For one that teaches an agent your product — setup, core concepts, common tasks, and gotchas — have your coding agent write it from the docs:
243
+
244
+ ```bash
245
+ blume skill --claude # or --codex
246
+ ```
247
+
248
+ `blume skill` opens Claude Code or Codex on the [`blume-write-skill`](/docs/advanced/skills#writing-your-sites-skill) skill, which writes a `SKILL.md` under your [`agents.skills`](#skills-discovery) folder (`./skills` when it isn't set, adding it to your config), named after your site so it replaces the generated one. You review and commit it like any other file; it costs your agent's tokens once, not on every build. Run it again after a big docs change, since a hand-written skill doesn't update itself. Without a flag, `blume skill` prints where the skill would go and how to point another agent at it.
249
+
250
+ Any skill of the same name in `agents.skills`, however you write it, replaces the generated one in the index, and at `/skill.md` too when it's a lone `SKILL.md`. A `public/skill.md` you ship wins over both. To turn the skill off:
251
+
252
+ ```ts blume.config.ts lineNumbers
253
+ agents: {
254
+ skillMd: false,
255
+ }
256
+ ```
257
+
231
258
  ## Skills discovery
232
259
 
233
260
  If your project ships [agent skills](https://agentskills.io) — the [Blume repo itself does](/docs/advanced/skills) — point `agents.skills` at the directory that holds them, and the build publishes them for discovery per the [Agent Skills Discovery RFC](https://github.com/cloudflare/agent-skills-discovery-rfc):
@@ -240,7 +267,7 @@ agents: {
240
267
 
241
268
  The path resolves against your project root, and each subdirectory with a `SKILL.md` becomes a published skill. A skill that's a lone `SKILL.md` is copied verbatim to `/.well-known/agent-skills/<name>/SKILL.md` (`type: "skill-md"`); a skill with supporting resources (`scripts/`, `references/`, `assets/`) is bundled into a deterministic `.tar.gz` (`type: "archive"`) so its relative references resolve after unpacking, with script execute bits preserved. The discovery index at `/.well-known/agent-skills/index.json` carries the v0.2.0 `$schema` and, per skill, its name, type, description (from the `SKILL.md` frontmatter), artifact URL, and the SHA-256 digest clients verify downloads against.
242
269
 
243
- Skills with a missing or spec-invalid `name`/`description` are skipped with a build warning rather than published broken, and a `public/.well-known/agent-skills/index.json` you ship yourself takes over the whole surface. Published skills are also listed in [`llms.txt`](/docs/discoverability/llms-txt#generated-sections).
270
+ Skills with a missing or spec-invalid `name`/`description` are skipped with a build warning rather than published broken, and a `public/.well-known/agent-skills/index.json` you ship yourself takes over the whole surface. Published skills, [your site's own](#your-sites-skill) included, are also listed in [`llms.txt`](/docs/discoverability/llms-txt#generated-sections).
244
271
 
245
272
  ## DNS-based discovery (DNS-AID)
246
273
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- title: SEO and AEO
2
+ title: SEO and GEO
3
3
  description: How a Blume site gets found — by search engines and social platforms on one side, coding agents and AI assistants on the other — and where each surface is configured.
4
4
  ---
5
5
 
@@ -44,7 +44,7 @@ agents: {
44
44
 
45
45
  ## Generated sections
46
46
 
47
- `llms.txt` closes with two generated sections that need no configuration. **Agent skills** lists each skill published through [`agents.skills`](/docs/discoverability/agent-discovery#skills-discovery) with its description (where a skill says when to use it). **Agent resources** links every machine-readable artifact the build emits — `llms-full.txt`, the per-page [raw Markdown](/docs/discoverability/markdown) mirror, the [MCP server](/docs/discoverability/mcp) and its discovery document, the skills index, the [API catalog](/docs/discoverability/agent-discovery#api-catalog), the [AI catalog](/docs/discoverability/agent-discovery#ai-catalog), [`agent-readability.json`](/docs/discoverability/agent-discovery#agent-readability), and the [sitemap](/docs/discoverability/sitemap-and-robots#sitemap) — each only when it exists, so an agent that reads nothing but `llms.txt` still finds the whole surface.
47
+ `llms.txt` closes with two generated sections that need no configuration. **Agent skills** lists [your site's own skill](/docs/discoverability/agent-discovery#your-sites-skill) and each skill published through [`agents.skills`](/docs/discoverability/agent-discovery#skills-discovery) with its description (where a skill says when to use it). **Agent resources** links every machine-readable artifact the build emits — `llms-full.txt`, the per-page [raw Markdown](/docs/discoverability/markdown) mirror, the [MCP server](/docs/discoverability/mcp) and its discovery document, the skills index, the [API catalog](/docs/discoverability/agent-discovery#api-catalog), the [AI catalog](/docs/discoverability/agent-discovery#ai-catalog), [`agent-readability.json`](/docs/discoverability/agent-discovery#agent-readability), and the [sitemap](/docs/discoverability/sitemap-and-robots#sitemap) — each only when it exists, so an agent that reads nothing but `llms.txt` still finds the whole surface.
48
48
 
49
49
  ## Excluding a page
50
50
 
@@ -34,6 +34,23 @@ seo:
34
34
  ---
35
35
  ```
36
36
 
37
+ ## Your own tags
38
+
39
+ For anything Blume has no setting for, add tags under `seo.metatags` and every page carries them, custom pages and API references included. It suits site-verification tokens from Google, Bing, or Facebook, a `theme-color`, or an app banner:
40
+
41
+ ```ts blume.config.ts lineNumbers
42
+ seo: {
43
+ metatags: {
44
+ "google-site-verification": "abc123",
45
+ "msvalidate.01": "def456",
46
+ "theme-color": "#0b5fff",
47
+ "apple-itunes-app": "app-id=123456789",
48
+ },
49
+ }
50
+ ```
51
+
52
+ Each key is the tag's name and each value its content. Open Graph families (`og:`, `fb:`, `article:`, and the rest) render with `property`, as the protocol names them, and everything else with `name`. The tags Blume writes itself, listed at the top of this page, can't be set here, so a page never carries two values for one tag. The build names the setting that controls each: `og:image` comes from [`seo.og`](/docs/discoverability/open-graph) or a page's `seo.image`, and `robots` from a page's `seo.noindex`.
53
+
37
54
  ## Per-page overrides
38
55
 
39
56
  Override any of the other tags per page with `seo` frontmatter:
@@ -0,0 +1,74 @@
1
+ ---
2
+ title: Hand-written API pages
3
+ description: Document an endpoint in MDX, without a spec, and still get the method and path, a Try it panel, request samples, and pinned examples.
4
+ ---
5
+
6
+ Not every API has an OpenAPI spec, and some endpoints read better written by hand. A page with `api` frontmatter documents one endpoint: its [fields](/docs/content/components#api-fields) describe the request and response, and Blume builds the rest of an [OpenAPI reference](/docs/references/openapi) page from them: the method and path at the top, a Try it panel and request samples in a column beside the content, and any [request and response examples](/docs/content/components#request-and-response-examples) pinned under them.
7
+
8
+ ````mdx docs/users/create.mdx lineNumbers
9
+ ---
10
+ title: Create a user
11
+ api: POST /workspaces/{workspaceId}/users
12
+ ---
13
+
14
+ Creates a user and sends them an invite.
15
+
16
+ <ParamField path="workspaceId" type="string" required>
17
+ The workspace to add the user to.
18
+ </ParamField>
19
+
20
+ <ParamField body="email" type="string" required placeholder="ada@example.com">
21
+ The user's email address.
22
+ </ParamField>
23
+
24
+ <ParamField body="role" type="string" default="member">
25
+ One of `owner`, `admin`, or `member`.
26
+ </ParamField>
27
+
28
+ <ResponseField name="id" type="string" required>
29
+ The new user's ID.
30
+ </ResponseField>
31
+
32
+ <ResponseExample>
33
+
34
+ ```json 201
35
+ { "id": "usr_8f2k", "status": "invited" }
36
+ ```
37
+
38
+ </ResponseExample>
39
+ ````
40
+
41
+ `api` takes an HTTP method and a path or a full URL. Path parameters are written in braces, `{workspaceId}`, and filled from the matching `path` field. A full URL, like `GET https://api.acme.com/v1/users`, is sent to as written; a path joins the site's [`api.server`](#site-defaults).
42
+
43
+ ## The playground and samples
44
+
45
+ The page's `ParamField`s become the Try it panel's inputs, the way an operation's parameters do in a spec: `path`, `query`, and `header` fields are parameters, and `body` fields make up a JSON body, with a field's nested fields (inside its [`Expandable`](/docs/content/components#expandable)) as the properties of an object. A `type` of `string[]` is an array, and one that isn't a JSON type, such as `enum<string>`, is sent as a string. A field's `default` fills the body, and its `placeholder` is the example value the samples show.
46
+
47
+ Beside the panel, the page gets request samples in cURL, JavaScript, and Python. A page with its own `RequestExample` shows that in their place.
48
+
49
+ Two more frontmatter keys tune the page:
50
+
51
+ - `authMethod` sets how the endpoint authenticates: `bearer`, `basic`, `key` (an API key in a header), or `none`. It overrides the site's [`api.auth`](#site-defaults).
52
+ - `playground` sets what the page shows: `interactive` (the default) for the Try it panel and samples, `simple` for the samples only, or `none` for neither. The method, path, and any examples stay.
53
+
54
+ ## Site defaults
55
+
56
+ `api` in `blume.config.ts` sets what every endpoint page shares:
57
+
58
+ ```ts blume.config.ts lineNumbers
59
+ export default defineConfig({
60
+ api: {
61
+ server: "https://api.acme.com/v1",
62
+ auth: { method: "key", name: "x-api-key" },
63
+ playground: { proxy: true },
64
+ },
65
+ });
66
+ ```
67
+
68
+ - `server` is the base URL an `api` path joins.
69
+ - `auth` is how requests authenticate unless a page sets `authMethod`: `method` is `bearer`, `basic`, `key`, or `none`, and `name` is the header an API key goes in (`x-api-key` by default). Without `auth`, pages send no credentials.
70
+ - `playground` takes the same values as an OpenAPI reference's [playground option](/docs/references/openapi#try-it-playground): `false` hides the Try it panel on every page, and `proxy` sends its requests through a CORS proxy, a URL of your own or `true` for Blume's built-in one. The built-in proxy needs [server output](/docs/deployment#server-rendering), and only forwards to `server`'s origin and the origins of any full URLs in `api` frontmatter.
71
+
72
+ The page's layout, fields, and examples are the same components as elsewhere, so the [Markdown copy](/docs/discoverability/llms-txt) of the page lists each field, and search indexes the page like any other.
73
+
74
+ The frontmatter keys and component props match Mintlify's, so pages written for it keep working as they are.
@@ -28,7 +28,7 @@ AsyncAPI **2.x specs are normalized to 3.x automatically** with the official Asy
28
28
 
29
29
  ## Code samples
30
30
 
31
- Code samples are **protocol-aware**, keyed off the operation's binding (or its servers' protocol): `wscat` and a browser `WebSocket` snippet for WebSockets, `kcat` for Kafka, `mosquitto_pub`/`mosquitto_sub` for MQTT. `codeSamples` filters that set, the same way it picks languages on `openapi()`; a protocol without a supported tool renders the message payload example alone rather than a fabricated client.
31
+ Code samples are **protocol-aware**, keyed off the operation's binding (or its servers' protocol): `wscat` and a browser `WebSocket` snippet for WebSockets, `kcat` for Kafka, `mosquitto_pub`/`mosquitto_sub` for MQTT. `codeSamples` filters that set, the same way it picks languages on `openapi()`, and `codeSamples: false` shows none. A protocol without a supported tool renders the message payload example alone rather than a fabricated client.
32
32
 
33
33
  ## Shared options
34
34
 
@@ -40,13 +40,13 @@ navigation: {
40
40
 
41
41
  ## Generated examples
42
42
 
43
- Every operation page carries a complete, valid example operation — one variable per argument, typed off the schema, with a bounded-depth selection set over the return type — plus matching example variables and an example response that mirrors the same selection. Code samples show the exact HTTP request (a JSON `POST` of `{ query, variables }`) in each configured language:
43
+ Every operation page carries a complete, valid example operation — one variable per argument, typed off the schema, with a bounded-depth selection set over the return type — plus matching example variables and an example response that mirrors the same selection. Code samples show the exact HTTP request (a JSON `POST` of `{ query, variables }`) in each configured language. The same [languages](/docs/references/openapi#code-samples-and-schemas) as `openapi()` are available, and `codeSamples: false` shows none:
44
44
 
45
45
  ```ts blume.config.ts lineNumbers
46
46
  reference: [
47
47
  graphql({
48
48
  spec: "./schema.graphql",
49
- codeSamples: ["curl", "js"], // built in: curl, js, python
49
+ codeSamples: ["curl", "js"], // any language openapi() offers
50
50
  }),
51
51
  ],
52
52
  ```
@@ -2,6 +2,6 @@ import { defineMeta } from "blume";
2
2
 
3
3
  export default defineMeta({
4
4
  order: 7,
5
- pages: ["openapi", "asyncapi", "graphql", "scalar"],
5
+ pages: ["openapi", "asyncapi", "graphql", "scalar", "api-pages"],
6
6
  title: "References",
7
7
  });
@@ -55,18 +55,64 @@ reference: [
55
55
 
56
56
  ## Code samples and schemas
57
57
 
58
- `codeSamples` picks which languages render per operation (built in: `curl`, `js`, `python`); `expandSchemas` starts nested schema rows expanded rather than collapsed:
58
+ `codeSamples` picks which languages render per operation, in the order you list them. It defaults to `["curl", "js", "python"]`. `expandSchemas` starts nested schema rows expanded rather than collapsed:
59
59
 
60
60
  ```ts blume.config.ts lineNumbers
61
61
  reference: [
62
62
  openapi({
63
63
  spec: "./openapi.yaml",
64
- codeSamples: ["curl", "js"],
64
+ codeSamples: ["curl", "python", "go", "java", "csharp"],
65
65
  expandSchemas: true,
66
66
  }),
67
67
  ],
68
68
  ```
69
69
 
70
+ Every sample is plain, copy-pasteable code for the request, written with the language's standard or most common HTTP client:
71
+
72
+ | Id | Language | Client | Also accepts |
73
+ | ------------ | ---------- | ------------------- | --------------------- |
74
+ | `curl` | cURL | `curl` | `bash`, `sh`, `shell` |
75
+ | `python` | Python | `requests` | `py` |
76
+ | `js` | JavaScript | `fetch` | `javascript` |
77
+ | `node` | Node.js | `axios` | `nodejs`, `node.js` |
78
+ | `typescript` | TypeScript | `fetch` | `ts` |
79
+ | `php` | PHP | the curl extension | |
80
+ | `go` | Go | `net/http` | `golang` |
81
+ | `java` | Java | `java.net.http` | |
82
+ | `ruby` | Ruby | `net/http` | `rb` |
83
+ | `powershell` | PowerShell | `Invoke-RestMethod` | `ps1` |
84
+ | `swift` | Swift | `URLSession` | |
85
+ | `csharp` | C# | `HttpClient` | `c#`, `cs` |
86
+ | `dotnet` | .NET | RestSharp | `.net`, `dot-net` |
87
+ | `c` | C | libcurl | |
88
+ | `cpp` | C++ | cpr | `c++` |
89
+ | `kotlin` | Kotlin | OkHttp | `kt` |
90
+ | `rust` | Rust | reqwest | `rs` |
91
+ | `dart` | Dart | `package:http` | `flutter` |
92
+
93
+ Each value is quoted by its own language's rules, so a header like `If-Match: "33a64df5"` or a body with a `$` in it reaches the API as written.
94
+
95
+ ### Your own samples
96
+
97
+ To show your SDK instead of a raw HTTP call, add `x-codeSamples` to an operation in the spec. It's the extension Redocly defined, and Speakeasy and Stainless can write it for you. Each entry takes a `lang`, the `source`, and an optional `label`:
98
+
99
+ ```yaml openapi.yaml lineNumbers
100
+ paths:
101
+ /plants:
102
+ get:
103
+ x-codeSamples:
104
+ - lang: typescript
105
+ label: SDK
106
+ source: |
107
+ const plants = await planter.plants.list();
108
+ - lang: bash
109
+ label: CLI
110
+ source: |
111
+ planter list
112
+ ```
113
+
114
+ These samples render as their own tabs, ahead of the generated ones, and stay as written when readers change the [Try it](#try-it-playground) form. The older `x-code-samples` spelling works too. To show only your own samples, set `codeSamples: false`.
115
+
70
116
  ## Try it playground
71
117
 
72
118
  Operation pages rendered natively ship an interactive **Try it** panel by default. Blume generates the form from the operation itself: an input per path, query, and header parameter, a body editor built from the request-body schema, everything prefilled from the spec's examples. Values go on the wire the way the spec describes them: array and object parameters follow their `style` and `explode` (`tags=dog&tags=cat` in a query, `1,2` in a path, `filter[color]=red` for `deepObject`), and an `application/x-www-form-urlencoded` or `multipart/form-data` body is sent as form fields rather than JSON. A server picker lists the operation's servers (its own `servers`, else its path's, else the spec's, with each `{variable}` at its `default`), with a free-text field for any other base URL, and auth inputs match the operation's [resolved security](#authorization) — bearer token, API key, and basic credentials, with OAuth2 as a token paste field (bring an access token; Blume doesn't run the flow).
@@ -100,7 +146,7 @@ reference: [
100
146
  ],
101
147
  ```
102
148
 
103
- The built-in proxy only forwards requests to the origins your specs declare in `servers` (at the document, path, or operation level, with variables at their defaults) — including across redirects — so a public docs deployment can't be aimed at other hosts on its network. A **Custom base URL** typed into the panel isn't a documented server: with the proxy enabled, requests to it are refused with a 403. It forwards only the headers the panel sets itself — the credentials and header parameters filled in, and the body's `Content-Type` — so cookies, credentials the browser attaches for the docs site (HTTP Basic auth on a password-protected preview, say), and headers your host adds (`X-Forwarded-For`, `CF-*`, `X-Vercel-*`) never reach the API. It reads a request body only up to 4 MB (anything larger gets a `413`), and every response it relays carries `Content-Security-Policy: sandbox`, `X-Content-Type-Options: nosniff`, and `Cross-Origin-Resource-Policy: same-origin` — plus `Content-Disposition: attachment` for HTML or SVG — so an API error page that echoes its input can't run script on the docs origin.
149
+ The built-in proxy only forwards requests to the origins your specs declare in `servers` (at the document, path, or operation level, with variables at their defaults) — including across redirects — so a public docs deployment can't be aimed at other hosts on its network. A **Custom base URL** typed into the panel isn't a documented server: with the proxy enabled, requests to it are refused with a 403. It forwards only the headers the panel sets itself — the credentials and header parameters filled in, and the body's `Content-Type` — so cookies, credentials the browser attaches for the docs site (HTTP Basic auth on a password-protected preview, say), and headers your host adds (`X-Forwarded-For`, `CF-*`, `X-Vercel-*`) never reach the API. It reads a request body only up to 4 MB (anything larger gets a `413`), and every response it relays carries `Content-Security-Policy: sandbox`, `X-Content-Type-Options: nosniff`, and `Cross-Origin-Resource-Policy: same-origin` — plus `Content-Disposition: attachment` for HTML or SVG — so an API error page that echoes its input can't run script on the docs origin. The proxy is also [rate limited](/docs/configuration/rate-limiting) per reader, like every server route a reader can call.
104
150
 
105
151
  ## Multiple specs
106
152
 
@@ -157,6 +203,36 @@ reference: [
157
203
 
158
204
  A [`scalar()`](/docs/references/scalar) embed takes only `noindex` of these — it already sits outside Blume's search and `llms.txt`, so the two `include*` settings have nothing to act on there.
159
205
 
206
+ ## Overlays
207
+
208
+ When the spec is generated by another tool or owned by another team, keep your docs changes out of it with an [OpenAPI Overlay](https://spec.openapis.org/overlay/latest.html): a separate file that lists changes to make to the spec, such as clearer descriptions, a public server URL, or internal operations removed. List overlays beside `spec`. Each is a local path or an `http(s)` URL, and they apply in order:
209
+
210
+ ```ts blume.config.ts lineNumbers
211
+ reference: [
212
+ openapi({
213
+ spec: "./openapi.json",
214
+ overlays: ["./overlays/public.yaml"],
215
+ }),
216
+ ],
217
+ ```
218
+
219
+ ```yaml overlays/public.yaml lineNumbers
220
+ overlay: 1.1.0
221
+ info:
222
+ title: Public docs
223
+ version: 1.0.0
224
+ actions:
225
+ - target: $.paths.*[?@['x-internal'] == true]
226
+ remove: true
227
+ - target: $.info
228
+ update:
229
+ description: The public Acme API.
230
+ ```
231
+
232
+ Each action's `target` is a [JSONPath](https://www.rfc-editor.org/rfc/rfc9535) expression. `update` merges into every node it selects: objects merge key by key, arrays gain the new items, and other values are replaced. `copy` merges in a node from elsewhere in the spec (Overlay 1.1), and `remove: true` deletes the targets. Overlay Specification 1.0 and 1.1 are both supported.
233
+
234
+ Overlays apply to the spec as written, before Blume upgrades it to OpenAPI 3.1, so an overlay targets the version you wrote the spec in. Everything built from the spec sees the result: the operation pages, the sidebar, search, `llms.txt`, and the MCP server. With `sources`, give each source its own `overlays`. An overlay that doesn't apply fails the reference the way an unreadable spec does, so a broken overlay can't publish what it was meant to hide.
235
+
160
236
  ## Authorization
161
237
 
162
238
  Operations that declare [security requirements](https://spec.openapis.org/oas/v3.1.0#security-requirement-object) render an **Authorization** section above their parameters, and the generated code samples send a placeholder credential (`Authorization: Bearer YOUR_TOKEN`, an API-key header, or a query key — whatever the scheme calls for). There's nothing to configure: Blume reads `security` from the spec, so the reference always matches what the API actually enforces.
@@ -168,6 +244,29 @@ The OpenAPI semantics carry over as written:
168
244
  - An empty `{}` entry means auth is **optional** for that operation, and the section says so.
169
245
  - OAuth2 scopes are listed per scheme; scheme `description`s from `components.securitySchemes` render inline.
170
246
 
247
+ ## Webhooks and callbacks
248
+
249
+ Your API's outgoing requests get documented too. Each [webhook](https://spec.openapis.org/oas/v3.1.0#oas-webhooks) in the spec's top-level `webhooks` gets its own page, like an operation: filed under its first tag, or a **Webhooks** group when it has none. The page shows the payload's schema and the responses your endpoint should send back. In place of the Try it panel and code samples, the rail shows an example payload, since your API sends this request rather than serving it. A webhook page lists only the `security` the webhook declares itself: the spec's root `security` guards calls to your API, so it doesn't apply to requests the API sends.
250
+
251
+ ```yaml openapi.yaml lineNumbers
252
+ webhooks:
253
+ newPet:
254
+ post:
255
+ summary: New pet added
256
+ requestBody:
257
+ content:
258
+ application/json:
259
+ schema:
260
+ $ref: "#/components/schemas/Pet"
261
+ responses:
262
+ "200":
263
+ description: Return a 200 to acknowledge the event.
264
+ ```
265
+
266
+ An operation's [`callbacks`](https://spec.openapis.org/oas/v3.1.0#callback-object), the requests your API makes back to a URL the caller supplied, render in a **Callbacks** section on that operation's page. Each shows the URL expression (`{$request.body#/callbackUrl}`), its method, its request body, and its expected responses. Callbacks declared under `components.callbacks` and referenced with `$ref` work the same way.
267
+
268
+ Webhook pages are real pages: they're in search, `llms.txt`, and the MCP server, and their Markdown tells agents it's a request the API sends, so an agent won't try to call it.
269
+
171
270
  ## Embedding Scalar instead
172
271
 
173
272
  `openapi()` always renders Blume's own pages. To embed [Scalar](https://scalar.com)'s self-contained API reference UI on a single route instead — its own sidebar, search, theme, and request client — list a `scalar()` adapter from `blume/reference` in place of (or beside) this one. The [Scalar](/docs/references/scalar) page covers what the embed does and doesn't do, and how to pass Scalar's own options through.
@@ -33,6 +33,8 @@ reference: [
33
33
  ],
34
34
  ```
35
35
 
36
+ [Overlays](/docs/references/openapi#overlays) work here too: list `overlays` beside `spec`, or on each source. The embed then inlines the spec with the overlays applied, so a remote spec with overlays is fetched at build time rather than from the browser. If an overlay doesn't apply, the build warns and leaves that page out rather than embed the spec without it.
37
+
36
38
  A Scalar embed and native pages can sit side by side in the list — an `openapi()` adapter for the current API and a `scalar()` adapter for a legacy one, say — as long as their routes differ. Like every reference, the embed doesn't add a header tab on its own; point a [navigation tab](/docs/content/navigation#tabs) at its route to surface it.
37
39
 
38
40
  ## What the embed doesn't do
@@ -43,7 +45,7 @@ The embed does follow Blume's light/dark toggle: it's pinned to the page's theme
43
45
 
44
46
  ## Passing Scalar options
45
47
 
46
- `theme` is the one option most people reach for, but Scalar supports many more. Every key you pass to `scalar()` beyond `spec`, `sources`, `route`, and `theme` is forwarded verbatim as [Scalar configuration](https://github.com/scalar/scalar/blob/main/documentation/configuration.md) to the embedded reference — Blume doesn't gate the keys, so anything Scalar accepts flows through (JSON values only, since the configuration is inlined into the generated page):
48
+ `theme` is the one option most people reach for, but Scalar supports many more. Every key you pass to `scalar()` beyond `spec`, `overlays`, `sources`, `route`, and `theme` is forwarded verbatim as [Scalar configuration](https://github.com/scalar/scalar/blob/main/documentation/configuration.md) to the embedded reference — Blume doesn't gate the keys, so anything Scalar accepts flows through (JSON values only, since the configuration is inlined into the generated page):
47
49
 
48
50
  ```ts blume.config.ts lineNumbers
49
51
  reference: [