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
@@ -14,22 +14,103 @@
14
14
  * prerendered Markdown 404 body with the 404 status, instead of the HTML
15
15
  * shell, and one that prefers JSON (or asks for a `.json` URL) gets the
16
16
  * prerendered problem-details 404.
17
+ *
18
+ * Under `deployment.base` every route here matches the served URL, base
19
+ * included, and names the files where Blume moves them (`static/<base>/`, see
20
+ * {@link rebaseAdapterRoutes}); `base` is the normalized base, `""` for none.
17
21
  */
18
22
 
23
+ import { mountBasePath } from "../core/base-path.ts";
19
24
  import { SVG_ASSET_HEADERS } from "./headers.ts";
20
25
 
21
- /**
22
- * Regex for the `accept` header condition. Written to hold under both matching
23
- * semantics a router may apply — full-string and substring — by anchoring the
24
- * end and letting `(.*,)?` absorb any earlier list entries: it requires a
25
- * `text/markdown` or `text/x-markdown` entry terminated by `;`, `,`, or the end
26
- * of the header. Kept lookaround-free so it stays valid in RE2, and lowercase
27
- * only — real agents send lowercase media types, and q-values are not compared
28
- * (a client sending `text/markdown` at `q=0` is pathological). Browsers never
29
- * send `text/markdown`, so ordinary page requests are unaffected.
26
+ /*
27
+ * The `accept` header conditions. Dev and the Cloudflare Worker negotiate the
28
+ * way `prefersMarkdown` (`astro/markdown-negotiation.ts`) does: media types in
29
+ * any case, and Markdown only when its q-value is above zero and at least
30
+ * HTML's. A routing condition is a regex over the whole header, with no
31
+ * arithmetic, so the comparison is spelled as two routes per rewrite:
32
+ *
33
+ * - a Markdown entry at full weight (no q-value, or `q=1`) always wins, since
34
+ * nothing can outrank it;
35
+ * - a Markdown entry below full weight wins only when the header has no
36
+ * `text/html` entry above zero (a `missing` condition), since two q-values
37
+ * below 1 can't be compared. That case, like `text/markdown;q=0.9,
38
+ * text/html;q=0.8`, gets HTML on Vercel where dev and Cloudflare send
39
+ * Markdown.
40
+ *
41
+ * Every pattern is anchored at both ends and lets `(?:.*,)?` absorb the
42
+ * earlier list entries, so it holds under full-string and substring matching
43
+ * alike. Case-insensitivity is spelled out letter by letter and the patterns
44
+ * are lookaround-free, so they read the same in RE2, PCRE, and JavaScript.
45
+ * Like dev, only the first `q=` parameter of an entry counts, and a `q=`
46
+ * value is read as a decimal. Browsers never send `text/markdown`, so
47
+ * ordinary page requests are unaffected.
30
48
  */
31
- export const ACCEPT_MARKDOWN_HEADER_VALUE =
32
- "(.*,)?\\s*text/(x-)?markdown(\\s*[;,].*)?$";
49
+
50
+ /** `word` in any case, one character class per letter: `[Tt][Ee][Xx][Tt]`. */
51
+ const anyCase = (word: string): string =>
52
+ word.replaceAll(/[a-z]/gu, (letter) => `[${letter.toUpperCase()}${letter}]`);
53
+
54
+ /** An entry's parameters before its first `q=`: any that isn't one. */
55
+ const OTHER_PARAMS = String.raw`(?:;\s*(?:[^\sq;,][^;,]*|q(?:[^=;,][^;,]*)?)?)*`;
56
+
57
+ /** Everything after an entry's first `q=` value, up to the next entry. */
58
+ const LATER_PARAMS = "(?:;[^,]*)?";
59
+
60
+ /** A `q=` value of 1 or more: full weight. */
61
+ const FULL_WEIGHT = String.raw`0*[1-9][0-9]*(?:\.[0-9]*)?`;
62
+
63
+ /** A `q=` value above zero and below 1. */
64
+ const PARTIAL_WEIGHT = String.raw`0*\.[0-9]*[1-9][0-9]*`;
65
+
66
+ /** A `q=` value that isn't zero: it has a character besides `0` and `.`. */
67
+ const NONZERO_WEIGHT = String.raw`[\s0.]*[^\s0.;,][^;,]*`;
68
+
69
+ /** A header with an entry of `type` weighed by `weight`, or by no `q=` when it's optional. */
70
+ const acceptEntry = (type: string, weight: string, optional: boolean) =>
71
+ String.raw`^(?:.*,)?\s*${type}\s*${OTHER_PARAMS}(?:;\s*q=\s*${weight}\s*${LATER_PARAMS})${optional ? "?" : ""}(?:,.*)?$`;
72
+
73
+ /** A header with a `type` entry at full weight. */
74
+ const fullWeight = (type: string) => acceptEntry(type, FULL_WEIGHT, true);
75
+
76
+ /** A header with a `type` entry weighed above zero and below 1. */
77
+ const partialWeight = (type: string) =>
78
+ acceptEntry(type, PARTIAL_WEIGHT, false);
79
+
80
+ /** A header with a `text/html` entry weighed above zero. */
81
+ const ACCEPT_HTML = acceptEntry(anyCase("text/html"), NONZERO_WEIGHT, true);
82
+
83
+ const MARKDOWN_TYPE = `${anyCase("text/")}(?:${anyCase("x-")})?${anyCase("markdown")}`;
84
+
85
+ const JSON_TYPE = `${anyCase("application/")}(?:${anyCase("problem")}\\+)?${anyCase("json")}`;
86
+
87
+ /** One header condition on a Build Output route. */
88
+ export interface VercelCondition {
89
+ key?: string;
90
+ type: string;
91
+ value?: string;
92
+ }
93
+
94
+ /** The conditions of one route: all of `has`, none of `missing`. */
95
+ export interface AcceptConditions {
96
+ has: VercelCondition[];
97
+ missing?: VercelCondition[];
98
+ }
99
+
100
+ const accept = (value: string): VercelCondition => ({
101
+ key: "accept",
102
+ type: "header",
103
+ value,
104
+ });
105
+
106
+ /** The pair of conditions a route answering `type` is written once for each. */
107
+ const preferring = (type: string): AcceptConditions[] => [
108
+ { has: [accept(fullWeight(type))] },
109
+ { has: [accept(partialWeight(type))], missing: [accept(ACCEPT_HTML)] },
110
+ ];
111
+
112
+ /** When a client prefers `text/markdown` (or `text/x-markdown`) over HTML. */
113
+ export const ACCEPT_MARKDOWN_CONDITIONS = preferring(MARKDOWN_TYPE);
33
114
 
34
115
  /**
35
116
  * The JSON counterpart, for the problem-details 404: `application/json` or
@@ -37,8 +118,7 @@ export const ACCEPT_MARKDOWN_HEADER_VALUE =
37
118
  * (the catch-all wildcard does not match), so ordinary page requests are
38
119
  * unaffected.
39
120
  */
40
- export const ACCEPT_JSON_HEADER_VALUE =
41
- "(.*,)?\\s*application/(problem\\+)?json(\\s*[;,].*)?$";
121
+ export const ACCEPT_JSON_CONDITIONS = preferring(JSON_TYPE);
42
122
 
43
123
  /**
44
124
  * A Build Output API route — the subset these helpers read and write. Parsed
@@ -49,23 +129,29 @@ export interface VercelRoute {
49
129
  continue?: boolean;
50
130
  dest?: string;
51
131
  handle?: string;
52
- has?: { key?: string; type: string; value?: string }[];
132
+ has?: VercelCondition[];
53
133
  headers?: Record<string, string>;
134
+ missing?: VercelCondition[];
54
135
  src?: string;
55
136
  status?: number;
56
137
  }
57
138
 
139
+ /** `route` once per condition pair, so it fires when either holds. */
140
+ const conditioned = (
141
+ route: VercelRoute,
142
+ conditions: readonly AcceptConditions[]
143
+ ): VercelRoute[] => conditions.map((condition) => ({ ...route, ...condition }));
144
+
58
145
  /** Whether a parsed route field is a real string (the config is raw JSON). */
59
146
  const isString = (value: string | undefined): value is string =>
60
147
  typeof value === "string";
61
148
 
62
- const ACCEPT_MARKDOWN_CONDITION: VercelRoute["has"] = [
63
- { key: "accept", type: "header", value: ACCEPT_MARKDOWN_HEADER_VALUE },
64
- ];
65
-
66
- const ACCEPT_JSON_CONDITION: VercelRoute["has"] = [
67
- { key: "accept", type: "header", value: ACCEPT_JSON_HEADER_VALUE },
68
- ];
149
+ /** The Markdown conditions' `has` values, to recognize a route this module wrote. */
150
+ const MARKDOWN_VALUES = new Set(
151
+ ACCEPT_MARKDOWN_CONDITIONS.flatMap(({ has }) =>
152
+ has.map((condition) => condition.value)
153
+ )
154
+ );
69
155
 
70
156
  const VARY_ACCEPT = { vary: "Accept" };
71
157
 
@@ -78,44 +164,6 @@ const NOT_FOUND_JSON_DEST = "/404.json";
78
164
  /** The adapter's own not-found fallback — the anchor the Markdown 404 precedes. */
79
165
  const NOT_FOUND_HTML_DEST = "/404.html";
80
166
 
81
- /**
82
- * Miss-phase routes that answer a missing page with the Markdown 404 body: any
83
- * path when the client prefers Markdown, and any `.md`/`.mdx` URL (a request
84
- * for a raw-Markdown mirror that has no page wants Markdown back, not the HTML
85
- * shell). Both keep the 404 status. Spliced immediately before the adapter's
86
- * `/404.html` fallback, so they run after every server route (the MCP
87
- * endpoint, server islands, images) has had its turn and never hijack a
88
- * request one of those would have answered.
89
- */
90
- const NOT_FOUND_MARKDOWN_ROUTES: readonly VercelRoute[] = [
91
- {
92
- dest: NOT_FOUND_MARKDOWN_DEST,
93
- has: ACCEPT_MARKDOWN_CONDITION,
94
- headers: VARY_ACCEPT,
95
- src: "^/.*$",
96
- status: 404,
97
- },
98
- { dest: NOT_FOUND_MARKDOWN_DEST, src: "^/.*\\.mdx?$", status: 404 },
99
- ];
100
-
101
- /**
102
- * The JSON 404's miss-phase routes, the problem-details twin of the Markdown
103
- * ones: any path when the client prefers JSON, and any `.json` URL no file
104
- * backs. Spliced at the same anchor, after every server route — so the
105
- * `/api/` catch-all (which answers its own namespace with a problem document)
106
- * has already had its turn.
107
- */
108
- const NOT_FOUND_JSON_ROUTES: readonly VercelRoute[] = [
109
- {
110
- dest: NOT_FOUND_JSON_DEST,
111
- has: ACCEPT_JSON_CONDITION,
112
- headers: VARY_ACCEPT,
113
- src: "^/.*$",
114
- status: 404,
115
- },
116
- { dest: NOT_FOUND_JSON_DEST, src: "^/.*\\.json$", status: 404 },
117
- ];
118
-
119
167
  /** Which prerendered 404 twins the build emitted, so their routes get wired. */
120
168
  export interface NotFoundVariants {
121
169
  json?: boolean;
@@ -140,6 +188,53 @@ const REGEX_SPECIALS = /[$()*+.?[\]^{|}\\]/gu;
140
188
  const routePattern = (route: string): string =>
141
189
  encodeURI(route).replace(REGEX_SPECIALS, "\\$&");
142
190
 
191
+ /**
192
+ * The `src` matching the homepage: `^/$`, or the base itself under one, served
193
+ * without a trailing slash (the adapter's trailing-slash route strips it).
194
+ */
195
+ const homeSource = (base: string): string =>
196
+ `^${routePattern(mountBasePath(base, "/"))}$`;
197
+
198
+ /**
199
+ * Miss-phase routes that answer a missing page with the Markdown 404 body: any
200
+ * path when the client prefers Markdown, and any `.md`/`.mdx` URL (a request
201
+ * for a raw-Markdown mirror that has no page wants Markdown back, not the HTML
202
+ * shell). Both keep the 404 status. Spliced immediately before the adapter's
203
+ * `/404.html` fallback, so they run after every server route (the MCP
204
+ * endpoint, server islands, images) has had its turn and never hijack a
205
+ * request one of those would have answered.
206
+ */
207
+ const notFoundMarkdownRoutes = (base: string): VercelRoute[] => {
208
+ const dest = mountBasePath(base, NOT_FOUND_MARKDOWN_DEST);
209
+ const prefix = routePattern(base);
210
+ return [
211
+ ...conditioned(
212
+ { dest, headers: VARY_ACCEPT, src: `^${prefix}/.*$`, status: 404 },
213
+ ACCEPT_MARKDOWN_CONDITIONS
214
+ ),
215
+ { dest, src: `^${prefix}/.*\\.mdx?$`, status: 404 },
216
+ ];
217
+ };
218
+
219
+ /**
220
+ * The JSON 404's miss-phase routes, the problem-details twin of the Markdown
221
+ * ones: any path when the client prefers JSON, and any `.json` URL no file
222
+ * backs. Spliced at the same anchor, after every server route — so the
223
+ * `/api/` catch-all (which answers its own namespace with a problem document)
224
+ * has already had its turn.
225
+ */
226
+ const notFoundJsonRoutes = (base: string): VercelRoute[] => {
227
+ const dest = mountBasePath(base, NOT_FOUND_JSON_DEST);
228
+ const prefix = routePattern(base);
229
+ return [
230
+ ...conditioned(
231
+ { dest, headers: VARY_ACCEPT, src: `^${prefix}/.*$`, status: 404 },
232
+ ACCEPT_JSON_CONDITIONS
233
+ ),
234
+ { dest, src: `^${prefix}/.*\\.json$`, status: 404 },
235
+ ];
236
+ };
237
+
143
238
  /** Group patterns so each group's alternation stays under the `src` limit. */
144
239
  const chunkPatterns = (patterns: readonly string[]): string[][] => {
145
240
  const chunks: string[][] = [];
@@ -190,41 +285,49 @@ export interface NegotiationRoutes {
190
285
  * rewrite also stamps `x-markdown-tokens` — the estimated token count of the
191
286
  * homepage mirror (Cloudflare's Markdown for Agents convention). Only the home
192
287
  * route can carry it: the other rewrites are chunked alternations spanning
193
- * many pages, and a count is per-page.
288
+ * many pages, and a count is per-page. Under a `base`, each path is matched
289
+ * and rewritten where it's served (`/docs` → `/docs/index.md`).
194
290
  */
195
291
  export const buildNegotiationRoutes = (
196
292
  routePaths: readonly string[],
197
- homeTokens?: number
293
+ homeTokens?: number,
294
+ base = ""
198
295
  ): NegotiationRoutes => {
199
296
  const home = routePaths.includes("/");
200
297
  const rest = routePaths
201
298
  .filter((path) => path !== "/")
202
- .map((path) => routePattern(path));
299
+ .map((path) => routePattern(mountBasePath(base, path)));
203
300
  const chunks = chunkPatterns(rest);
204
301
 
205
302
  const rewriteRoutes: VercelRoute[] = home
206
- ? [
303
+ ? conditioned(
207
304
  {
208
- dest: "/index.md",
209
- has: ACCEPT_MARKDOWN_CONDITION,
305
+ dest: mountBasePath(base, "/index.md"),
210
306
  headers:
211
307
  homeTokens === undefined
212
308
  ? VARY_ACCEPT
213
309
  : { ...VARY_ACCEPT, "x-markdown-tokens": String(homeTokens) },
214
- src: "^/$",
310
+ src: homeSource(base),
215
311
  },
216
- ]
312
+ ACCEPT_MARKDOWN_CONDITIONS
313
+ )
217
314
  : [];
218
315
  for (const chunk of chunks) {
219
- rewriteRoutes.push({
220
- dest: "$1.md",
221
- has: ACCEPT_MARKDOWN_CONDITION,
222
- headers: VARY_ACCEPT,
223
- src: `^(${chunk.join("|")})/?$`,
224
- });
316
+ rewriteRoutes.push(
317
+ ...conditioned(
318
+ {
319
+ dest: "$1.md",
320
+ headers: VARY_ACCEPT,
321
+ src: `^(${chunk.join("|")})/?$`,
322
+ },
323
+ ACCEPT_MARKDOWN_CONDITIONS
324
+ )
325
+ );
225
326
  }
226
327
 
227
- const headerChunks = chunkPatterns(home ? ["/", ...rest] : rest);
328
+ const headerChunks = chunkPatterns(
329
+ home ? [routePattern(mountBasePath(base, "/")), ...rest] : rest
330
+ );
228
331
  const headerRoutes: VercelRoute[] = headerChunks.map((chunk) => ({
229
332
  continue: true,
230
333
  headers: VARY_ACCEPT,
@@ -234,9 +337,6 @@ export const buildNegotiationRoutes = (
234
337
  return { headerRoutes, rewriteRoutes };
235
338
  };
236
339
 
237
- /** The `src` of the injected homepage `Link` header route. */
238
- const HOME_SRC = "^/$";
239
-
240
340
  const ALLOW_ANY_ORIGIN = "*";
241
341
 
242
342
  /**
@@ -244,18 +344,18 @@ const ALLOW_ANY_ORIGIN = "*";
244
344
  * one static discovery document (see `crossOriginDiscoveryPaths`), so a
245
345
  * registry reading it from another origin isn't refused by the browser.
246
346
  */
247
- const corsRoute = (path: string): VercelRoute => ({
347
+ const corsRoute = (path: string, base: string): VercelRoute => ({
248
348
  continue: true,
249
349
  headers: { "access-control-allow-origin": ALLOW_ANY_ORIGIN },
250
- src: `^${routePattern(path)}$`,
350
+ src: `^${routePattern(mountBasePath(base, path))}$`,
251
351
  });
252
352
 
253
353
  /**
254
354
  * The `src` of the route stamping the content-source SVG headers: every `.svg`
255
- * under `/blume-assets/`. The adapter copies the static files into the Build
256
- * Output tree without the deployment base, like every path matched here.
355
+ * under `/blume-assets/`.
257
356
  */
258
- const SVG_ASSET_SRC = String.raw`^/blume-assets/.+\.svg$`;
357
+ const svgAssetSource = (base: string): string =>
358
+ String.raw`^${routePattern(base)}/blume-assets/.+\.svg$`;
259
359
 
260
360
  /**
261
361
  * A main-phase `continue` route stamping the sandbox headers
@@ -263,7 +363,7 @@ const SVG_ASSET_SRC = String.raw`^/blume-assets/.+\.svg$`;
263
363
  * static layer — where the prerendered asset endpoint's own headers never
264
364
  * reach.
265
365
  */
266
- const svgAssetRoute = (): VercelRoute => ({
366
+ const svgAssetRoute = (base: string): VercelRoute => ({
267
367
  continue: true,
268
368
  headers: Object.fromEntries(
269
369
  Object.entries(SVG_ASSET_HEADERS).map(([name, value]) => [
@@ -271,7 +371,7 @@ const svgAssetRoute = (): VercelRoute => ({
271
371
  value,
272
372
  ])
273
373
  ),
274
- src: SVG_ASSET_SRC,
374
+ src: svgAssetSource(base),
275
375
  });
276
376
 
277
377
  /** Whether a route is a `corsRoute` — the same three-field shape test as the others. */
@@ -291,22 +391,23 @@ const isCorsRoute = (route: VercelRoute): boolean =>
291
391
  * no user-authored route competes in this file); the Markdown 404 routes by
292
392
  * their `/404.md` destination.
293
393
  */
294
- const isNegotiationRoute = (route: VercelRoute): boolean =>
295
- route.has?.some(
296
- (condition) => condition.value === ACCEPT_MARKDOWN_HEADER_VALUE
297
- ) === true ||
298
- (route.dest === NOT_FOUND_MARKDOWN_DEST && route.status === 404) ||
299
- (route.dest === NOT_FOUND_JSON_DEST && route.status === 404) ||
394
+ const isNegotiationRoute = (route: VercelRoute, base: string): boolean =>
395
+ route.has?.some((condition) => MARKDOWN_VALUES.has(condition.value)) ===
396
+ true ||
397
+ (route.dest === mountBasePath(base, NOT_FOUND_MARKDOWN_DEST) &&
398
+ route.status === 404) ||
399
+ (route.dest === mountBasePath(base, NOT_FOUND_JSON_DEST) &&
400
+ route.status === 404) ||
300
401
  (route.continue === true &&
301
402
  route.headers?.vary === "Accept" &&
302
403
  isString(route.src) &&
303
404
  Object.keys(route).length === 3) ||
304
405
  (route.continue === true &&
305
406
  isString(route.headers?.link) &&
306
- route.src === HOME_SRC &&
407
+ route.src === homeSource(base) &&
307
408
  Object.keys(route).length === 3) ||
308
409
  isCorsRoute(route) ||
309
- (route.continue === true && route.src === SVG_ASSET_SRC);
410
+ (route.continue === true && route.src === svgAssetSource(base));
310
411
 
311
412
  /**
312
413
  * Splice the negotiation routes into a Build Output `config.json`, plus — when
@@ -325,7 +426,9 @@ const isNegotiationRoute = (route: VercelRoute): boolean =>
325
426
  * phase right before the adapter's `/404.html` fallback — and nowhere when
326
427
  * that fallback is absent, since a `dest` with no file behind it would serve
327
428
  * nothing. With `svgAssets`, a main-phase route also sandboxes the SVGs a
328
- * content source downloaded (see {@link svgAssetRoute}). Returns the updated JSON
429
+ * content source downloaded (see {@link svgAssetRoute}). Under a `base`, every
430
+ * route matches and names paths beneath it, and the fallback anchor is the
431
+ * one {@link rebaseAdapterRoutes} moved there. Returns the updated JSON
329
432
  * text (tab-indented, like the adapter's own output), or `null` when there is
330
433
  * nowhere safe to splice: an unparsable config, no `routes` array, or no
331
434
  * `handle: "filesystem"` marker to anchor the splice.
@@ -338,7 +441,8 @@ export const injectNegotiationRoutes = (
338
441
  homeTokens?: number,
339
442
  notFound: NotFoundVariants = {},
340
443
  corsPaths: readonly string[] = [],
341
- svgAssets = false
444
+ svgAssets = false,
445
+ base = ""
342
446
  ): string | null => {
343
447
  const overrideEntries = Object.entries(contentTypeOverrides ?? {});
344
448
  let config: {
@@ -358,7 +462,9 @@ export const injectNegotiationRoutes = (
358
462
  // user's own override of the same path is simply refreshed.
359
463
  config.overrides = { ...config.overrides, [path]: { contentType } };
360
464
  }
361
- const routes = config.routes.filter((route) => !isNegotiationRoute(route));
465
+ const routes = config.routes.filter(
466
+ (route) => !isNegotiationRoute(route, base)
467
+ );
362
468
  const filesystemIndex = routes.findIndex(
363
469
  (route) => route.handle === "filesystem"
364
470
  );
@@ -367,30 +473,32 @@ export const injectNegotiationRoutes = (
367
473
  }
368
474
  const { headerRoutes, rewriteRoutes } = buildNegotiationRoutes(
369
475
  routePaths,
370
- homeTokens
476
+ homeTokens,
477
+ base
371
478
  );
372
479
  if (homeLinkHeader) {
373
480
  headerRoutes.push({
374
481
  continue: true,
375
482
  headers: { link: homeLinkHeader },
376
- src: HOME_SRC,
483
+ src: homeSource(base),
377
484
  });
378
485
  }
379
- headerRoutes.push(...corsPaths.map(corsRoute));
486
+ headerRoutes.push(...corsPaths.map((path) => corsRoute(path, base)));
380
487
  if (svgAssets) {
381
- headerRoutes.push(svgAssetRoute());
488
+ headerRoutes.push(svgAssetRoute(base));
382
489
  }
383
490
  // Headers first: `continue` routes accumulate, so a request the rewrite
384
491
  // route then terminates (Markdown negotiation on the homepage) still carries
385
492
  // the Link header.
386
493
  routes.splice(filesystemIndex, 0, ...headerRoutes, ...rewriteRoutes);
387
494
  const notFoundRoutes = [
388
- ...(notFound.markdown ? NOT_FOUND_MARKDOWN_ROUTES : []),
389
- ...(notFound.json ? NOT_FOUND_JSON_ROUTES : []),
495
+ ...(notFound.markdown ? notFoundMarkdownRoutes(base) : []),
496
+ ...(notFound.json ? notFoundJsonRoutes(base) : []),
390
497
  ];
391
498
  if (notFoundRoutes.length > 0) {
499
+ const fallbackDest = mountBasePath(base, NOT_FOUND_HTML_DEST);
392
500
  const fallbackIndex = routes.findIndex(
393
- (route) => route.status === 404 && route.dest === NOT_FOUND_HTML_DEST
501
+ (route) => route.status === 404 && route.dest === fallbackDest
394
502
  );
395
503
  if (fallbackIndex !== -1) {
396
504
  routes.splice(fallbackIndex, 0, ...notFoundRoutes);
@@ -399,3 +507,119 @@ export const injectNegotiationRoutes = (
399
507
  config.routes = routes;
400
508
  return `${JSON.stringify(config, null, "\t")}\n`;
401
509
  };
510
+
511
+ /**
512
+ * Splice routes for the pattern redirects (see `vercelPatternRoutes`) into a
513
+ * Build Output `config.json`, in the main phase just ahead of
514
+ * `handle: "filesystem"`: after the exact redirects the adapter puts first,
515
+ * so an exact path still wins. A second pass replaces its own routes. Returns
516
+ * the updated JSON text, or `null` when there is nowhere safe to splice: an
517
+ * unparsable config, no `routes` array, or no `handle: "filesystem"` marker.
518
+ */
519
+ export const injectRedirectRoutes = (
520
+ configText: string,
521
+ redirectRoutes: readonly VercelRoute[]
522
+ ): string | null => {
523
+ let config: { routes?: VercelRoute[] };
524
+ try {
525
+ config = JSON.parse(configText);
526
+ } catch {
527
+ return null;
528
+ }
529
+ if (!Array.isArray(config.routes)) {
530
+ return null;
531
+ }
532
+ const ours = new Set(redirectRoutes.map((route) => route.src));
533
+ const routes = config.routes.filter(
534
+ (route) => !(ours.has(route.src) && isString(route.headers?.Location))
535
+ );
536
+ const filesystemIndex = routes.findIndex(
537
+ (route) => route.handle === "filesystem"
538
+ );
539
+ if (filesystemIndex === -1) {
540
+ return null;
541
+ }
542
+ routes.splice(filesystemIndex, 0, ...redirectRoutes);
543
+ config.routes = routes;
544
+ return `${JSON.stringify(config, null, "\t")}\n`;
545
+ };
546
+
547
+ /**
548
+ * The trailing-slash route `trailingSlash: "never"` gives the adapter's config
549
+ * (`^/(.*)/$` → `/$1`): it keeps whatever path it strips the slash from, base
550
+ * and all, so it already serves a based site.
551
+ */
552
+ const isTrailingSlashRoute = (route: VercelRoute): boolean =>
553
+ route.headers?.Location === "/$1";
554
+
555
+ /** The pattern body of the root route, anchored (`^/$`) or not (`/`). */
556
+ const ROOT_BODY = /^\/\$?$/u;
557
+
558
+ /**
559
+ * Re-anchor an adapter route pattern under the base, whose escaped form is
560
+ * `prefix`: each matches a base-less path from its start, and the root route
561
+ * becomes the base itself, served without a trailing slash.
562
+ */
563
+ const rebaseSource = (src: string, prefix: string): string => {
564
+ const body = src.startsWith("^") ? src.slice(1) : src;
565
+ return `^${prefix}${ROOT_BODY.test(body) ? "$" : body}`;
566
+ };
567
+
568
+ /**
569
+ * Move the adapter's routes under `deployment.base`, to match the static files
570
+ * Blume moves to `static/<base>/`. `@astrojs/vercel` routes a server build as
571
+ * if it were served from the root: its route patterns (the `_astro/` cache
572
+ * header, every server route) match base-less paths, its 404 fallback names
573
+ * `/404.html`, and it joins the base onto each redirect with no slash on the
574
+ * `from` side and a second time on the `to` side, which Astro hands it
575
+ * already based (`^/docsold$` → `/docs/docs/new`). Each pattern is
576
+ * re-anchored under the base and the fallback names the base's `404.html`;
577
+ * the adapter's redirects make way for `redirectRoutes`, the exact redirects
578
+ * as the host matches them. Returns the updated JSON text, or `null` when
579
+ * there is nowhere safe to splice: an unparsable config, no `routes` array,
580
+ * or no `handle: "filesystem"` marker.
581
+ */
582
+ export const rebaseAdapterRoutes = (
583
+ configText: string,
584
+ base: string,
585
+ redirectRoutes: readonly VercelRoute[]
586
+ ): string | null => {
587
+ let config: { routes?: VercelRoute[] };
588
+ try {
589
+ config = JSON.parse(configText);
590
+ } catch {
591
+ return null;
592
+ }
593
+ if (!Array.isArray(config.routes)) {
594
+ return null;
595
+ }
596
+ const filesystemIndex = config.routes.findIndex(
597
+ (route) => route.handle === "filesystem"
598
+ );
599
+ if (filesystemIndex === -1) {
600
+ return null;
601
+ }
602
+ const prefix = routePattern(base);
603
+ const routes = config.routes.flatMap((route, index): VercelRoute[] => {
604
+ // Ahead of the filesystem handler, the adapter writes its redirects and
605
+ // nothing else with a `Location`.
606
+ if (index < filesystemIndex && isString(route.headers?.Location)) {
607
+ return isTrailingSlashRoute(route) ? [route] : [];
608
+ }
609
+ const rebased = { ...route };
610
+ if (isString(route.src)) {
611
+ rebased.src = rebaseSource(route.src, prefix);
612
+ }
613
+ if (route.status === 404 && route.dest === NOT_FOUND_HTML_DEST) {
614
+ rebased.dest = mountBasePath(base, NOT_FOUND_HTML_DEST);
615
+ }
616
+ return [rebased];
617
+ });
618
+ routes.splice(
619
+ routes.findIndex((route) => route.handle === "filesystem"),
620
+ 0,
621
+ ...redirectRoutes
622
+ );
623
+ config.routes = routes;
624
+ return `${JSON.stringify(config, null, "\t")}\n`;
625
+ };
@@ -188,7 +188,24 @@ const CLAUDE_READER_MAX_TURNS = "25";
188
188
  const CLAUDE_JUDGE_MAX_TURNS = "1";
189
189
 
190
190
  const claudeArgs = (context: InvocationContext): string[] => {
191
- const base = ["-p", "--output-format", "json", "--strict-mcp-config"];
191
+ // `--tools ""` turns off every Claude Code built-in, including ones the
192
+ // deny list below doesn't name (`Monitor` runs shell commands, `Skill`
193
+ // loads the user's skills), so a reader has only the docs MCP tools and the
194
+ // judge has no tools at all. The deny list stays as a second layer.
195
+ // `--setting-sources ""` is codex's `--ignore-user-config`: no settings
196
+ // files, so the user's hooks, plugins, and CLAUDE.md can't hand the reader
197
+ // context the docs never gave it. Not `--bare`, which also stops reading a
198
+ // subscription login and fails with "Not logged in".
199
+ const base = [
200
+ "-p",
201
+ "--output-format",
202
+ "json",
203
+ "--strict-mcp-config",
204
+ "--tools",
205
+ "",
206
+ "--setting-sources",
207
+ "",
208
+ ];
192
209
  if (context.mcp) {
193
210
  const allowed = MCP_TOOL_NAMES.map(
194
211
  (tool) => `mcp__${MCP_SERVER_NAME}__${tool}`
@@ -40,8 +40,11 @@ Reply with ONLY this JSON object on a single line, no markdown fences:
40
40
  {"pass": true|false, "score": 0.0-1.0, "missing": ["expected facts absent or contradicted"], "notes": "one sentence"}`;
41
41
  };
42
42
 
43
- /** The `--fix` handoff prompt: where the report is and the ground rules. */
44
- export const evalFixPrompt = (reportPath: string): string =>
43
+ /**
44
+ * The `--fix` handoff prompt: where the report is, the ground rules, and the
45
+ * `blume eval` command that verifies the fixes (the run's own flags).
46
+ */
47
+ export const evalFixPrompt = (reportPath: string, command: string): string =>
45
48
  `Fix the documentation gaps found by \`blume eval\` in this project.
46
49
 
47
50
  The full report is at ${reportPath}. It is JSON: each entry in \`eval.results\` with status "fail" is one question the documentation could not answer. Each carries the \`question\`, the \`expected\` facts, the judge's \`missing\` facts, and the reader agent's \`answer\` (what the docs currently convey). The matching \`diagnostics\` entry names the source \`file\` of the page that should answer it.
@@ -51,7 +54,7 @@ Work through every failed question:
51
54
  2. Edit the documentation so it states the missing facts explicitly. Add prose, not filler; keep the page's voice.
52
55
  3. Never delete questions from the evals file or weaken expected facts.
53
56
 
54
- When you are done, run \`blume eval\` to verify, and repeat until every question passes.`;
57
+ When you are done, run \`${command}\` to verify, and repeat until every question passes.`;
55
58
 
56
59
  /** The `eval init` prompt: draft a starter evals file from the docs. */
57
60
  export const initPrompt = (evalsPath: string): string =>