blume 2.0.3 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (607) hide show
  1. package/AGENTS.md +1 -0
  2. package/CHANGELOG.md +142 -0
  3. package/README.md +1 -1
  4. package/dist/cli/{chunk-5f86nr5m.js → chunk-073rdvgb.js} +21 -48
  5. package/dist/cli/{chunk-5f86nr5m.js.map → chunk-073rdvgb.js.map} +4 -5
  6. package/dist/cli/{chunk-ga0pf4aj.js → chunk-0n9xkp3d.js} +12 -3
  7. package/dist/cli/{chunk-ga0pf4aj.js.map → chunk-0n9xkp3d.js.map} +3 -3
  8. package/dist/cli/chunk-150jk7km.js +52 -0
  9. package/dist/cli/chunk-150jk7km.js.map +10 -0
  10. package/dist/cli/{chunk-9t7a85s3.js → chunk-313vnn40.js} +30 -19
  11. package/dist/cli/chunk-313vnn40.js.map +10 -0
  12. package/dist/cli/{chunk-tkacnehg.js → chunk-3v3ffbrj.js} +2 -2
  13. package/dist/cli/{chunk-q58y5e6a.js → chunk-3x6x3pph.js} +46 -24
  14. package/dist/cli/chunk-3x6x3pph.js.map +10 -0
  15. package/dist/cli/{chunk-1d7ve1dm.js → chunk-42t4jxdx.js} +37 -5
  16. package/dist/cli/chunk-42t4jxdx.js.map +12 -0
  17. package/dist/cli/{chunk-8ktnccpt.js → chunk-54ztw1nj.js} +21 -2
  18. package/dist/cli/chunk-54ztw1nj.js.map +10 -0
  19. package/dist/cli/{chunk-hm3vjy5s.js → chunk-5vjtgs6e.js} +28 -10
  20. package/dist/cli/{chunk-hm3vjy5s.js.map → chunk-5vjtgs6e.js.map} +4 -4
  21. package/dist/cli/chunk-66w9j7p6.js +322 -0
  22. package/dist/cli/chunk-66w9j7p6.js.map +14 -0
  23. package/dist/cli/{chunk-2hsdwb9n.js → chunk-7ppf1rva.js} +34 -18
  24. package/dist/cli/chunk-7ppf1rva.js.map +10 -0
  25. package/dist/cli/{chunk-r9rcc4w7.js → chunk-8y2kmjv8.js} +7 -7
  26. package/dist/cli/{chunk-r20tn01b.js → chunk-c1y538kd.js} +8 -2
  27. package/dist/cli/{chunk-r20tn01b.js.map → chunk-c1y538kd.js.map} +3 -3
  28. package/dist/cli/{chunk-2eytanqx.js → chunk-c887fj0s.js} +2 -2
  29. package/dist/cli/{chunk-p3v96n38.js → chunk-cfyg47zg.js} +16 -6
  30. package/dist/cli/chunk-cfyg47zg.js.map +10 -0
  31. package/dist/cli/chunk-cm89scdv.js +443 -0
  32. package/dist/cli/chunk-cm89scdv.js.map +11 -0
  33. package/dist/cli/{chunk-akbpwfxc.js → chunk-dzwehpy7.js} +2 -2
  34. package/dist/cli/{chunk-wrr3j9w9.js → chunk-e6atmn4h.js} +299 -72
  35. package/dist/cli/chunk-e6atmn4h.js.map +36 -0
  36. package/dist/cli/{chunk-tzmab476.js → chunk-e8xw9qkh.js} +8 -6
  37. package/dist/cli/{chunk-tzmab476.js.map → chunk-e8xw9qkh.js.map} +3 -3
  38. package/dist/cli/{chunk-6dsbexzp.js → chunk-e9q7a2ak.js} +17 -15
  39. package/dist/cli/{chunk-6dsbexzp.js.map → chunk-e9q7a2ak.js.map} +2 -2
  40. package/dist/cli/{chunk-pv29h0wf.js → chunk-ehweg4fr.js} +5155 -1249
  41. package/dist/cli/chunk-ehweg4fr.js.map +218 -0
  42. package/dist/cli/{chunk-c8chx29p.js → chunk-ep6nd0rn.js} +390 -318
  43. package/dist/cli/chunk-ep6nd0rn.js.map +36 -0
  44. package/dist/cli/{chunk-p73c0m7w.js → chunk-fdrkhpjy.js} +18 -15
  45. package/dist/cli/{chunk-p73c0m7w.js.map → chunk-fdrkhpjy.js.map} +3 -3
  46. package/dist/cli/{chunk-2z928egk.js → chunk-ft9vfys6.js} +5 -5
  47. package/dist/cli/{chunk-5m5nmvyq.js → chunk-gb420vvz.js} +316 -79
  48. package/dist/cli/chunk-gb420vvz.js.map +17 -0
  49. package/dist/cli/{chunk-vg9r4eb9.js → chunk-hebn0tpx.js} +86 -13
  50. package/dist/cli/chunk-hebn0tpx.js.map +14 -0
  51. package/dist/cli/{chunk-35d4wj9f.js → chunk-htrwgabr.js} +41 -23
  52. package/dist/cli/chunk-htrwgabr.js.map +15 -0
  53. package/dist/cli/{chunk-g4hq16wv.js → chunk-hx3xqnk9.js} +16 -14
  54. package/dist/cli/{chunk-g4hq16wv.js.map → chunk-hx3xqnk9.js.map} +2 -2
  55. package/dist/cli/chunk-j134vrp2.js +65 -0
  56. package/dist/cli/chunk-j134vrp2.js.map +10 -0
  57. package/dist/cli/{chunk-a58773jm.js → chunk-kma9f6sf.js} +2 -2
  58. package/dist/cli/chunk-kwrg63tm.js +31 -0
  59. package/dist/cli/chunk-kwrg63tm.js.map +10 -0
  60. package/dist/cli/{chunk-ey84smr6.js → chunk-mrcccgnh.js} +6 -6
  61. package/dist/cli/{chunk-ey84smr6.js.map → chunk-mrcccgnh.js.map} +1 -1
  62. package/dist/cli/{chunk-yfyb25rh.js → chunk-n0v7m8ry.js} +24 -8
  63. package/dist/cli/chunk-n0v7m8ry.js.map +14 -0
  64. package/dist/cli/{chunk-j85vccga.js → chunk-ncj7w596.js} +576 -367
  65. package/dist/cli/chunk-ncj7w596.js.map +35 -0
  66. package/dist/cli/{chunk-5xvm6tfj.js → chunk-nq1d20w2.js} +17 -15
  67. package/dist/cli/{chunk-5xvm6tfj.js.map → chunk-nq1d20w2.js.map} +2 -2
  68. package/dist/cli/{chunk-pehfxfta.js → chunk-nrns10mj.js} +6 -98
  69. package/dist/cli/chunk-nrns10mj.js.map +10 -0
  70. package/dist/cli/{chunk-crgn1q09.js → chunk-sds923fa.js} +4 -4
  71. package/dist/cli/chunk-t2m5j4pr.js +104 -0
  72. package/dist/cli/chunk-t2m5j4pr.js.map +11 -0
  73. package/dist/cli/{chunk-acanzt5p.js → chunk-tdqhyxfz.js} +6 -6
  74. package/dist/cli/chunk-tphac74f.js +126 -0
  75. package/dist/cli/chunk-tphac74f.js.map +11 -0
  76. package/dist/cli/chunk-w1r4vase.js +18 -0
  77. package/dist/cli/chunk-w1r4vase.js.map +10 -0
  78. package/dist/cli/{chunk-3em5wd2y.js → chunk-zccbjcj0.js} +4 -3
  79. package/dist/cli/{chunk-3em5wd2y.js.map → chunk-zccbjcj0.js.map} +2 -2
  80. package/dist/cli/index.js +31 -20
  81. package/dist/cli/index.js.map +5 -5
  82. package/dist/types/ai/agent-surface.d.ts +8 -7
  83. package/dist/types/ai/ai-catalog.d.ts +7 -0
  84. package/dist/types/ai/ask-context.d.ts +26 -0
  85. package/dist/types/ai/ask.d.ts +271 -58
  86. package/dist/types/ai/component-markdown.d.ts +2 -0
  87. package/dist/types/ai/index.d.ts +2 -2
  88. package/dist/types/ai/openapi-components.d.ts +4 -1
  89. package/dist/types/ai/site-skill.d.ts +26 -0
  90. package/dist/types/ai/skills.d.ts +16 -0
  91. package/dist/types/astro/integration.d.ts +9 -0
  92. package/dist/types/astro/markdown-negotiation.d.ts +3 -0
  93. package/dist/types/astro/pages.d.ts +10 -0
  94. package/dist/types/captcha/hcaptcha.d.ts +27 -0
  95. package/dist/types/captcha/index.d.ts +28 -0
  96. package/dist/types/captcha/schema.d.ts +29 -0
  97. package/dist/types/captcha/turnstile.d.ts +28 -0
  98. package/dist/types/cli/init/scaffold.d.ts +1 -1
  99. package/dist/types/components/content/api-field.d.ts +62 -0
  100. package/dist/types/components/content/api-page.d.ts +100 -0
  101. package/dist/types/components/layout/nav-utils.d.ts +26 -0
  102. package/dist/types/components/openapi/code-samples.d.ts +29 -0
  103. package/dist/types/components/openapi/helpers.d.ts +158 -0
  104. package/dist/types/components/openapi/security.d.ts +95 -0
  105. package/dist/types/components/openapi/snippets.d.ts +60 -0
  106. package/dist/types/consent/ethyca.d.ts +48 -0
  107. package/dist/types/consent/index.d.ts +37 -0
  108. package/dist/types/consent/native.d.ts +28 -0
  109. package/dist/types/consent/osano.d.ts +42 -0
  110. package/dist/types/consent/schema.d.ts +110 -0
  111. package/dist/types/core/changelog-index.d.ts +8 -0
  112. package/dist/types/core/config-input.d.ts +217 -23
  113. package/dist/types/core/data.d.ts +53 -4
  114. package/dist/types/core/footer.d.ts +8 -0
  115. package/dist/types/core/i18n-ui.d.ts +88 -0
  116. package/dist/types/core/includes.d.ts +8 -2
  117. package/dist/types/core/links.d.ts +8 -0
  118. package/dist/types/core/localizable.d.ts +7 -0
  119. package/dist/types/core/page-modes.d.ts +29 -0
  120. package/dist/types/core/probe.d.ts +2 -0
  121. package/dist/types/core/project-graph.d.ts +6 -0
  122. package/dist/types/core/redirect-patterns.d.ts +152 -0
  123. package/dist/types/core/related.d.ts +40 -0
  124. package/dist/types/core/schema.d.ts +670 -56
  125. package/dist/types/core/sources/cache.d.ts +3 -0
  126. package/dist/types/core/sources/contentful-rich-text.d.ts +11 -2
  127. package/dist/types/core/sources/contentful.d.ts +17 -1
  128. package/dist/types/core/sources/lexical.d.ts +5 -0
  129. package/dist/types/core/sources/normalize.d.ts +20 -1
  130. package/dist/types/core/sources/read.d.ts +5 -0
  131. package/dist/types/core/sources/remote.d.ts +37 -10
  132. package/dist/types/core/sources/sanity.d.ts +1 -1
  133. package/dist/types/core/types.d.ts +18 -1
  134. package/dist/types/core/variables.d.ts +42 -0
  135. package/dist/types/deploy/adapter-output.d.ts +2 -1
  136. package/dist/types/deploy/adapters/cloudflare.d.ts +7 -5
  137. package/dist/types/deploy/adapters/node.d.ts +58 -23
  138. package/dist/types/deploy/adapters/registry.d.ts +10 -0
  139. package/dist/types/deploy/adapters/types.d.ts +3 -2
  140. package/dist/types/deploy/node-headers.d.ts +13 -2
  141. package/dist/types/deploy/platforms/cloudflare.d.ts +15 -7
  142. package/dist/types/deploy/platforms/netlify.d.ts +7 -0
  143. package/dist/types/deploy/platforms/node.d.ts +3 -1
  144. package/dist/types/deploy/platforms/types.d.ts +18 -7
  145. package/dist/types/deploy/platforms/vercel.d.ts +26 -1
  146. package/dist/types/deploy/redirects.d.ts +55 -11
  147. package/dist/types/deploy/sitemap.d.ts +2 -1
  148. package/dist/types/deploy/vercel-negotiation.d.ts +51 -21
  149. package/dist/types/markdown/code-title.d.ts +17 -0
  150. package/dist/types/markdown/features.d.ts +3 -2
  151. package/dist/types/narration/build.d.ts +70 -0
  152. package/dist/types/narration/provider.d.ts +26 -0
  153. package/dist/types/narration/script.d.ts +154 -0
  154. package/dist/types/openapi/checks.d.ts +27 -0
  155. package/dist/types/openapi/graphql.d.ts +6 -0
  156. package/dist/types/openapi/model.d.ts +27 -10
  157. package/dist/types/openapi/overlay.d.ts +54 -0
  158. package/dist/types/openapi/parse.d.ts +16 -0
  159. package/dist/types/openapi/references.d.ts +30 -4
  160. package/dist/types/ratelimit/cloudflare.d.ts +39 -0
  161. package/dist/types/ratelimit/index.d.ts +27 -0
  162. package/dist/types/ratelimit/memory.d.ts +36 -0
  163. package/dist/types/ratelimit/schema.d.ts +119 -0
  164. package/dist/types/ratelimit/upstash.d.ts +46 -0
  165. package/dist/types/ratelimit/wrangler.d.ts +23 -0
  166. package/dist/types/reference/asyncapi.d.ts +10 -10
  167. package/dist/types/reference/graphql.d.ts +89 -11
  168. package/dist/types/reference/index.d.ts +1 -1
  169. package/dist/types/reference/openapi.d.ts +33 -12
  170. package/dist/types/reference/options.d.ts +82 -6
  171. package/dist/types/reference/scalar.d.ts +13 -0
  172. package/dist/types/reference/schema.d.ts +144 -36
  173. package/dist/types/search/adapters/registry.d.ts +3 -0
  174. package/dist/types/search/adapters/typesense.d.ts +14 -4
  175. package/dist/types/search/build.d.ts +3 -1
  176. package/dist/types/search/documents.d.ts +12 -1
  177. package/dist/types/search/orama-index.d.ts +13 -9
  178. package/dist/types/search/sync/algolia.d.ts +5 -2
  179. package/dist/types/search/sync/index.d.ts +3 -1
  180. package/dist/types/search/sync/typesense.d.ts +11 -7
  181. package/dist/types/seo/metatags.d.ts +20 -0
  182. package/dist/types/sources/contentful.d.ts +16 -0
  183. package/dist/types/sources/github-releases.d.ts +8 -1
  184. package/dist/types/sources/payload.d.ts +2 -0
  185. package/dist/types/sources/registry.d.ts +54 -0
  186. package/dist/types/sources/shared.d.ts +3 -0
  187. package/dist/types/sources/strapi.d.ts +2 -0
  188. package/docs/01-quickstart.mdx +4 -2
  189. package/docs/02-deployment.mdx +71 -17
  190. package/docs/03-upgrading.mdx +4 -4
  191. package/docs/04-migrating.mdx +1 -1
  192. package/docs/advanced/changelog.mdx +13 -2
  193. package/docs/advanced/skills.mdx +15 -1
  194. package/docs/cli/audit.mdx +51 -8
  195. package/docs/cli/doctor.mdx +5 -1
  196. package/docs/cli/evals.mdx +4 -4
  197. package/docs/cli/index.mdx +1 -0
  198. package/docs/cli/translate.mdx +1 -0
  199. package/docs/cli/validate.mdx +4 -3
  200. package/docs/configuration/analytics.mdx +3 -1
  201. package/docs/configuration/assistant.mdx +194 -38
  202. package/docs/configuration/consent.mdx +100 -0
  203. package/docs/configuration/customization.mdx +8 -6
  204. package/docs/configuration/export.mdx +5 -3
  205. package/docs/configuration/index.mdx +64 -5
  206. package/docs/configuration/meta.ts +3 -0
  207. package/docs/configuration/narration.mdx +119 -0
  208. package/docs/configuration/rate-limiting.mdx +90 -0
  209. package/docs/configuration/search.mdx +63 -12
  210. package/docs/configuration/theming.mdx +3 -1
  211. package/docs/content/components.mdx +108 -2
  212. package/docs/content/frontmatter.mdx +80 -2
  213. package/docs/content/i18n.mdx +39 -4
  214. package/docs/content/includes.mdx +16 -0
  215. package/docs/content/meta.mdx +1 -0
  216. package/docs/content/meta.ts +1 -0
  217. package/docs/content/navigation.mdx +32 -10
  218. package/docs/content/sources/contentful.mdx +35 -0
  219. package/docs/content/sources/custom.mdx +43 -0
  220. package/docs/content/sources/github-releases.mdx +39 -0
  221. package/docs/content/sources/index.mdx +91 -0
  222. package/docs/content/sources/meta.ts +16 -0
  223. package/docs/content/sources/notion.mdx +32 -0
  224. package/docs/content/sources/obsidian.mdx +37 -0
  225. package/docs/content/sources/payload.mdx +31 -0
  226. package/docs/content/sources/remote-mdx.mdx +44 -0
  227. package/docs/content/sources/sanity.mdx +29 -0
  228. package/docs/content/sources/strapi.mdx +32 -0
  229. package/docs/content/syntax.mdx +69 -4
  230. package/docs/content/variables.mdx +41 -0
  231. package/docs/discoverability/agent-discovery.mdx +29 -2
  232. package/docs/discoverability/index.mdx +2 -2
  233. package/docs/discoverability/json-api.mdx +2 -0
  234. package/docs/discoverability/llms-txt.mdx +2 -2
  235. package/docs/discoverability/markdown.mdx +3 -1
  236. package/docs/discoverability/mcp.mdx +4 -0
  237. package/docs/discoverability/metadata.mdx +17 -0
  238. package/docs/discoverability/sitemap-and-robots.mdx +1 -1
  239. package/docs/references/api-pages.mdx +74 -0
  240. package/docs/references/asyncapi.mdx +4 -2
  241. package/docs/references/graphql.mdx +14 -2
  242. package/docs/references/meta.ts +1 -1
  243. package/docs/references/openapi.mdx +148 -7
  244. package/docs/references/scalar.mdx +3 -1
  245. package/package.json +43 -8
  246. package/skills/blume/SKILL.md +3 -0
  247. package/skills/blume-migrate/SKILL.md +6 -6
  248. package/skills/blume-migrate/references/docusaurus.md +7 -7
  249. package/skills/blume-migrate/references/fumadocs.md +17 -13
  250. package/skills/blume-migrate/references/mintlify.md +37 -19
  251. package/skills/blume-migrate/references/nextra.md +10 -7
  252. package/skills/blume-migrate/references/starlight.md +11 -8
  253. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +60 -14
  254. package/skills/blume-write-skill/SKILL.md +82 -0
  255. package/src/ai/agent-readability.ts +2 -1
  256. package/src/ai/agent-surface.ts +9 -7
  257. package/src/ai/ai-catalog.ts +6 -3
  258. package/src/ai/ask-context.ts +103 -20
  259. package/src/ai/ask-data.ts +6 -0
  260. package/src/ai/ask-limits.ts +13 -0
  261. package/src/ai/ask-tools.ts +142 -0
  262. package/src/ai/ask.ts +354 -76
  263. package/src/ai/changelog-markdown.ts +11 -6
  264. package/src/ai/component-markdown.ts +76 -1
  265. package/src/ai/cors.ts +14 -3
  266. package/src/ai/index.ts +12 -0
  267. package/src/ai/llms.ts +6 -2
  268. package/src/ai/markdown.ts +2 -1
  269. package/src/ai/mcp/data.ts +6 -0
  270. package/src/ai/mcp/query.ts +14 -6
  271. package/src/ai/openapi-components.ts +36 -6
  272. package/src/ai/relative-links.ts +4 -38
  273. package/src/ai/site-skill.ts +274 -0
  274. package/src/ai/skills.ts +35 -2
  275. package/src/ai/write-skill.ts +97 -0
  276. package/src/astro/generate.ts +210 -66
  277. package/src/astro/index.ts +1 -0
  278. package/src/astro/integration.ts +42 -4
  279. package/src/astro/markdown-negotiation.ts +3 -0
  280. package/src/astro/module-types.ts +9 -1
  281. package/src/astro/pages.ts +23 -0
  282. package/src/astro/templates.ts +406 -108
  283. package/src/astro/variables.ts +31 -0
  284. package/src/audit/catalog.ts +38 -3
  285. package/src/audit/checks/contrast.ts +183 -0
  286. package/src/audit/checks/i18n.ts +2 -3
  287. package/src/audit/checks/indexability.ts +2 -3
  288. package/src/audit/checks/links.ts +19 -13
  289. package/src/audit/checks/network.ts +110 -6
  290. package/src/audit/checks/redirects.ts +6 -4
  291. package/src/audit/checks/robots.ts +58 -2
  292. package/src/audit/checks/sitemap.ts +2 -3
  293. package/src/audit/redirects.ts +46 -15
  294. package/src/audit/run.ts +5 -0
  295. package/src/audit/types.ts +4 -1
  296. package/src/audit/url.ts +14 -5
  297. package/src/blume-modules.d.ts +12 -1
  298. package/src/captcha/client.ts +152 -0
  299. package/src/captcha/hcaptcha.ts +36 -0
  300. package/src/captcha/index.ts +28 -0
  301. package/src/captcha/schema.ts +32 -0
  302. package/src/captcha/turnstile.ts +38 -0
  303. package/src/captcha/verify.ts +71 -0
  304. package/src/cli/args.ts +51 -0
  305. package/src/cli/build-failure.ts +23 -0
  306. package/src/cli/command-meta.ts +5 -0
  307. package/src/cli/commands/audit.ts +10 -1
  308. package/src/cli/commands/dev.ts +18 -2
  309. package/src/cli/commands/doctor.ts +67 -28
  310. package/src/cli/commands/eval.ts +36 -4
  311. package/src/cli/commands/init.ts +6 -4
  312. package/src/cli/commands/skill.ts +96 -0
  313. package/src/cli/commands/validate.ts +10 -1
  314. package/src/cli/index.ts +5 -0
  315. package/src/cli/init/scaffold.ts +3 -3
  316. package/src/cli/prepare.ts +6 -3
  317. package/src/cli/required-secrets.ts +72 -30
  318. package/src/components/BlumePage.astro +2 -0
  319. package/src/components/content/ApiEndpoint.astro +18 -0
  320. package/src/components/content/ApiField.astro +79 -0
  321. package/src/components/content/ApiPlayground.astro +66 -0
  322. package/src/components/content/ApiRail.astro +19 -0
  323. package/src/components/content/Color.astro +1 -0
  324. package/src/components/content/FileTree.astro +1 -0
  325. package/src/components/content/GithubInfo.astro +1 -0
  326. package/src/components/content/Panel.astro +1 -0
  327. package/src/components/content/ParamField.astro +25 -0
  328. package/src/components/content/RequestExample.astro +17 -0
  329. package/src/components/content/ResponseExample.astro +17 -0
  330. package/src/components/content/ResponseField.astro +32 -0
  331. package/src/components/content/Step.astro +1 -0
  332. package/src/components/content/Tile.astro +1 -0
  333. package/src/components/content/Tooltip.astro +1 -0
  334. package/src/components/content/Tree.astro +1 -0
  335. package/src/components/content/TypeTable.astro +4 -1
  336. package/src/components/content/View.astro +21 -0
  337. package/src/components/content/ViewSwitcher.astro +168 -0
  338. package/src/components/content/api-field.ts +103 -0
  339. package/src/components/content/api-page.ts +248 -0
  340. package/src/components/content/mermaid-element.ts +15 -1
  341. package/src/components/content/view-switcher.ts +71 -0
  342. package/src/components/index.ts +2 -0
  343. package/src/components/islands/Assistant.astro +10 -1
  344. package/src/components/islands/assistant.tsx +104 -14
  345. package/src/components/islands/code-question.ts +62 -0
  346. package/src/components/islands/hooks.ts +136 -21
  347. package/src/components/islands/support-link.ts +68 -0
  348. package/src/components/layout/Analytics.astro +11 -2
  349. package/src/components/layout/Banner.astro +14 -10
  350. package/src/components/layout/Breadcrumbs.astro +6 -2
  351. package/src/components/layout/ConsentBanner.astro +62 -0
  352. package/src/components/layout/ConsentHead.astro +38 -0
  353. package/src/components/layout/Directory.astro +94 -0
  354. package/src/components/layout/DrawerSwitchers.astro +51 -0
  355. package/src/components/layout/Empty.astro +2 -2
  356. package/src/components/layout/Header.astro +122 -80
  357. package/src/components/layout/LanguageSwitcher.astro +48 -15
  358. package/src/components/layout/NarrationPlayer.astro +1085 -0
  359. package/src/components/layout/NavSelector.astro +33 -7
  360. package/src/components/layout/PageActions.astro +139 -24
  361. package/src/components/layout/PageFeedback.astro +54 -49
  362. package/src/components/layout/PageLayout.astro +71 -3
  363. package/src/components/layout/ReferenceLayout.astro +17 -6
  364. package/src/components/layout/RelatedPages.astro +42 -0
  365. package/src/components/layout/RootLayout.astro +318 -47
  366. package/src/components/layout/Search.astro +47 -19
  367. package/src/components/layout/SiteFooter.astro +124 -0
  368. package/src/components/layout/SiteMetatags.astro +16 -0
  369. package/src/components/layout/TableOfContents.astro +24 -17
  370. package/src/components/layout/consent/native.ts +79 -0
  371. package/src/components/layout/consent/types.ts +37 -0
  372. package/src/components/layout/footer-links.ts +53 -0
  373. package/src/components/layout/footer-socials.ts +87 -0
  374. package/src/components/layout/head-scripts.ts +6 -1
  375. package/src/components/layout/locale-redirect.ts +107 -0
  376. package/src/components/layout/nav-utils.ts +83 -0
  377. package/src/components/layout/page-feedback.ts +96 -0
  378. package/src/components/layout/search/algolia.ts +10 -1
  379. package/src/components/layout/search/analytics.ts +126 -0
  380. package/src/components/layout/search/endpoint.ts +48 -5
  381. package/src/components/layout/search/flexsearch.ts +17 -5
  382. package/src/components/layout/search/orama-cloud.ts +26 -14
  383. package/src/components/layout/search/pagefind.ts +114 -5
  384. package/src/components/layout/search/types.ts +32 -0
  385. package/src/components/layout/search/typesense.ts +19 -3
  386. package/src/components/openapi/AsyncApiOperation.astro +14 -7
  387. package/src/components/openapi/Authorization.astro +10 -0
  388. package/src/components/openapi/GraphqlOperation.astro +5 -0
  389. package/src/components/openapi/Operation.astro +184 -13
  390. package/src/components/openapi/ParametersTable.astro +8 -4
  391. package/src/components/openapi/Playground.astro +7 -1
  392. package/src/components/openapi/RequestBody.astro +24 -5
  393. package/src/components/openapi/RequestPanel.astro +59 -7
  394. package/src/components/openapi/Responses.astro +7 -4
  395. package/src/components/openapi/SchemaTable.astro +15 -3
  396. package/src/components/openapi/async-snippets.ts +65 -9
  397. package/src/components/openapi/async.ts +123 -29
  398. package/src/components/openapi/code-samples.ts +97 -0
  399. package/src/components/openapi/graphql-helpers.ts +35 -12
  400. package/src/components/openapi/helpers.ts +36 -2
  401. package/src/components/openapi/message-model.ts +54 -11
  402. package/src/components/openapi/message.ts +14 -4
  403. package/src/components/openapi/operation-model.ts +16 -7
  404. package/src/components/openapi/playground-client.ts +24 -5
  405. package/src/components/openapi/playground-schema.ts +1 -1
  406. package/src/components/openapi/schema-tree.ts +44 -14
  407. package/src/components/openapi/security.ts +16 -3
  408. package/src/components/openapi/snippets.ts +873 -10
  409. package/src/components/openapi/validate-json.ts +4 -3
  410. package/src/components/props.ts +6 -0
  411. package/src/consent/client.ts +95 -0
  412. package/src/consent/clients.ts +19 -0
  413. package/src/consent/ethyca.ts +71 -0
  414. package/src/consent/head.ts +24 -0
  415. package/src/consent/index.ts +37 -0
  416. package/src/consent/init.ts +29 -0
  417. package/src/consent/native.ts +36 -0
  418. package/src/consent/osano.ts +59 -0
  419. package/src/consent/schema.ts +30 -0
  420. package/src/core/builtin-tags.ts +5 -0
  421. package/src/core/changelog-index.ts +31 -1
  422. package/src/core/client-address.ts +19 -0
  423. package/src/core/component-diagnostics.ts +53 -0
  424. package/src/core/component-overrides.ts +32 -0
  425. package/src/core/config-input.ts +225 -23
  426. package/src/core/data.ts +46 -4
  427. package/src/core/diagnostics.ts +8 -2
  428. package/src/core/footer.ts +26 -0
  429. package/src/core/graph.ts +39 -36
  430. package/src/core/i18n-ui.ts +84 -2
  431. package/src/core/includes.ts +21 -4
  432. package/src/core/links.ts +63 -19
  433. package/src/core/localizable.ts +26 -0
  434. package/src/core/navigation.ts +43 -12
  435. package/src/core/page-modes.ts +56 -0
  436. package/src/core/probe.ts +26 -6
  437. package/src/core/project-graph.ts +163 -16
  438. package/src/core/redirect-patterns.ts +520 -0
  439. package/src/core/related.ts +131 -0
  440. package/src/core/schema.ts +339 -66
  441. package/src/core/sources/cache.ts +6 -0
  442. package/src/core/sources/contentful-rich-text.ts +29 -6
  443. package/src/core/sources/contentful.ts +135 -25
  444. package/src/core/sources/filesystem.ts +26 -4
  445. package/src/core/sources/github-releases.ts +12 -1
  446. package/src/core/sources/lexical.ts +15 -10
  447. package/src/core/sources/mdx-remote.ts +62 -10
  448. package/src/core/sources/normalize.ts +228 -41
  449. package/src/core/sources/notion.ts +7 -1
  450. package/src/core/sources/obsidian.ts +16 -3
  451. package/src/core/sources/payload.ts +38 -15
  452. package/src/core/sources/read.ts +8 -3
  453. package/src/core/sources/remote.ts +72 -15
  454. package/src/core/sources/resolve.ts +11 -1
  455. package/src/core/sources/sanity.ts +43 -25
  456. package/src/core/sources/strapi.ts +2 -2
  457. package/src/core/types.ts +19 -1
  458. package/src/core/ui-packs/ar.ts +44 -0
  459. package/src/core/ui-packs/bg.ts +46 -0
  460. package/src/core/ui-packs/bn.ts +44 -0
  461. package/src/core/ui-packs/ca.ts +47 -0
  462. package/src/core/ui-packs/cs.ts +45 -0
  463. package/src/core/ui-packs/da.ts +46 -0
  464. package/src/core/ui-packs/de.ts +47 -0
  465. package/src/core/ui-packs/el.ts +46 -0
  466. package/src/core/ui-packs/es.ts +47 -0
  467. package/src/core/ui-packs/fa.ts +46 -0
  468. package/src/core/ui-packs/fi.ts +46 -0
  469. package/src/core/ui-packs/fr.ts +47 -0
  470. package/src/core/ui-packs/he.ts +44 -0
  471. package/src/core/ui-packs/hi.ts +45 -0
  472. package/src/core/ui-packs/hr.ts +46 -0
  473. package/src/core/ui-packs/hu.ts +45 -0
  474. package/src/core/ui-packs/id.ts +46 -0
  475. package/src/core/ui-packs/it.ts +46 -0
  476. package/src/core/ui-packs/ja.ts +46 -0
  477. package/src/core/ui-packs/ko.ts +45 -0
  478. package/src/core/ui-packs/nl.ts +47 -0
  479. package/src/core/ui-packs/no.ts +45 -0
  480. package/src/core/ui-packs/pl.ts +46 -0
  481. package/src/core/ui-packs/pt-br.ts +47 -0
  482. package/src/core/ui-packs/pt.ts +46 -0
  483. package/src/core/ui-packs/ro.ts +46 -0
  484. package/src/core/ui-packs/ru.ts +46 -0
  485. package/src/core/ui-packs/sk.ts +45 -0
  486. package/src/core/ui-packs/sr.ts +46 -0
  487. package/src/core/ui-packs/sv.ts +45 -0
  488. package/src/core/ui-packs/th.ts +44 -0
  489. package/src/core/ui-packs/tr.ts +46 -0
  490. package/src/core/ui-packs/uk.ts +46 -0
  491. package/src/core/ui-packs/vi.ts +46 -0
  492. package/src/core/ui-packs/zh-tw.ts +44 -0
  493. package/src/core/ui-packs/zh.ts +44 -0
  494. package/src/core/variables.ts +103 -0
  495. package/src/deploy/adapter-output.ts +7 -2
  496. package/src/deploy/adapters/cloudflare.ts +7 -5
  497. package/src/deploy/adapters/node.ts +47 -9
  498. package/src/deploy/adapters/types.ts +7 -3
  499. package/src/deploy/artifacts.ts +79 -9
  500. package/src/deploy/cloudflare-negotiation.ts +35 -5
  501. package/src/deploy/headers.ts +2 -1
  502. package/src/deploy/node-headers.ts +63 -9
  503. package/src/deploy/platforms/cloudflare.ts +45 -9
  504. package/src/deploy/platforms/netlify.ts +35 -2
  505. package/src/deploy/platforms/node.ts +12 -2
  506. package/src/deploy/platforms/types.ts +21 -7
  507. package/src/deploy/platforms/vercel.ts +149 -17
  508. package/src/deploy/redirects.ts +151 -24
  509. package/src/deploy/rss.ts +3 -1
  510. package/src/deploy/sitemap.ts +39 -5
  511. package/src/deploy/vercel-negotiation.ts +326 -102
  512. package/src/eval/agents.ts +18 -1
  513. package/src/eval/prompts.ts +6 -3
  514. package/src/eval/report.ts +34 -13
  515. package/src/eval/run.ts +5 -0
  516. package/src/markdown/api-rail.ts +259 -0
  517. package/src/markdown/code-title.ts +35 -9
  518. package/src/markdown/features.ts +3 -2
  519. package/src/markdown/fence-meta.ts +2 -0
  520. package/src/markdown/include.ts +11 -1
  521. package/src/markdown/index.ts +28 -5
  522. package/src/markdown/math.ts +4 -4
  523. package/src/markdown/relative-links.ts +3 -2
  524. package/src/markdown/variables.ts +56 -0
  525. package/src/markdown/views.ts +82 -0
  526. package/src/narration/build.ts +412 -0
  527. package/src/narration/player.ts +249 -0
  528. package/src/narration/provider.ts +29 -0
  529. package/src/narration/script.ts +592 -0
  530. package/src/og/card.ts +7 -6
  531. package/src/openapi/checks.ts +273 -0
  532. package/src/openapi/graphql-build.ts +21 -0
  533. package/src/openapi/graphql.ts +11 -0
  534. package/src/openapi/model.ts +72 -27
  535. package/src/openapi/overlay.ts +256 -0
  536. package/src/openapi/parse.ts +182 -4
  537. package/src/openapi/references.ts +64 -8
  538. package/src/openapi/render-mdx.ts +5 -1
  539. package/src/openapi/scalar.ts +40 -6
  540. package/src/openapi/source.ts +40 -15
  541. package/src/ratelimit/cloudflare.ts +54 -0
  542. package/src/ratelimit/index.ts +30 -0
  543. package/src/ratelimit/memory.ts +45 -0
  544. package/src/ratelimit/runtime.ts +247 -0
  545. package/src/ratelimit/schema.ts +28 -0
  546. package/src/ratelimit/upstash.ts +60 -0
  547. package/src/ratelimit/wrangler.ts +70 -0
  548. package/src/reference/asyncapi.ts +2 -2
  549. package/src/reference/graphql.ts +20 -3
  550. package/src/reference/index.ts +1 -0
  551. package/src/reference/openapi.ts +21 -7
  552. package/src/reference/options.ts +80 -8
  553. package/src/reference/scalar.ts +33 -8
  554. package/src/registry/eject.ts +23 -5
  555. package/src/registry/registry.ts +7 -0
  556. package/src/search/adapters/locale-scope.ts +30 -0
  557. package/src/search/adapters/typesense.ts +14 -4
  558. package/src/search/build.ts +21 -3
  559. package/src/search/documents.ts +26 -2
  560. package/src/search/orama-index.ts +191 -34
  561. package/src/search/pagefind-ranking.ts +39 -0
  562. package/src/search/source-pages.ts +29 -0
  563. package/src/search/sync/algolia.ts +111 -10
  564. package/src/search/sync/index.ts +52 -13
  565. package/src/search/sync/orama-cloud.ts +3 -0
  566. package/src/search/sync/typesense.ts +76 -26
  567. package/src/seo/metatags.ts +67 -0
  568. package/src/sources/contentful.ts +12 -0
  569. package/src/sources/github-releases.ts +7 -1
  570. package/src/sources/shared.ts +3 -0
  571. package/src/theme/chrome-icons.ts +1 -0
  572. package/src/theme/color.ts +500 -0
  573. package/src/theme/entry.ts +85 -6
  574. package/src/theme/palette.ts +121 -45
  575. package/src/translate/prompts.ts +1 -0
  576. package/src/translate/validate.ts +31 -3
  577. package/src/upgrade/upgrade.ts +4 -171
  578. package/dist/cli/chunk-1d7ve1dm.js.map +0 -12
  579. package/dist/cli/chunk-2hsdwb9n.js.map +0 -10
  580. package/dist/cli/chunk-35d4wj9f.js.map +0 -15
  581. package/dist/cli/chunk-364znk6q.js +0 -75
  582. package/dist/cli/chunk-364znk6q.js.map +0 -11
  583. package/dist/cli/chunk-5m5nmvyq.js.map +0 -13
  584. package/dist/cli/chunk-8ktnccpt.js.map +0 -10
  585. package/dist/cli/chunk-9t7a85s3.js.map +0 -10
  586. package/dist/cli/chunk-b07cmahc.js +0 -145
  587. package/dist/cli/chunk-b07cmahc.js.map +0 -11
  588. package/dist/cli/chunk-c8chx29p.js.map +0 -36
  589. package/dist/cli/chunk-e04dxsz1.js +0 -39
  590. package/dist/cli/chunk-e04dxsz1.js.map +0 -10
  591. package/dist/cli/chunk-j85vccga.js.map +0 -36
  592. package/dist/cli/chunk-p3v96n38.js.map +0 -10
  593. package/dist/cli/chunk-pehfxfta.js.map +0 -10
  594. package/dist/cli/chunk-pv29h0wf.js.map +0 -190
  595. package/dist/cli/chunk-q58y5e6a.js.map +0 -10
  596. package/dist/cli/chunk-vg9r4eb9.js.map +0 -14
  597. package/dist/cli/chunk-wrr3j9w9.js.map +0 -35
  598. package/dist/cli/chunk-yfyb25rh.js.map +0 -14
  599. package/docs/content/sources.mdx +0 -334
  600. /package/dist/cli/{chunk-tkacnehg.js.map → chunk-3v3ffbrj.js.map} +0 -0
  601. /package/dist/cli/{chunk-r9rcc4w7.js.map → chunk-8y2kmjv8.js.map} +0 -0
  602. /package/dist/cli/{chunk-2eytanqx.js.map → chunk-c887fj0s.js.map} +0 -0
  603. /package/dist/cli/{chunk-akbpwfxc.js.map → chunk-dzwehpy7.js.map} +0 -0
  604. /package/dist/cli/{chunk-2z928egk.js.map → chunk-ft9vfys6.js.map} +0 -0
  605. /package/dist/cli/{chunk-a58773jm.js.map → chunk-kma9f6sf.js.map} +0 -0
  606. /package/dist/cli/{chunk-crgn1q09.js.map → chunk-sds923fa.js.map} +0 -0
  607. /package/dist/cli/{chunk-acanzt5p.js.map → chunk-tdqhyxfz.js.map} +0 -0
@@ -8,9 +8,19 @@ import {
8
8
  } from "../ai/ask.ts";
9
9
  import type { ComponentMarkdown } from "../ai/component-markdown.ts";
10
10
  import { analyticsConfigSchema } from "../analytics/schema.ts";
11
+ import { captchaAdapterSchema } from "../captcha/schema.ts";
12
+ import {
13
+ API_ENDPOINT,
14
+ AUTH_METHODS,
15
+ PLAYGROUND_MODES,
16
+ } from "../components/content/api-page.ts";
17
+ import { consentConfigSchema } from "../consent/schema.ts";
11
18
  import { resolvedDeploymentSchema } from "../deploy/adapters/registry.ts";
12
19
  import type { CodeTheme } from "../markdown/themes.ts";
20
+ import { narrationProviderSchema } from "../narration/provider.ts";
13
21
  import { normalizeRoute } from "../openapi/references.ts";
22
+ import { rateLimitConfigSchema } from "../ratelimit/schema.ts";
23
+ import { playgroundSchema } from "../reference/options.ts";
14
24
  import {
15
25
  referenceConfigSchema,
16
26
  removedReferenceKeysHint,
@@ -21,6 +31,7 @@ import {
21
31
  resolvedSearchAdapterSchema,
22
32
  } from "../search/adapters/registry.ts";
23
33
  import type { SearchAdapterInput } from "../search/adapters/registry.ts";
34
+ import { ownedMetatag } from "../seo/metatags.ts";
24
35
  import { normalizeXHandle } from "../seo/x-handle.ts";
25
36
  import { filesystem } from "../sources/filesystem.ts";
26
37
  import {
@@ -28,14 +39,19 @@ import {
28
39
  resolvedSourceAdapterSchema,
29
40
  } from "../sources/registry.ts";
30
41
  import { FONT_SLUGS, isFontSlug } from "../theme/fonts.ts";
31
- import { normalizeBasePath } from "./base-path.ts";
42
+ import { isExternalUrl, normalizeBasePath } from "./base-path.ts";
43
+ import { FOOTER_SOCIALS } from "./footer.ts";
32
44
  import { PUBLIC_HOST_URL } from "./github.ts";
33
45
  import { uiLocaleOverridesSchema } from "./i18n-ui.ts";
34
46
  import { openInChatProviders } from "./open-in-chat.ts";
47
+ import { PAGE_MODES } from "./page-modes.ts";
48
+ import { redirectPatternError } from "./redirect-patterns.ts";
49
+ import { MAX_RELATED } from "./related.ts";
35
50
  import { isStandardSchema } from "./standard-schema.ts";
36
51
  import type { StandardSchema } from "./standard-schema.ts";
37
52
  import { trimEnd } from "./trim.ts";
38
53
  import { unrecognizedKeysMessage } from "./unrecognized-keys.ts";
54
+ import { VARIABLE_NAME } from "./variables.ts";
39
55
 
40
56
  /**
41
57
  * An absolute HTTP(S) URL, for any field that lands verbatim in an `href` —
@@ -119,6 +135,15 @@ const dateSchema = z
119
135
  const sidebarDisplaySchema = z.enum(["flat", "group", "page"]);
120
136
  export type SidebarDisplay = z.infer<typeof sidebarDisplaySchema>;
121
137
 
138
+ /**
139
+ * How a group's own page lists the group's other pages below its content:
140
+ * `card` a grid of cards, `accordion` a list with each subgroup collapsible,
141
+ * `none` no listing (the default). A group without its own page has nowhere
142
+ * to show one. Nested groups inherit the nearest setting above them.
143
+ */
144
+ const directoryModeSchema = z.enum(["accordion", "card", "none"]);
145
+ export type DirectoryMode = z.infer<typeof directoryModeSchema>;
146
+
122
147
  // ---------------------------------------------------------------------------
123
148
  // Page frontmatter
124
149
  // ---------------------------------------------------------------------------
@@ -155,16 +180,18 @@ const seoMetaSchema = z.strictObject({
155
180
  x: z.strictObject({ creator: xHandleSchema }).optional(),
156
181
  });
157
182
 
158
- const searchMetaSchema = z.strictObject(
159
- {
160
- exclude: z.boolean().default(false),
161
- tags: z.array(z.string()).optional(),
162
- },
163
- removedKeysHint({
164
- boost:
165
- "search.boost was removed: search never read it, so the page ranked the same without it. Delete the field.",
166
- })
167
- );
183
+ const searchMetaSchema = z.strictObject({
184
+ /**
185
+ * Multiply the page's search relevance: above 1 ranks it higher, below 1
186
+ * lower. Keep it to a few key pages, and under 10: a large boost floats a
187
+ * page over better matches.
188
+ */
189
+ boost: z.number().positive().optional(),
190
+ exclude: z.boolean().default(false),
191
+ /** Extra terms the page is found by, beyond its own text. */
192
+ keywords: z.array(z.string()).optional(),
193
+ tags: z.array(z.string()).optional(),
194
+ });
168
195
 
169
196
  const aiMetaSchema = z.strictObject({
170
197
  /** Exclude this page from llms.txt and llms-full.txt. */
@@ -201,9 +228,31 @@ const authorSchema = z.union([
201
228
  // `.default({})` on an object with inner defaults (or a transform) would
202
229
  // resolve to a bare `{}` instead of the fully-defaulted shape.
203
230
 
231
+ /** A `related` link: a root-relative page path or an absolute URL. */
232
+ const relatedLinkSchema = z
233
+ .string()
234
+ .refine((link) => link.startsWith("/") || isExternalUrl(link), {
235
+ message:
236
+ 'A related link is a root-relative path ("/guides/setup") or an absolute URL.',
237
+ });
238
+
204
239
  /** Frontmatter accepted on any content page. */
205
240
  const pageMetaBaseSchema = z.strictObject({
206
241
  ai: aiMetaSchema.prefault({}),
242
+ /**
243
+ * A hand-written endpoint: an HTTP method and a path or full URL
244
+ * (`POST /v1/users`). The page's `<ParamField>`s build its playground and
245
+ * request samples.
246
+ */
247
+ api: z
248
+ .string()
249
+ .refine((value) => API_ENDPOINT.test(value.trim()), {
250
+ message:
251
+ 'api takes an HTTP method and a path or URL, like "POST /v1/users" or "GET https://api.acme.com/v1/users/{id}".',
252
+ })
253
+ .optional(),
254
+ /** How this page's endpoint authenticates, over the site's `api.auth`. */
255
+ authMethod: z.enum(AUTH_METHODS).optional(),
207
256
  /** Post author(s) for blog/changelog content; preserved, not yet rendered. */
208
257
  authors: z.union([authorSchema, z.array(authorSchema)]).optional(),
209
258
  changelog: changelogMetaSchema.optional(),
@@ -216,7 +265,37 @@ const pageMetaBaseSchema = z.strictObject({
216
265
  icon: iconName.optional(),
217
266
  /** Overrides the git-derived last-modified date when `lastModified` is on. */
218
267
  lastModified: dateSchema.optional(),
268
+ /** What the page shows around its content (see `core/page-modes.ts`). */
269
+ mode: z.enum(PAGE_MODES).optional(),
270
+ /** `false` keeps the "Listen to this page" player off this page. */
271
+ narration: z.boolean().default(true),
219
272
  noindex: z.boolean().default(false),
273
+ /** `false` drops the previous/next links at the foot of this page. */
274
+ pagination: z.boolean().default(true),
275
+ /** What an `api` page's playground shows: `interactive`, `simple` (samples only), or `none`. */
276
+ playground: z.enum(PLAYGROUND_MODES).optional(),
277
+ /**
278
+ * Pages to suggest at the foot of this one: root-relative paths, absolute
279
+ * URLs, or `{ Title: link }` to name a link. `false` lists none.
280
+ */
281
+ related: z
282
+ .union([
283
+ z.literal(false),
284
+ z
285
+ .array(
286
+ z.union([
287
+ relatedLinkSchema,
288
+ z
289
+ .record(z.string().min(1), relatedLinkSchema)
290
+ .refine((entry) => Object.keys(entry).length === 1, {
291
+ message:
292
+ 'A titled related entry has one title and one link: { "Title": "/path" }.',
293
+ }),
294
+ ])
295
+ )
296
+ .max(MAX_RELATED),
297
+ ])
298
+ .optional(),
220
299
  search: searchMetaSchema.prefault({}),
221
300
  seo: seoMetaSchema.prefault({}),
222
301
  sidebar: sidebarMetaSchema.prefault({}),
@@ -292,6 +371,8 @@ const customKeySchemaRecord = (where: string) =>
292
371
 
293
372
  export const folderMetaSchema = z.strictObject({
294
373
  collapsed: z.boolean().optional(),
374
+ /** List the group's pages on its index page; inherited by nested folders. */
375
+ directory: directoryModeSchema.optional(),
295
376
  /** Render mode for this group; overrides `navigation.sidebar.display`. */
296
377
  display: sidebarDisplaySchema.optional(),
297
378
  icon: iconName.optional(),
@@ -333,19 +414,87 @@ const logoConfigSchema = z.union([
333
414
  }),
334
415
  ]);
335
416
 
336
- /** Site-wide announcement banner: a string, or text with an optional link. */
417
+ /**
418
+ * A label that may localize (header tabs and links, the banner, the footer):
419
+ * a plain string, or a map of locale code to label (`{ en: "Docs", ja: "ドキュメント" }`).
420
+ * Resolved per locale by `core/localizable.ts` — the active locale's entry
421
+ * wins, then the default locale's, then the map's first entry — so a
422
+ * single-locale site can keep plain strings and an i18n site can translate
423
+ * its labels without forking the config.
424
+ */
425
+ const localizableLabelSchema = z.union([
426
+ z.string(),
427
+ z
428
+ .record(z.string(), z.string())
429
+ .refine((value) => Object.keys(value).length > 0, {
430
+ message: "Provide at least one locale's label.",
431
+ }),
432
+ ]);
433
+
434
+ export type LocalizableLabel = z.infer<typeof localizableLabelSchema>;
435
+
436
+ /**
437
+ * Site-wide announcement banner: a string, or text with an optional link. The
438
+ * object form's `content` and link `text` may be per-locale maps.
439
+ */
337
440
  const bannerConfigSchema = z.union([
338
441
  z.string(),
339
442
  z.strictObject({
340
- content: z.string(),
443
+ content: localizableLabelSchema,
341
444
  /** Show a dismiss button; the choice is remembered per visitor. */
342
445
  dismissible: z.boolean().default(false),
343
- /** Stable key for remembering dismissal; defaults to the content. */
446
+ /**
447
+ * Stable key for remembering dismissal; defaults to the content (the
448
+ * default locale's, for a per-locale map).
449
+ */
344
450
  id: z.string().optional(),
345
- link: z.strictObject({ href: z.string(), text: z.string() }).optional(),
451
+ link: z
452
+ .strictObject({ href: z.string(), text: localizableLabelSchema })
453
+ .optional(),
346
454
  }),
347
455
  ]);
348
456
 
457
+ /**
458
+ * Defaults for hand-written endpoint pages (`api` frontmatter): the server a
459
+ * path joins, how requests authenticate, and the playground, which takes an
460
+ * OpenAPI reference's `playground` option.
461
+ */
462
+ const apiConfigSchema = z.strictObject({
463
+ auth: z
464
+ .strictObject({
465
+ method: z.enum(AUTH_METHODS),
466
+ /** The header an API key goes in. Defaults to `x-api-key`. */
467
+ name: z.string().optional(),
468
+ })
469
+ .optional(),
470
+ playground: playgroundSchema,
471
+ /** The base URL an `api` path joins (`https://api.acme.com/v1`). */
472
+ server: z.string().optional(),
473
+ });
474
+
475
+ /**
476
+ * The site footer: a row of links, and social profile icons. Unset, the site
477
+ * has no footer (a `components.ts` `Footer` still renders).
478
+ */
479
+ const footerConfigSchema = z.strictObject({
480
+ /** Links in one row, in the order written. */
481
+ links: z
482
+ .array(z.strictObject({ href: z.string(), label: localizableLabelSchema }))
483
+ .default([]),
484
+ /** Social profiles, platform to URL, shown as icons in the order written. */
485
+ socials: z.partialRecord(z.enum(FOOTER_SOCIALS), z.string()).default({}),
486
+ });
487
+
488
+ /**
489
+ * The generated `/changelog` index. Its title and description may be
490
+ * per-locale maps; unset, they're the `changelog` UI strings `i18n.ui`
491
+ * translates.
492
+ */
493
+ const changelogConfigSchema = z.strictObject({
494
+ description: localizableLabelSchema.optional(),
495
+ title: localizableLabelSchema.optional(),
496
+ });
497
+
349
498
  /** A validated `content.sources` entry: an adapter descriptor from `blume/sources`. */
350
499
  export type { ContentSourceAdapter } from "../sources/registry.ts";
351
500
 
@@ -426,25 +575,6 @@ const contentConfigSchema = z
426
575
  ],
427
576
  }));
428
577
 
429
- /**
430
- * A header label that may localize: a plain string, or a map of locale code to
431
- * label (`{ en: "Docs", ja: "ドキュメント" }`). Resolved when each locale's
432
- * navigation is built — the active locale's entry wins, then the default
433
- * locale's, then the map's first entry — so a single-locale site can keep
434
- * plain strings and an i18n site can translate its header without forking the
435
- * config.
436
- */
437
- const localizableLabelSchema = z.union([
438
- z.string(),
439
- z
440
- .record(z.string(), z.string())
441
- .refine((value) => Object.keys(value).length > 0, {
442
- message: "Provide at least one locale's label.",
443
- }),
444
- ]);
445
-
446
- export type LocalizableLabel = z.infer<typeof localizableLabelSchema>;
447
-
448
578
  const navTabSchema = z.strictObject({
449
579
  // Rejected empty rather than accepted: an empty `href` would render a link to
450
580
  // nowhere, and it can't mean "resolve it for me" either — that's what
@@ -484,9 +614,6 @@ const navSelectorSchema = z.strictObject({
484
614
  label: z.string(),
485
615
  });
486
616
 
487
- const directoryModeSchema = z.enum(["accordion", "card", "none"]);
488
- export type DirectoryMode = z.infer<typeof directoryModeSchema>;
489
-
490
617
  /** A node in an explicit sidebar config: a page reference or a group/link. */
491
618
  export type SidebarItemConfig =
492
619
  | string
@@ -661,9 +788,17 @@ const searchIndexingSchema = z
661
788
  })
662
789
  .prefault({});
663
790
 
791
+ /** What the search dialog's analytics events carry. */
792
+ const searchAnalyticsSchema = z
793
+ .strictObject({
794
+ queries: z.boolean().default(true),
795
+ })
796
+ .prefault({});
797
+
664
798
  /** The object form of `search`: the adapter plus its adapter-independent settings. */
665
799
  const searchOptionsSchema = z.strictObject(
666
800
  {
801
+ analytics: searchAnalyticsSchema,
667
802
  indexing: searchIndexingSchema,
668
803
  /** Curated links for the Cmd+K empty state; defaults to the first sidebar pages. */
669
804
  popular: z.array(searchPopularLinkSchema).default([]),
@@ -842,6 +977,9 @@ const aiConfigFields = {
842
977
  // origin. Each URL is reduced to its origin so a trailing slash or path
843
978
  // can't defeat the exact match the route performs. Read by the
844
979
  // generated route only; an external `endpoint` owns its own CORS.
980
+ // A bot check from `blume/captcha`: the panel sends a token with each
981
+ // question and the generated route verifies it before the model runs.
982
+ captcha: captchaAdapterSchema.optional(),
845
983
  cors: z
846
984
  .array(
847
985
  z.union([
@@ -890,6 +1028,27 @@ const aiConfigFields = {
890
1028
  })
891
1029
  )
892
1030
  .default([]),
1031
+ // Where the panel's Contact support link goes: an email address
1032
+ // (`mailto:`, which starts an email with the conversation), a URL,
1033
+ // or a root-relative page (which get the conversation's `thread` id).
1034
+ support: z
1035
+ .string()
1036
+ .refine(
1037
+ (link) =>
1038
+ link.startsWith("mailto:") ||
1039
+ link.startsWith("/") ||
1040
+ isExternalUrl(link),
1041
+ {
1042
+ message:
1043
+ 'ai.assistant.support is a mailto: address, a URL, or a root-relative path, like "mailto:help@example.com".',
1044
+ }
1045
+ )
1046
+ .optional(),
1047
+ // Search and read-page tools the model can call over several steps.
1048
+ // No default here: unset follows the adapter (`toolsByDefault` in
1049
+ // `ai/ask.ts`), on for the hosted catalogs and off for an
1050
+ // OpenAI-compatible backend that may not speak tool calling.
1051
+ tools: z.boolean().optional(),
893
1052
  },
894
1053
  assistantMovedFieldsHint
895
1054
  )
@@ -946,7 +1105,7 @@ const aiConfigSchema = z.strictObject(
946
1105
  const featuredLinkSchema = z.strictObject({
947
1106
  href: z.string(),
948
1107
  icon: iconName.optional(),
949
- label: z.string(),
1108
+ label: localizableLabelSchema,
950
1109
  });
951
1110
 
952
1111
  /**
@@ -956,7 +1115,7 @@ const featuredLinkSchema = z.strictObject({
956
1115
  */
957
1116
  const headerActionSchema = z.strictObject({
958
1117
  href: z.string(),
959
- label: z.string(),
1118
+ label: localizableLabelSchema,
960
1119
  });
961
1120
 
962
1121
  const navigationConfigSchema = z.strictObject({
@@ -973,7 +1132,7 @@ const navigationConfigSchema = z.strictObject({
973
1132
  /** Pinned links shown above the generated sidebar sections. */
974
1133
  featured: z.array(featuredLinkSchema).default([]),
975
1134
  /**
976
- * The GitHub link in the header. `true` derives it from `github`, `false`
1135
+ * The GitHub link in the footer. `true` derives it from `github`, `false`
977
1136
  * hides it, and an absolute URL points it anywhere on GitHub — an
978
1137
  * organization, say, when the docs repo itself is private and `github` has
979
1138
  * to stay unset. The mark stays the GitHub one, so a URL elsewhere belongs in
@@ -1026,6 +1185,45 @@ const exportConfigSchema = z
1026
1185
  isBoolean(value) ? { epub: value, pdf: value } : value
1027
1186
  );
1028
1187
 
1188
+ // The "Was this page helpful?" rating, on by default. `comments: true` adds a
1189
+ // box after the rating where the reader can say more, sent through the
1190
+ // analytics adapters as a `feedback_comment` event. Both forms normalize to
1191
+ // `{ enabled, comments }`.
1192
+ const feedbackConfigSchema = z
1193
+ .union([
1194
+ z.boolean(),
1195
+ z.strictObject({
1196
+ comments: z.boolean().default(false),
1197
+ enabled: z.boolean().default(true),
1198
+ }),
1199
+ ])
1200
+ // With the rating off there is nothing to comment on.
1201
+ .transform((value) =>
1202
+ isBoolean(value)
1203
+ ? { comments: false, enabled: value }
1204
+ : { comments: value.enabled && value.comments, enabled: value.enabled }
1205
+ );
1206
+
1207
+ // "Listen to this page". Off by default. `true` reads pages aloud with the
1208
+ // reader's browser voices, which needs no key and works on any host; an object
1209
+ // with a `provider` (`gateway()` from `blume/ai`) generates neural audio at
1210
+ // build instead, one cached clip per sentence, and falls back to the browser's
1211
+ // voices where no clips exist (`blume dev`, a build without the key). Both
1212
+ // normalize to `{ enabled, provider }`.
1213
+ const narrationConfigSchema = z
1214
+ .union([
1215
+ z.boolean(),
1216
+ z.strictObject({
1217
+ enabled: z.boolean().default(true),
1218
+ provider: narrationProviderSchema.optional(),
1219
+ }),
1220
+ ])
1221
+ .transform((value) =>
1222
+ isBoolean(value)
1223
+ ? { enabled: value, provider: null }
1224
+ : { enabled: value.enabled, provider: value.provider ?? null }
1225
+ );
1226
+
1029
1227
  /** A configured locale: ISO-ish code plus display metadata for the switcher. */
1030
1228
  const localeSchema = z.strictObject({
1031
1229
  code: z.string().min(1),
@@ -1055,6 +1253,12 @@ const i18nConfigSchema = z
1055
1253
  locales: z.array(localeSchema).min(1),
1056
1254
  /** `"dir"`: locale directories (`fr/page.mdx`). `"dot"`: filename suffix (`page.fr.mdx`). */
1057
1255
  parser: z.enum(["dir", "dot"]).default("dir"),
1256
+ /**
1257
+ * Send a visitor who lands on the default language's home page to the
1258
+ * home page in their browser's preferred language, until they pick a
1259
+ * language with the switcher. Off by default.
1260
+ */
1261
+ routeByBrowserLanguage: z.boolean().default(false),
1058
1262
  /** Per-locale UI string overrides: `{ fr: { search: { button: "…" } } }`. */
1059
1263
  ui: uiLocaleOverridesSchema.optional(),
1060
1264
  })
@@ -1156,13 +1360,6 @@ const versionsConfigSchema = z
1156
1360
  }
1157
1361
  });
1158
1362
 
1159
- /**
1160
- * A pattern segment in a redirect path: a named `:param` segment or a `*`
1161
- * splat. `from` is matched as an exact path, and hosts disagree on patterns —
1162
- * a static build would even write a literal `:slug` folder — so both ends are
1163
- * checked. An absolute `to` URL's own scheme and host are skipped.
1164
- */
1165
- const REDIRECT_PATTERN = /(?:^|\/):[A-Za-z_]|\*/u;
1166
1363
  const URL_ORIGIN = /^[a-z][\d+.a-z-]*:\/\/[^/]*/iu;
1167
1364
 
1168
1365
  /**
@@ -1183,23 +1380,33 @@ const REDIRECT_START = {
1183
1380
  },
1184
1381
  };
1185
1382
 
1186
- const exactRedirectPath = (end: "from" | "to") =>
1187
- z
1188
- .string()
1189
- .refine(REDIRECT_START[end].allows, {
1190
- message: REDIRECT_START[end].message,
1191
- })
1192
- .refine((path) => !REDIRECT_PATTERN.test(path.replace(URL_ORIGIN, "")), {
1193
- message: `redirects take exact paths: \`${end}\` can't hold a \`:param\` segment or a \`*\` wildcard. Add one redirect per path, or put pattern rules in your host's redirect config (vercel.json, _redirects).`,
1194
- });
1383
+ const redirectPath = (end: "from" | "to") =>
1384
+ z.string().refine(REDIRECT_START[end].allows, {
1385
+ message: REDIRECT_START[end].message,
1386
+ });
1195
1387
 
1196
- const redirectSchema = z.strictObject({
1197
- from: exactRedirectPath("from"),
1198
- status: z
1199
- .union([z.literal(301), z.literal(302), z.literal(307), z.literal(308)])
1200
- .default(301),
1201
- to: exactRedirectPath("to"),
1202
- });
1388
+ /**
1389
+ * A redirect: an exact path, or a pattern (`/beta/:slug*`, `/old/*`) whose
1390
+ * captures `to` may read (see `core/redirect-patterns.ts`).
1391
+ */
1392
+ const redirectSchema = z
1393
+ .strictObject({
1394
+ from: redirectPath("from"),
1395
+ status: z
1396
+ .union([z.literal(301), z.literal(302), z.literal(307), z.literal(308)])
1397
+ .default(301),
1398
+ to: redirectPath("to"),
1399
+ })
1400
+ .superRefine((redirect, ctx) => {
1401
+ const error = redirectPatternError(redirect.from, redirect.to);
1402
+ if (error) {
1403
+ ctx.addIssue({
1404
+ code: z.ZodIssueCode.custom,
1405
+ message: error.message,
1406
+ path: [error.end],
1407
+ });
1408
+ }
1409
+ });
1203
1410
 
1204
1411
  /**
1205
1412
  * One authorized remote image source, passed through to Astro's
@@ -1417,6 +1624,26 @@ type SoftwareResolved = z.output<typeof softwareConfigSchema>;
1417
1624
 
1418
1625
  /** Discoverability features: OG images, feeds, sitemap, structured data. */
1419
1626
  const seoConfigFields = {
1627
+ /**
1628
+ * Meta tags written into every page's head, name to content: site
1629
+ * verification, `theme-color`, and anything else Blume has no setting for.
1630
+ * A tag Blume writes itself is refused with the setting that controls it.
1631
+ */
1632
+ metatags: z
1633
+ .record(z.string().min(1), z.string())
1634
+ .optional()
1635
+ .superRefine((tags, ctx) => {
1636
+ for (const name of Object.keys(tags ?? {})) {
1637
+ const owner = ownedMetatag(name);
1638
+ if (owner) {
1639
+ ctx.addIssue({
1640
+ code: "custom",
1641
+ message: `seo.metatags can't set "${name}": Blume writes that tag. Set it with ${owner}.`,
1642
+ path: [name],
1643
+ });
1644
+ }
1645
+ }
1646
+ }),
1420
1647
  og: ogConfigSchema.default({}),
1421
1648
  /** The organization behind the site, as an `Organization` JSON-LD node. */
1422
1649
  organization: organizationConfigSchema.optional(),
@@ -1519,6 +1746,14 @@ const agentsConfigSchema = z.strictObject({
1519
1746
  .default({}),
1520
1747
  /** Expose the docs as an MCP server for connecting agents. */
1521
1748
  mcp: mcpConfigSchema.prefault({}),
1749
+ /**
1750
+ * Generate the site's own agent skill: a `SKILL.md` named after the site,
1751
+ * built from its navigation, page descriptions, and agent surfaces, served
1752
+ * at `/skill.md` and in the skills discovery index. Needs a
1753
+ * `deployment.site`. A skill in `agents.skills` with the same name replaces
1754
+ * it. On by default.
1755
+ */
1756
+ skillMd: z.boolean().default(true),
1522
1757
  /**
1523
1758
  * Publish Agent Skills for discovery: a directory (resolved against the
1524
1759
  * project root) whose subdirectories each hold a `SKILL.md`. The build
@@ -1805,9 +2040,9 @@ const markdownConfigSchema = z.strictObject(
1805
2040
  const reactConfigSchema = z.strictObject({
1806
2041
  /**
1807
2042
  * Auto-memoize React components/hooks with the React Compiler
1808
- * (`babel-plugin-react-compiler`). On by default whenever React is enabled
2043
+ * (`oxc-transform-react`). On by default whenever React is enabled
1809
2044
  * (a project `.tsx`/`.jsx`, a React island/example/override, or the assistant); set
1810
- * to `false` to skip the compiler's babel pass.
2045
+ * to `false` to skip the compiler pass.
1811
2046
  */
1812
2047
  compiler: z.boolean().default(true),
1813
2048
  });
@@ -1867,6 +2102,7 @@ export const blumeConfigSchema = z
1867
2102
  ai: aiConfigSchema.prefault({}),
1868
2103
  // Adapters from `blume/analytics`, each a serializable descriptor.
1869
2104
  analytics: analyticsConfigSchema,
2105
+ api: apiConfigSchema.prefault({}),
1870
2106
  banner: bannerConfigSchema.optional(),
1871
2107
  /**
1872
2108
  * Site-wide mount point prepended to every generated route (e.g. `/docs`),
@@ -1884,6 +2120,9 @@ export const blumeConfigSchema = z
1884
2120
  })
1885
2121
  .optional()
1886
2122
  .transform((value) => normalizeBasePath(value)),
2123
+ changelog: changelogConfigSchema.optional(),
2124
+ // An adapter from `blume/consent`; analytics waits for the reader.
2125
+ consent: consentConfigSchema,
1887
2126
  content: contentConfigSchema.prefault({}),
1888
2127
  /**
1889
2128
  * Date presentation for the "last updated" stamp and the changelog timeline.
@@ -1907,7 +2146,8 @@ export const blumeConfigSchema = z
1907
2146
  */
1908
2147
  examples: examplesConfigSchema.prefault("examples"),
1909
2148
  export: exportConfigSchema.prefault(false),
1910
- feedback: z.boolean().default(true),
2149
+ feedback: feedbackConfigSchema.prefault(true),
2150
+ footer: footerConfigSchema.optional(),
1911
2151
  /** Opt-in custom frontmatter keys, validated by user-supplied schemas. */
1912
2152
  frontmatter: frontmatterConfigSchema.prefault({}),
1913
2153
  github: githubConfigSchema.optional(),
@@ -1917,7 +2157,10 @@ export const blumeConfigSchema = z
1917
2157
  lastModified: lastModifiedConfigSchema.default(false),
1918
2158
  logo: logoConfigSchema.optional(),
1919
2159
  markdown: markdownConfigSchema.prefault({}),
2160
+ narration: narrationConfigSchema.prefault(false),
1920
2161
  navigation: navigationConfigSchema.prefault({}),
2162
+ // An adapter from `blume/ratelimit`, `memory()` by default; `false` is off.
2163
+ rateLimit: rateLimitConfigSchema,
1921
2164
  react: reactConfigSchema.prefault({}),
1922
2165
  redirects: z.array(redirectSchema).default([]),
1923
2166
  /** API references: adapters from `blume/reference`, each a serializable descriptor. */
@@ -1927,6 +2170,23 @@ export const blumeConfigSchema = z
1927
2170
  theme: themeConfigSchema.prefault({}),
1928
2171
  title: z.string().default("Documentation"),
1929
2172
  toc: tocConfigSchema,
2173
+ /**
2174
+ * Content variables: `{{name}}` in a page body reads the value (see
2175
+ * `core/variables.ts`). Names take letters, digits, `_`, and `-`;
2176
+ * values are one line, so a substitution never moves the lines a
2177
+ * diagnostic points at.
2178
+ */
2179
+ variables: z
2180
+ .record(
2181
+ z.string().regex(VARIABLE_NAME, {
2182
+ message:
2183
+ "Variable names take letters, digits, `_`, and `-`, like `version` or `api-url`.",
2184
+ }),
2185
+ z.string().refine((value) => !/[\r\n]/u.test(value), {
2186
+ message: "A variable's value must fit on one line.",
2187
+ })
2188
+ )
2189
+ .default({}),
1930
2190
  versions: versionsConfigSchema.optional(),
1931
2191
  },
1932
2192
  {
@@ -1987,6 +2247,19 @@ export const blumeConfigSchema = z
1987
2247
  }
1988
2248
  }
1989
2249
  }
2250
+ // Cloudflare's rate limiting is a Worker binding, which only a Cloudflare
2251
+ // deployment has.
2252
+ if (
2253
+ config.rateLimit?.kind === "cloudflare" &&
2254
+ config.deployment.kind !== "cloudflare"
2255
+ ) {
2256
+ ctx.addIssue({
2257
+ code: z.ZodIssueCode.custom,
2258
+ message:
2259
+ 'rateLimit: cloudflare() counts with a Workers binding, so it needs `deployment: cloudflare()` from "blume/deploy". On another host, use upstash() or memory() from "blume/ratelimit".',
2260
+ path: ["rateLimit"],
2261
+ });
2262
+ }
1990
2263
  });
1991
2264
 
1992
2265
  /** Resolved config: every field present after defaults are applied. */
@@ -109,6 +109,9 @@ export const snapshotCache = (cacheDir: string): SnapshotCache => {
109
109
  * snapshot exists, serve it without fetching (cache-first dev). On fetch failure,
110
110
  * serve the last-known-good snapshot with a warning so a CMS/network outage
111
111
  * doesn't fail the build; if there is no snapshot either, surface a hard error.
112
+ * A `BlumeError` from the source (a missing SDK or token, checked before any
113
+ * request, or a misconfiguration) already names its cause, so without a
114
+ * snapshot it surfaces as itself rather than as a fetch failure.
112
115
  */
113
116
  export const loadWithCache = async (
114
117
  name: string,
@@ -138,6 +141,9 @@ export const loadWithCache = async (
138
141
  };
139
142
  return { diagnostics: [diagnostic], entries: fallback };
140
143
  }
144
+ if (error instanceof BlumeError) {
145
+ throw error;
146
+ }
141
147
  // SAFETY: same invariant as above — `fetchEntries` failures are Errors.
142
148
  throw new BlumeError({
143
149
  code: "BLUME_SOURCE_FETCH_FAILED",