blume 2.0.2 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (601) hide show
  1. package/AGENTS.md +1 -0
  2. package/CHANGELOG.md +113 -0
  3. package/README.md +1 -1
  4. package/dist/cli/{chunk-6k4ftwze.js → chunk-0gz36akr.js} +18 -2
  5. package/dist/cli/{chunk-6k4ftwze.js.map → chunk-0gz36akr.js.map} +3 -3
  6. package/dist/cli/{chunk-zp79m0ts.js → chunk-1n228xqb.js} +2 -2
  7. package/dist/cli/{chunk-ddndchfr.js → chunk-2f3ndkpw.js} +57 -29
  8. package/dist/cli/chunk-2f3ndkpw.js.map +14 -0
  9. package/dist/cli/{chunk-fsmrqk8a.js → chunk-2f7n4qzt.js} +8 -2
  10. package/dist/cli/{chunk-fsmrqk8a.js.map → chunk-2f7n4qzt.js.map} +3 -3
  11. package/dist/cli/{chunk-ppzjqwx2.js → chunk-2n4ps1b3.js} +17 -18
  12. package/dist/cli/{chunk-ppzjqwx2.js.map → chunk-2n4ps1b3.js.map} +3 -3
  13. package/dist/cli/{chunk-5r8g91qn.js → chunk-43zcf4gz.js} +398 -159
  14. package/dist/cli/chunk-43zcf4gz.js.map +36 -0
  15. package/dist/cli/chunk-4ejxbzee.js +39 -0
  16. package/dist/cli/chunk-4ejxbzee.js.map +10 -0
  17. package/dist/cli/{chunk-hr8ne106.js → chunk-4h8z9vv5.js} +296 -77
  18. package/dist/cli/chunk-4h8z9vv5.js.map +16 -0
  19. package/dist/cli/{chunk-k7pj68a8.js → chunk-5cg2rzgf.js} +18 -16
  20. package/dist/cli/{chunk-k7pj68a8.js.map → chunk-5cg2rzgf.js.map} +2 -2
  21. package/dist/cli/{chunk-h2ez8dzb.js → chunk-5ffywsmb.js} +4 -4
  22. package/dist/cli/{chunk-g698a744.js → chunk-5wj2qwn2.js} +5 -5
  23. package/dist/cli/chunk-6bm0xpgd.js +18 -0
  24. package/dist/cli/chunk-6bm0xpgd.js.map +10 -0
  25. package/dist/cli/{chunk-3yce002v.js → chunk-713mngdq.js} +15 -8
  26. package/dist/cli/chunk-713mngdq.js.map +14 -0
  27. package/dist/cli/{chunk-91ws1n6j.js → chunk-9geymqns.js} +17 -15
  28. package/dist/cli/{chunk-91ws1n6j.js.map → chunk-9geymqns.js.map} +2 -2
  29. package/dist/cli/{chunk-6dtt0zfn.js → chunk-9xr4dgee.js} +27 -18
  30. package/dist/cli/chunk-9xr4dgee.js.map +10 -0
  31. package/dist/cli/{chunk-sqw4ekg1.js → chunk-amrb6hgh.js} +2 -2
  32. package/dist/cli/chunk-cm89scdv.js +443 -0
  33. package/dist/cli/chunk-cm89scdv.js.map +11 -0
  34. package/dist/cli/{chunk-wdrt2k2v.js → chunk-cmtcnh9f.js} +90 -26
  35. package/dist/cli/chunk-cmtcnh9f.js.map +10 -0
  36. package/dist/cli/chunk-cy5bm0j0.js +104 -0
  37. package/dist/cli/chunk-cy5bm0j0.js.map +11 -0
  38. package/dist/cli/{chunk-7mbqtmgb.js → chunk-e7vvk5ya.js} +21 -12
  39. package/dist/cli/chunk-e7vvk5ya.js.map +10 -0
  40. package/dist/cli/{chunk-qkb5a8sa.js → chunk-g2z9x01v.js} +6 -98
  41. package/dist/cli/chunk-g2z9x01v.js.map +10 -0
  42. package/dist/cli/{chunk-273ygyr4.js → chunk-ga0pf4aj.js} +7 -7
  43. package/dist/cli/{chunk-273ygyr4.js.map → chunk-ga0pf4aj.js.map} +3 -3
  44. package/dist/cli/{chunk-h7k3nq3v.js → chunk-gp84za0h.js} +2 -2
  45. package/dist/cli/{chunk-hqp2ajnh.js → chunk-h2a48yhd.js} +195 -73
  46. package/dist/cli/chunk-h2a48yhd.js.map +36 -0
  47. package/dist/cli/{chunk-pbg5a4s3.js → chunk-jnwj5kjj.js} +71 -47
  48. package/dist/cli/chunk-jnwj5kjj.js.map +19 -0
  49. package/dist/cli/chunk-kwrg63tm.js +31 -0
  50. package/dist/cli/chunk-kwrg63tm.js.map +10 -0
  51. package/dist/cli/{chunk-8g8ytmgx.js → chunk-nd1821sk.js} +21 -19
  52. package/dist/cli/{chunk-8g8ytmgx.js.map → chunk-nd1821sk.js.map} +2 -2
  53. package/dist/cli/{chunk-w4bxdvsa.js → chunk-ntv4qrt7.js} +17 -15
  54. package/dist/cli/{chunk-w4bxdvsa.js.map → chunk-ntv4qrt7.js.map} +2 -2
  55. package/dist/cli/{chunk-bfwp9vp6.js → chunk-p804f3pe.js} +7 -7
  56. package/dist/cli/{chunk-4e9b9ra6.js → chunk-py62fewh.js} +6 -6
  57. package/dist/cli/{chunk-4e9b9ra6.js.map → chunk-py62fewh.js.map} +1 -1
  58. package/dist/cli/chunk-q3wxxq39.js +126 -0
  59. package/dist/cli/chunk-q3wxxq39.js.map +11 -0
  60. package/dist/cli/{chunk-j85scx15.js → chunk-rekqz4qg.js} +142 -46
  61. package/dist/cli/chunk-rekqz4qg.js.map +10 -0
  62. package/dist/cli/{chunk-00gs3wqs.js → chunk-ta27feev.js} +1 -1
  63. package/dist/cli/{chunk-v6ya5kcb.js → chunk-xkz5xbtv.js} +4031 -1132
  64. package/dist/cli/chunk-xkz5xbtv.js.map +213 -0
  65. package/dist/cli/{chunk-bbnwccaz.js → chunk-y4ykbark.js} +2 -2
  66. package/dist/cli/{chunk-n1yg3tj3.js → chunk-y5h4r2y0.js} +6 -6
  67. package/dist/cli/{chunk-n1yg3tj3.js.map → chunk-y5h4r2y0.js.map} +3 -3
  68. package/dist/cli/{chunk-nfcyttvj.js → chunk-yj17vs39.js} +9 -9
  69. package/dist/cli/{chunk-nfcyttvj.js.map → chunk-yj17vs39.js.map} +1 -1
  70. package/dist/cli/{chunk-7vtckvaw.js → chunk-yxmk4pec.js} +16 -14
  71. package/dist/cli/{chunk-7vtckvaw.js.map → chunk-yxmk4pec.js.map} +2 -2
  72. package/dist/cli/{chunk-gs7r695n.js → chunk-zccbjcj0.js} +22 -8
  73. package/dist/cli/{chunk-gs7r695n.js.map → chunk-zccbjcj0.js.map} +3 -3
  74. package/dist/cli/{chunk-xh43dwgw.js → chunk-zf748avv.js} +415 -225
  75. package/dist/cli/chunk-zf748avv.js.map +37 -0
  76. package/dist/cli/{chunk-d1v5rhy0.js → chunk-zhvd6adm.js} +16 -14
  77. package/dist/cli/{chunk-d1v5rhy0.js.map → chunk-zhvd6adm.js.map} +2 -2
  78. package/dist/cli/index.js +28 -20
  79. package/dist/cli/index.js.map +5 -5
  80. package/dist/types/ai/agent-surface.d.ts +8 -7
  81. package/dist/types/ai/ai-catalog.d.ts +7 -0
  82. package/dist/types/ai/ask-context.d.ts +22 -0
  83. package/dist/types/ai/ask.d.ts +271 -58
  84. package/dist/types/ai/index.d.ts +2 -2
  85. package/dist/types/ai/link-headers.d.ts +8 -1
  86. package/dist/types/ai/openapi-components.d.ts +5 -2
  87. package/dist/types/ai/relative-links.d.ts +9 -7
  88. package/dist/types/ai/site-skill.d.ts +26 -0
  89. package/dist/types/ai/skills.d.ts +20 -1
  90. package/dist/types/ai/tar.d.ts +1 -3
  91. package/dist/types/analytics/index.d.ts +2 -0
  92. package/dist/types/analytics/one-dollar-stats.d.ts +48 -0
  93. package/dist/types/analytics/schema.d.ts +14 -0
  94. package/dist/types/astro/integration.d.ts +11 -2
  95. package/dist/types/captcha/hcaptcha.d.ts +27 -0
  96. package/dist/types/captcha/index.d.ts +28 -0
  97. package/dist/types/captcha/schema.d.ts +29 -0
  98. package/dist/types/captcha/turnstile.d.ts +28 -0
  99. package/dist/types/components/content/api-field.d.ts +59 -0
  100. package/dist/types/components/content/api-page.d.ts +95 -0
  101. package/dist/types/components/layout/nav-utils.d.ts +26 -0
  102. package/dist/types/components/openapi/helpers.d.ts +137 -0
  103. package/dist/types/components/openapi/security.d.ts +85 -0
  104. package/dist/types/consent/ethyca.d.ts +48 -0
  105. package/dist/types/consent/index.d.ts +37 -0
  106. package/dist/types/consent/native.d.ts +28 -0
  107. package/dist/types/consent/osano.d.ts +42 -0
  108. package/dist/types/consent/schema.d.ts +110 -0
  109. package/dist/types/core/base-path.d.ts +21 -7
  110. package/dist/types/core/config-input.d.ts +196 -27
  111. package/dist/types/core/data.d.ts +49 -4
  112. package/dist/types/core/directive-diagnostics.d.ts +12 -0
  113. package/dist/types/core/footer.d.ts +8 -0
  114. package/dist/types/core/heading-markers.d.ts +5 -7
  115. package/dist/types/core/i18n-ui.d.ts +86 -0
  116. package/dist/types/core/i18n.d.ts +2 -0
  117. package/dist/types/core/includes.d.ts +8 -2
  118. package/dist/types/core/last-modified.d.ts +10 -0
  119. package/dist/types/core/localizable.d.ts +7 -0
  120. package/dist/types/core/meta.d.ts +8 -0
  121. package/dist/types/core/page-modes.d.ts +29 -0
  122. package/dist/types/core/redirect-patterns.d.ts +144 -0
  123. package/dist/types/core/related.d.ts +40 -0
  124. package/dist/types/core/schema.d.ts +558 -56
  125. package/dist/types/core/sources/lower.d.ts +29 -16
  126. package/dist/types/core/sources/normalize.d.ts +13 -1
  127. package/dist/types/core/sources/read.d.ts +5 -0
  128. package/dist/types/core/sources/watch.d.ts +12 -5
  129. package/dist/types/core/standard-schema.d.ts +5 -0
  130. package/dist/types/core/types.d.ts +1 -1
  131. package/dist/types/core/variables.d.ts +42 -0
  132. package/dist/types/deploy/artifacts.d.ts +6 -4
  133. package/dist/types/deploy/headers.d.ts +5 -0
  134. package/dist/types/deploy/node-headers.d.ts +13 -2
  135. package/dist/types/deploy/platforms/cloudflare.d.ts +5 -0
  136. package/dist/types/deploy/platforms/netlify.d.ts +7 -0
  137. package/dist/types/deploy/platforms/types.d.ts +8 -0
  138. package/dist/types/deploy/platforms/vercel.d.ts +6 -0
  139. package/dist/types/deploy/redirects.d.ts +61 -22
  140. package/dist/types/deploy/vercel-negotiation.d.ts +9 -0
  141. package/dist/types/markdown/code-title.d.ts +17 -0
  142. package/dist/types/markdown/directives.d.ts +62 -0
  143. package/dist/types/markdown/features.d.ts +24 -2
  144. package/dist/types/markdown/mdast.d.ts +63 -0
  145. package/dist/types/narration/build.d.ts +70 -0
  146. package/dist/types/narration/provider.d.ts +26 -0
  147. package/dist/types/narration/script.d.ts +154 -0
  148. package/dist/types/openapi/asyncapi.d.ts +4 -2
  149. package/dist/types/openapi/model.d.ts +12 -6
  150. package/dist/types/openapi/overlay.d.ts +54 -0
  151. package/dist/types/openapi/parse.d.ts +13 -0
  152. package/dist/types/openapi/references.d.ts +14 -4
  153. package/dist/types/ratelimit/cloudflare.d.ts +39 -0
  154. package/dist/types/ratelimit/index.d.ts +27 -0
  155. package/dist/types/ratelimit/memory.d.ts +36 -0
  156. package/dist/types/ratelimit/schema.d.ts +111 -0
  157. package/dist/types/ratelimit/upstash.d.ts +21 -0
  158. package/dist/types/ratelimit/wrangler.d.ts +23 -0
  159. package/dist/types/reference/asyncapi.d.ts +10 -10
  160. package/dist/types/reference/graphql.d.ts +13 -10
  161. package/dist/types/reference/openapi.d.ts +33 -12
  162. package/dist/types/reference/options.d.ts +45 -5
  163. package/dist/types/reference/scalar.d.ts +13 -0
  164. package/dist/types/reference/schema.d.ts +60 -36
  165. package/dist/types/search/documents.d.ts +12 -1
  166. package/dist/types/search/orama-index.d.ts +4 -0
  167. package/dist/types/seo/metatags.d.ts +20 -0
  168. package/docs/02-deployment.mdx +44 -6
  169. package/docs/03-upgrading.mdx +4 -4
  170. package/docs/04-migrating.mdx +1 -1
  171. package/docs/08-faq.mdx +1 -1
  172. package/docs/advanced/changelog.mdx +2 -2
  173. package/docs/advanced/custom-pages.mdx +3 -3
  174. package/docs/advanced/skills.mdx +14 -0
  175. package/docs/cli/audit.mdx +38 -2
  176. package/docs/cli/evals.mdx +3 -3
  177. package/docs/cli/index.mdx +1 -0
  178. package/docs/cli/translate.mdx +3 -3
  179. package/docs/cli/version.mdx +1 -1
  180. package/docs/configuration/analytics.mdx +22 -1
  181. package/docs/configuration/assistant.mdx +188 -36
  182. package/docs/configuration/consent.mdx +100 -0
  183. package/docs/configuration/customization.mdx +6 -4
  184. package/docs/configuration/index.mdx +63 -5
  185. package/docs/configuration/meta.ts +3 -0
  186. package/docs/configuration/narration.mdx +119 -0
  187. package/docs/configuration/rate-limiting.mdx +69 -0
  188. package/docs/configuration/search.mdx +30 -2
  189. package/docs/configuration/theming.mdx +3 -1
  190. package/docs/content/components.mdx +107 -1
  191. package/docs/content/frontmatter.mdx +77 -1
  192. package/docs/content/i18n.mdx +40 -3
  193. package/docs/content/includes.mdx +16 -0
  194. package/docs/content/index.mdx +4 -2
  195. package/docs/content/islands.mdx +1 -1
  196. package/docs/content/meta.mdx +4 -1
  197. package/docs/content/meta.ts +1 -0
  198. package/docs/content/navigation.mdx +28 -6
  199. package/docs/content/sources/contentful.mdx +32 -0
  200. package/docs/content/sources/custom.mdx +39 -0
  201. package/docs/content/sources/github-releases.mdx +31 -0
  202. package/docs/content/sources/index.mdx +91 -0
  203. package/docs/content/sources/meta.ts +16 -0
  204. package/docs/content/sources/notion.mdx +32 -0
  205. package/docs/content/sources/obsidian.mdx +37 -0
  206. package/docs/content/sources/payload.mdx +30 -0
  207. package/docs/content/sources/remote-mdx.mdx +40 -0
  208. package/docs/content/sources/sanity.mdx +29 -0
  209. package/docs/content/sources/strapi.mdx +31 -0
  210. package/docs/content/syntax.mdx +81 -2
  211. package/docs/content/variables.mdx +41 -0
  212. package/docs/content/versioning.mdx +1 -1
  213. package/docs/discoverability/agent-discovery.mdx +30 -3
  214. package/docs/discoverability/index.mdx +3 -2
  215. package/docs/discoverability/llms-txt.mdx +1 -1
  216. package/docs/discoverability/markdown.mdx +2 -2
  217. package/docs/discoverability/metadata.mdx +17 -0
  218. package/docs/discoverability/open-graph.mdx +5 -3
  219. package/docs/discoverability/rss.mdx +1 -1
  220. package/docs/references/api-pages.mdx +74 -0
  221. package/docs/references/asyncapi.mdx +2 -2
  222. package/docs/references/graphql.mdx +3 -3
  223. package/docs/references/meta.ts +1 -1
  224. package/docs/references/openapi.mdx +106 -5
  225. package/docs/references/scalar.mdx +3 -1
  226. package/package.json +36 -1
  227. package/skills/blume/SKILL.md +3 -0
  228. package/skills/blume-migrate/SKILL.md +5 -5
  229. package/skills/blume-migrate/references/docusaurus.md +3 -3
  230. package/skills/blume-migrate/references/fumadocs.md +1 -1
  231. package/skills/blume-migrate/references/mintlify.md +37 -19
  232. package/skills/blume-migrate/references/nextra.md +3 -3
  233. package/skills/blume-migrate/references/starlight.md +2 -1
  234. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +67 -16
  235. package/skills/blume-write-skill/SKILL.md +82 -0
  236. package/src/ai/agent-readability.ts +4 -3
  237. package/src/ai/agent-surface.ts +9 -7
  238. package/src/ai/ai-catalog.ts +12 -5
  239. package/src/ai/api/handlers.ts +2 -2
  240. package/src/ai/api/spec.ts +2 -2
  241. package/src/ai/api-catalog.ts +6 -2
  242. package/src/ai/ask-context.ts +73 -20
  243. package/src/ai/ask-data.ts +6 -0
  244. package/src/ai/ask-tools.ts +142 -0
  245. package/src/ai/ask.ts +354 -76
  246. package/src/ai/changelog-markdown.ts +2 -2
  247. package/src/ai/component-markdown.ts +75 -0
  248. package/src/ai/cors.ts +2 -0
  249. package/src/ai/index.ts +12 -0
  250. package/src/ai/link-headers.ts +14 -5
  251. package/src/ai/llms.ts +24 -9
  252. package/src/ai/markdown.ts +2 -1
  253. package/src/ai/mcp/data.ts +6 -0
  254. package/src/ai/mcp/discovery.ts +2 -2
  255. package/src/ai/mcp/query.ts +2 -2
  256. package/src/ai/mcp/server.ts +2 -2
  257. package/src/ai/openapi-components.ts +31 -9
  258. package/src/ai/relative-links.ts +42 -26
  259. package/src/ai/serializers.ts +2 -1
  260. package/src/ai/site-skill.ts +274 -0
  261. package/src/ai/skills.ts +49 -3
  262. package/src/ai/tar.ts +139 -12
  263. package/src/ai/write-skill.ts +97 -0
  264. package/src/analytics/head.ts +4 -0
  265. package/src/analytics/index.ts +5 -0
  266. package/src/analytics/one-dollar-stats.ts +86 -0
  267. package/src/analytics/schema.ts +2 -0
  268. package/src/astro/generate.ts +133 -19
  269. package/src/astro/include-hmr.ts +31 -12
  270. package/src/astro/include-refresh.ts +19 -8
  271. package/src/astro/index.ts +1 -0
  272. package/src/astro/integration.ts +143 -14
  273. package/src/astro/module-types.ts +6 -0
  274. package/src/astro/runtime-modules.ts +28 -13
  275. package/src/astro/templates.ts +364 -67
  276. package/src/astro/variables.ts +31 -0
  277. package/src/audit/catalog.ts +19 -0
  278. package/src/audit/checks/contrast.ts +183 -0
  279. package/src/audit/checks/i18n.ts +2 -3
  280. package/src/audit/checks/indexability.ts +2 -3
  281. package/src/audit/checks/links.ts +13 -13
  282. package/src/audit/checks/sitemap.ts +2 -3
  283. package/src/audit/graph.ts +3 -1
  284. package/src/audit/redirects.ts +46 -15
  285. package/src/audit/run.ts +14 -9
  286. package/src/audit/snapshot.ts +5 -0
  287. package/src/audit/types.ts +2 -1
  288. package/src/blume-modules.d.ts +7 -0
  289. package/src/captcha/client.ts +152 -0
  290. package/src/captcha/hcaptcha.ts +36 -0
  291. package/src/captcha/index.ts +28 -0
  292. package/src/captcha/schema.ts +32 -0
  293. package/src/captcha/turnstile.ts +38 -0
  294. package/src/captcha/verify.ts +71 -0
  295. package/src/cli/args.ts +32 -0
  296. package/src/cli/command-meta.ts +5 -0
  297. package/src/cli/commands/audit.ts +4 -1
  298. package/src/cli/commands/doctor.ts +25 -17
  299. package/src/cli/commands/eval.ts +19 -11
  300. package/src/cli/commands/skill.ts +96 -0
  301. package/src/cli/commands/translate.ts +9 -12
  302. package/src/cli/commands/validate.ts +3 -1
  303. package/src/cli/dev-lock.ts +157 -37
  304. package/src/cli/index.ts +5 -0
  305. package/src/cli/report-format.ts +11 -6
  306. package/src/cli/required-secrets.ts +71 -29
  307. package/src/components/BlumePage.astro +2 -0
  308. package/src/components/colors.ts +19 -0
  309. package/src/components/content/ApiEndpoint.astro +18 -0
  310. package/src/components/content/ApiField.astro +79 -0
  311. package/src/components/content/ApiPlayground.astro +62 -0
  312. package/src/components/content/ApiRail.astro +19 -0
  313. package/src/components/content/Badge.astro +5 -3
  314. package/src/components/content/Color.astro +1 -0
  315. package/src/components/content/Component.astro +2 -2
  316. package/src/components/content/FileTree.astro +1 -0
  317. package/src/components/content/GithubInfo.astro +1 -0
  318. package/src/components/content/Panel.astro +1 -0
  319. package/src/components/content/ParamField.astro +25 -0
  320. package/src/components/content/RequestExample.astro +17 -0
  321. package/src/components/content/ResponseExample.astro +17 -0
  322. package/src/components/content/ResponseField.astro +32 -0
  323. package/src/components/content/Step.astro +1 -0
  324. package/src/components/content/Tab.astro +0 -1
  325. package/src/components/content/Tabs.astro +4 -0
  326. package/src/components/content/Tile.astro +1 -0
  327. package/src/components/content/Tooltip.astro +1 -0
  328. package/src/components/content/Tree.astro +1 -0
  329. package/src/components/content/TypeTable.astro +4 -1
  330. package/src/components/content/View.astro +21 -0
  331. package/src/components/content/ViewSwitcher.astro +168 -0
  332. package/src/components/content/api-field.ts +100 -0
  333. package/src/components/content/api-page.ts +224 -0
  334. package/src/components/content/badge-color.ts +5 -2
  335. package/src/components/content/base-href.ts +13 -36
  336. package/src/components/content/view-switcher.ts +71 -0
  337. package/src/components/index.ts +2 -0
  338. package/src/components/islands/Assistant.astro +10 -1
  339. package/src/components/islands/assistant.tsx +125 -18
  340. package/src/components/islands/base-path.ts +47 -10
  341. package/src/components/islands/code-question.ts +62 -0
  342. package/src/components/islands/hooks.ts +145 -40
  343. package/src/components/islands/support-link.ts +68 -0
  344. package/src/components/islands/webmcp.ts +4 -2
  345. package/src/components/layout/Analytics.astro +11 -2
  346. package/src/components/layout/Banner.astro +14 -10
  347. package/src/components/layout/Breadcrumbs.astro +11 -4
  348. package/src/components/layout/ConsentBanner.astro +62 -0
  349. package/src/components/layout/ConsentHead.astro +38 -0
  350. package/src/components/layout/Directory.astro +94 -0
  351. package/src/components/layout/DiscoveryLinks.astro +9 -5
  352. package/src/components/layout/DrawerSwitchers.astro +51 -0
  353. package/src/components/layout/Empty.astro +2 -2
  354. package/src/components/layout/Header.astro +124 -82
  355. package/src/components/layout/LanguageSwitcher.astro +50 -17
  356. package/src/components/layout/NarrationPlayer.astro +1085 -0
  357. package/src/components/layout/NavSelector.astro +35 -9
  358. package/src/components/layout/NavTabMenu.astro +3 -3
  359. package/src/components/layout/NavTree.astro +16 -6
  360. package/src/components/layout/PageActions.astro +4 -3
  361. package/src/components/layout/PageFeedback.astro +54 -49
  362. package/src/components/layout/PageLayout.astro +78 -9
  363. package/src/components/layout/Pagination.astro +3 -3
  364. package/src/components/layout/ReferenceLayout.astro +17 -6
  365. package/src/components/layout/RelatedPages.astro +42 -0
  366. package/src/components/layout/RootLayout.astro +313 -50
  367. package/src/components/layout/Search.astro +64 -30
  368. package/src/components/layout/SiteFooter.astro +124 -0
  369. package/src/components/layout/SiteMetatags.astro +16 -0
  370. package/src/components/layout/VersionBanner.astro +2 -2
  371. package/src/components/layout/analytics-client.ts +12 -0
  372. package/src/components/layout/consent/native.ts +79 -0
  373. package/src/components/layout/consent/types.ts +37 -0
  374. package/src/components/layout/footer-links.ts +53 -0
  375. package/src/components/layout/footer-socials.ts +87 -0
  376. package/src/components/layout/locale-redirect.ts +107 -0
  377. package/src/components/layout/nav-utils.ts +83 -0
  378. package/src/components/layout/page-feedback.ts +96 -0
  379. package/src/components/layout/search/analytics.ts +98 -0
  380. package/src/components/layout/search/flexsearch.ts +17 -5
  381. package/src/components/layout/search/orama-cloud.ts +26 -14
  382. package/src/components/layout/search/types.ts +20 -0
  383. package/src/components/layout/search/typesense.ts +5 -1
  384. package/src/components/layout/toc-active.ts +41 -0
  385. package/src/components/layout/toc-element.ts +8 -14
  386. package/src/components/openapi/ApiTagOperations.astro +2 -2
  387. package/src/components/openapi/AsyncApiOperation.astro +7 -4
  388. package/src/components/openapi/GraphqlChip.astro +2 -2
  389. package/src/components/openapi/Operation.astro +163 -13
  390. package/src/components/openapi/Playground.astro +4 -4
  391. package/src/components/openapi/RequestBody.astro +6 -4
  392. package/src/components/openapi/RequestPanel.astro +63 -8
  393. package/src/components/openapi/Responses.astro +6 -4
  394. package/src/components/openapi/SchemaTable.astro +7 -0
  395. package/src/components/openapi/async-snippets.ts +4 -4
  396. package/src/components/openapi/async.ts +38 -6
  397. package/src/components/openapi/code-samples.ts +97 -0
  398. package/src/components/openapi/helpers.ts +19 -9
  399. package/src/components/openapi/message-composer.ts +6 -1
  400. package/src/components/openapi/message.ts +17 -2
  401. package/src/components/openapi/operation-model.ts +81 -11
  402. package/src/components/openapi/panel.ts +4 -2
  403. package/src/components/openapi/param-style.ts +181 -0
  404. package/src/components/openapi/playground-client.ts +73 -16
  405. package/src/components/openapi/request.ts +190 -26
  406. package/src/components/openapi/schema-tree.ts +30 -24
  407. package/src/components/openapi/security.ts +6 -3
  408. package/src/components/openapi/snippets.ts +953 -13
  409. package/src/components/openapi/ws-client.ts +18 -2
  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/base-path.ts +62 -16
  421. package/src/core/builtin-tags.ts +5 -0
  422. package/src/core/client-address.ts +19 -0
  423. package/src/core/config-input.ts +203 -27
  424. package/src/core/data.ts +44 -4
  425. package/src/core/diagnostics.ts +5 -2
  426. package/src/core/directive-diagnostics.ts +99 -0
  427. package/src/core/footer.ts +26 -0
  428. package/src/core/frontmatter.ts +21 -18
  429. package/src/core/graph.ts +39 -36
  430. package/src/core/heading-markers.ts +5 -18
  431. package/src/core/i18n-ui.ts +82 -2
  432. package/src/core/i18n.ts +31 -3
  433. package/src/core/includes.ts +20 -3
  434. package/src/core/last-modified.ts +25 -3
  435. package/src/core/links.ts +16 -2
  436. package/src/core/locale-links.ts +5 -1
  437. package/src/core/localizable.ts +26 -0
  438. package/src/core/meta.ts +33 -19
  439. package/src/core/navigation.ts +71 -20
  440. package/src/core/page-modes.ts +56 -0
  441. package/src/core/project-graph.ts +138 -10
  442. package/src/core/redirect-patterns.ts +491 -0
  443. package/src/core/related.ts +131 -0
  444. package/src/core/schema.ts +342 -68
  445. package/src/core/sources/contentful-rich-text.ts +27 -20
  446. package/src/core/sources/filesystem.ts +20 -2
  447. package/src/core/sources/github-releases.ts +25 -6
  448. package/src/core/sources/lexical.ts +23 -18
  449. package/src/core/sources/lower.ts +201 -34
  450. package/src/core/sources/mdx-remote.ts +51 -16
  451. package/src/core/sources/normalize.ts +327 -91
  452. package/src/core/sources/notion.ts +52 -25
  453. package/src/core/sources/obsidian.ts +23 -6
  454. package/src/core/sources/portable-text.ts +38 -21
  455. package/src/core/sources/read.ts +8 -3
  456. package/src/core/sources/strapi-blocks.ts +20 -16
  457. package/src/core/sources/watch.ts +20 -7
  458. package/src/core/standard-schema.ts +10 -6
  459. package/src/core/types.ts +1 -1
  460. package/src/core/ui-packs/ar.ts +43 -0
  461. package/src/core/ui-packs/bg.ts +45 -0
  462. package/src/core/ui-packs/bn.ts +43 -0
  463. package/src/core/ui-packs/ca.ts +46 -0
  464. package/src/core/ui-packs/cs.ts +44 -0
  465. package/src/core/ui-packs/da.ts +45 -0
  466. package/src/core/ui-packs/de.ts +46 -0
  467. package/src/core/ui-packs/el.ts +45 -0
  468. package/src/core/ui-packs/es.ts +46 -0
  469. package/src/core/ui-packs/fa.ts +45 -0
  470. package/src/core/ui-packs/fi.ts +45 -0
  471. package/src/core/ui-packs/fr.ts +46 -0
  472. package/src/core/ui-packs/he.ts +43 -0
  473. package/src/core/ui-packs/hi.ts +44 -0
  474. package/src/core/ui-packs/hr.ts +45 -0
  475. package/src/core/ui-packs/hu.ts +44 -0
  476. package/src/core/ui-packs/id.ts +45 -0
  477. package/src/core/ui-packs/it.ts +45 -0
  478. package/src/core/ui-packs/ja.ts +45 -0
  479. package/src/core/ui-packs/ko.ts +44 -0
  480. package/src/core/ui-packs/nl.ts +46 -0
  481. package/src/core/ui-packs/no.ts +44 -0
  482. package/src/core/ui-packs/pl.ts +45 -0
  483. package/src/core/ui-packs/pt-br.ts +46 -0
  484. package/src/core/ui-packs/pt.ts +45 -0
  485. package/src/core/ui-packs/ro.ts +45 -0
  486. package/src/core/ui-packs/ru.ts +45 -0
  487. package/src/core/ui-packs/sk.ts +44 -0
  488. package/src/core/ui-packs/sr.ts +45 -0
  489. package/src/core/ui-packs/sv.ts +44 -0
  490. package/src/core/ui-packs/th.ts +43 -0
  491. package/src/core/ui-packs/tr.ts +45 -0
  492. package/src/core/ui-packs/uk.ts +45 -0
  493. package/src/core/ui-packs/vi.ts +45 -0
  494. package/src/core/ui-packs/zh-tw.ts +43 -0
  495. package/src/core/ui-packs/zh.ts +43 -0
  496. package/src/core/variables.ts +103 -0
  497. package/src/core/version-cut.ts +52 -15
  498. package/src/deploy/artifacts.ts +104 -14
  499. package/src/deploy/cloudflare-negotiation.ts +39 -17
  500. package/src/deploy/headers.ts +10 -5
  501. package/src/deploy/node-headers.ts +64 -10
  502. package/src/deploy/platforms/cloudflare.ts +44 -3
  503. package/src/deploy/platforms/netlify.ts +34 -1
  504. package/src/deploy/platforms/node.ts +1 -0
  505. package/src/deploy/platforms/static.ts +1 -0
  506. package/src/deploy/platforms/types.ts +8 -0
  507. package/src/deploy/platforms/vercel.ts +38 -2
  508. package/src/deploy/redirects.ts +139 -36
  509. package/src/deploy/robots.ts +2 -2
  510. package/src/deploy/rss.ts +4 -3
  511. package/src/deploy/sitemap.ts +7 -5
  512. package/src/deploy/vercel-negotiation.ts +36 -0
  513. package/src/markdown/api-rail.ts +259 -0
  514. package/src/markdown/base-links.ts +34 -36
  515. package/src/markdown/code-title.ts +35 -9
  516. package/src/markdown/directives.ts +242 -36
  517. package/src/markdown/features.ts +20 -2
  518. package/src/markdown/fence-meta.ts +2 -0
  519. package/src/markdown/include.ts +11 -1
  520. package/src/markdown/index.ts +35 -11
  521. package/src/markdown/math.ts +4 -4
  522. package/src/markdown/mdast.ts +5 -2
  523. package/src/markdown/relative-links.ts +12 -4
  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 +136 -11
  531. package/src/og/derive.ts +41 -31
  532. package/src/og/index.ts +1 -0
  533. package/src/openapi/asyncapi.ts +4 -2
  534. package/src/openapi/model.ts +77 -36
  535. package/src/openapi/overlay.ts +256 -0
  536. package/src/openapi/parse.ts +55 -2
  537. package/src/openapi/proxy.ts +63 -10
  538. package/src/openapi/references.ts +21 -5
  539. package/src/openapi/render-mdx.ts +46 -3
  540. package/src/openapi/scalar.ts +40 -6
  541. package/src/openapi/source.ts +1 -1
  542. package/src/ratelimit/cloudflare.ts +54 -0
  543. package/src/ratelimit/index.ts +30 -0
  544. package/src/ratelimit/memory.ts +45 -0
  545. package/src/ratelimit/runtime.ts +229 -0
  546. package/src/ratelimit/schema.ts +28 -0
  547. package/src/ratelimit/upstash.ts +30 -0
  548. package/src/ratelimit/wrangler.ts +70 -0
  549. package/src/reference/asyncapi.ts +2 -2
  550. package/src/reference/graphql.ts +5 -2
  551. package/src/reference/openapi.ts +21 -7
  552. package/src/reference/options.ts +51 -7
  553. package/src/reference/scalar.ts +33 -8
  554. package/src/registry/eject.ts +201 -28
  555. package/src/registry/registry.ts +7 -0
  556. package/src/search/adapters/version-scope.ts +30 -0
  557. package/src/search/documents.ts +93 -28
  558. package/src/search/orama-index.ts +28 -3
  559. package/src/search/pagefind-ranking.ts +39 -0
  560. package/src/search/popular.ts +2 -1
  561. package/src/search/sync/algolia.ts +15 -4
  562. package/src/search/sync/orama-cloud.ts +3 -0
  563. package/src/search/sync/typesense.ts +5 -0
  564. package/src/seo/jsonld.ts +7 -3
  565. package/src/seo/metatags.ts +67 -0
  566. package/src/theme/chrome-icons.ts +1 -0
  567. package/src/theme/color.ts +500 -0
  568. package/src/theme/entry.ts +56 -3
  569. package/src/theme/palette.ts +121 -45
  570. package/src/translate/meta.ts +68 -24
  571. package/src/translate/prompts.ts +1 -0
  572. package/src/translate/run.ts +3 -3
  573. package/src/translate/validate.ts +12 -3
  574. package/src/translate/work-list.ts +56 -17
  575. package/src/upgrade/upgrade.ts +4 -171
  576. package/dist/cli/chunk-3yce002v.js.map +0 -14
  577. package/dist/cli/chunk-5r8g91qn.js.map +0 -34
  578. package/dist/cli/chunk-6dtt0zfn.js.map +0 -10
  579. package/dist/cli/chunk-7mbqtmgb.js.map +0 -10
  580. package/dist/cli/chunk-ddndchfr.js.map +0 -14
  581. package/dist/cli/chunk-esh98wmb.js +0 -23
  582. package/dist/cli/chunk-esh98wmb.js.map +0 -10
  583. package/dist/cli/chunk-hqp2ajnh.js.map +0 -35
  584. package/dist/cli/chunk-hr8ne106.js.map +0 -13
  585. package/dist/cli/chunk-j85scx15.js.map +0 -10
  586. package/dist/cli/chunk-mqc662a6.js +0 -75
  587. package/dist/cli/chunk-mqc662a6.js.map +0 -11
  588. package/dist/cli/chunk-pbg5a4s3.js.map +0 -19
  589. package/dist/cli/chunk-qkb5a8sa.js.map +0 -10
  590. package/dist/cli/chunk-v6ya5kcb.js.map +0 -189
  591. package/dist/cli/chunk-wdrt2k2v.js.map +0 -10
  592. package/dist/cli/chunk-xh43dwgw.js.map +0 -36
  593. package/docs/content/sources.mdx +0 -338
  594. /package/dist/cli/{chunk-zp79m0ts.js.map → chunk-1n228xqb.js.map} +0 -0
  595. /package/dist/cli/{chunk-h2ez8dzb.js.map → chunk-5ffywsmb.js.map} +0 -0
  596. /package/dist/cli/{chunk-g698a744.js.map → chunk-5wj2qwn2.js.map} +0 -0
  597. /package/dist/cli/{chunk-sqw4ekg1.js.map → chunk-amrb6hgh.js.map} +0 -0
  598. /package/dist/cli/{chunk-h7k3nq3v.js.map → chunk-gp84za0h.js.map} +0 -0
  599. /package/dist/cli/{chunk-bfwp9vp6.js.map → chunk-p804f3pe.js.map} +0 -0
  600. /package/dist/cli/{chunk-00gs3wqs.js.map → chunk-ta27feev.js.map} +0 -0
  601. /package/dist/cli/{chunk-bbnwccaz.js.map → chunk-y4ykbark.js.map} +0 -0
package/AGENTS.md CHANGED
@@ -5,6 +5,7 @@ This package ships agent skills for working with Blume documentation projects. B
5
5
  - `./skills/blume/SKILL.md` — build and maintain a Blume site: scaffolding, writing Markdown/MDX, `blume.config.ts`, navigation, search, theming, SEO, and the `blume` CLI.
6
6
  - `./skills/blume-migrate/SKILL.md` — migrate an existing docs site (Mintlify, Fumadocs, Docusaurus, Starlight, Nextra, or another framework) to Blume. `blume migrate [source] --codex` (or `--claude`) opens an agent on it.
7
7
  - `./skills/blume-update-docs/SKILL.md` — audit a Blume docs site against the product it documents, fix stale pages, and open a maintenance pull request.
8
+ - `./skills/blume-write-skill/SKILL.md` — write the docs site's own agent skill from its docs, in place of the page map Blume generates at `/skill.md`. `blume skill --codex` (or `--claude`) opens an agent on it.
8
9
 
9
10
  Use the relevant `SKILL.md` as the source of truth for Blume-specific workflows. The full Blume documentation is bundled under `./docs/`, and the `blume` skill points at it.
10
11
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,118 @@
1
1
  # blume
2
2
 
3
+ ## 2.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 99119e7: API references can generate code samples in 18 languages: cURL, Python, JavaScript, Node.js, TypeScript, PHP, Go, Java, Ruby, PowerShell, Swift, C#, .NET, C, C++, Kotlin, Rust, and Dart. List them in `codeSamples` on `openapi()` or `graphql()` in the order you want them, by id or by a common alias such as `golang`, `c#`, or `ts`. Each sample quotes values by its own language's string rules and updates live with the Try it form. `node` and `typescript` now generate their own samples (axios, and typed `fetch`) instead of repeating the JavaScript one.
8
+
9
+ Operations also render the spec's own `x-codeSamples` (or `x-code-samples`) as tabs ahead of the generated samples, for SDK snippets written by hand or by Speakeasy or Stainless. Set `codeSamples: false` to show only those.
10
+ - f4854ef: Add a bot check for the assistant. `ai.assistant.captcha` takes an adapter from `blume/captcha`, `turnstile({ siteKey })` or `hcaptcha({ siteKey })`, run invisibly: the provider's script loads with a reader's first question, the panel sends a fresh token with each question, and the generated route verifies it (with `TURNSTILE_SECRET_KEY` or `HCAPTCHA_SECRET_KEY`) before the model runs. A failed check answers `403`, which the panel shows as a translated "we couldn't check that you're human"; a missing secret answers with the assistant's "not configured" notice, and `blume build` warns. With an external `endpoint`, the token is sent as `captcha` in the request body for that backend to verify. `useAssistant` takes the `captcha` settings and a `verifyMessage` too.
11
+ - 253ad9e: The assistant can now search the docs and read whole pages itself. Beyond the pages retrieved up front, the model gets a `search_docs` tool and a `read_page` tool, and can search, read, and search again before it answers, within one question and at most five steps. The tools run on your server against the assistant's snapshot and keep to the reader's language and docs version. When nothing matches a question up front, the assistant now searches instead of saying it doesn't know. They're on by default, except for `openai()` with a `baseUrl`, whose model may not support tool calling. Set `ai.assistant.tools` to change either.
12
+ - 6653f72: The assistant can now call OpenAI, Anthropic, Gemini, and Grok directly. `openai({ model })`, `anthropic({ model })`, `gemini({ model })`, and `grok({ model })` from `blume/ai` send each question to the provider's own API with your key, through its AI SDK package (`@ai-sdk/openai`, `@ai-sdk/anthropic`, `@ai-sdk/google`, or `@ai-sdk/xai`, installed as needed). They read `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, and `XAI_API_KEY`, turn the docs tools on, and send `reasoning` as each model's own control. `openai()` also takes a `baseUrl` for any OpenAI-compatible endpoint, which it calls through `@ai-sdk/openai-compatible` with the docs tools off, as `openaiCompatible()` did. `openaiCompatible()` still works and now returns the same adapter.
13
+ - aca5ac9: Add content variables. Define values under `variables` in `blume.config.ts`, like `{ version: "2.1.0", "api-url": "https://api.example.com" }`, and `{{version}}` anywhere in a page reads the value: in prose, headings, links, code, and component props, in `.md` and `.mdx` pages and the files they include. Search, the `.md` mirrors, and `llms-full.txt` show the value too. An undefined name in prose fails the build at its line (`BLUME_UNDEFINED_VARIABLE`); inside code it's left as written. Sites that define no variables are unaffected.
14
+ - 9ac2262: Add cookie consent. `consent` in `blume.config.ts` takes an adapter from `blume/consent`, and with it set every `analytics` adapter waits for the reader: its tags stay in the page as inert `text/plain` until the reader allows analytics, then run in order, once per page load, and Vercel Web Analytics sends nothing until then. `native()` is Blume's own banner, a card with Accept and Decline side by side and an optional `policy` link, translated in every built-in language and shown in `blume dev` too. `osano({ customerId, configId })` and `ethyca({ privacyCenter, propertyId, notice })` load Osano or Ethyca's Fides instead, and follow the answers readers give there. The site footer gains a Cookie settings link that reopens the banner or the manager's preferences (any element with `data-blume-consent-open` works in a custom footer), and a reader who takes consent back gets a reload so analytics stops. The "Was this page helpful?" rating, which reports through analytics too, only shows for readers who allowed analytics. Your own scripts can wait the same way with `type="text/plain" data-blume-consent="analytics"`, or listen for the `blume:consent` event.
15
+ - c3e1cd3: Add directory listings. `directory` in a folder's `meta.ts`, or on a group in an explicit `navigation.sidebar`, lists the group's pages below the content of its own page (a folder's `index` page, or the group's `root`): `card` as a grid of cards with each page's icon and description, `accordion` as rows with each subgroup a section that opens to its pages, and `none`, the default, as nothing. Nested groups inherit the nearest setting, so one `directory` in the content root's `meta.ts` gives every section a listing, and any folder can set its own. The values match Mintlify's `directory`.
16
+ - 0036165: Add components for documenting endpoints by hand. `<ParamField>` is a request parameter, named by where it goes (`<ParamField query="limit" type="integer" default={20}>`, or `path`, `header`, `body`), and `<ResponseField name="id" type="string">` is a field of the response, with optional `pre` and `post` labels. Both take `type`, `required`, `deprecated`, and `default`, hold their description as content, and nest an object's fields inside an `<Expandable>`. They render like the OpenAPI reference's rows, and a page's Markdown copy lists each field with its type and flags. `<RequestExample>` and `<ResponseExample>` hold code blocks as tabs (or a language menu with `dropdown`); on a wide screen they pin to a column beside the page, request above response, and stay in view while the reader scrolls, the way an OpenAPI operation's examples do. A page with `api` frontmatter (`api: POST /v1/users`) documents one endpoint without a spec: it shows the method and path at the top, and its `<ParamField>`s build a Try it panel and request samples in cURL, JavaScript, and Python, the same ones an OpenAPI operation gets, with nested fields as the properties of a JSON body. `authMethod` and `playground` frontmatter tune a page, and a new top-level `api` config sets the `server` a path joins, the default `auth`, and the `playground`, whose `proxy: true` routes sends through the built-in CORS proxy for those origins. The names and props match Mintlify's, so migrated pages keep them as written.
17
+ - 72f7cf5: Slim the header down to icon buttons, making room in it. Search is now an icon button beside the theme toggle and the assistant, in place of the search field, and each icon button names itself in a tooltip on hover and keyboard focus: "Search (⌘K)" (`Ctrl K` off Apple devices), "Toggle theme", and "Open assistant". The GitHub link leaves the header for the new site footer, which is now the one place the site links its repository and social profiles; `navigation.repo` works as before and applies there. The header's tabs now sit at its center on wide screens. The version and language pickers lose their border to match the icon buttons, the header's right-hand controls sit closer together, and the language switcher drops its globe icon and always shows the language's name. Its menu now widens to fit, so a long language name no longer runs into the "Not translated" note beside it. A `Header` override is unaffected. The "Open assistant" tooltip is a new `assistant.open` UI string, translated in every built-in language pack.
18
+ - f8bd737: Pass props to an `<include>`: every attribute other than `lang` and `meta` becomes a value the included file reads with `{{name}}`, like `<include plan="Pro" feature="SSO">./_snippets/upgrade.mdx</include>`. Props reach the partial's own nested includes and take precedence over a site-wide variable with the same name.
19
+ - 0758eaa: OpenAPI references now document webhooks and callbacks. Each webhook in a 3.1 spec's `webhooks` gets its own page, filed under its first tag or a Webhooks group. The page shows its payload schema, the responses your endpoint should send, and an example payload in place of the Try it panel. It lists only the security the webhook itself declares, since the spec's root `security` guards calls to the API, not the requests it sends. Webhook pages are in search, `llms.txt`, and the MCP server, and their Markdown marks them as requests the API sends. An operation's `callbacks`, inline or `$ref`'d from `components.callbacks`, render in a Callbacks section on its page with their URL expression, method, request body, and responses. Specs with webhooks no longer log a warning that they're missing from the reference.
20
+ - 2153f6b: Add page layout modes. `mode` in a page's frontmatter sets what it shows around its content: `wide` drops the table of contents and lets the content take its width, `center` drops the sidebar too and centers a wider column, `custom` keeps only the header for a landing page written in MDX, and `frame` is `custom` with the sidebar. `custom` and `frame` pages render no title, description, breadcrumbs, page-end links, or site footer, so they bring their own heading. Every mode stays in search and keeps its Markdown copy. The values match Mintlify's; its full-page `assistant` mode has no equivalent.
21
+ - ecbfb0e: Add narration: a "Listen to this page" player that reads each page aloud and highlights the sentence being read. Turn it on with `narration: true` to use the reader's browser voices, which need no key and work on any host. Or pass `narration: { provider: gateway({ model: "openai/tts-1-hd", voice: "alloy" }) }` to generate neural audio at build: one cached clip per sentence, shipped as static files, with browser voices as the fallback in `blume dev` or when the key is missing.
22
+
23
+ The player announces callouts, steps, tabs, and collapsible sections with short spoken cues, and skips code, tables, media, and type tables. It opens a closed section or switches to a hidden tab when narration reaches it, and keeps the sentence in view until the reader scrolls away. It offers speeds from 0.8× to 2×, and stops when the reader opens another page. Set `narration: false` in a page's frontmatter to leave it out, or add `data-blume-narration="skip"` to keep any element out of narration.
24
+ - aea605b: Add pattern redirects. A redirect's `from` can now cover many paths, written the way Mintlify and `vercel.json` write them: `:name` matches one segment (`/blog/:slug`), `:name*` as the last segment matches the rest of the path (`/beta/:slug*` sends `/beta/a/b` to `/v2/a/b` and `/beta` to `/v2`), and `*` ending a segment matches the rest from there (`/old/article-*`). `to` reads the captures (`/v2/:slug*`, `/new/article-*`). Every host gets them in its own syntax: `_redirects` splats, `vercel.json` sources, and the routing config of a Vercel or Netlify server build, while the Node and Cloudflare servers and `blume dev` match them directly. Exact redirects win over a pattern that covers the same path. A pattern that also matches a page fails the build (`BLUME_REDIRECT_MATCHES_PAGE`), since hosts disagree on which answers, and links into a pattern count as valid in `blume validate` and `blume audit`.
25
+ - 09efac4: Add rate limiting to the server routes a reader can call: the assistant, the API playground proxy, and Mixedbread search. Each reader, by IP address, gets 30 requests per route every 10 minutes by default, and past that the route answers `429 Too Many Requests` with a `Retry-After` header, which the assistant turns into a translated "try again in a few minutes". `rateLimit` in `blume.config.ts` takes an adapter from `blume/ratelimit`: `memory()`, the default, counts in the server's memory (exact on `node()`, per instance on serverless hosts); `upstash()` shares the count through Upstash Redis's REST API (`UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN`); and `cloudflare()` counts with Workers rate limiting, declaring the binding in the built Worker's config. Each takes `requests` and `window` (seconds). A request whose address the host can't tell is let through, and so is one a shared store fails to count. `rateLimit: false` turns it off.
26
+ - 0dd30af: Add a site footer: one row, as tall as the header, below the content on every page. Your repository link moves there from the header (see the header changes), as the first of the footer's icons, and `footer` in `blume.config.ts` adds the rest: `links`, a list of `{ label, href }` shown on one side, and `socials`, profile URLs keyed by platform (`x`, `discord`, `linkedin`, `youtube`, `website`, and more) shown as brand icons on the other, both in the order written. A `github` social replaces the repository link, and `navigation.repo` still hides it or points it elsewhere. Custom pages built on `PageLayout` show the footer too, unless they fill the layout's `footer` slot. A `Footer` layout override still replaces it, and `blume add footer` copies the built-in into your project to edit. A site with no repository, links, or profiles has no footer.
27
+
28
+ ### Patch Changes
29
+
30
+ - ade0397: Add an Ask button to code blocks when the assistant is on. It opens the assistant with the block attached as a chip above the input; the question goes to the model with the code as a fenced block (up to 6,000 characters), and an empty question asks it to explain the code. The conversation shows the attached code under the question, and the button's label and the chip's text are translated in every built-in language. Code groups get one button in their tab strip, which asks about the tab that's showing.
31
+ - 57db488: Add a support handoff to the assistant. `ai.assistant.support` takes a `mailto:` address, a URL, or a page on your site, and once there's a conversation the panel shows a translated "Contact support" link: an email starts with the conversation as its body, and a link gets the conversation's id as a `thread` query parameter. The assistant's `ask`, `ask_answer`, and `ask_error` analytics events now carry the same `thread`, and `useAssistant` returns it.
32
+ - 0654705: Add `blume skill`, which writes your docs site's agent skill with a coding agent. `blume skill --claude` (or `--codex`) opens the agent on the new `blume-write-skill` skill. The agent writes a `SKILL.md` grounded in the docs that teaches an agent to use the product: setup, core concepts, common tasks, and gotchas, each linked to its page. The file goes under `agents.skills`, named after the site so it replaces the generated skill at `/skill.md`. Run it again to refresh the skill after big docs changes.
33
+ - c3e1cd3: Add `wrap` and `expandable` to code fences. `wrap` after the language (` ```ts wrap `) wraps one block's long lines instead of scrolling them, the way `markdown.code.wrap` does for every block. `expandable` collapses a long block to its first lines under a fade, with a Show more toggle that opens it in full, no inner scroll, and Show less to close it again; a block under 16 lines renders as usual. Both compose with a title, line numbers, and each other, and the toggle's labels are translated in every built-in language.
34
+ - a7c8ceb: Fix header controls overlapping on narrow screens. With many controls on (tabs, a version selector, the language switcher, search, and the icon buttons), the version and language pills squeezed on top of each other on phones, and the tab bar ran into them on tablets. Below the desktop breakpoint, selectors, the version selector, and the language switcher now sit at the top of the navigation drawer, full width. The remaining header controls keep their size, and only the site title truncates to make room.
35
+ - 553a58f: Apply OpenAPI Overlays to a spec before it renders. List `overlays` beside `spec` in `openapi()` or `scalar()`, or on each source, as local paths or `http(s)` URLs, applied in order. Overlay Specification 1.0 and 1.1 are supported: `update`, `copy`, and `remove` actions, with targets as RFC 9535 JSONPath. Overlays apply to the spec as written, before it's upgraded to OpenAPI 3.1, and everything built from the spec sees the result. An overlay that fails stops its reference from rendering, the way an unreadable spec does, so it can't publish what it was meant to hide.
36
+ - d6190ee: OpenAPI overlays can now add a property named `constructor` (it used to fail with a merge error), and a `__proto__` key in an overlay no longer reaches `Object.prototype`. Parsing a long run of digits in a theme color no longer takes quadratic time.
37
+ - c3e1cd3: Add `pagination: false` to page frontmatter, which leaves the previous and next links off that page. The page keeps its place in the order, so its neighbors still link to it. The Mintlify migration maps `hideFooterPagination: true` to it.
38
+ - 7f9fa4a: Add `<View>` for pages written for several audiences, such as one per programming language. Each distinct `title` becomes an option in a picker above the page's content, and only the chosen view's blocks show. Prose outside any `<View>` shows in every view, and headings in hidden views drop out of the table of contents. The reader's pick is remembered across pages and written to the URL as `?view=`, a link to a heading opens its view, and the choice applies before the page paints. The agent-facing Markdown includes every view under its name.
39
+ - 0a29cb2: Translate the banner, header links, and footer per locale. The banner's `content` and link `text`, `navigation.actions`, `navigation.cta`, and `navigation.featured` labels, and `footer` link labels now accept a map of locale code to text, like tab labels do. Each page shows its locale's entry and falls back to the default locale's. Internal footer links now move into the reader's locale whenever that locale serves the page, as header links already did.
40
+ - 4e4b258: Add related pages. `related` in a page's frontmatter lists up to ten pages to suggest at its foot, shown as cards under a translated "Related pages" heading: a root-relative path shows that page's title and description (its translation, on a translated page), a `{ Title: link }` entry names the card itself, and an absolute URL links off the site. Paths are checked like the page's other links, so `blume validate` reports one that matches no page. The key matches Mintlify's.
41
+ - 8f040cd: Add `i18n.routeByBrowserLanguage`, which sends visitors who land on the default language's home page to the home page in their browser's preferred language. Blume tries each language the browser prefers, in order, matching an exact locale code and then the base language, and a visitor whose first match is the default language stays. Picking a language with the switcher stops the routing for that reader, and only visitors arriving from outside the site are routed. The redirect runs in the browser before the page paints, so it works on static hosts. It's off by default.
42
+ - 169966c: Report search to your analytics. Once a query settles (a second without typing, or the reader picks a result or closes the dialog), the search dialog sends a `search` event with the `query`, its number of `results`, and the `path`, so queries with no results are easy to find. Picking a result sends `search_select` with the `query`, the result's `position`, and its `url`. Both go through every analytics adapter with an event API and as `blume:track` events.
43
+ - 0f8a46e: Rank pages with `search.boost` and `search.keywords` frontmatter. `boost` multiplies a page's search relevance: above 1 moves it up, below 1 moves it down. The default Orama search, FlexSearch, the MCP server, and the assistant apply it exactly. Pagefind weighs a boosted page's text more heavily, Algolia and Typesense sort by it among close matches, and Orama Cloud re-sorts a wider set of results. `keywords` are extra terms a page is found by, beyond its own text. Blume 1 accepted `search.boost` without reading it, so a page that still sets it now ranks by it. The Mintlify migration moves `keywords`, `boost`, and `searchable: false` into the `search` block.
44
+ - aa2babe: Add `seo.metatags` for tags written into every page's head: site-verification tokens, `theme-color`, an app banner, and anything else Blume has no setting for. Open Graph families render with `property` and everything else with `name`. A tag Blume writes itself, such as `description`, `robots`, `og:image`, or a `twitter:` card tag, is refused, and the build names the setting that controls it.
45
+ - 7dee91b: Publish an agent skill for every site. The build writes a `SKILL.md` named after the site's title, served at `/skill.md` and listed in the skills discovery index, `llms.txt`, and the AI catalog. It's built from what Blume already knows, with no model call: how to read any page as Markdown, `llms.txt`, the MCP server, the API references, the changelog, and a map of the docs with each page's description. It needs `deployment.site` for its absolute links. A skill of the same name in `agents.skills` replaces it, and `agents.skillMd: false` turns it off.
46
+ - 7a58b53: Meet WCAG AA contrast with the accent colors, and check a site's theme colors in `blume audit`. Each accent preset now has a darker light-mode shade and a lighter dark-mode shade, so accent text and button labels reach 4.5:1 in both modes. Blue is the default, so this changes sites that set no accent too. Labels on accent and action fills are white while white reaches AA on that color, and dark otherwise. A light accent, and every preset in dark mode, now gets dark labels instead of unreadable white ones. A new `accessibility` category in `blume audit` measures a custom `theme.accent`, `theme.action`, or `theme.background` against the same bar in both modes, and says when it can't read a color.
47
+ - 0c0acd5: Tell the translation agent to keep frontmatter valid YAML by quoting a translated value that contains a colon or starts with a quote or backtick, and name the YAML error and its line and column when `blume translate` rejects a page whose frontmatter doesn't parse.
48
+ - f50ef8a: The `BLUME_WIKILINK_UNRESOLVED` and `BLUME_WIKILINK_AMBIGUOUS` diagnostics now link to the Obsidian source page instead of the content sources overview.
49
+ - aecd4b9: Add written feedback. `feedback: { comments: true }` offers a box after the "Was this page helpful?" rating where the reader can say what worked or what's missing. A comment is sent as a `feedback_comment` event, with the rating, path, title, and the comment (up to 1,000 characters), through every analytics adapter with an event API and as a `blume:track` event. The box's label and button are UI strings, translated in every built-in language. `feedback: true` and `false` work as before, and the `Feedback` layout slot now receives a `comments` prop.
50
+
51
+ ## 2.0.3
52
+
53
+ ### Patch Changes
54
+
55
+ - 090211e: Operation links in the agent-facing Markdown of an API reference overview (`/<route>.md`, `llms-full.txt`, and MCP `get_page`) now include `deployment.base`. With a base of `/sub`, they used to point at `/api/pets/list-pets`, which 404s. In `llms.txt`, page and feed titles with square brackets (`[Beta] Webhooks`) are escaped, so their lines stay links.
56
+ - ad20661: With `deployment.base` set, root-relative images and file links in the agent-facing Markdown (`/<route>.md`, `llms-full.txt`, and MCP `get_page`) now gain the base the way the rendered page's do: `![logo](/logo.png)` becomes `/sub/logo.png` and `[spec](/spec.pdf)` becomes `/sub/spec.pdf`. They used to keep the bare root path, which 404s on a subpath deploy.
57
+ - be607f9: A file path longer than 100 bytes inside a published agent skill no longer fails `blume build`: the skill's `.tar.gz` stores it in the ustar `prefix` field up to 255 bytes and in a PAX extended header beyond that, so standard `tar` readers unpack it at its full path. Two skill directories that declare the same `name` now publish one index entry, from the directory that sorts first, with a build warning naming both, instead of two entries for one artifact whose digest could not match what was served.
58
+ - 090211e: A schema with both properties and a `oneOf`/`anyOf` now shows its shared properties above the variants. Before, `{ properties: { amount, currency }, oneOf: [Card, BankAccount] }` showed only "One of Card | BankAccount". An OpenAPI spec whose `operationId`, tag, summary, or description YAML reads as a number (`operationId: 404`, `tags: [2024]`) now loads, using the number as text, where it used to fail with "text.normalize is not a function". The request and response tabs' Left and Right arrow keys follow the reading direction on right-to-left pages, as `<Tabs>` does.
59
+ - 4f7785c: An OpenAPI, AsyncAPI, or GraphQL description that ends inside an unclosed code fence no longer swallows the reference UI rendered after it. The fence used to run to the end of the page, so an operation page lost its parameters, responses, and playground, and an overview page lost its operation lists. Blume now closes the fence where the description ends.
60
+ - af7c4c9: ArkType schemas now work in `frontmatter.extend` and `content.types.<type>.frontmatter`. ArkType types are callable functions, and Blume only accepted Standard Schemas that were plain objects, so config validation rejected them.
61
+ - 08219a2: Closing the assistant panel mid-answer now stops the model call. The generated assistant route passes the request's abort signal to the model, which used to keep generating, and billing, the rest of the answer after the reader left.
62
+ - 6933892: With the assistant panel docked open on desktop, moving to another page keeps the page laid out beside the panel. Each navigation used to replay the page's slide to make room for the panel.
63
+ - 090211e: AsyncAPI operations whose bindings are a `$ref` to `#/components/*Bindings` now resolve it. Before, the protocol read as "$ref", so a WebSocket operation got no samples and the composer said "Live sending isn't possible for $ref". Channel parameters are URL-encoded only for URL-based protocols (WebSocket, HTTP), so a Kafka or MQTT topic reads `sensors/sensor 1` rather than `sensors/sensor%201`. The message composer no longer hangs on "Connecting…" when the browser refuses a malformed WebSocket URL: it shows the error and lets you connect again.
64
+ - b6992c0: `blume audit` no longer counts an inline SVG's `<title>` as a page title, so an accessible icon doesn't raise `BLUME_AUDIT_TITLE_MULTIPLE` (an error) or stand in for a missing head `<title>`. Text-fragment links (`#:~:text=…`) aren't reported as broken anchors, and only the id in front of a fragment's `:~:` has to exist. An i18n fallback copy of a page is no longer reported as an orphan, which failed `--strict`. A failing audit now delivers its whole report through a pipe; it used to exit before a long report finished writing, cutting the CI log off partway.
65
+ - 6933892: `<Badge variant="accent">` now uses your theme accent, as documented, instead of a fixed blue. Its label mixes the accent toward the text color, so it stays readable over the accent tint in light and dark mode.
66
+ - ad20661: A header link, featured link, or other configured link that already carries `deployment.base` followed by a fragment or query, like `/sub#install` under `base: "/sub"`, is no longer prefixed a second time. It used to render as `/sub/sub#install`.
67
+ - 3311cee: A link that already carries `basePath` followed by a fragment or query, like `[Install](/docs#install)` under `basePath: "/docs"`, is no longer prefixed a second time. It used to render as `/docs/docs#install`, a 404 the link checker didn't catch.
68
+ - f373654: A page whose body starts with a `---` horizontal rule, right below its front matter, no longer loses everything up to the next `---` line. The renderer read that stretch as a second front matter block and dropped it; both rules and the text between them now render, in `.md` and `.mdx` pages alike. Headings in that stretch are now anchors `blume validate` accepts and title an untitled page, the same as any other heading.
69
+ - 7258f30: `blume translate`, `blume validate`, and `blume doctor` now let their whole report reach a piped stderr before exiting with a failure. Before, they exited immediately after writing, so a long list of findings could be cut off in CI logs and `| tee` output — the same fix `blume audit` and `blume eval` got.
70
+ - 3311cee: A `cloudflare()` server build with a `base` now writes its `_headers` rules into `dist/client/_headers`, where Cloudflare reads them, next to the adapter's own. They used to land in `dist/client/<base>/_headers`, which Cloudflare never reads and served publicly, so the Markdown and text charset, the discovery files' media types and CORS header, and the agent skills media types were dropped. The Markdown and JSON 404 answers are wired up under a base too, and a `base` written without a leading slash (`cloudflare({ base: "docs" })`) no longer stops `Accept: text/markdown` negotiation from running.
71
+ - f373654: Rich text from a CMS source (Notion, Contentful, Sanity, Strapi, Payload) renders neighboring runs with the same formatting as one: two bold runs side by side used to show `****` between them, and two italic runs `**`. Neighboring runs under the same link in Notion and Sanity now form one link. A bare URL containing `_`, `~`, or `*` stays a working link instead of gaining backslashes in its address, and struck-through text in a Contentful table cell no longer shows `~~~~`.
72
+ - 4f7785c: Rich text from a CMS source (Notion, Contentful, Sanity, Strapi, Payload) renders more faithfully. A `^` no longer turns text like "2^10 and 2^20" into superscript, `$$` no longer starts math, and a colon before a word (`10:30`, `pets:read`) stays text. Two struck-through runs side by side now render as one strikethrough instead of showing `~~~~`, and a heading that ends in " #" keeps its `#`.
73
+
74
+ A Notion page with no Slug property is now routed by its whole title, with slashes turned into hyphens: "CI/CD Setup" publishes at `ci-cd-setup` instead of at `cd-setup` in an invented `ci` group. A Slug property still sets a nested path. A Notion video URL that contains a double quote or a backslash no longer breaks the page.
75
+ - af7c4c9: An unknown `dateFormat.timeZone` (`Asia/Tokio`) is now a config error that points at the key, instead of a `RangeError` thrown later while rendering a page's "last updated" date or the changelog.
76
+ - af7c4c9: Blume now warns when a content folder is named after the default locale (`docs/en/` with `defaultLocale: "en"`). The default locale's pages live at the content root, so such a folder is ordinary content that publishes at `/en/…` (`/en/en/…` when the default locale keeps its prefix, and `/fr/en/…` as fallbacks); before, it did so silently.
77
+ - 08219a2: The homepage `Link` header `blume dev` sends now lists only what the dev server serves: the JSON API's OpenAPI description and the homepage's Markdown mirror. It used to also advertise `/llms.txt`, `/agent-readability.json`, and the API and AI catalogs, which `blume build` writes and the dev server answered with 404s. Built and deployed sites send the same header as before.
78
+ - b6992c0: Two `blume dev` servers started at the same moment in one project no longer both get the `.blume` lock. The lock file now appears with its content already written, a lock that's still being written is waited on instead of being read as stale, and a stale lock left by a stopped server is cleared by one process at a time, so a starting server can't delete the lock another one claimed a moment earlier.
79
+ - 1b83039: `blume dev` now shows a body edit to a `.md` page on sites whose content pages render in Astro's `prerender` environment, such as sites deployed with `cloudflare()`. Astro refreshes its content store in the `ssr` environment only, so the page kept rendering its old body until something structural changed or the dev server restarted.
80
+ - 08219a2: `blume dev` now refreshes Blume's generated data in every Vite environment, not only the ones Vite's legacy module graph covers. On sites whose content pages render in Astro's `prerender` environment, such as sites deployed with `cloudflare()`, a page added while the dev server runs now opens instead of 404ing, sidebar label edits show up, `.md` mirrors follow body edits, and an edit to an included partial reaches the pages that include it. Until now they all kept their startup state until the dev server restarted.
81
+ - 08219a2: The first search in a fresh `blume dev` session no longer reloads the page. The configured search adapter's browser library (`@orama/orama` for the default search, or the FlexSearch, Algolia, Orama Cloud, or Typesense client) is now pre-bundled when the dev server starts. It used to be discovered only when the search dialog first opened, which re-optimized dependencies and reloaded the page.
82
+ - be9d98e: `blume dev` reloads on edits under a nested folder that shares a name with an excluded one. With `exclude: ["drafts/**"]`, which leaves out `drafts/` at the content root only, the watcher used to ignore every folder named `drafts`, so edits to pages in `guides/drafts/` never showed up until a restart.
83
+ - be9d98e: `blume eject` now writes the JSON docs API (`/api/docs/…` and `/openapi.json`) into the ejected app, which its `llms.txt`, homepage `Link` header, and not-found page already pointed to, so those links no longer 404. The ejected `src/generated/includes.json` now lists partials and their pages relative to the project instead of as absolute paths from the machine that ran eject, so editing a partial still refreshes the pages that include it in any checkout.
84
+ - 1b83039: `blume eject` now writes the not-found page's Markdown and JSON twins (`/404.md` and `/404.json`) beside its `404.astro`, so the ejected app answers the same missing-page URLs as the hidden runtime. Like the page itself, they're left out when the project already owns `/404` with a `pages/404.astro` or a `404.md` content page.
85
+ - 1b83039: A failing `blume eval` now delivers its whole report through a pipe before exiting non-zero. It used to exit as soon as it wrote the summary, so a long `--verbose` report was cut off partway in CI logs.
86
+ - b6992c0: `blume eval --threshold` rejects an empty value, which it used to read as `0`, so `--threshold "$EVAL_THRESHOLD"` with the variable unset switched the gate off. An absolute `--file` path is used as given instead of being nested under the project root, for runs and for `blume eval init`, whose "already exists" check missed it. `--timeout` on `blume eval` and `blume translate` refuses values above 2147483 seconds (about 24 days), which used to make every agent time out at once. Durations in their reports round before they split into minutes, so a run prints `2m 0s` rather than `1m 60s`, and `1m 0s` rather than `60.0s`.
87
+ - af7c4c9: Front matter that opens with a blank line after its `---` fence is now read as front matter, the way Astro reads it. Blume used to treat that `---` as a divider, so the page's `draft`, `slug`, and `hidden` were ignored while Astro stripped the block, and its YAML leaked into search and `llms.txt`. A block that holds no key–value pairs now reads as empty front matter, again matching Astro.
88
+ - ad20661: Every URL Blume generates for a page now puts `deployment.base` in front, even when the page's route starts with the same segment. With `base: "/guides"`, a page at `guides/setup.md` is served at `/guides/guides/setup`, but its canonical URL, sidebar and tab links, breadcrumbs, previous/next links, search results, JSON-LD, `llms.txt` and `llms-full.txt` entries, JSON API and MCP URLs, and relative links to it (`./setup`) all pointed at `/guides/setup`. They now point where the page is served. A root-relative link you write that already starts with the base is still left as written. The language switcher's links to locales a page has no translation for get the same fix for `basePath`.
89
+ - be9d98e: With `lastModified: "git"`, a project opened through a symlink (such as `/tmp` on macOS, which links to `/private/tmp`) now gets its "Last updated" dates. Blume compared the project's path against the one git reports with the link resolved, so it treated every content folder as outside the repository and dated no page, without a warning.
90
+ - af7c4c9: Heading anchors and titles now come from a heading's rendered text rather than its Markdown source, so they match the ids on the page. `## See [the docs](/x)` anchors as `#see-the-docs` (not `#see-the-docsx`), `## An _important_ note` as `#an-important-note`, and entities, code spans, inline HTML, images, reference links, footnote references (`## Setup[^note]` → `#setup1`, numbered in the page's footnote order), and smart punctuation (`A -- B` → `#a--b`) read the way the renderer reads them. `blume validate` no longer reports a correct link to such a heading as a broken anchor, `blume translate` pins the id the page actually has, Obsidian heading links land on the section, and a page without a frontmatter `title` is titled "Using Astro with Blume" instead of the raw "Using [Astro](https://astro.build) with **Blume**".
91
+ - 6933892: `usePage()` and `useBlume()` now return the page being viewed after a client-side navigation. They used to keep answering with the first page's route, title, and navigation for the rest of the visit, so an island on a page reached by a link, or after switching language, read the wrong page. An island kept across navigations with `transition:persist` now updates too.
92
+ - 4f7785c: Text in an `.mdx` page that reads as an inline or leaf directive now renders exactly as written. A colon followed by a letter or digit, as in `16:9`, `10:30am`, `og:image`, or `pets:read`, used to drop everything from the colon to the end of the word ("16:9 frame" rendered as "16 frame"), and a `::name[label]{attrs}` line vanished. This also covers headings and their anchors, callout titles and bodies, and API reference descriptions. `:::` callouts work as before, and `.md` pages were never affected.
93
+ - be9d98e: `mdxRemote()` pages lose their file and folder names' ordering prefixes the way a local content folder's do: `01-getting-started/02-install.mdx` now routes to `/sdk/getting-started/install` instead of `/sdk/01-getting-started/02-install`, while the prefixes still order the sidebar.
94
+ - f373654: A `:::` block in an `.mdx` page whose name isn't a callout type, like a `:::details` carried over from another docs tool or a typo like `:::warnig`, no longer disappears along with everything inside it. Its content now renders between its `:::` lines, which stay on the page as written, and `blume dev`, `blume build`, and `blume check` warn with `BLUME_UNKNOWN_DIRECTIVE`, naming the callout types. A callout nested inside another (a `::::note` around a `:::tip`) now renders instead of vanishing. Site search now indexes text like `16:9` and `og:image` as the page shows it, instead of as "16" and "og".
95
+ - be607f9: The `blume-migrate` skill's Mintlify codemod removes a dropped front matter key's list items even when they're written flush at column 0 (`keywords:` then `- one`), instead of leaving them behind as front matter that no longer parses. Its Fumadocs and Nextra guides now map locale file suffixes (`page.cn.mdx`, `index.en.mdx`) to `i18n.parser: "dot"` as-is rather than restructuring them into locale folders.
96
+ - 3e95f16: OG cards render every script by default. A Noto fallback for each script is now part of the default card font stack, not only for configured locales, so a Japanese, Hindi, or Arabic title renders instead of tofu on any site, including sites with no `i18n` block and ejected projects. A card fetches from Google Fonts only when its text has a glyph the built-in font can't draw, and then only the families and subsets those glyphs need, so English cards fetch nothing and Latin-only sites still build offline. Configured locales move their family to the front, so a `zh` site draws Han in Chinese forms.
97
+ - 3cd8149: Add a `oneDollarStats()` analytics adapter. It renders the OneDollarStats tracker tag, needs no key, and forwards every option as a `data-` attribute (`hostname`, `devmode`, …). `hostname` must be a bare host name, and `"hash-routing": "false"` leaves the attribute off, since the tracker turns hash routing on whenever it's present. Blume's custom events (page feedback and Ask AI) reach `window.stonks.event` with their property values as strings.
98
+ - 090211e: The API playground's built-in proxy (`playground: { proxy: true }`) now forwards only the headers the Try it panel sets itself: the credentials and header parameters filled in, and the body's `Content-Type`. It used to forward every request header except a handful, so credentials the browser or the host attached on their own reached the documented API: HTTP Basic credentials for a password-protected docs site, a Cloudflare Access `cf-access-jwt-assertion`, Vercel's `x-vercel-oidc-token`, and `X-Forwarded-For`/`X-Real-IP`. Platform headers are never forwarded, even when a spec declares a header parameter with the same name.
99
+ - 1b83039: A `trace` operation's Try it panel now says that browsers can't send `TRACE` requests when you press Send, instead of blaming CORS or an unreachable API: `fetch` refuses the method before anything goes out. Its JavaScript sample, which is built on `fetch`, is now a note saying so rather than code that could only throw. The cURL and Python samples still send it.
100
+ - 090211e: The Try it panel and the code samples now send requests the way the spec describes them. Array and object parameters follow their `style` and `explode`, with OpenAPI's defaults when the spec sets none: `tags: ["dog", "cat"]` becomes `?tags=dog&tags=cat` rather than a URL-encoded JSON array, a path array becomes `1,2`, and a `deepObject` filter becomes `filter[color]=red`, with `spaceDelimited`, `pipeDelimited`, `label`, and `matrix` supported too. An `application/x-www-form-urlencoded` body is sent as `name=value` pairs (following its `encoding`), and a `multipart/form-data` body as form fields: curl uses `--form-string`, JavaScript a `FormData`, and Python `files=`. Before, both were sent as JSON. A `text/plain` or XML body is sent as written, without being blocked as "Invalid JSON", and its string example is no longer wrapped in quotes.
101
+
102
+ Examples that `$ref` an entry under `#/components/examples` now prefill the panel and the response samples instead of being replaced by a generated placeholder. A GraphQL endpoint keeps its trailing slash (`https://host/graphql/` was sent to `…/graphql`). The HEAD curl sample uses `--head` so it no longer waits for a body that never comes, and the Python sample for TRACE uses `requests.request("TRACE", …)`, since `requests.trace` doesn't exist.
103
+ - 3311cee: With `deployment.base` set, root-relative images and file links in content now gain the base: `![logo](/logo.png)` renders as `/sub/logo.png` and `[spec](/spec.pdf)` (or `<Card href="/spec.pdf">`) as `/sub/spec.pdf`, where Astro serves `public/`. They used to keep the bare root path and 404 on a subpath deploy. Public files still never gain `basePath`, and neither does a redirect whose `to` names one: with `basePath: "/docs"`, a redirect to `/files/whitepaper.pdf` now lands on the file instead of `/docs/files/whitepaper.pdf`.
104
+ - be9d98e: `mdxRemote()` and `githubReleases()` give up on a request after 30 seconds, like the CMS sources, and the diagnostic names the URL that didn't respond. A server that accepted the connection and never answered used to hang `blume build` and every `blume dev` rescan indefinitely.
105
+ - af7c4c9: `%` is now dropped from routes the way `:` already was, so `sdks/100%.md` publishes at `/sdks/100` and sidebar and pagination links reach it, where a bare `%` used to break the build's URL decoding; `#` and `?` in a frontmatter `slug` are dropped the same way. A file or folder with `#` or `?` in its name is reported as an error and left out of the site: Astro's content loader can't read such a file, so it used to publish a page that only ever said "Page not found". A frontmatter `slug` with a `.` or `..` segment (`../../etc/escape`, `guides/./x`) is now rejected with an error at the key instead of publishing a page no link could reach. A group folder's numeric prefix now works on either side of the parentheses: `(01-zeta)` sorts first instead of last, and `01-(gamma)` publishes at `/g` as a "Gamma" group instead of at `/(gamma)/g`.
106
+ - 6933892: On a versioned site, the search dialog shows its "All versions" toggle only with a search provider that scopes results by version: Orama, FlexSearch, Algolia, or Typesense. With Pagefind, Orama Cloud, or Mixedbread the toggle used to show but change nothing, since their results always span every version.
107
+ - 3311cee: The sitemap and RSS feeds now always put `deployment.base` in front of a page's URL, even when the page's route starts with the same segment. With `base: "/guides"`, a page at `guides/setup.md` is listed at `/guides/guides/setup`, where it's served, instead of `/guides/setup`.
108
+ - 1b83039: An agent skill's `.tar.gz` now always ends with the two zero blocks that mark the end of a tar archive. A skill whose files filled the archive to within a block of its 10 KB record ended with one zero block or none, and GNU `tar` warned "A lone zero block" while unpacking it. Archives that already ended correctly keep the same bytes and digest.
109
+ - 6933892: Every tab panel now has the `tabpanel` role its tab points at, including the panels of a `<CodeGroup>`, a TypeScript/JavaScript pair, and a nested tab group, which had none. Panels of `<Tabs dropdown>` no longer claim the `tabpanel` role, since that layout shows a select instead of tabs to label them.
110
+ - 6933892: The "On this page" highlight now skips headings inside tab panels that aren't selected. Such a heading used to count as already scrolled past, so the table of contents highlighted it from the top of the page.
111
+ - 1b83039: `blume translate` now looks for `meta.ts` titles only in the folders the content source's `include` globs reach, the same meta files the build reads. A `meta.ts` elsewhere under a project-rooted source (beside tooling, say) configures no sidebar group, and it used to be translated anyway.
112
+ - b6992c0: `blume translate` rewrites an existing translation where it lives. A stale hand-written `fr/guide.md` for a `guide.mdx` source is updated in place instead of gaining a `fr/guide.mdx` beside it (which failed the next build with a duplicate route), a locale's hand-written `meta.js` or `meta.mjs` is rewritten instead of getting a `meta.ts` next to it, and a locale folder authored in another casing (`pt-br/` for `pt-BR`) now receives the locale's `meta.ts` too, not a second `pt-BR/` folder. A source file saved with a byte order mark keeps its frontmatter in the translation; it used to be written with the body alone. Looking for `meta.ts` titles now skips the content source's `exclude` globs and moves past a meta file that fails to load, where an application file like `src/lib/meta.ts` in a project-rooted source used to crash the run with `BLUME_INTERNAL`.
113
+ - 3311cee: Redirect `from` paths in the `vercel.json` a static build writes are now escaped, since Vercel reads each `source` as a pattern. A `from` like `/c++-guide` or `/what?` used to make the whole file invalid, and `/faq(old)` never matched.
114
+ - be9d98e: `blume version` now also rewrites reference-style link definitions (`[setup]: /guides/setup`), single-quoted `href='…'` and `src='…'` attributes, and links that spell out the `basePath` (`/docs/guides/setup` becomes `/docs/v1.0/guides/setup`) inside the snapshot. Those links used to keep pointing at the current docs.
115
+
3
116
  ## 2.0.2
4
117
 
5
118
  ### Patch Changes
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Blume
2
2
 
3
- [![npm downloads](https://img.shields.io/npm/dm/blume.svg)](https://www.npmjs.com/package/blume) [![Socket Badge](https://socket.dev/api/badge/npm/package/blume)](https://socket.dev/npm/package/blume) ![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/haydenbleasel/blume?utm_source=oss&utm_medium=github&utm_campaign=haydenbleasel%2Fblume&labelColor=171717&color=FF570A&link=https%3A%2F%2Fcoderabbit.ai&label=CodeRabbit+Reviews)
3
+ [![npm downloads](https://img.shields.io/npm/dm/blume.svg)](https://www.npmjs.com/package/blume) [![Socket Badge](https://badge.socket.dev/npm/package/blume/latest)](https://socket.dev/npm/package/blume/overview/latest) ![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/haydenbleasel/blume?utm_source=oss&utm_medium=github&utm_campaign=haydenbleasel%2Fblume&labelColor=171717&color=FF570A&link=https%3A%2F%2Fcoderabbit.ai&label=CodeRabbit+Reviews)
4
4
 
5
5
  **The open-source docs framework for humans and agents.** Fast, AI-ready, and zero-config. Free and open source, forever.
6
6
 
@@ -449,6 +449,22 @@ var CHECKS = [
449
449
  tier: "static",
450
450
  title: "Hreflang group has a language conflict"
451
451
  },
452
+ {
453
+ category: "accessibility",
454
+ fix: "Pick a darker shade for light mode or a lighter one for dark mode, per mode with `theme.accent: { light, dark }`.",
455
+ id: "BLUME_AUDIT_THEME_CONTRAST_LOW",
456
+ severity: "warning",
457
+ tier: "static",
458
+ title: "Theme colors miss WCAG AA contrast"
459
+ },
460
+ {
461
+ category: "accessibility",
462
+ fix: "Write the color as hex, `rgb()`, `hsl()`, `hwb()`, `lab()`, `lch()`, `oklab()`, `oklch()`, or a named color so its contrast can be checked.",
463
+ id: "BLUME_AUDIT_THEME_COLOR_UNCHECKED",
464
+ severity: "info",
465
+ tier: "static",
466
+ title: "Theme color can't be checked for contrast"
467
+ },
452
468
  {
453
469
  category: "assets",
454
470
  fix: 'Add descriptive `alt` text, or `alt=""` if the image is decorative.',
@@ -977,5 +993,5 @@ var launchInstalledAgent = async (bin, prompt, platform = process.platform) => {
977
993
 
978
994
  export { CHECKS, finding, formatReport, reportJson, formatCatalog, AGENTS, writeAgentReport, fixPrompt, launchAgent, launchInstalledAgent };
979
995
 
980
- //# debugId=E6F2D36C0552C7F664756E2164756E21
981
- //# sourceMappingURL=chunk-6k4ftwze.js.map
996
+ //# debugId=FF011B636C2DD0A664756E2164756E21
997
+ //# sourceMappingURL=chunk-0gz36akr.js.map
@@ -3,10 +3,10 @@
3
3
  "sources": ["../src/audit/report.ts", "../src/audit/catalog.ts", "../src/audit/agent.ts"],
4
4
  "sourcesContent": [
5
5
  "import { colors } from \"consola/utils\";\nimport type { ColorFunction } from \"consola/utils\";\nimport { relative } from \"pathe\";\n\nimport { countBySeverity } from \"../core/diagnostics.ts\";\nimport type { Diagnostic, DiagnosticSeverity } from \"../core/types.ts\";\nimport { CHECKS, checkMeta } from \"./catalog.ts\";\nimport type { CheckId } from \"./catalog.ts\";\nimport type { AuditResult } from \"./run.ts\";\nimport type { AuditCategory, AuditTier } from \"./types.ts\";\n\nconst SEVERITY_COLOR = {\n error: colors.red,\n info: colors.blue,\n warning: colors.yellow,\n} satisfies Record<DiagnosticSeverity, ColorFunction>;\n\nconst GLYPH = {\n error: \"✖\",\n info: \"ℹ\",\n warning: \"⚠\",\n} satisfies Record<DiagnosticSeverity, string>;\n\n/** How many affected pages to list before collapsing the rest. */\nconst PREVIEW = 3;\n\n/** The skippable tiers, each with the flag that runs it. */\nconst TIER_FLAGS: { tier: AuditTier; flag: string }[] = [\n { flag: \"--external\", tier: \"external\" },\n { flag: \"--url <origin>\", tier: \"network\" },\n];\n\ninterface CheckRollup {\n id: CheckId;\n count: number;\n /** Distinct affected URLs — one page can carry several findings. */\n pages: number;\n severity: DiagnosticSeverity;\n category: AuditCategory;\n title: string;\n findings: Diagnostic[];\n}\n\nconst SEVERITY_ORDER = {\n error: 0,\n info: 2,\n warning: 1,\n} satisfies Record<DiagnosticSeverity, number>;\n\n/**\n * The group's severity: the worst of its findings'. A check can downgrade a\n * finding at runtime (an external link answering 503 is a warning, not the\n * catalog's error), and the group's glyph must agree with the summary counts.\n */\nconst worstSeverity = (findings: Diagnostic[]): DiagnosticSeverity => {\n let worst: DiagnosticSeverity = \"info\";\n for (const finding of findings) {\n if (SEVERITY_ORDER[finding.severity] < SEVERITY_ORDER[worst]) {\n worst = finding.severity;\n }\n }\n return worst;\n};\n\n/** The first finding per affected URL, in report order. */\nconst firstPerPage = (findings: Diagnostic[]): Diagnostic[] => {\n const seen = new Set<string | undefined>();\n return findings.filter((finding) => {\n if (seen.has(finding.url)) {\n return false;\n }\n seen.add(finding.url);\n return true;\n });\n};\n\n/**\n * Group findings by check. This is the difference between a report people read\n * and one they close: 214 pages × 6 findings is an unreadable wall, but \"Meta\n * description missing — 12 pages\" is a to-do list.\n */\nexport const rollup = (diagnostics: Diagnostic[]): CheckRollup[] => {\n const groups = new Map<string, Diagnostic[]>();\n for (const diagnostic of diagnostics) {\n const group = groups.get(diagnostic.code);\n if (group) {\n group.push(diagnostic);\n } else {\n groups.set(diagnostic.code, [diagnostic]);\n }\n }\n\n const checks = [...groups.entries()].map(([id, findings]) => {\n // SAFETY: audit findings are only ever created through `finding()`, whose\n // codes are the check catalog's ids.\n const checkId = id as CheckId;\n const { category, title } = checkMeta(checkId);\n return {\n category,\n count: findings.length,\n findings,\n id: checkId,\n pages: firstPerPage(findings).length,\n severity: worstSeverity(findings),\n title,\n };\n });\n\n // Rank each category by the worst thing in it, so the categories that need\n // attention lead. Sorting on severity alone would interleave the categories\n // and print \"content\" three separate times.\n const worst = new Map<AuditCategory, number>();\n for (const check of checks) {\n const rank = SEVERITY_ORDER[check.severity];\n worst.set(\n check.category,\n Math.min(worst.get(check.category) ?? rank, rank)\n );\n }\n\n return checks.toSorted(\n (a, b) =>\n (worst.get(a.category) ?? 0) - (worst.get(b.category) ?? 0) ||\n a.category.localeCompare(b.category) ||\n SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity] ||\n b.count - a.count\n );\n};\n\n/** Categories that had no findings but were never run, and the flag that runs them. */\nconst skippedTiers = (tiers: Record<AuditTier, boolean>): string[] =>\n TIER_FLAGS.filter(({ tier }) => !tiers[tier]).map(({ flag, tier }) => {\n const label = CHECKS.filter((check) => check.tier === tier).length;\n return ` ${colors.dim(`⊘ ${tier.padEnd(12)} skipped — pass ${flag} (${label} checks)`)}`;\n });\n\n/** How many checks actually ran, i.e. those whose tier was enabled. */\nconst activeChecks = (tiers: Record<AuditTier, boolean>): number =>\n CHECKS.filter((check) => tiers[check.tier]).length;\n\n/**\n * Individual checks performed: every rule that ran, against every page crawled.\n * The headline number — it's what makes \"39 warnings\" legible as a proportion\n * rather than a bare count.\n */\nexport const auditCount = (result: AuditResult): number =>\n activeChecks(result.tiers) * result.pages;\n\nconst summaryLine = (\n counts: Record<DiagnosticSeverity, number>,\n audits: number\n): string =>\n [\n `${audits.toLocaleString(\"en-US\")} audit${audits === 1 ? \"\" : \"s\"}`,\n `${counts.error} error${counts.error === 1 ? \"\" : \"s\"}`,\n `${counts.warning} warning${counts.warning === 1 ? \"\" : \"s\"}`,\n `${counts.info} note${counts.info === 1 ? \"\" : \"s\"}`,\n ].join(\" · \");\n\n/** One affected page: the URL, and the source file that fixes it. */\nconst findingLine = (diagnostic: Diagnostic, root: string): string => {\n const url = diagnostic.url ?? \"\";\n const source = diagnostic.file\n ? colors.dim(\n `${relative(root, diagnostic.file)}${\n diagnostic.line === undefined ? \"\" : `:${diagnostic.line}`\n }`\n )\n : \"\";\n // padEnd alone yields no gap once the URL reaches the column width.\n return ` ${url.padEnd(34)} ${source}`.trimEnd();\n};\n\n/**\n * Render the audit as a report grouped by check, with each check's affected\n * pages, the source file to edit, and the fix.\n */\nexport const formatReport = (\n result: AuditResult,\n root: string,\n options: { verbose?: boolean } = {}\n): string => {\n const counts = countBySeverity(result.diagnostics);\n const groups = rollup(result.diagnostics);\n const lines: string[] = [];\n\n const where = result.origin\n ? `${relative(root, result.staticDir) || \"dist\"} + ${result.origin}`\n : `${relative(root, result.staticDir) || \"dist\"} · offline`;\n lines.push(\n \"\",\n ` ${colors.bold(\"blume audit\")} ${colors.dim(`${result.pages} pages · ${where}`)}`,\n ` ${summaryLine(counts, auditCount(result))}`,\n \"\"\n );\n\n if (groups.length === 0) {\n lines.push(` ${colors.green(\"✔ No issues found.\")}`, \"\");\n }\n\n let category: AuditCategory | null = null;\n for (const group of groups) {\n const { category: next } = group;\n if (next !== category) {\n category = next;\n lines.push(` ${colors.bold(category)}`, \"\");\n }\n\n const color = SEVERITY_COLOR[group.severity];\n const pages = `${group.pages} page${group.pages === 1 ? \"\" : \"s\"}`;\n lines.push(\n ` ${color(`${GLYPH[group.severity]} ${group.title}`)} ${colors.dim(pages)}`\n );\n\n // The preview lists affected pages, each once; --verbose lists every\n // finding, since each one's message names a different specific.\n const shown = options.verbose\n ? group.findings\n : firstPerPage(group.findings).slice(0, PREVIEW);\n for (const diagnostic of shown) {\n lines.push(findingLine(diagnostic, root));\n // The message names the specifics the rolled-up line can't — which target\n // is broken, what the duplicate is — so --verbose prints it per finding.\n if (options.verbose) {\n lines.push(` ${colors.dim(diagnostic.message)}`);\n }\n }\n const hidden = options.verbose ? 0 : group.pages - shown.length;\n if (hidden > 0) {\n lines.push(` ${colors.dim(`… and ${hidden} more (--verbose)`)}`);\n }\n\n // Findings share the catalog's fix unless one overrode it (a navigation\n // entry, a page generated from an API spec), so each distinct fix is\n // printed once.\n const fixes = new Set(\n group.findings.flatMap((finding) =>\n finding.suggestion ? [finding.suggestion] : []\n )\n );\n for (const fix of fixes) {\n lines.push(` ${colors.cyan(`fix: ${fix}`)}`);\n }\n lines.push(\"\");\n }\n\n const skipped = skippedTiers(result.tiers);\n if (skipped.length > 0) {\n lines.push(...skipped, \"\");\n }\n\n return lines.join(\"\\n\");\n};\n\n/**\n * The machine-readable report. The existing `diagnostics` + `summary` shape is\n * preserved exactly — anything already parsing `blume validate --json` keeps\n * working — with the audit-specific rollup added alongside it.\n */\nexport const reportJson = (result: AuditResult, root: string): string => {\n const diagnostics = result.diagnostics.map((diagnostic) =>\n diagnostic.file\n ? { ...diagnostic, file: relative(root, diagnostic.file) }\n : diagnostic\n );\n return `${JSON.stringify(\n {\n audit: {\n /** Checks run × pages crawled — the total number of individual audits. */\n audits: auditCount(result),\n checks: rollup(result.diagnostics).map((group) => ({\n category: group.category,\n count: group.count,\n id: group.id,\n severity: group.severity,\n })),\n origin: result.origin,\n pages: result.pages,\n staticDir: relative(root, result.staticDir),\n tiers: result.tiers,\n },\n diagnostics,\n summary: countBySeverity(result.diagnostics),\n },\n null,\n 2\n )}\\n`;\n};\n\n/** `--list-checks`: the catalog, which is also the docs' source of truth. */\nexport const formatCatalog = (): string => {\n const lines: string[] = [\"\"];\n let category: AuditCategory | null = null;\n for (const check of [...CHECKS].toSorted((a, b) =>\n a.category.localeCompare(b.category)\n )) {\n const { category: next } = check;\n if (next !== category) {\n category = next;\n lines.push(` ${colors.bold(category)}`);\n }\n const tier =\n check.tier === \"static\" ? \"\" : ` ${colors.dim(`[${check.tier}]`)}`;\n lines.push(\n ` ${SEVERITY_COLOR[check.severity](GLYPH[check.severity])} ${check.id.replace(\"BLUME_AUDIT_\", \"\").toLowerCase().padEnd(34)} ${colors.dim(check.title)}${tier}`\n );\n }\n lines.push(\"\", ` ${CHECKS.length} checks.`, \"\");\n return lines.join(\"\\n\");\n};\n",
6
- "import type { Diagnostic } from \"../core/types.ts\";\nimport type { CheckMeta } from \"./types.ts\";\n\n/**\n * Every check `blume audit` can report.\n *\n * This is deliberately ~76 checks, not the ~173 rows an Ahrefs site audit\n * lists. Ahrefs crawls the open web, so its list is padded with rows that are\n * structurally impossible for an Astro-built Blume site, and shipping those as\n * permanent zeroes teaches people to ignore the report. What was dropped, and\n * why:\n *\n * - **Every `nofollow` check** (~6 rows, including all four \"nofollow incoming\n * internal links\" variants). `RootLayout.astro` only ever emits `<meta\n * name=\"robots\" content=\"noindex\">`; Blume has no way to emit `nofollow`, so\n * they could never fire. Hand-written `rel=\"nofollow\"` in MDX is still caught\n * by `INTERNAL_LINK_NOFOLLOW`.\n * - **The whole JavaScript + CSS section** (~11 rows: \"JS redirects\", \"CSS\n * redirects\", \"page has redirected JS/CSS\", \"HTTPS page links to HTTP\n * JS/CSS\"…). Vite emits content-hashed, existent, non-redirecting bundles.\n * Collapsed into one `SUBRESOURCE_MISSING`.\n * - **Ahrefs' indexable/non-indexable duplicates** (~20 rows). Carried instead\n * by `PageSnapshot.indexable` on a single finding.\n * - **\"Only one dofollow incoming internal link\"** and **\"page has no outgoing\n * links\"**. Blume's sidebar links every page from every page, so these fire on\n * ~100% of pages. Pure noise.\n * - **\"3XX redirect\"** and **\"302 redirect\"**: having a redirect is inventory,\n * not a finding. **\"HTTP to HTTPS redirect\"** is correct behavior.\n * - **\"Font size too small\" / \"tap targets too close\" / \"content not sized\n * correctly\"**: properties of the theme, identical on every page. A regression\n * there is a Blume bug, not a user finding.\n * - **\"Document uses plugins\"** and **\"poor FID\"** (FID is deprecated; lab\n * Lighthouse cannot measure INP, so we report TBT and say so).\n * - **\"More than three parameters in URL\"**: a static docs site emits no query\n * strings in its own links.\n * - **\"Page in multiple sitemaps\"**: Blume emits exactly one sitemap.\n * - **Core Web Vitals** (LCP/CLS/INP): these need a real browser. Rather than\n * ship a `--lighthouse` flag that silently reports nothing, they are deferred\n * until Lighthouse is actually wired in. A tier the audit cannot run is worse\n * than one it openly doesn't have.\n *\n * Severity discipline: `error` means \"this is definitely broken\". Anything\n * advisory ships as `warning`/`info`. One noisy check and the whole command gets\n * switched off.\n */\nexport const CHECKS = [\n // Content\n {\n category: \"content\",\n fix: \"Add a `title` to the page's frontmatter.\",\n id: \"BLUME_AUDIT_TITLE_MISSING\",\n severity: \"error\",\n tier: \"static\",\n title: \"Title tag missing or empty\",\n },\n {\n category: \"content\",\n fix: \"Remove the extra <title> from the page's layout or MDX.\",\n id: \"BLUME_AUDIT_TITLE_MULTIPLE\",\n severity: \"error\",\n tier: \"static\",\n title: \"Multiple title tags\",\n },\n {\n category: \"content\",\n fix: \"Rewrite `title` in the frontmatter to fit the length range.\",\n id: \"BLUME_AUDIT_TITLE_LENGTH\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Title too long or too short\",\n },\n {\n category: \"content\",\n fix: \"Add a `description` to the page's frontmatter.\",\n id: \"BLUME_AUDIT_DESCRIPTION_MISSING\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Meta description missing or empty\",\n },\n {\n category: \"content\",\n fix: \"Remove the extra description <meta> from the page's layout or MDX.\",\n id: \"BLUME_AUDIT_DESCRIPTION_MULTIPLE\",\n severity: \"error\",\n tier: \"static\",\n title: \"Multiple meta description tags\",\n },\n {\n category: \"content\",\n fix: \"Rewrite `description` in the frontmatter to fit the length range.\",\n id: \"BLUME_AUDIT_DESCRIPTION_LENGTH\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Meta description too long or too short\",\n },\n {\n category: \"content\",\n fix: \"Give the page a `title` — Blume renders it as the page's <h1>.\",\n id: \"BLUME_AUDIT_H1_MISSING\",\n severity: \"warning\",\n tier: \"static\",\n title: \"H1 tag missing or empty\",\n },\n {\n category: \"content\",\n fix: \"Demote the extra `# Heading` in the body to `##` — Blume already renders `title` as the h1.\",\n id: \"BLUME_AUDIT_H1_MULTIPLE\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Multiple H1 tags\",\n },\n {\n category: \"content\",\n fix: \"Expand the page, or fold it into a larger one.\",\n id: \"BLUME_AUDIT_LOW_WORD_COUNT\",\n severity: \"info\",\n tier: \"static\",\n title: \"Low word count\",\n },\n {\n category: \"content\",\n fix: \"Adjust the heading to the next level down — skipped levels break table-of-contents nesting and screen-reader outlines.\",\n id: \"BLUME_AUDIT_HEADING_SKIP\",\n severity: \"info\",\n tier: \"static\",\n title: \"Heading levels skip (e.g. h2 to h4)\",\n },\n {\n category: \"content\",\n fix: \"Correct the `date`, or hold the page back until it is meant to be live.\",\n id: \"BLUME_AUDIT_FUTURE_DATED_PAGE\",\n severity: \"info\",\n tier: \"static\",\n title: \"Page is dated in the future\",\n },\n {\n category: \"content\",\n fix: \"Restore the viewport <meta> in your ejected layout.\",\n id: \"BLUME_AUDIT_VIEWPORT_MISSING\",\n severity: \"error\",\n tier: \"static\",\n title: \"Viewport not set\",\n },\n\n // Duplicates\n {\n category: \"duplicates\",\n fix: \"Give each page a distinct `title` — search engines show it as the result headline.\",\n id: \"BLUME_AUDIT_DUPLICATE_TITLE\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Duplicate title\",\n },\n {\n category: \"duplicates\",\n fix: \"Give each page a distinct `description`.\",\n id: \"BLUME_AUDIT_DUPLICATE_DESCRIPTION\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Duplicate meta description\",\n },\n {\n category: \"duplicates\",\n fix: \"Merge the pages, or set `seo.canonical` on all but one.\",\n id: \"BLUME_AUDIT_DUPLICATE_CONTENT\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Duplicate pages without canonical\",\n },\n\n // Indexability\n {\n category: \"indexability\",\n fix: \"Set `deployment.site` in blume.config.ts to the site's public URL.\",\n id: \"BLUME_AUDIT_SITE_NOT_SET\",\n severity: \"warning\",\n tier: \"static\",\n title: \"deployment.site is not set\",\n },\n {\n category: \"indexability\",\n fix: \"Audit a production-like build (e.g. `VERCEL=1 VERCEL_PROJECT_PRODUCTION_URL=<host> blume build`) or the deployment itself with `--url <origin>`. Do not hardcode `deployment.site` — the platform sets it on every deploy.\",\n id: \"BLUME_AUDIT_SITE_INFERRED_AT_DEPLOY\",\n severity: \"info\",\n tier: \"static\",\n title: \"deployment.site is inferred at deploy time\",\n },\n {\n category: \"indexability\",\n fix: \"Drop the canonical from noindex pages — Google treats the pairing as contradictory and may ignore one of the two.\",\n id: \"BLUME_AUDIT_CANONICAL_ON_NOINDEX\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Page is noindex but declares a canonical\",\n },\n {\n category: \"indexability\",\n fix: \"Rebuild without `--preview` before deploying, or remove `draft: true` if the page is ready to ship.\",\n id: \"BLUME_AUDIT_DRAFT_PAGE_PUBLISHED\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Draft page is in the build\",\n },\n {\n category: \"indexability\",\n fix: \"Set `deployment.site` so Blume can emit absolute canonical URLs.\",\n id: \"BLUME_AUDIT_CANONICAL_MISSING\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Canonical URL missing\",\n },\n {\n category: \"indexability\",\n fix: \"Point `seo.canonical` at this page, or remove it to use the default self-canonical.\",\n id: \"BLUME_AUDIT_CANONICAL_NOT_SELF\",\n severity: \"info\",\n tier: \"static\",\n title: \"Non-canonical page\",\n },\n {\n category: \"indexability\",\n fix: \"Point `seo.canonical` at a page that exists and doesn't redirect.\",\n id: \"BLUME_AUDIT_CANONICAL_BAD_TARGET\",\n severity: \"error\",\n tier: \"static\",\n title: \"Canonical points to a broken or redirecting page\",\n },\n {\n category: \"indexability\",\n fix: \"Use the same protocol in `seo.canonical` as in `deployment.site`.\",\n id: \"BLUME_AUDIT_CANONICAL_PROTOCOL_MISMATCH\",\n severity: \"error\",\n tier: \"static\",\n title: \"Canonical protocol does not match the site\",\n },\n {\n category: \"indexability\",\n fix: \"Remove `noindex` from the page's frontmatter if it should be indexed.\",\n id: \"BLUME_AUDIT_ROBOTS_META_UNEXPECTED\",\n severity: \"info\",\n tier: \"static\",\n title: \"Page is not indexable\",\n },\n {\n category: \"indexability\",\n fix: \"Split the page — Googlebot stops reading an HTML document at 2 MB.\",\n id: \"BLUME_AUDIT_HTML_TOO_LARGE\",\n severity: \"error\",\n tier: \"static\",\n title: \"Page exceeds Googlebot's 2 MB crawl limit\",\n },\n {\n category: \"indexability\",\n fix: \"Remove the X-Robots-Tag header, or align it with the page's robots meta.\",\n id: \"BLUME_AUDIT_ROBOTS_HEADER_CONFLICT\",\n severity: \"error\",\n tier: \"network\",\n title: \"X-Robots-Tag header conflicts with the page's robots meta\",\n },\n\n // Links\n {\n category: \"links\",\n fix: \"Fix the link target, or create the page it points at.\",\n id: \"BLUME_AUDIT_LINK_TO_BROKEN\",\n severity: \"error\",\n tier: \"static\",\n title: \"Page has links to a broken page\",\n },\n {\n category: \"links\",\n fix: \"Link straight to the destination instead of through the redirect.\",\n id: \"BLUME_AUDIT_LINK_TO_REDIRECT\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Page has links to a redirect\",\n },\n {\n category: \"links\",\n fix: \"Link to this page from the body of a related page.\",\n id: \"BLUME_AUDIT_ORPHAN_PAGE\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Orphan page (only reachable from navigation)\",\n },\n {\n category: \"links\",\n fix: \"Use a root-relative path (`/docs/x`) so the link survives previews and `basePath`.\",\n id: \"BLUME_AUDIT_INTERNAL_LINK_ABSOLUTE\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Internal link hardcodes the site's own origin\",\n },\n {\n category: \"links\",\n fix: 'Drop `rel=\"nofollow\"` — it stops this page passing authority internally.',\n id: \"BLUME_AUDIT_INTERNAL_LINK_NOFOLLOW\",\n severity: \"info\",\n tier: \"static\",\n title: \"Page has nofollow outgoing internal links\",\n },\n {\n category: \"links\",\n fix: \"Check `basePath` / `deployment.base` — a trailing slash there produces `//`.\",\n id: \"BLUME_AUDIT_DOUBLE_SLASH_URL\",\n severity: \"error\",\n tier: \"static\",\n title: \"Double slash in URL\",\n },\n {\n category: \"links\",\n fix: \"Point the fragment at a heading that exists on the target page, or fix the heading it meant.\",\n id: \"BLUME_AUDIT_ANCHOR_BROKEN\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Link fragment matches no id on the target page\",\n },\n {\n category: \"links\",\n fix: \"Rename the source file to a lowercase, hyphenated slug — and add a redirect from the old URL if it was already published.\",\n id: \"BLUME_AUDIT_URL_STYLE\",\n severity: \"info\",\n tier: \"static\",\n title: \"URL contains uppercase, underscores, or spaces\",\n },\n\n // Redirects\n {\n category: \"redirects\",\n fix: \"Point the redirect at a page that exists.\",\n id: \"BLUME_AUDIT_REDIRECT_BROKEN\",\n severity: \"error\",\n tier: \"static\",\n title: \"Broken redirect\",\n },\n {\n category: \"redirects\",\n fix: \"Break the cycle in `redirects` — it never resolves.\",\n id: \"BLUME_AUDIT_REDIRECT_LOOP\",\n severity: \"error\",\n tier: \"static\",\n title: \"Redirect loop\",\n },\n {\n category: \"redirects\",\n fix: \"Point every hop straight at the final destination.\",\n id: \"BLUME_AUDIT_REDIRECT_CHAIN\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Redirect chain\",\n },\n {\n category: \"redirects\",\n fix: \"Use a real redirect in `blume.config.ts` instead of a meta refresh.\",\n id: \"BLUME_AUDIT_META_REFRESH\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Meta refresh redirect\",\n },\n {\n category: \"redirects\",\n fix: \"Remove the redirect, or delete the page it shadows — the page wins and the redirect never fires.\",\n id: \"BLUME_AUDIT_REDIRECT_SOURCE_IS_PAGE\",\n severity: \"error\",\n tier: \"static\",\n title: \"Redirect source is also a real page\",\n },\n {\n category: \"redirects\",\n fix: \"Redirect to the HTTPS URL.\",\n id: \"BLUME_AUDIT_REDIRECT_TO_HTTP\",\n severity: \"error\",\n tier: \"network\",\n title: \"HTTPS to HTTP redirect\",\n },\n\n // Social\n {\n category: \"social\",\n fix: \"Add a `description` — Blume fills the rest of the Open Graph tags for you.\",\n id: \"BLUME_AUDIT_OG_INCOMPLETE\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Open Graph tags missing or incomplete\",\n },\n {\n category: \"social\",\n fix: \"Set `deployment.site` to turn on generated OG images, or set `seo.image` on the page.\",\n id: \"BLUME_AUDIT_OG_IMAGE_MISSING\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Open Graph image missing\",\n },\n {\n category: \"social\",\n fix: \"Point `seo.image` at a file that exists, or rebuild — a dead og:image renders as a blank card everywhere the page is shared.\",\n id: \"BLUME_AUDIT_OG_IMAGE_BROKEN\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Open Graph image is not in the build\",\n },\n {\n category: \"social\",\n fix: \"Use an image of at least 1200×630 — smaller ones render blurry or get cropped into small-card layouts.\",\n id: \"BLUME_AUDIT_OG_IMAGE_SMALL\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Open Graph image is too small for large cards\",\n },\n {\n category: \"social\",\n fix: \"Align `og:url` with the page's canonical URL.\",\n id: \"BLUME_AUDIT_OG_URL_MISMATCH\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Open Graph URL not matching canonical\",\n },\n {\n category: \"social\",\n fix: \"Set `seo.x.handle` in blume.config.ts so X can attribute the card.\",\n id: \"BLUME_AUDIT_TWITTER_CARD_INCOMPLETE\",\n severity: \"warning\",\n tier: \"static\",\n title: \"X (Twitter) card missing or incomplete\",\n },\n\n // Localization\n {\n category: \"i18n\",\n fix: \"Restore the `lang` attribute on <html> in your ejected layout.\",\n id: \"BLUME_AUDIT_HTML_LANG_MISSING\",\n severity: \"error\",\n tier: \"static\",\n title: \"HTML lang attribute missing\",\n },\n {\n category: \"i18n\",\n fix: \"Use a valid BCP 47 tag (e.g. `en`, `en-GB`) for the locale.\",\n id: \"BLUME_AUDIT_HTML_LANG_INVALID\",\n severity: \"error\",\n tier: \"static\",\n title: \"HTML lang attribute invalid\",\n },\n {\n category: \"i18n\",\n fix: \"The page's <html lang> must match its own hreflang annotation.\",\n id: \"BLUME_AUDIT_HREFLANG_LANG_MISMATCH\",\n severity: \"error\",\n tier: \"static\",\n title: \"Hreflang and HTML lang mismatch\",\n },\n {\n category: \"i18n\",\n fix: \"Use a valid BCP 47 tag in the hreflang annotation.\",\n id: \"BLUME_AUDIT_HREFLANG_INVALID\",\n severity: \"error\",\n tier: \"static\",\n title: \"Hreflang annotation invalid\",\n },\n {\n category: \"i18n\",\n fix: \"A page's hreflang set must include a self-reference.\",\n id: \"BLUME_AUDIT_HREFLANG_SELF_MISSING\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Self-reference hreflang annotation missing\",\n },\n {\n category: \"i18n\",\n fix: \"Add an `x-default` alternate pointing at the default-locale page.\",\n id: \"BLUME_AUDIT_HREFLANG_XDEFAULT_MISSING\",\n severity: \"info\",\n tier: \"static\",\n title: \"X-default hreflang annotation missing\",\n },\n {\n category: \"i18n\",\n fix: \"Every page in an hreflang group must link back to every other one.\",\n id: \"BLUME_AUDIT_HREFLANG_NO_RETURN_TAG\",\n severity: \"error\",\n tier: \"static\",\n title: \"Missing reciprocal hreflang (no return-tag)\",\n },\n {\n category: \"i18n\",\n fix: \"Point the hreflang alternate at a page that exists and is canonical.\",\n id: \"BLUME_AUDIT_HREFLANG_BAD_TARGET\",\n severity: \"error\",\n tier: \"static\",\n title: \"Hreflang points to a broken, redirecting, or non-canonical page\",\n },\n {\n category: \"i18n\",\n fix: \"Each language in an hreflang group must name exactly one page.\",\n id: \"BLUME_AUDIT_HREFLANG_CONFLICT\",\n severity: \"error\",\n tier: \"static\",\n title: \"Hreflang group has a language conflict\",\n },\n\n // Assets\n {\n category: \"assets\",\n fix: 'Add descriptive `alt` text, or `alt=\"\"` if the image is decorative.',\n id: \"BLUME_AUDIT_IMAGE_ALT_MISSING\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Missing alt text\",\n },\n {\n category: \"assets\",\n fix: \"Fix the image path, or add the file to public/.\",\n id: \"BLUME_AUDIT_IMAGE_BROKEN\",\n severity: \"error\",\n tier: \"static\",\n title: \"Image broken\",\n },\n {\n category: \"assets\",\n fix: \"Compress the asset, or serve a modern format (WebP/AVIF).\",\n id: \"BLUME_AUDIT_ASSET_TOO_LARGE\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Asset file size too large\",\n },\n {\n category: \"assets\",\n fix: \"Set `width` and `height` so the browser can reserve space (avoids layout shift).\",\n id: \"BLUME_AUDIT_IMAGE_MISSING_DIMENSIONS\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Image has no width/height\",\n },\n {\n category: \"assets\",\n fix: \"Fix the reference, or restore the missing file.\",\n id: \"BLUME_AUDIT_SUBRESOURCE_MISSING\",\n severity: \"error\",\n tier: \"static\",\n title: \"Referenced script, style, or asset is missing from the build\",\n },\n {\n category: \"assets\",\n fix: \"Load the subresource over HTTPS — browsers block mixed content.\",\n id: \"BLUME_AUDIT_MIXED_CONTENT\",\n severity: \"error\",\n tier: \"static\",\n title: \"HTTPS/HTTP mixed content\",\n },\n\n // Sitemap\n {\n category: \"sitemap\",\n fix: \"Remove `draft`/`hidden`/`noindex` from the page's frontmatter if it should be indexed.\",\n id: \"BLUME_AUDIT_INDEXABLE_PAGE_NOT_IN_SITEMAP\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Indexable page not in sitemap\",\n },\n {\n category: \"sitemap\",\n fix: \"A noindex page should not be advertised in the sitemap.\",\n id: \"BLUME_AUDIT_NOINDEX_IN_SITEMAP\",\n severity: \"error\",\n tier: \"static\",\n title: \"Noindex page in sitemap\",\n },\n {\n category: \"sitemap\",\n fix: \"List only canonical URLs in the sitemap.\",\n id: \"BLUME_AUDIT_NON_CANONICAL_IN_SITEMAP\",\n severity: \"error\",\n tier: \"static\",\n title: \"Non-canonical page in sitemap\",\n },\n {\n category: \"sitemap\",\n fix: \"Remove the URL from the sitemap, or build the page it names.\",\n id: \"BLUME_AUDIT_SITEMAP_BAD_URL\",\n severity: \"error\",\n tier: \"static\",\n title: \"Sitemap names a page that does not exist or redirects\",\n },\n {\n category: \"sitemap\",\n fix: \"Sitemaps must be valid XML in the sitemaps.org urlset or sitemap index format.\",\n id: \"BLUME_AUDIT_SITEMAP_INVALID\",\n severity: \"error\",\n tier: \"static\",\n title: \"Sitemap has a syntax error or wrong format\",\n },\n {\n category: \"sitemap\",\n fix: \"Split the sitemap — the limits are 50 MB and 50,000 URLs.\",\n id: \"BLUME_AUDIT_SITEMAP_TOO_LARGE\",\n severity: \"error\",\n tier: \"static\",\n title: \"Sitemap exceeds 50 MB or 50,000 URLs\",\n },\n {\n category: \"sitemap\",\n fix: \"Use a real W3C date that is not in the future — search engines that catch a sitemap lying about freshness stop trusting its lastmod entirely.\",\n id: \"BLUME_AUDIT_SITEMAP_LASTMOD_INVALID\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Sitemap lastmod is invalid or in the future\",\n },\n {\n category: \"sitemap\",\n fix: \"A sitemap may only list URLs on its own origin.\",\n id: \"BLUME_AUDIT_SITEMAP_OUT_OF_SCOPE\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Sitemap includes URLs out of its scope\",\n },\n {\n category: \"sitemap\",\n fix: \"Make sitemap.xml reachable at the site root.\",\n id: \"BLUME_AUDIT_SITEMAP_NOT_ACCESSIBLE\",\n severity: \"error\",\n tier: \"network\",\n title: \"Sitemap is not accessible\",\n },\n\n // robots.txt\n {\n category: \"robots\",\n fix: \"Set `seo.robots: true` to generate robots.txt.\",\n id: \"BLUME_AUDIT_ROBOTS_MISSING\",\n severity: \"warning\",\n tier: \"static\",\n title: \"robots.txt missing\",\n },\n {\n category: \"robots\",\n fix: \"Every robots.txt line must be a `Field: value` directive or a comment.\",\n id: \"BLUME_AUDIT_ROBOTS_INVALID\",\n severity: \"error\",\n tier: \"static\",\n title: \"robots.txt has a syntax error\",\n },\n {\n category: \"robots\",\n fix: \"A page can't be both disallowed in robots.txt and advertised in the sitemap.\",\n id: \"BLUME_AUDIT_ROBOTS_DISALLOWS_INDEXABLE\",\n severity: \"error\",\n tier: \"static\",\n title: \"robots.txt disallows a page that is in the sitemap\",\n },\n {\n category: \"robots\",\n fix: \"Set `deployment.site` so robots.txt can reference the sitemap.\",\n id: \"BLUME_AUDIT_ROBOTS_SITEMAP_MISSING\",\n severity: \"info\",\n tier: \"static\",\n title: \"robots.txt does not reference the sitemap\",\n },\n {\n category: \"robots\",\n fix: \"Make robots.txt reachable at the site root.\",\n id: \"BLUME_AUDIT_ROBOTS_NOT_ACCESSIBLE\",\n severity: \"error\",\n tier: \"network\",\n title: \"robots.txt is not accessible\",\n },\n\n // AI discovery. `llms.txt` is the sitemap of the AI era, and no SEO crawler\n // audits it — they audit for Google. Blume emits it, so Blume checks it.\n {\n category: \"ai\",\n fix: \"Rebuild — `agents.llmsTxt` is enabled but the build has no llms.txt. If that's intentional, set `agents.llmsTxt: false`.\",\n id: \"BLUME_AUDIT_LLMS_TXT_MISSING\",\n severity: \"warning\",\n tier: \"static\",\n title: \"llms.txt missing from the build\",\n },\n {\n category: \"ai\",\n fix: \"Rebuild so llms.txt matches the site — a stale entry sends an AI agent to a page that is not there.\",\n id: \"BLUME_AUDIT_LLMS_TXT_STALE_ENTRY\",\n severity: \"warning\",\n tier: \"static\",\n title: \"llms.txt lists a page the build does not serve\",\n },\n {\n category: \"ai\",\n fix: \"Rebuild so llms.txt matches the site; if the page is deliberately excluded, set `ai.exclude: true` in its front matter.\",\n id: \"BLUME_AUDIT_LLMS_TXT_PAGE_MISSING\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Indexable page missing from llms.txt\",\n },\n // DNS-AID (draft-mozleywilliams-dnsop-dnsaid) lives in the DNS zone, not the\n // build, so unlike the llms.txt checks these can only observe and advise —\n // hence `info`: publishing the records is a DNS-provider action Blume can't\n // take for you, and the draft is young enough that absence is the norm.\n {\n category: \"ai\",\n fix: \"Publish a ServiceMode SVCB or HTTPS record at `_index._agents.<host>` with your DNS provider so agents can discover the site via DNS (DNS-AID).\",\n id: \"BLUME_AUDIT_DNS_AID_MISSING\",\n severity: \"info\",\n tier: \"network\",\n title: \"No DNS-AID agent-discovery records\",\n },\n {\n category: \"ai\",\n fix: \"Enable DNSSEC for the zone so validating resolvers return authenticated DNS-AID answers; if your DNS provider doesn't support DNSSEC, the records still work unsigned.\",\n id: \"BLUME_AUDIT_DNS_AID_UNSIGNED\",\n severity: \"info\",\n tier: \"network\",\n title: \"DNS-AID records are not DNSSEC-authenticated\",\n },\n\n // Structured data. Note we validate only what Blume itself emits — we do not\n // claim Google-rich-results or full schema.org validation (the former is an\n // undocumented network API, the latter a vocabulary we don't bundle).\n {\n category: \"structured-data\",\n fix: \"The JSON-LD block must be valid JSON.\",\n id: \"BLUME_AUDIT_JSONLD_INVALID\",\n severity: \"error\",\n tier: \"static\",\n title: \"Structured data is not valid JSON\",\n },\n {\n category: \"structured-data\",\n fix: \"Every JSON-LD node needs `@context` and `@type`.\",\n id: \"BLUME_AUDIT_JSONLD_INCOMPLETE\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Structured data is missing required properties\",\n },\n\n // Network (`--url`)\n {\n category: \"network\",\n fix: \"The page is linked or in the sitemap but the deployment 404s it.\",\n id: \"BLUME_AUDIT_HTTP_4XX\",\n severity: \"error\",\n tier: \"network\",\n title: \"4XX page\",\n },\n {\n category: \"network\",\n fix: \"The deployment is erroring on this page.\",\n id: \"BLUME_AUDIT_HTTP_5XX\",\n severity: \"error\",\n tier: \"network\",\n title: \"5XX page\",\n },\n {\n category: \"network\",\n fix: \"The page did not respond in time.\",\n id: \"BLUME_AUDIT_HTTP_TIMEOUT\",\n severity: \"error\",\n tier: \"network\",\n title: \"Timed out\",\n },\n {\n category: \"network\",\n fix: \"Enable gzip or brotli on the host.\",\n id: \"BLUME_AUDIT_NOT_COMPRESSED\",\n severity: \"warning\",\n tier: \"network\",\n title: \"Not compressed\",\n },\n {\n category: \"network\",\n fix: \"The page was slow to respond.\",\n id: \"BLUME_AUDIT_SLOW_RESPONSE\",\n severity: \"warning\",\n tier: \"network\",\n title: \"Slow page\",\n },\n\n // External (`--external`)\n {\n category: \"network\",\n fix: \"Fix or remove the outbound link.\",\n id: \"BLUME_AUDIT_EXTERNAL_LINK_BROKEN\",\n severity: \"error\",\n tier: \"external\",\n title: \"External link is broken\",\n },\n {\n category: \"network\",\n fix: \"Link straight to the destination.\",\n id: \"BLUME_AUDIT_EXTERNAL_LINK_REDIRECT\",\n severity: \"info\",\n tier: \"external\",\n title: \"External link redirects\",\n },\n] as const satisfies readonly CheckMeta[];\n\nexport type CheckId = (typeof CHECKS)[number][\"id\"];\n\nconst BY_ID = new Map<string, CheckMeta>(\n CHECKS.map((check) => [check.id, check])\n);\n\nexport const checkMeta = (id: CheckId): CheckMeta => {\n const meta = BY_ID.get(id);\n if (!meta) {\n throw new Error(`Unknown audit check: ${id}`);\n }\n return meta;\n};\n\nconst DOCS_BASE = \"https://useblume.dev/docs/cli/audit\";\n\n/** `BLUME_AUDIT_TITLE_TOO_LONG` -> `…/audit#title-too-long`. */\nexport const checkDocsUrl = (id: CheckId): string =>\n `${DOCS_BASE}#${id.replace(\"BLUME_AUDIT_\", \"\").toLowerCase().replaceAll(\"_\", \"-\")}`;\n\n/** Where a finding happened: the built URL, plus the source file that fixes it. */\nexport interface FindingSite {\n url: string;\n file?: string;\n line?: number;\n column?: number;\n}\n\n/**\n * Build a diagnostic for a check. Severity, remediation, and the docs anchor all\n * come from the catalog, so a check body only supplies what's specific to the\n * occurrence: where it happened and what was actually wrong.\n */\nexport const finding = (\n id: CheckId,\n site: FindingSite,\n detail: string,\n fix?: string\n): Diagnostic => {\n const meta = checkMeta(id);\n return {\n code: id,\n column: site.column,\n docsUrl: checkDocsUrl(id),\n file: site.file,\n line: site.line,\n message: detail,\n severity: meta.severity,\n suggestion: fix ?? meta.fix,\n url: site.url,\n };\n};\n",
6
+ "import type { Diagnostic } from \"../core/types.ts\";\nimport type { CheckMeta } from \"./types.ts\";\n\n/**\n * Every check `blume audit` can report.\n *\n * This is deliberately ~76 checks, not the ~173 rows an Ahrefs site audit\n * lists. Ahrefs crawls the open web, so its list is padded with rows that are\n * structurally impossible for an Astro-built Blume site, and shipping those as\n * permanent zeroes teaches people to ignore the report. What was dropped, and\n * why:\n *\n * - **Every `nofollow` check** (~6 rows, including all four \"nofollow incoming\n * internal links\" variants). `RootLayout.astro` only ever emits `<meta\n * name=\"robots\" content=\"noindex\">`; Blume has no way to emit `nofollow`, so\n * they could never fire. Hand-written `rel=\"nofollow\"` in MDX is still caught\n * by `INTERNAL_LINK_NOFOLLOW`.\n * - **The whole JavaScript + CSS section** (~11 rows: \"JS redirects\", \"CSS\n * redirects\", \"page has redirected JS/CSS\", \"HTTPS page links to HTTP\n * JS/CSS\"…). Vite emits content-hashed, existent, non-redirecting bundles.\n * Collapsed into one `SUBRESOURCE_MISSING`.\n * - **Ahrefs' indexable/non-indexable duplicates** (~20 rows). Carried instead\n * by `PageSnapshot.indexable` on a single finding.\n * - **\"Only one dofollow incoming internal link\"** and **\"page has no outgoing\n * links\"**. Blume's sidebar links every page from every page, so these fire on\n * ~100% of pages. Pure noise.\n * - **\"3XX redirect\"** and **\"302 redirect\"**: having a redirect is inventory,\n * not a finding. **\"HTTP to HTTPS redirect\"** is correct behavior.\n * - **\"Font size too small\" / \"tap targets too close\" / \"content not sized\n * correctly\"**: properties of the theme, identical on every page. A regression\n * there is a Blume bug, not a user finding.\n * - **\"Document uses plugins\"** and **\"poor FID\"** (FID is deprecated; lab\n * Lighthouse cannot measure INP, so we report TBT and say so).\n * - **\"More than three parameters in URL\"**: a static docs site emits no query\n * strings in its own links.\n * - **\"Page in multiple sitemaps\"**: Blume emits exactly one sitemap.\n * - **Core Web Vitals** (LCP/CLS/INP): these need a real browser. Rather than\n * ship a `--lighthouse` flag that silently reports nothing, they are deferred\n * until Lighthouse is actually wired in. A tier the audit cannot run is worse\n * than one it openly doesn't have.\n *\n * Severity discipline: `error` means \"this is definitely broken\". Anything\n * advisory ships as `warning`/`info`. One noisy check and the whole command gets\n * switched off.\n */\nexport const CHECKS = [\n // Content\n {\n category: \"content\",\n fix: \"Add a `title` to the page's frontmatter.\",\n id: \"BLUME_AUDIT_TITLE_MISSING\",\n severity: \"error\",\n tier: \"static\",\n title: \"Title tag missing or empty\",\n },\n {\n category: \"content\",\n fix: \"Remove the extra <title> from the page's layout or MDX.\",\n id: \"BLUME_AUDIT_TITLE_MULTIPLE\",\n severity: \"error\",\n tier: \"static\",\n title: \"Multiple title tags\",\n },\n {\n category: \"content\",\n fix: \"Rewrite `title` in the frontmatter to fit the length range.\",\n id: \"BLUME_AUDIT_TITLE_LENGTH\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Title too long or too short\",\n },\n {\n category: \"content\",\n fix: \"Add a `description` to the page's frontmatter.\",\n id: \"BLUME_AUDIT_DESCRIPTION_MISSING\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Meta description missing or empty\",\n },\n {\n category: \"content\",\n fix: \"Remove the extra description <meta> from the page's layout or MDX.\",\n id: \"BLUME_AUDIT_DESCRIPTION_MULTIPLE\",\n severity: \"error\",\n tier: \"static\",\n title: \"Multiple meta description tags\",\n },\n {\n category: \"content\",\n fix: \"Rewrite `description` in the frontmatter to fit the length range.\",\n id: \"BLUME_AUDIT_DESCRIPTION_LENGTH\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Meta description too long or too short\",\n },\n {\n category: \"content\",\n fix: \"Give the page a `title` — Blume renders it as the page's <h1>.\",\n id: \"BLUME_AUDIT_H1_MISSING\",\n severity: \"warning\",\n tier: \"static\",\n title: \"H1 tag missing or empty\",\n },\n {\n category: \"content\",\n fix: \"Demote the extra `# Heading` in the body to `##` — Blume already renders `title` as the h1.\",\n id: \"BLUME_AUDIT_H1_MULTIPLE\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Multiple H1 tags\",\n },\n {\n category: \"content\",\n fix: \"Expand the page, or fold it into a larger one.\",\n id: \"BLUME_AUDIT_LOW_WORD_COUNT\",\n severity: \"info\",\n tier: \"static\",\n title: \"Low word count\",\n },\n {\n category: \"content\",\n fix: \"Adjust the heading to the next level down — skipped levels break table-of-contents nesting and screen-reader outlines.\",\n id: \"BLUME_AUDIT_HEADING_SKIP\",\n severity: \"info\",\n tier: \"static\",\n title: \"Heading levels skip (e.g. h2 to h4)\",\n },\n {\n category: \"content\",\n fix: \"Correct the `date`, or hold the page back until it is meant to be live.\",\n id: \"BLUME_AUDIT_FUTURE_DATED_PAGE\",\n severity: \"info\",\n tier: \"static\",\n title: \"Page is dated in the future\",\n },\n {\n category: \"content\",\n fix: \"Restore the viewport <meta> in your ejected layout.\",\n id: \"BLUME_AUDIT_VIEWPORT_MISSING\",\n severity: \"error\",\n tier: \"static\",\n title: \"Viewport not set\",\n },\n\n // Duplicates\n {\n category: \"duplicates\",\n fix: \"Give each page a distinct `title` — search engines show it as the result headline.\",\n id: \"BLUME_AUDIT_DUPLICATE_TITLE\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Duplicate title\",\n },\n {\n category: \"duplicates\",\n fix: \"Give each page a distinct `description`.\",\n id: \"BLUME_AUDIT_DUPLICATE_DESCRIPTION\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Duplicate meta description\",\n },\n {\n category: \"duplicates\",\n fix: \"Merge the pages, or set `seo.canonical` on all but one.\",\n id: \"BLUME_AUDIT_DUPLICATE_CONTENT\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Duplicate pages without canonical\",\n },\n\n // Indexability\n {\n category: \"indexability\",\n fix: \"Set `deployment.site` in blume.config.ts to the site's public URL.\",\n id: \"BLUME_AUDIT_SITE_NOT_SET\",\n severity: \"warning\",\n tier: \"static\",\n title: \"deployment.site is not set\",\n },\n {\n category: \"indexability\",\n fix: \"Audit a production-like build (e.g. `VERCEL=1 VERCEL_PROJECT_PRODUCTION_URL=<host> blume build`) or the deployment itself with `--url <origin>`. Do not hardcode `deployment.site` — the platform sets it on every deploy.\",\n id: \"BLUME_AUDIT_SITE_INFERRED_AT_DEPLOY\",\n severity: \"info\",\n tier: \"static\",\n title: \"deployment.site is inferred at deploy time\",\n },\n {\n category: \"indexability\",\n fix: \"Drop the canonical from noindex pages — Google treats the pairing as contradictory and may ignore one of the two.\",\n id: \"BLUME_AUDIT_CANONICAL_ON_NOINDEX\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Page is noindex but declares a canonical\",\n },\n {\n category: \"indexability\",\n fix: \"Rebuild without `--preview` before deploying, or remove `draft: true` if the page is ready to ship.\",\n id: \"BLUME_AUDIT_DRAFT_PAGE_PUBLISHED\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Draft page is in the build\",\n },\n {\n category: \"indexability\",\n fix: \"Set `deployment.site` so Blume can emit absolute canonical URLs.\",\n id: \"BLUME_AUDIT_CANONICAL_MISSING\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Canonical URL missing\",\n },\n {\n category: \"indexability\",\n fix: \"Point `seo.canonical` at this page, or remove it to use the default self-canonical.\",\n id: \"BLUME_AUDIT_CANONICAL_NOT_SELF\",\n severity: \"info\",\n tier: \"static\",\n title: \"Non-canonical page\",\n },\n {\n category: \"indexability\",\n fix: \"Point `seo.canonical` at a page that exists and doesn't redirect.\",\n id: \"BLUME_AUDIT_CANONICAL_BAD_TARGET\",\n severity: \"error\",\n tier: \"static\",\n title: \"Canonical points to a broken or redirecting page\",\n },\n {\n category: \"indexability\",\n fix: \"Use the same protocol in `seo.canonical` as in `deployment.site`.\",\n id: \"BLUME_AUDIT_CANONICAL_PROTOCOL_MISMATCH\",\n severity: \"error\",\n tier: \"static\",\n title: \"Canonical protocol does not match the site\",\n },\n {\n category: \"indexability\",\n fix: \"Remove `noindex` from the page's frontmatter if it should be indexed.\",\n id: \"BLUME_AUDIT_ROBOTS_META_UNEXPECTED\",\n severity: \"info\",\n tier: \"static\",\n title: \"Page is not indexable\",\n },\n {\n category: \"indexability\",\n fix: \"Split the page — Googlebot stops reading an HTML document at 2 MB.\",\n id: \"BLUME_AUDIT_HTML_TOO_LARGE\",\n severity: \"error\",\n tier: \"static\",\n title: \"Page exceeds Googlebot's 2 MB crawl limit\",\n },\n {\n category: \"indexability\",\n fix: \"Remove the X-Robots-Tag header, or align it with the page's robots meta.\",\n id: \"BLUME_AUDIT_ROBOTS_HEADER_CONFLICT\",\n severity: \"error\",\n tier: \"network\",\n title: \"X-Robots-Tag header conflicts with the page's robots meta\",\n },\n\n // Links\n {\n category: \"links\",\n fix: \"Fix the link target, or create the page it points at.\",\n id: \"BLUME_AUDIT_LINK_TO_BROKEN\",\n severity: \"error\",\n tier: \"static\",\n title: \"Page has links to a broken page\",\n },\n {\n category: \"links\",\n fix: \"Link straight to the destination instead of through the redirect.\",\n id: \"BLUME_AUDIT_LINK_TO_REDIRECT\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Page has links to a redirect\",\n },\n {\n category: \"links\",\n fix: \"Link to this page from the body of a related page.\",\n id: \"BLUME_AUDIT_ORPHAN_PAGE\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Orphan page (only reachable from navigation)\",\n },\n {\n category: \"links\",\n fix: \"Use a root-relative path (`/docs/x`) so the link survives previews and `basePath`.\",\n id: \"BLUME_AUDIT_INTERNAL_LINK_ABSOLUTE\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Internal link hardcodes the site's own origin\",\n },\n {\n category: \"links\",\n fix: 'Drop `rel=\"nofollow\"` — it stops this page passing authority internally.',\n id: \"BLUME_AUDIT_INTERNAL_LINK_NOFOLLOW\",\n severity: \"info\",\n tier: \"static\",\n title: \"Page has nofollow outgoing internal links\",\n },\n {\n category: \"links\",\n fix: \"Check `basePath` / `deployment.base` — a trailing slash there produces `//`.\",\n id: \"BLUME_AUDIT_DOUBLE_SLASH_URL\",\n severity: \"error\",\n tier: \"static\",\n title: \"Double slash in URL\",\n },\n {\n category: \"links\",\n fix: \"Point the fragment at a heading that exists on the target page, or fix the heading it meant.\",\n id: \"BLUME_AUDIT_ANCHOR_BROKEN\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Link fragment matches no id on the target page\",\n },\n {\n category: \"links\",\n fix: \"Rename the source file to a lowercase, hyphenated slug — and add a redirect from the old URL if it was already published.\",\n id: \"BLUME_AUDIT_URL_STYLE\",\n severity: \"info\",\n tier: \"static\",\n title: \"URL contains uppercase, underscores, or spaces\",\n },\n\n // Redirects\n {\n category: \"redirects\",\n fix: \"Point the redirect at a page that exists.\",\n id: \"BLUME_AUDIT_REDIRECT_BROKEN\",\n severity: \"error\",\n tier: \"static\",\n title: \"Broken redirect\",\n },\n {\n category: \"redirects\",\n fix: \"Break the cycle in `redirects` — it never resolves.\",\n id: \"BLUME_AUDIT_REDIRECT_LOOP\",\n severity: \"error\",\n tier: \"static\",\n title: \"Redirect loop\",\n },\n {\n category: \"redirects\",\n fix: \"Point every hop straight at the final destination.\",\n id: \"BLUME_AUDIT_REDIRECT_CHAIN\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Redirect chain\",\n },\n {\n category: \"redirects\",\n fix: \"Use a real redirect in `blume.config.ts` instead of a meta refresh.\",\n id: \"BLUME_AUDIT_META_REFRESH\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Meta refresh redirect\",\n },\n {\n category: \"redirects\",\n fix: \"Remove the redirect, or delete the page it shadows — the page wins and the redirect never fires.\",\n id: \"BLUME_AUDIT_REDIRECT_SOURCE_IS_PAGE\",\n severity: \"error\",\n tier: \"static\",\n title: \"Redirect source is also a real page\",\n },\n {\n category: \"redirects\",\n fix: \"Redirect to the HTTPS URL.\",\n id: \"BLUME_AUDIT_REDIRECT_TO_HTTP\",\n severity: \"error\",\n tier: \"network\",\n title: \"HTTPS to HTTP redirect\",\n },\n\n // Social\n {\n category: \"social\",\n fix: \"Add a `description` — Blume fills the rest of the Open Graph tags for you.\",\n id: \"BLUME_AUDIT_OG_INCOMPLETE\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Open Graph tags missing or incomplete\",\n },\n {\n category: \"social\",\n fix: \"Set `deployment.site` to turn on generated OG images, or set `seo.image` on the page.\",\n id: \"BLUME_AUDIT_OG_IMAGE_MISSING\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Open Graph image missing\",\n },\n {\n category: \"social\",\n fix: \"Point `seo.image` at a file that exists, or rebuild — a dead og:image renders as a blank card everywhere the page is shared.\",\n id: \"BLUME_AUDIT_OG_IMAGE_BROKEN\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Open Graph image is not in the build\",\n },\n {\n category: \"social\",\n fix: \"Use an image of at least 1200×630 — smaller ones render blurry or get cropped into small-card layouts.\",\n id: \"BLUME_AUDIT_OG_IMAGE_SMALL\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Open Graph image is too small for large cards\",\n },\n {\n category: \"social\",\n fix: \"Align `og:url` with the page's canonical URL.\",\n id: \"BLUME_AUDIT_OG_URL_MISMATCH\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Open Graph URL not matching canonical\",\n },\n {\n category: \"social\",\n fix: \"Set `seo.x.handle` in blume.config.ts so X can attribute the card.\",\n id: \"BLUME_AUDIT_TWITTER_CARD_INCOMPLETE\",\n severity: \"warning\",\n tier: \"static\",\n title: \"X (Twitter) card missing or incomplete\",\n },\n\n // Localization\n {\n category: \"i18n\",\n fix: \"Restore the `lang` attribute on <html> in your ejected layout.\",\n id: \"BLUME_AUDIT_HTML_LANG_MISSING\",\n severity: \"error\",\n tier: \"static\",\n title: \"HTML lang attribute missing\",\n },\n {\n category: \"i18n\",\n fix: \"Use a valid BCP 47 tag (e.g. `en`, `en-GB`) for the locale.\",\n id: \"BLUME_AUDIT_HTML_LANG_INVALID\",\n severity: \"error\",\n tier: \"static\",\n title: \"HTML lang attribute invalid\",\n },\n {\n category: \"i18n\",\n fix: \"The page's <html lang> must match its own hreflang annotation.\",\n id: \"BLUME_AUDIT_HREFLANG_LANG_MISMATCH\",\n severity: \"error\",\n tier: \"static\",\n title: \"Hreflang and HTML lang mismatch\",\n },\n {\n category: \"i18n\",\n fix: \"Use a valid BCP 47 tag in the hreflang annotation.\",\n id: \"BLUME_AUDIT_HREFLANG_INVALID\",\n severity: \"error\",\n tier: \"static\",\n title: \"Hreflang annotation invalid\",\n },\n {\n category: \"i18n\",\n fix: \"A page's hreflang set must include a self-reference.\",\n id: \"BLUME_AUDIT_HREFLANG_SELF_MISSING\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Self-reference hreflang annotation missing\",\n },\n {\n category: \"i18n\",\n fix: \"Add an `x-default` alternate pointing at the default-locale page.\",\n id: \"BLUME_AUDIT_HREFLANG_XDEFAULT_MISSING\",\n severity: \"info\",\n tier: \"static\",\n title: \"X-default hreflang annotation missing\",\n },\n {\n category: \"i18n\",\n fix: \"Every page in an hreflang group must link back to every other one.\",\n id: \"BLUME_AUDIT_HREFLANG_NO_RETURN_TAG\",\n severity: \"error\",\n tier: \"static\",\n title: \"Missing reciprocal hreflang (no return-tag)\",\n },\n {\n category: \"i18n\",\n fix: \"Point the hreflang alternate at a page that exists and is canonical.\",\n id: \"BLUME_AUDIT_HREFLANG_BAD_TARGET\",\n severity: \"error\",\n tier: \"static\",\n title: \"Hreflang points to a broken, redirecting, or non-canonical page\",\n },\n {\n category: \"i18n\",\n fix: \"Each language in an hreflang group must name exactly one page.\",\n id: \"BLUME_AUDIT_HREFLANG_CONFLICT\",\n severity: \"error\",\n tier: \"static\",\n title: \"Hreflang group has a language conflict\",\n },\n\n // Accessibility. Theme colors are one per site, not per page: these run\n // on the config, the way the build resolves it.\n {\n category: \"accessibility\",\n fix: \"Pick a darker shade for light mode or a lighter one for dark mode, per mode with `theme.accent: { light, dark }`.\",\n id: \"BLUME_AUDIT_THEME_CONTRAST_LOW\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Theme colors miss WCAG AA contrast\",\n },\n {\n category: \"accessibility\",\n fix: \"Write the color as hex, `rgb()`, `hsl()`, `hwb()`, `lab()`, `lch()`, `oklab()`, `oklch()`, or a named color so its contrast can be checked.\",\n id: \"BLUME_AUDIT_THEME_COLOR_UNCHECKED\",\n severity: \"info\",\n tier: \"static\",\n title: \"Theme color can't be checked for contrast\",\n },\n\n // Assets\n {\n category: \"assets\",\n fix: 'Add descriptive `alt` text, or `alt=\"\"` if the image is decorative.',\n id: \"BLUME_AUDIT_IMAGE_ALT_MISSING\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Missing alt text\",\n },\n {\n category: \"assets\",\n fix: \"Fix the image path, or add the file to public/.\",\n id: \"BLUME_AUDIT_IMAGE_BROKEN\",\n severity: \"error\",\n tier: \"static\",\n title: \"Image broken\",\n },\n {\n category: \"assets\",\n fix: \"Compress the asset, or serve a modern format (WebP/AVIF).\",\n id: \"BLUME_AUDIT_ASSET_TOO_LARGE\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Asset file size too large\",\n },\n {\n category: \"assets\",\n fix: \"Set `width` and `height` so the browser can reserve space (avoids layout shift).\",\n id: \"BLUME_AUDIT_IMAGE_MISSING_DIMENSIONS\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Image has no width/height\",\n },\n {\n category: \"assets\",\n fix: \"Fix the reference, or restore the missing file.\",\n id: \"BLUME_AUDIT_SUBRESOURCE_MISSING\",\n severity: \"error\",\n tier: \"static\",\n title: \"Referenced script, style, or asset is missing from the build\",\n },\n {\n category: \"assets\",\n fix: \"Load the subresource over HTTPS — browsers block mixed content.\",\n id: \"BLUME_AUDIT_MIXED_CONTENT\",\n severity: \"error\",\n tier: \"static\",\n title: \"HTTPS/HTTP mixed content\",\n },\n\n // Sitemap\n {\n category: \"sitemap\",\n fix: \"Remove `draft`/`hidden`/`noindex` from the page's frontmatter if it should be indexed.\",\n id: \"BLUME_AUDIT_INDEXABLE_PAGE_NOT_IN_SITEMAP\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Indexable page not in sitemap\",\n },\n {\n category: \"sitemap\",\n fix: \"A noindex page should not be advertised in the sitemap.\",\n id: \"BLUME_AUDIT_NOINDEX_IN_SITEMAP\",\n severity: \"error\",\n tier: \"static\",\n title: \"Noindex page in sitemap\",\n },\n {\n category: \"sitemap\",\n fix: \"List only canonical URLs in the sitemap.\",\n id: \"BLUME_AUDIT_NON_CANONICAL_IN_SITEMAP\",\n severity: \"error\",\n tier: \"static\",\n title: \"Non-canonical page in sitemap\",\n },\n {\n category: \"sitemap\",\n fix: \"Remove the URL from the sitemap, or build the page it names.\",\n id: \"BLUME_AUDIT_SITEMAP_BAD_URL\",\n severity: \"error\",\n tier: \"static\",\n title: \"Sitemap names a page that does not exist or redirects\",\n },\n {\n category: \"sitemap\",\n fix: \"Sitemaps must be valid XML in the sitemaps.org urlset or sitemap index format.\",\n id: \"BLUME_AUDIT_SITEMAP_INVALID\",\n severity: \"error\",\n tier: \"static\",\n title: \"Sitemap has a syntax error or wrong format\",\n },\n {\n category: \"sitemap\",\n fix: \"Split the sitemap — the limits are 50 MB and 50,000 URLs.\",\n id: \"BLUME_AUDIT_SITEMAP_TOO_LARGE\",\n severity: \"error\",\n tier: \"static\",\n title: \"Sitemap exceeds 50 MB or 50,000 URLs\",\n },\n {\n category: \"sitemap\",\n fix: \"Use a real W3C date that is not in the future — search engines that catch a sitemap lying about freshness stop trusting its lastmod entirely.\",\n id: \"BLUME_AUDIT_SITEMAP_LASTMOD_INVALID\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Sitemap lastmod is invalid or in the future\",\n },\n {\n category: \"sitemap\",\n fix: \"A sitemap may only list URLs on its own origin.\",\n id: \"BLUME_AUDIT_SITEMAP_OUT_OF_SCOPE\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Sitemap includes URLs out of its scope\",\n },\n {\n category: \"sitemap\",\n fix: \"Make sitemap.xml reachable at the site root.\",\n id: \"BLUME_AUDIT_SITEMAP_NOT_ACCESSIBLE\",\n severity: \"error\",\n tier: \"network\",\n title: \"Sitemap is not accessible\",\n },\n\n // robots.txt\n {\n category: \"robots\",\n fix: \"Set `seo.robots: true` to generate robots.txt.\",\n id: \"BLUME_AUDIT_ROBOTS_MISSING\",\n severity: \"warning\",\n tier: \"static\",\n title: \"robots.txt missing\",\n },\n {\n category: \"robots\",\n fix: \"Every robots.txt line must be a `Field: value` directive or a comment.\",\n id: \"BLUME_AUDIT_ROBOTS_INVALID\",\n severity: \"error\",\n tier: \"static\",\n title: \"robots.txt has a syntax error\",\n },\n {\n category: \"robots\",\n fix: \"A page can't be both disallowed in robots.txt and advertised in the sitemap.\",\n id: \"BLUME_AUDIT_ROBOTS_DISALLOWS_INDEXABLE\",\n severity: \"error\",\n tier: \"static\",\n title: \"robots.txt disallows a page that is in the sitemap\",\n },\n {\n category: \"robots\",\n fix: \"Set `deployment.site` so robots.txt can reference the sitemap.\",\n id: \"BLUME_AUDIT_ROBOTS_SITEMAP_MISSING\",\n severity: \"info\",\n tier: \"static\",\n title: \"robots.txt does not reference the sitemap\",\n },\n {\n category: \"robots\",\n fix: \"Make robots.txt reachable at the site root.\",\n id: \"BLUME_AUDIT_ROBOTS_NOT_ACCESSIBLE\",\n severity: \"error\",\n tier: \"network\",\n title: \"robots.txt is not accessible\",\n },\n\n // AI discovery. `llms.txt` is the sitemap of the AI era, and no SEO crawler\n // audits it — they audit for Google. Blume emits it, so Blume checks it.\n {\n category: \"ai\",\n fix: \"Rebuild — `agents.llmsTxt` is enabled but the build has no llms.txt. If that's intentional, set `agents.llmsTxt: false`.\",\n id: \"BLUME_AUDIT_LLMS_TXT_MISSING\",\n severity: \"warning\",\n tier: \"static\",\n title: \"llms.txt missing from the build\",\n },\n {\n category: \"ai\",\n fix: \"Rebuild so llms.txt matches the site — a stale entry sends an AI agent to a page that is not there.\",\n id: \"BLUME_AUDIT_LLMS_TXT_STALE_ENTRY\",\n severity: \"warning\",\n tier: \"static\",\n title: \"llms.txt lists a page the build does not serve\",\n },\n {\n category: \"ai\",\n fix: \"Rebuild so llms.txt matches the site; if the page is deliberately excluded, set `ai.exclude: true` in its front matter.\",\n id: \"BLUME_AUDIT_LLMS_TXT_PAGE_MISSING\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Indexable page missing from llms.txt\",\n },\n // DNS-AID (draft-mozleywilliams-dnsop-dnsaid) lives in the DNS zone, not the\n // build, so unlike the llms.txt checks these can only observe and advise —\n // hence `info`: publishing the records is a DNS-provider action Blume can't\n // take for you, and the draft is young enough that absence is the norm.\n {\n category: \"ai\",\n fix: \"Publish a ServiceMode SVCB or HTTPS record at `_index._agents.<host>` with your DNS provider so agents can discover the site via DNS (DNS-AID).\",\n id: \"BLUME_AUDIT_DNS_AID_MISSING\",\n severity: \"info\",\n tier: \"network\",\n title: \"No DNS-AID agent-discovery records\",\n },\n {\n category: \"ai\",\n fix: \"Enable DNSSEC for the zone so validating resolvers return authenticated DNS-AID answers; if your DNS provider doesn't support DNSSEC, the records still work unsigned.\",\n id: \"BLUME_AUDIT_DNS_AID_UNSIGNED\",\n severity: \"info\",\n tier: \"network\",\n title: \"DNS-AID records are not DNSSEC-authenticated\",\n },\n\n // Structured data. Note we validate only what Blume itself emits — we do not\n // claim Google-rich-results or full schema.org validation (the former is an\n // undocumented network API, the latter a vocabulary we don't bundle).\n {\n category: \"structured-data\",\n fix: \"The JSON-LD block must be valid JSON.\",\n id: \"BLUME_AUDIT_JSONLD_INVALID\",\n severity: \"error\",\n tier: \"static\",\n title: \"Structured data is not valid JSON\",\n },\n {\n category: \"structured-data\",\n fix: \"Every JSON-LD node needs `@context` and `@type`.\",\n id: \"BLUME_AUDIT_JSONLD_INCOMPLETE\",\n severity: \"warning\",\n tier: \"static\",\n title: \"Structured data is missing required properties\",\n },\n\n // Network (`--url`)\n {\n category: \"network\",\n fix: \"The page is linked or in the sitemap but the deployment 404s it.\",\n id: \"BLUME_AUDIT_HTTP_4XX\",\n severity: \"error\",\n tier: \"network\",\n title: \"4XX page\",\n },\n {\n category: \"network\",\n fix: \"The deployment is erroring on this page.\",\n id: \"BLUME_AUDIT_HTTP_5XX\",\n severity: \"error\",\n tier: \"network\",\n title: \"5XX page\",\n },\n {\n category: \"network\",\n fix: \"The page did not respond in time.\",\n id: \"BLUME_AUDIT_HTTP_TIMEOUT\",\n severity: \"error\",\n tier: \"network\",\n title: \"Timed out\",\n },\n {\n category: \"network\",\n fix: \"Enable gzip or brotli on the host.\",\n id: \"BLUME_AUDIT_NOT_COMPRESSED\",\n severity: \"warning\",\n tier: \"network\",\n title: \"Not compressed\",\n },\n {\n category: \"network\",\n fix: \"The page was slow to respond.\",\n id: \"BLUME_AUDIT_SLOW_RESPONSE\",\n severity: \"warning\",\n tier: \"network\",\n title: \"Slow page\",\n },\n\n // External (`--external`)\n {\n category: \"network\",\n fix: \"Fix or remove the outbound link.\",\n id: \"BLUME_AUDIT_EXTERNAL_LINK_BROKEN\",\n severity: \"error\",\n tier: \"external\",\n title: \"External link is broken\",\n },\n {\n category: \"network\",\n fix: \"Link straight to the destination.\",\n id: \"BLUME_AUDIT_EXTERNAL_LINK_REDIRECT\",\n severity: \"info\",\n tier: \"external\",\n title: \"External link redirects\",\n },\n] as const satisfies readonly CheckMeta[];\n\nexport type CheckId = (typeof CHECKS)[number][\"id\"];\n\nconst BY_ID = new Map<string, CheckMeta>(\n CHECKS.map((check) => [check.id, check])\n);\n\nexport const checkMeta = (id: CheckId): CheckMeta => {\n const meta = BY_ID.get(id);\n if (!meta) {\n throw new Error(`Unknown audit check: ${id}`);\n }\n return meta;\n};\n\nconst DOCS_BASE = \"https://useblume.dev/docs/cli/audit\";\n\n/** `BLUME_AUDIT_TITLE_TOO_LONG` -> `…/audit#title-too-long`. */\nexport const checkDocsUrl = (id: CheckId): string =>\n `${DOCS_BASE}#${id.replace(\"BLUME_AUDIT_\", \"\").toLowerCase().replaceAll(\"_\", \"-\")}`;\n\n/** Where a finding happened: the built URL, plus the source file that fixes it. */\nexport interface FindingSite {\n url: string;\n file?: string;\n line?: number;\n column?: number;\n}\n\n/**\n * Build a diagnostic for a check. Severity, remediation, and the docs anchor all\n * come from the catalog, so a check body only supplies what's specific to the\n * occurrence: where it happened and what was actually wrong.\n */\nexport const finding = (\n id: CheckId,\n site: FindingSite,\n detail: string,\n fix?: string\n): Diagnostic => {\n const meta = checkMeta(id);\n return {\n code: id,\n column: site.column,\n docsUrl: checkDocsUrl(id),\n file: site.file,\n line: site.line,\n message: detail,\n severity: meta.severity,\n suggestion: fix ?? meta.fix,\n url: site.url,\n };\n};\n",
7
7
  "import { mkdtemp, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\n\nimport spawn from \"cross-spawn\";\nimport { join } from \"pathe\";\n\nimport { reportJson } from \"./report.ts\";\nimport type { AuditResult } from \"./run.ts\";\n\n/** A coding agent CLI the audit can hand its findings to (`--claude`, `--codex`). */\nexport interface AgentCli {\n /** The executable to look up on PATH. */\n bin: string;\n /** How to install it, shown when the executable is missing. */\n install: string;\n /** Display name for messages. */\n name: string;\n}\n\nexport type AgentKind = \"claude\" | \"codex\";\n\nexport const AGENTS = {\n claude: {\n bin: \"claude\",\n install: \"npm install -g @anthropic-ai/claude-code\",\n name: \"Claude Code\",\n },\n codex: {\n bin: \"codex\",\n install: \"npm install -g @openai/codex\",\n name: \"Codex\",\n },\n} satisfies Record<AgentKind, AgentCli>;\n\n/**\n * Write the full JSON report where the agent can read it. A file rather than\n * inline prompt text: a large site's report can exceed the platform's argv\n * limit, and the JSON already carries every finding untruncated — the terminal\n * report previews three pages per check, the file never does.\n */\nexport const writeAgentReport = async (\n result: AuditResult,\n root: string\n): Promise<string> => {\n const dir = await mkdtemp(join(tmpdir(), \"blume-audit-\"));\n const path = join(dir, \"report.json\");\n await writeFile(path, reportJson(result, root));\n return path;\n};\n\n/** The handoff prompt: where the report is, how to read it, and the ground rules. */\nexport const fixPrompt = (reportPath: string): string =>\n `Fix the issues found by \\`blume audit\\` in this project.\n\nThe full audit report is at ${reportPath}. It is JSON: each entry in \\`diagnostics\\` is one finding, with the check \\`code\\`, a \\`message\\` explaining what is wrong, the affected page \\`url\\`, the source \\`file\\` to edit (relative to the current directory, with a \\`line\\` when the finding points at a specific front matter key), and a \\`suggestion\\` describing the fix.\n\nWork through every finding:\n1. Read the report and group the findings by \\`file\\`.\n2. Apply each finding's \\`suggestion\\` by editing the named source file — most fixes are front matter edits at the cited line.\n3. Never fix a finding by deleting a page, removing content, or hiding it from the audit; if a finding genuinely needs a human decision, leave it and say so in your summary.\n\nWhen you are done, run \\`blume build\\` and then \\`blume audit\\` to verify, and repeat until the audit reports no issues.`;\n\nconst spawnAgent = (bin: string, args: string[]): Promise<number> =>\n // oxlint-disable-next-line promise/avoid-new -- adapt spawn's event callbacks\n new Promise((resolve, reject) => {\n const child = spawn(bin, args, { stdio: \"inherit\" });\n child.once(\"error\", reject);\n child.once(\"close\", (code) => resolve(code ?? 1));\n });\n\n/**\n * Run the agent CLI interactively with the handoff prompt, inheriting the\n * terminal so the user watches and steers the fixes rather than granting a\n * headless process blanket write access. Resolves with the agent's exit code;\n * rejects when the executable isn't on PATH — on every platform, since\n * cross-spawn resolves npm's `.cmd` shims itself and escapes their arguments\n * (the old `shell: true` launch left quoting to a hand-built command line and\n * reported a missing executable as cmd.exe's exit code 9009).\n *\n * cmd.exe still cannot carry the multi-line prompt as an argument (a newline\n * ends the command), so on Windows the prompt is written to a file next to\n * the report and handed over via a one-line pointer.\n */\nexport const launchAgent = async (\n bin: string,\n prompt: string,\n platform: NodeJS.Platform = process.platform\n): Promise<number> => {\n if (platform !== \"win32\") {\n return await spawnAgent(bin, [prompt]);\n }\n const dir = await mkdtemp(join(tmpdir(), \"blume-audit-\"));\n const promptPath = join(dir, \"prompt.md\");\n await writeFile(promptPath, prompt);\n return await spawnAgent(bin, [\n `Read ${promptPath} and follow its instructions exactly.`,\n ]);\n};\n\n/**\n * {@link launchAgent}, but resolving `null` instead of rejecting when the\n * executable isn't on PATH, so each command can print its own install hint.\n * Only `ENOENT` means \"not installed\" — any other spawn failure (`EACCES`,\n * `EMFILE`, …) must surface as itself, not be masked by an irrelevant hint.\n */\nexport const launchInstalledAgent = async (\n bin: string,\n prompt: string,\n platform: NodeJS.Platform = process.platform\n): Promise<number | null> => {\n try {\n return await launchAgent(bin, prompt, platform);\n } catch (error) {\n // SAFETY: only the `code` tag is inspected; a spawn failure throws an\n // ErrnoException, and any other thrown value fails the comparison and\n // rethrows unchanged.\n if ((error as NodeJS.ErrnoException)?.code !== \"ENOENT\") {\n throw error;\n }\n return null;\n }\n};\n"
8
8
  ],
9
- "mappings": "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",
10
- "debugId": "E6F2D36C0552C7F664756E2164756E21",
9
+ "mappings": "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",
10
+ "debugId": "FF011B636C2DD0A664756E2164756E21",
11
11
  "names": []
12
12
  }
@@ -9,7 +9,7 @@ import {
9
9
  import {
10
10
  AGENTS,
11
11
  launchInstalledAgent
12
- } from "./chunk-6k4ftwze.js";
12
+ } from "./chunk-0gz36akr.js";
13
13
 
14
14
  // src/cli/commands/migrate.ts
15
15
  import { defineCommand } from "citty";
@@ -173,4 +173,4 @@ export {
173
173
  };
174
174
 
175
175
  //# debugId=A9196D6CE8B0C0B464756E2164756E21
176
- //# sourceMappingURL=chunk-zp79m0ts.js.map
176
+ //# sourceMappingURL=chunk-1n228xqb.js.map