blume 2.1.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (397) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/dist/cli/{chunk-5cg2rzgf.js → chunk-073rdvgb.js} +19 -48
  3. package/dist/cli/{chunk-5cg2rzgf.js.map → chunk-073rdvgb.js.map} +4 -5
  4. package/dist/cli/{chunk-ga0pf4aj.js → chunk-0n9xkp3d.js} +12 -3
  5. package/dist/cli/{chunk-ga0pf4aj.js.map → chunk-0n9xkp3d.js.map} +3 -3
  6. package/dist/cli/chunk-150jk7km.js +52 -0
  7. package/dist/cli/chunk-150jk7km.js.map +10 -0
  8. package/dist/cli/{chunk-rekqz4qg.js → chunk-313vnn40.js} +16 -15
  9. package/dist/cli/chunk-313vnn40.js.map +10 -0
  10. package/dist/cli/{chunk-gp84za0h.js → chunk-3v3ffbrj.js} +2 -2
  11. package/dist/cli/{chunk-9xr4dgee.js → chunk-3x6x3pph.js} +29 -16
  12. package/dist/cli/chunk-3x6x3pph.js.map +10 -0
  13. package/dist/cli/{chunk-0gz36akr.js → chunk-42t4jxdx.js} +21 -5
  14. package/dist/cli/chunk-42t4jxdx.js.map +12 -0
  15. package/dist/cli/{chunk-ta27feev.js → chunk-54ztw1nj.js} +21 -2
  16. package/dist/cli/chunk-54ztw1nj.js.map +10 -0
  17. package/dist/cli/{chunk-jnwj5kjj.js → chunk-5vjtgs6e.js} +20 -7
  18. package/dist/cli/{chunk-jnwj5kjj.js.map → chunk-5vjtgs6e.js.map} +3 -3
  19. package/dist/cli/chunk-66w9j7p6.js +322 -0
  20. package/dist/cli/chunk-66w9j7p6.js.map +14 -0
  21. package/dist/cli/{chunk-nd1821sk.js → chunk-7ppf1rva.js} +32 -18
  22. package/dist/cli/chunk-7ppf1rva.js.map +10 -0
  23. package/dist/cli/{chunk-p804f3pe.js → chunk-8y2kmjv8.js} +6 -6
  24. package/dist/cli/{chunk-2f7n4qzt.js → chunk-c1y538kd.js} +1 -1
  25. package/dist/cli/{chunk-1n228xqb.js → chunk-c887fj0s.js} +2 -2
  26. package/dist/cli/{chunk-y5h4r2y0.js → chunk-cfyg47zg.js} +15 -5
  27. package/dist/cli/chunk-cfyg47zg.js.map +10 -0
  28. package/dist/cli/{chunk-cmtcnh9f.js → chunk-dzwehpy7.js} +2 -2
  29. package/dist/cli/{chunk-h2a48yhd.js → chunk-e6atmn4h.js} +119 -15
  30. package/dist/cli/{chunk-h2a48yhd.js.map → chunk-e6atmn4h.js.map} +10 -10
  31. package/dist/cli/{chunk-5ffywsmb.js → chunk-e8xw9qkh.js} +8 -6
  32. package/dist/cli/{chunk-5ffywsmb.js.map → chunk-e8xw9qkh.js.map} +3 -3
  33. package/dist/cli/{chunk-ntv4qrt7.js → chunk-e9q7a2ak.js} +14 -14
  34. package/dist/cli/{chunk-xkz5xbtv.js → chunk-ehweg4fr.js} +2366 -1114
  35. package/dist/cli/chunk-ehweg4fr.js.map +218 -0
  36. package/dist/cli/{chunk-zf748avv.js → chunk-ep6nd0rn.js} +176 -281
  37. package/dist/cli/{chunk-zf748avv.js.map → chunk-ep6nd0rn.js.map} +11 -12
  38. package/dist/cli/{chunk-yxmk4pec.js → chunk-fdrkhpjy.js} +16 -15
  39. package/dist/cli/{chunk-yxmk4pec.js.map → chunk-fdrkhpjy.js.map} +3 -3
  40. package/dist/cli/{chunk-5wj2qwn2.js → chunk-ft9vfys6.js} +4 -4
  41. package/dist/cli/{chunk-4h8z9vv5.js → chunk-gb420vvz.js} +89 -46
  42. package/dist/cli/chunk-gb420vvz.js.map +17 -0
  43. package/dist/cli/{chunk-713mngdq.js → chunk-hebn0tpx.js} +76 -10
  44. package/dist/cli/chunk-hebn0tpx.js.map +14 -0
  45. package/dist/cli/{chunk-2n4ps1b3.js → chunk-htrwgabr.js} +40 -22
  46. package/dist/cli/chunk-htrwgabr.js.map +15 -0
  47. package/dist/cli/{chunk-zhvd6adm.js → chunk-hx3xqnk9.js} +13 -13
  48. package/dist/cli/chunk-j134vrp2.js +65 -0
  49. package/dist/cli/chunk-j134vrp2.js.map +10 -0
  50. package/dist/cli/{chunk-y4ykbark.js → chunk-kma9f6sf.js} +2 -2
  51. package/dist/cli/{chunk-py62fewh.js → chunk-mrcccgnh.js} +3 -3
  52. package/dist/cli/{chunk-2f3ndkpw.js → chunk-n0v7m8ry.js} +3 -3
  53. package/dist/cli/{chunk-43zcf4gz.js → chunk-ncj7w596.js} +522 -353
  54. package/dist/cli/chunk-ncj7w596.js.map +35 -0
  55. package/dist/cli/{chunk-9geymqns.js → chunk-nq1d20w2.js} +14 -14
  56. package/dist/cli/{chunk-g2z9x01v.js → chunk-nrns10mj.js} +3 -3
  57. package/dist/cli/{chunk-e7vvk5ya.js → chunk-sds923fa.js} +3 -3
  58. package/dist/cli/{chunk-cy5bm0j0.js → chunk-t2m5j4pr.js} +2 -2
  59. package/dist/cli/{chunk-cy5bm0j0.js.map → chunk-t2m5j4pr.js.map} +1 -1
  60. package/dist/cli/{chunk-yj17vs39.js → chunk-tdqhyxfz.js} +5 -5
  61. package/dist/cli/{chunk-q3wxxq39.js → chunk-tphac74f.js} +5 -5
  62. package/dist/cli/{chunk-6bm0xpgd.js → chunk-w1r4vase.js} +2 -2
  63. package/dist/cli/index.js +24 -19
  64. package/dist/cli/index.js.map +3 -3
  65. package/dist/types/ai/ask-context.d.ts +6 -2
  66. package/dist/types/ai/component-markdown.d.ts +2 -0
  67. package/dist/types/ai/openapi-components.d.ts +4 -1
  68. package/dist/types/astro/integration.d.ts +5 -4
  69. package/dist/types/astro/markdown-negotiation.d.ts +3 -0
  70. package/dist/types/astro/pages.d.ts +10 -0
  71. package/dist/types/cli/init/scaffold.d.ts +1 -1
  72. package/dist/types/components/content/api-field.d.ts +4 -1
  73. package/dist/types/components/content/api-page.d.ts +6 -1
  74. package/dist/types/components/openapi/code-samples.d.ts +29 -0
  75. package/dist/types/components/openapi/helpers.d.ts +22 -1
  76. package/dist/types/components/openapi/security.d.ts +10 -0
  77. package/dist/types/components/openapi/snippets.d.ts +60 -0
  78. package/dist/types/core/changelog-index.d.ts +8 -0
  79. package/dist/types/core/config-input.d.ts +30 -3
  80. package/dist/types/core/data.d.ts +4 -0
  81. package/dist/types/core/i18n-ui.d.ts +2 -0
  82. package/dist/types/core/links.d.ts +8 -0
  83. package/dist/types/core/probe.d.ts +2 -0
  84. package/dist/types/core/project-graph.d.ts +6 -0
  85. package/dist/types/core/redirect-patterns.d.ts +11 -3
  86. package/dist/types/core/schema.d.ts +119 -0
  87. package/dist/types/core/sources/cache.d.ts +3 -0
  88. package/dist/types/core/sources/contentful-rich-text.d.ts +11 -2
  89. package/dist/types/core/sources/contentful.d.ts +17 -1
  90. package/dist/types/core/sources/lexical.d.ts +5 -0
  91. package/dist/types/core/sources/normalize.d.ts +20 -1
  92. package/dist/types/core/sources/remote.d.ts +37 -10
  93. package/dist/types/core/sources/sanity.d.ts +1 -1
  94. package/dist/types/core/types.d.ts +17 -0
  95. package/dist/types/deploy/adapter-output.d.ts +2 -1
  96. package/dist/types/deploy/adapters/cloudflare.d.ts +7 -5
  97. package/dist/types/deploy/adapters/node.d.ts +58 -23
  98. package/dist/types/deploy/adapters/registry.d.ts +10 -0
  99. package/dist/types/deploy/adapters/types.d.ts +3 -2
  100. package/dist/types/deploy/platforms/cloudflare.d.ts +10 -7
  101. package/dist/types/deploy/platforms/node.d.ts +3 -1
  102. package/dist/types/deploy/platforms/types.d.ts +18 -7
  103. package/dist/types/deploy/platforms/vercel.d.ts +20 -1
  104. package/dist/types/deploy/redirects.d.ts +17 -1
  105. package/dist/types/deploy/sitemap.d.ts +2 -1
  106. package/dist/types/deploy/vercel-negotiation.d.ts +42 -21
  107. package/dist/types/openapi/checks.d.ts +27 -0
  108. package/dist/types/openapi/graphql.d.ts +6 -0
  109. package/dist/types/openapi/model.d.ts +15 -4
  110. package/dist/types/openapi/parse.d.ts +3 -0
  111. package/dist/types/openapi/references.d.ts +16 -0
  112. package/dist/types/ratelimit/index.d.ts +1 -1
  113. package/dist/types/ratelimit/schema.d.ts +8 -0
  114. package/dist/types/ratelimit/upstash.d.ts +39 -14
  115. package/dist/types/reference/graphql.d.ts +76 -1
  116. package/dist/types/reference/index.d.ts +1 -1
  117. package/dist/types/reference/openapi.d.ts +2 -2
  118. package/dist/types/reference/options.d.ts +37 -1
  119. package/dist/types/reference/schema.d.ts +84 -0
  120. package/dist/types/search/adapters/registry.d.ts +3 -0
  121. package/dist/types/search/adapters/typesense.d.ts +14 -4
  122. package/dist/types/search/build.d.ts +3 -1
  123. package/dist/types/search/orama-index.d.ts +9 -9
  124. package/dist/types/search/sync/algolia.d.ts +5 -2
  125. package/dist/types/search/sync/index.d.ts +3 -1
  126. package/dist/types/search/sync/typesense.d.ts +11 -7
  127. package/dist/types/sources/contentful.d.ts +16 -0
  128. package/dist/types/sources/github-releases.d.ts +8 -1
  129. package/dist/types/sources/payload.d.ts +2 -0
  130. package/dist/types/sources/registry.d.ts +54 -0
  131. package/dist/types/sources/shared.d.ts +3 -0
  132. package/dist/types/sources/strapi.d.ts +2 -0
  133. package/docs/01-quickstart.mdx +4 -2
  134. package/docs/02-deployment.mdx +31 -13
  135. package/docs/advanced/changelog.mdx +11 -0
  136. package/docs/advanced/skills.mdx +1 -1
  137. package/docs/cli/audit.mdx +30 -7
  138. package/docs/cli/doctor.mdx +5 -1
  139. package/docs/cli/evals.mdx +4 -4
  140. package/docs/cli/translate.mdx +1 -0
  141. package/docs/cli/validate.mdx +4 -3
  142. package/docs/configuration/analytics.mdx +1 -1
  143. package/docs/configuration/assistant.mdx +6 -2
  144. package/docs/configuration/customization.mdx +4 -2
  145. package/docs/configuration/export.mdx +5 -3
  146. package/docs/configuration/index.mdx +3 -2
  147. package/docs/configuration/rate-limiting.mdx +23 -2
  148. package/docs/configuration/search.mdx +37 -14
  149. package/docs/content/components.mdx +2 -2
  150. package/docs/content/frontmatter.mdx +5 -3
  151. package/docs/content/i18n.mdx +2 -2
  152. package/docs/content/includes.mdx +1 -1
  153. package/docs/content/navigation.mdx +4 -4
  154. package/docs/content/sources/contentful.mdx +7 -4
  155. package/docs/content/sources/custom.mdx +7 -3
  156. package/docs/content/sources/github-releases.mdx +10 -2
  157. package/docs/content/sources/index.mdx +3 -3
  158. package/docs/content/sources/notion.mdx +1 -1
  159. package/docs/content/sources/obsidian.mdx +1 -1
  160. package/docs/content/sources/payload.mdx +3 -2
  161. package/docs/content/sources/remote-mdx.mdx +4 -0
  162. package/docs/content/sources/sanity.mdx +2 -2
  163. package/docs/content/sources/strapi.mdx +3 -2
  164. package/docs/content/syntax.mdx +2 -2
  165. package/docs/discoverability/agent-discovery.mdx +1 -1
  166. package/docs/discoverability/index.mdx +1 -1
  167. package/docs/discoverability/json-api.mdx +2 -0
  168. package/docs/discoverability/llms-txt.mdx +1 -1
  169. package/docs/discoverability/markdown.mdx +3 -1
  170. package/docs/discoverability/mcp.mdx +4 -0
  171. package/docs/discoverability/sitemap-and-robots.mdx +1 -1
  172. package/docs/references/api-pages.mdx +1 -1
  173. package/docs/references/asyncapi.mdx +3 -1
  174. package/docs/references/graphql.mdx +13 -1
  175. package/docs/references/openapi.mdx +50 -8
  176. package/package.json +9 -9
  177. package/skills/blume-migrate/SKILL.md +1 -1
  178. package/skills/blume-migrate/references/docusaurus.md +4 -4
  179. package/skills/blume-migrate/references/fumadocs.md +17 -13
  180. package/skills/blume-migrate/references/nextra.md +8 -5
  181. package/skills/blume-migrate/references/starlight.md +9 -7
  182. package/src/ai/ask-context.ts +33 -3
  183. package/src/ai/ask-limits.ts +13 -0
  184. package/src/ai/changelog-markdown.ts +11 -6
  185. package/src/ai/component-markdown.ts +1 -1
  186. package/src/ai/cors.ts +12 -3
  187. package/src/ai/mcp/query.ts +14 -6
  188. package/src/ai/openapi-components.ts +28 -4
  189. package/src/ai/relative-links.ts +4 -38
  190. package/src/astro/generate.ts +78 -48
  191. package/src/astro/integration.ts +16 -13
  192. package/src/astro/markdown-negotiation.ts +3 -0
  193. package/src/astro/module-types.ts +3 -1
  194. package/src/astro/pages.ts +23 -0
  195. package/src/astro/templates.ts +125 -80
  196. package/src/audit/catalog.ts +19 -3
  197. package/src/audit/checks/links.ts +14 -1
  198. package/src/audit/checks/network.ts +110 -6
  199. package/src/audit/checks/redirects.ts +6 -4
  200. package/src/audit/checks/robots.ts +58 -2
  201. package/src/audit/run.ts +3 -0
  202. package/src/audit/types.ts +2 -0
  203. package/src/audit/url.ts +14 -5
  204. package/src/blume-modules.d.ts +5 -1
  205. package/src/cli/args.ts +51 -0
  206. package/src/cli/build-failure.ts +23 -0
  207. package/src/cli/commands/audit.ts +10 -1
  208. package/src/cli/commands/dev.ts +18 -2
  209. package/src/cli/commands/doctor.ts +45 -12
  210. package/src/cli/commands/eval.ts +36 -4
  211. package/src/cli/commands/init.ts +6 -4
  212. package/src/cli/commands/validate.ts +10 -1
  213. package/src/cli/init/scaffold.ts +3 -3
  214. package/src/cli/prepare.ts +6 -3
  215. package/src/cli/required-secrets.ts +1 -1
  216. package/src/components/content/ApiPlayground.astro +6 -2
  217. package/src/components/content/api-field.ts +4 -1
  218. package/src/components/content/api-page.ts +31 -7
  219. package/src/components/content/mermaid-element.ts +15 -1
  220. package/src/components/islands/hooks.ts +27 -1
  221. package/src/components/layout/PageActions.astro +139 -24
  222. package/src/components/layout/RootLayout.astro +10 -2
  223. package/src/components/layout/Search.astro +17 -3
  224. package/src/components/layout/TableOfContents.astro +24 -17
  225. package/src/components/layout/head-scripts.ts +6 -1
  226. package/src/components/layout/search/algolia.ts +10 -1
  227. package/src/components/layout/search/analytics.ts +42 -14
  228. package/src/components/layout/search/endpoint.ts +48 -5
  229. package/src/components/layout/search/pagefind.ts +114 -5
  230. package/src/components/layout/search/types.ts +12 -0
  231. package/src/components/layout/search/typesense.ts +18 -6
  232. package/src/components/openapi/AsyncApiOperation.astro +14 -7
  233. package/src/components/openapi/Authorization.astro +10 -0
  234. package/src/components/openapi/GraphqlOperation.astro +5 -0
  235. package/src/components/openapi/Operation.astro +24 -1
  236. package/src/components/openapi/ParametersTable.astro +8 -4
  237. package/src/components/openapi/Playground.astro +7 -1
  238. package/src/components/openapi/RequestBody.astro +19 -2
  239. package/src/components/openapi/Responses.astro +1 -0
  240. package/src/components/openapi/SchemaTable.astro +15 -3
  241. package/src/components/openapi/async-snippets.ts +61 -5
  242. package/src/components/openapi/async.ts +123 -29
  243. package/src/components/openapi/graphql-helpers.ts +35 -12
  244. package/src/components/openapi/helpers.ts +36 -2
  245. package/src/components/openapi/message-model.ts +54 -11
  246. package/src/components/openapi/message.ts +14 -4
  247. package/src/components/openapi/operation-model.ts +16 -7
  248. package/src/components/openapi/playground-client.ts +24 -5
  249. package/src/components/openapi/playground-schema.ts +1 -1
  250. package/src/components/openapi/schema-tree.ts +44 -14
  251. package/src/components/openapi/security.ts +10 -0
  252. package/src/components/openapi/snippets.ts +5 -3
  253. package/src/components/openapi/validate-json.ts +4 -3
  254. package/src/core/changelog-index.ts +31 -1
  255. package/src/core/component-diagnostics.ts +53 -0
  256. package/src/core/component-overrides.ts +32 -0
  257. package/src/core/config-input.ts +31 -3
  258. package/src/core/data.ts +2 -0
  259. package/src/core/diagnostics.ts +5 -0
  260. package/src/core/i18n-ui.ts +2 -0
  261. package/src/core/includes.ts +1 -1
  262. package/src/core/links.ts +47 -17
  263. package/src/core/probe.ts +26 -6
  264. package/src/core/project-graph.ts +59 -18
  265. package/src/core/redirect-patterns.ts +36 -7
  266. package/src/core/schema.ts +21 -2
  267. package/src/core/sources/cache.ts +6 -0
  268. package/src/core/sources/contentful-rich-text.ts +29 -6
  269. package/src/core/sources/contentful.ts +135 -25
  270. package/src/core/sources/filesystem.ts +26 -4
  271. package/src/core/sources/github-releases.ts +12 -1
  272. package/src/core/sources/lexical.ts +15 -10
  273. package/src/core/sources/mdx-remote.ts +62 -10
  274. package/src/core/sources/normalize.ts +227 -41
  275. package/src/core/sources/notion.ts +7 -1
  276. package/src/core/sources/obsidian.ts +16 -3
  277. package/src/core/sources/payload.ts +38 -15
  278. package/src/core/sources/remote.ts +72 -15
  279. package/src/core/sources/resolve.ts +11 -1
  280. package/src/core/sources/sanity.ts +43 -25
  281. package/src/core/sources/strapi.ts +2 -2
  282. package/src/core/types.ts +18 -0
  283. package/src/core/ui-packs/ar.ts +1 -0
  284. package/src/core/ui-packs/bg.ts +1 -0
  285. package/src/core/ui-packs/bn.ts +1 -0
  286. package/src/core/ui-packs/ca.ts +1 -0
  287. package/src/core/ui-packs/cs.ts +1 -0
  288. package/src/core/ui-packs/da.ts +1 -0
  289. package/src/core/ui-packs/de.ts +1 -0
  290. package/src/core/ui-packs/el.ts +1 -0
  291. package/src/core/ui-packs/es.ts +1 -0
  292. package/src/core/ui-packs/fa.ts +1 -0
  293. package/src/core/ui-packs/fi.ts +1 -0
  294. package/src/core/ui-packs/fr.ts +1 -0
  295. package/src/core/ui-packs/he.ts +1 -0
  296. package/src/core/ui-packs/hi.ts +1 -0
  297. package/src/core/ui-packs/hr.ts +1 -0
  298. package/src/core/ui-packs/hu.ts +1 -0
  299. package/src/core/ui-packs/id.ts +1 -0
  300. package/src/core/ui-packs/it.ts +1 -0
  301. package/src/core/ui-packs/ja.ts +1 -0
  302. package/src/core/ui-packs/ko.ts +1 -0
  303. package/src/core/ui-packs/nl.ts +1 -0
  304. package/src/core/ui-packs/no.ts +1 -0
  305. package/src/core/ui-packs/pl.ts +1 -0
  306. package/src/core/ui-packs/pt-br.ts +1 -0
  307. package/src/core/ui-packs/pt.ts +1 -0
  308. package/src/core/ui-packs/ro.ts +1 -0
  309. package/src/core/ui-packs/ru.ts +1 -0
  310. package/src/core/ui-packs/sk.ts +1 -0
  311. package/src/core/ui-packs/sr.ts +1 -0
  312. package/src/core/ui-packs/sv.ts +1 -0
  313. package/src/core/ui-packs/th.ts +1 -0
  314. package/src/core/ui-packs/tr.ts +1 -0
  315. package/src/core/ui-packs/uk.ts +1 -0
  316. package/src/core/ui-packs/vi.ts +1 -0
  317. package/src/core/ui-packs/zh-tw.ts +1 -0
  318. package/src/core/ui-packs/zh.ts +1 -0
  319. package/src/deploy/adapter-output.ts +7 -2
  320. package/src/deploy/adapters/cloudflare.ts +7 -5
  321. package/src/deploy/adapters/node.ts +47 -9
  322. package/src/deploy/adapters/types.ts +7 -3
  323. package/src/deploy/artifacts.ts +2 -1
  324. package/src/deploy/platforms/cloudflare.ts +13 -9
  325. package/src/deploy/platforms/netlify.ts +2 -1
  326. package/src/deploy/platforms/node.ts +12 -2
  327. package/src/deploy/platforms/types.ts +21 -7
  328. package/src/deploy/platforms/vercel.ts +113 -16
  329. package/src/deploy/redirects.ts +66 -6
  330. package/src/deploy/rss.ts +3 -1
  331. package/src/deploy/sitemap.ts +39 -5
  332. package/src/deploy/vercel-negotiation.ts +290 -102
  333. package/src/eval/agents.ts +18 -1
  334. package/src/eval/prompts.ts +6 -3
  335. package/src/eval/report.ts +34 -13
  336. package/src/eval/run.ts +5 -0
  337. package/src/markdown/relative-links.ts +3 -2
  338. package/src/openapi/checks.ts +273 -0
  339. package/src/openapi/graphql-build.ts +21 -0
  340. package/src/openapi/graphql.ts +11 -0
  341. package/src/openapi/model.ts +15 -3
  342. package/src/openapi/parse.ts +127 -2
  343. package/src/openapi/references.ts +43 -3
  344. package/src/openapi/source.ts +39 -14
  345. package/src/ratelimit/index.ts +1 -1
  346. package/src/ratelimit/runtime.ts +37 -19
  347. package/src/ratelimit/upstash.ts +40 -10
  348. package/src/reference/graphql.ts +15 -1
  349. package/src/reference/index.ts +1 -0
  350. package/src/reference/openapi.ts +2 -2
  351. package/src/reference/options.ts +29 -1
  352. package/src/registry/eject.ts +3 -1
  353. package/src/search/adapters/locale-scope.ts +30 -0
  354. package/src/search/adapters/typesense.ts +14 -4
  355. package/src/search/build.ts +21 -3
  356. package/src/search/orama-index.ts +163 -31
  357. package/src/search/source-pages.ts +29 -0
  358. package/src/search/sync/algolia.ts +99 -9
  359. package/src/search/sync/index.ts +52 -13
  360. package/src/search/sync/typesense.ts +72 -27
  361. package/src/sources/contentful.ts +12 -0
  362. package/src/sources/github-releases.ts +7 -1
  363. package/src/sources/shared.ts +3 -0
  364. package/src/theme/entry.ts +29 -3
  365. package/src/translate/validate.ts +23 -1
  366. package/dist/cli/chunk-0gz36akr.js.map +0 -12
  367. package/dist/cli/chunk-2n4ps1b3.js.map +0 -15
  368. package/dist/cli/chunk-43zcf4gz.js.map +0 -36
  369. package/dist/cli/chunk-4ejxbzee.js +0 -39
  370. package/dist/cli/chunk-4ejxbzee.js.map +0 -10
  371. package/dist/cli/chunk-4h8z9vv5.js.map +0 -16
  372. package/dist/cli/chunk-713mngdq.js.map +0 -14
  373. package/dist/cli/chunk-9xr4dgee.js.map +0 -10
  374. package/dist/cli/chunk-amrb6hgh.js +0 -145
  375. package/dist/cli/chunk-amrb6hgh.js.map +0 -11
  376. package/dist/cli/chunk-nd1821sk.js.map +0 -10
  377. package/dist/cli/chunk-rekqz4qg.js.map +0 -10
  378. package/dist/cli/chunk-ta27feev.js.map +0 -10
  379. package/dist/cli/chunk-xkz5xbtv.js.map +0 -213
  380. package/dist/cli/chunk-y5h4r2y0.js.map +0 -10
  381. /package/dist/cli/{chunk-gp84za0h.js.map → chunk-3v3ffbrj.js.map} +0 -0
  382. /package/dist/cli/{chunk-p804f3pe.js.map → chunk-8y2kmjv8.js.map} +0 -0
  383. /package/dist/cli/{chunk-2f7n4qzt.js.map → chunk-c1y538kd.js.map} +0 -0
  384. /package/dist/cli/{chunk-1n228xqb.js.map → chunk-c887fj0s.js.map} +0 -0
  385. /package/dist/cli/{chunk-cmtcnh9f.js.map → chunk-dzwehpy7.js.map} +0 -0
  386. /package/dist/cli/{chunk-ntv4qrt7.js.map → chunk-e9q7a2ak.js.map} +0 -0
  387. /package/dist/cli/{chunk-5wj2qwn2.js.map → chunk-ft9vfys6.js.map} +0 -0
  388. /package/dist/cli/{chunk-zhvd6adm.js.map → chunk-hx3xqnk9.js.map} +0 -0
  389. /package/dist/cli/{chunk-y4ykbark.js.map → chunk-kma9f6sf.js.map} +0 -0
  390. /package/dist/cli/{chunk-py62fewh.js.map → chunk-mrcccgnh.js.map} +0 -0
  391. /package/dist/cli/{chunk-2f3ndkpw.js.map → chunk-n0v7m8ry.js.map} +0 -0
  392. /package/dist/cli/{chunk-9geymqns.js.map → chunk-nq1d20w2.js.map} +0 -0
  393. /package/dist/cli/{chunk-g2z9x01v.js.map → chunk-nrns10mj.js.map} +0 -0
  394. /package/dist/cli/{chunk-e7vvk5ya.js.map → chunk-sds923fa.js.map} +0 -0
  395. /package/dist/cli/{chunk-yj17vs39.js.map → chunk-tdqhyxfz.js.map} +0 -0
  396. /package/dist/cli/{chunk-q3wxxq39.js.map → chunk-tphac74f.js.map} +0 -0
  397. /package/dist/cli/{chunk-6bm0xpgd.js.map → chunk-w1r4vase.js.map} +0 -0
@@ -4,7 +4,7 @@ import {
4
4
  customStaticRoutes,
5
5
  hasGeneratedChangelog,
6
6
  validateLinks
7
- } from "./chunk-xkz5xbtv.js";
7
+ } from "./chunk-ehweg4fr.js";
8
8
  import {
9
9
  commandMeta,
10
10
  reportInternalError,
@@ -18,7 +18,10 @@ import"./chunk-zccbjcj0.js";
18
18
  import {
19
19
  buildManifest,
20
20
  scanProject
21
- } from "./chunk-43zcf4gz.js";
21
+ } from "./chunk-ncj7w596.js";
22
+ import {
23
+ parseIgnoreFlag
24
+ } from "./chunk-j134vrp2.js";
22
25
 
23
26
  // src/cli/commands/validate.ts
24
27
  import { existsSync } from "node:fs";
@@ -30,6 +33,11 @@ var validateCommand = defineCommand({
30
33
  description: "Check external (HTTP) links over the network.",
31
34
  type: "boolean"
32
35
  },
36
+ ignore: {
37
+ description: 'With --external, skip external links whose URL matches this glob (e.g. "https://example.com/**"). Repeat it for more; internal links are always checked.',
38
+ type: "string",
39
+ valueHint: "glob"
40
+ },
33
41
  json: {
34
42
  description: "Emit diagnostics as JSON on stdout (for CI/editors).",
35
43
  type: "boolean"
@@ -40,8 +48,9 @@ var validateCommand = defineCommand({
40
48
  }
41
49
  },
42
50
  meta: commandMeta.validate,
43
- async run({ args }) {
51
+ async run({ args, rawArgs }) {
44
52
  const root = process.cwd();
53
+ const ignore = parseIgnoreFlag(rawArgs);
45
54
  const diagnostics = [];
46
55
  try {
47
56
  const project = await scanProject(root, { mode: "build" });
@@ -65,6 +74,7 @@ var validateCommand = defineCommand({
65
74
  checkExternal: Boolean(args.external),
66
75
  extraRoutes,
67
76
  i18n: project.config.i18n,
77
+ ignore,
68
78
  publicDir: existsSync(publicDir) ? publicDir : null,
69
79
  redirects: project.config.redirects
70
80
  }));
@@ -98,5 +108,5 @@ export {
98
108
  validateCommand
99
109
  };
100
110
 
101
- //# debugId=B6F7C40ED16163B464756E2164756E21
102
- //# sourceMappingURL=chunk-y5h4r2y0.js.map
111
+ //# debugId=8AE58981D23D676D64756E2164756E21
112
+ //# sourceMappingURL=chunk-cfyg47zg.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/cli/commands/validate.ts"],
4
+ "sourcesContent": [
5
+ "import { existsSync } from \"node:fs\";\n\nimport { defineCommand } from \"citty\";\nimport { join } from \"pathe\";\n\nimport {\n customStaticRoutes,\n discoverPages,\n hasGeneratedChangelog,\n} from \"../../astro/pages.ts\";\nimport { BlumeError } from \"../../core/diagnostics.ts\";\nimport { validateLinks } from \"../../core/links.ts\";\nimport { buildManifest } from \"../../core/manifest.ts\";\nimport { scanProject } from \"../../core/project-graph.ts\";\nimport type { Diagnostic } from \"../../core/types.ts\";\nimport { parseIgnoreFlag } from \"../args.ts\";\nimport { commandMeta } from \"../command-meta.ts\";\nimport { reportInternalError } from \"../internal-error.ts\";\nimport {\n flushStdout,\n logger,\n reportDiagnostics,\n reportDiagnosticsJson,\n} from \"../log.ts\";\n\nexport const validateCommand = defineCommand({\n args: {\n external: {\n description: \"Check external (HTTP) links over the network.\",\n type: \"boolean\",\n },\n ignore: {\n description:\n 'With --external, skip external links whose URL matches this glob (e.g. \"https://example.com/**\"). Repeat it for more; internal links are always checked.',\n type: \"string\",\n valueHint: \"glob\",\n },\n json: {\n description: \"Emit diagnostics as JSON on stdout (for CI/editors).\",\n type: \"boolean\",\n },\n strict: {\n description: \"Treat warnings as errors.\",\n type: \"boolean\",\n },\n },\n meta: commandMeta.validate,\n async run({ args, rawArgs }) {\n const root = process.cwd();\n const ignore = parseIgnoreFlag(rawArgs);\n const diagnostics: Diagnostic[] = [];\n\n try {\n const project = await scanProject(root, { mode: \"build\" });\n // Surface content/graph problems too: a page that fails to parse is a\n // link-validation blind spot, so silently passing would be misleading.\n diagnostics.push(...project.diagnostics);\n\n // Custom `.astro` pages and the generated changelog index are servable\n // routes the content graph can't see — without them, a docs link to e.g.\n // a custom landing page fails as BLUME_BROKEN_LINK.\n const userPages = project.context.pagesRoot\n ? await discoverPages(project.context.pagesRoot)\n : [];\n const extraRoutes = customStaticRoutes(userPages);\n if (hasGeneratedChangelog(project, userPages)) {\n extraRoutes.push(\"/changelog\");\n }\n\n // Fallback-materialized locale routes (an untranslated page prerendered\n // at its localized URL) are servable but absent from the content graph —\n // without them a link to an untranslated sibling under a non-default\n // locale fails as BLUME_BROKEN_LINK even though the built site serves it.\n if (project.config.i18n) {\n const manifest = buildManifest({\n config: project.config,\n context: project.context,\n graph: project.graph,\n });\n extraRoutes.push(\n ...manifest.routes.flatMap((route) =>\n route.fallback ? [route.path] : []\n )\n );\n }\n\n const publicDir = join(root, \"public\");\n diagnostics.push(\n ...(await validateLinks(project.graph, {\n basePath: project.config.basePath,\n checkExternal: Boolean(args.external),\n extraRoutes,\n i18n: project.config.i18n,\n ignore,\n publicDir: existsSync(publicDir) ? publicDir : null,\n redirects: project.config.redirects,\n }))\n );\n } catch (error) {\n if (error instanceof BlumeError) {\n diagnostics.push(error.diagnostic);\n } else {\n reportInternalError(error);\n process.exit(1);\n }\n }\n\n // `--strict` escalates warnings to failures; info-level notes (e.g.\n // BLUME_ASSETS_UNCHECKED when there is no public/ dir) stay advisory.\n const strictFailure =\n Boolean(args.strict) &&\n diagnostics.some((diagnostic) => diagnostic.severity !== \"info\");\n\n if (args.json) {\n // Drain stdout before exiting non-zero: `process.exit` would otherwise\n // truncate the JSON payload mid-write when stdout is a pipe — exactly how\n // `--json` is consumed in CI/editors.\n const hadErrors = reportDiagnosticsJson(diagnostics, root);\n if (hadErrors || strictFailure) {\n await flushStdout();\n process.exit(1);\n }\n return;\n }\n\n const hadErrors = reportDiagnostics(diagnostics, root);\n if (diagnostics.length === 0) {\n logger.success(\"No broken links found.\");\n }\n if (hadErrors || strictFailure) {\n // Set the code and return rather than `process.exit`, which doesn't wait\n // for a piped stderr: a long diagnostic list would be cut off.\n process.exitCode = 1;\n }\n },\n});\n"
6
+ ],
7
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAEA;AACA;AAsBO,IAAM,kBAAkB,cAAc;AAAA,EAC3C,MAAM;AAAA,IACJ,UAAU;AAAA,MACR,aAAa;AAAA,MACb,MAAM;AAAA,IACR;AAAA,IACA,QAAQ;AAAA,MACN,aACE;AAAA,MACF,MAAM;AAAA,MACN,WAAW;AAAA,IACb;AAAA,IACA,MAAM;AAAA,MACJ,aAAa;AAAA,MACb,MAAM;AAAA,IACR;AAAA,IACA,QAAQ;AAAA,MACN,aAAa;AAAA,MACb,MAAM;AAAA,IACR;AAAA,EACF;AAAA,EACA,MAAM,YAAY;AAAA,OACZ,IAAG,GAAG,MAAM,WAAW;AAAA,IAC3B,MAAM,OAAO,QAAQ,IAAI;AAAA,IACzB,MAAM,SAAS,gBAAgB,OAAO;AAAA,IACtC,MAAM,cAA4B,CAAC;AAAA,IAEnC,IAAI;AAAA,MACF,MAAM,UAAU,MAAM,YAAY,MAAM,EAAE,MAAM,QAAQ,CAAC;AAAA,MAGzD,YAAY,KAAK,GAAG,QAAQ,WAAW;AAAA,MAKvC,MAAM,YAAY,QAAQ,QAAQ,YAC9B,MAAM,cAAc,QAAQ,QAAQ,SAAS,IAC7C,CAAC;AAAA,MACL,MAAM,cAAc,mBAAmB,SAAS;AAAA,MAChD,IAAI,sBAAsB,SAAS,SAAS,GAAG;AAAA,QAC7C,YAAY,KAAK,YAAY;AAAA,MAC/B;AAAA,MAMA,IAAI,QAAQ,OAAO,MAAM;AAAA,QACvB,MAAM,WAAW,cAAc;AAAA,UAC7B,QAAQ,QAAQ;AAAA,UAChB,SAAS,QAAQ;AAAA,UACjB,OAAO,QAAQ;AAAA,QACjB,CAAC;AAAA,QACD,YAAY,KACV,GAAG,SAAS,OAAO,QAAQ,CAAC,UAC1B,MAAM,WAAW,CAAC,MAAM,IAAI,IAAI,CAAC,CACnC,CACF;AAAA,MACF;AAAA,MAEA,MAAM,YAAY,KAAK,MAAM,QAAQ;AAAA,MACrC,YAAY,KACV,GAAI,MAAM,cAAc,QAAQ,OAAO;AAAA,QACrC,UAAU,QAAQ,OAAO;AAAA,QACzB,eAAe,QAAQ,KAAK,QAAQ;AAAA,QACpC;AAAA,QACA,MAAM,QAAQ,OAAO;AAAA,QACrB;AAAA,QACA,WAAW,WAAW,SAAS,IAAI,YAAY;AAAA,QAC/C,WAAW,QAAQ,OAAO;AAAA,MAC5B,CAAC,CACH;AAAA,MACA,OAAO,OAAO;AAAA,MACd,IAAI,iBAAiB,YAAY;AAAA,QAC/B,YAAY,KAAK,MAAM,UAAU;AAAA,MACnC,EAAO;AAAA,QACL,oBAAoB,KAAK;AAAA,QACzB,QAAQ,KAAK,CAAC;AAAA;AAAA;AAAA,IAMlB,MAAM,gBACJ,QAAQ,KAAK,MAAM,KACnB,YAAY,KAAK,CAAC,eAAe,WAAW,aAAa,MAAM;AAAA,IAEjE,IAAI,KAAK,MAAM;AAAA,MAIb,MAAM,YAAY,sBAAsB,aAAa,IAAI;AAAA,MACzD,IAAI,aAAa,eAAe;AAAA,QAC9B,MAAM,YAAY;AAAA,QAClB,QAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AAAA,IAEA,MAAM,YAAY,kBAAkB,aAAa,IAAI;AAAA,IACrD,IAAI,YAAY,WAAW,GAAG;AAAA,MAC5B,OAAO,QAAQ,wBAAwB;AAAA,IACzC;AAAA,IACA,IAAI,aAAa,eAAe;AAAA,MAG9B,QAAQ,WAAW;AAAA,IACrB;AAAA;AAEJ,CAAC;",
8
+ "debugId": "8AE58981D23D676D64756E2164756E21",
9
+ "names": []
10
+ }
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  resolveRuntimeDir
4
- } from "./chunk-xkz5xbtv.js";
4
+ } from "./chunk-ehweg4fr.js";
5
5
  import {
6
6
  logger
7
7
  } from "./index.js";
@@ -197,4 +197,4 @@ var refuseIfDevRunning = (root, action, options = {}) => {
197
197
  export { readDevLock, DevLockHeldError, acquireDevLock, updateDevLockPort, describeDevLock, refuseIfDevRunning };
198
198
 
199
199
  //# debugId=BA0F0BBCC7BD124F64756E2164756E21
200
- //# sourceMappingURL=chunk-cmtcnh9f.js.map
200
+ //# sourceMappingURL=chunk-dzwehpy7.js.map
@@ -19,7 +19,7 @@ import {
19
19
  writeAgentReport,
20
20
  fixPrompt,
21
21
  launchInstalledAgent
22
- } from "./chunk-0gz36akr.js";
22
+ } from "./chunk-42t4jxdx.js";
23
23
  import {
24
24
  routeSetFor,
25
25
  frontmatter_default,
@@ -29,7 +29,7 @@ import {
29
29
  probeAll,
30
30
  applyBaseToAstroRedirects,
31
31
  deployPlatform
32
- } from "./chunk-xkz5xbtv.js";
32
+ } from "./chunk-ehweg4fr.js";
33
33
  import {
34
34
  normalizeBasePath,
35
35
  normalizePath,
@@ -38,7 +38,7 @@ import {
38
38
  import {
39
39
  deployStaticDir,
40
40
  examplesRouteBase
41
- } from "./chunk-4h8z9vv5.js";
41
+ } from "./chunk-gb420vvz.js";
42
42
  import {
43
43
  parseColor,
44
44
  composite,
@@ -51,7 +51,10 @@ import {
51
51
  } from "./chunk-cm89scdv.js";
52
52
  import {
53
53
  scanProject
54
- } from "./chunk-43zcf4gz.js";
54
+ } from "./chunk-ncj7w596.js";
55
+ import {
56
+ parseIgnoreFlag
57
+ } from "./chunk-j134vrp2.js";
55
58
 
56
59
  // src/cli/commands/audit.ts
57
60
  import { defineCommand } from "citty";
@@ -128,7 +131,11 @@ var resolveHref = (pageUrl, href, origin, deployBase = "") => {
128
131
  if (origin && parsed.origin === origin) {
129
132
  const served = decodePath(parsed.pathname);
130
133
  if (!underBase(deployBase, served)) {
131
- return { kind: "outside-base", path: normalizePath(served) };
134
+ return {
135
+ absolute: true,
136
+ kind: "outside-base",
137
+ path: normalizePath(served)
138
+ };
132
139
  }
133
140
  return {
134
141
  hash: parsed.hash.slice(1),
@@ -147,7 +154,11 @@ var resolveHref = (pageUrl, href, origin, deployBase = "") => {
147
154
  }
148
155
  const served = decodePath(resolved.pathname);
149
156
  if (target.startsWith("/") && !underBase(deployBase, served)) {
150
- return { kind: "outside-base", path: normalizePath(served) };
157
+ return {
158
+ absolute: false,
159
+ kind: "outside-base",
160
+ path: normalizePath(served)
161
+ };
151
162
  }
152
163
  return {
153
164
  hash: resolved.hash.slice(1),
@@ -965,6 +976,9 @@ var linkChecks = {
965
976
  return;
966
977
  }
967
978
  if (resolved.kind === "outside-base") {
979
+ if (resolved.absolute) {
980
+ return;
981
+ }
968
982
  if (link.content) {
969
983
  found.push(finding("BLUME_AUDIT_LINK_TO_BROKEN", pageSite(context, page), `Link to ${link.href} is missing deployment.base (${deployBase}), so the deployed site does not serve it.`));
970
984
  } else if (!brokenChrome.has(resolved.path)) {
@@ -1018,7 +1032,10 @@ var linkChecks = {
1018
1032
  }
1019
1033
  const homeUrl = normalizeBasePath(context.project.config.basePath) || "/";
1020
1034
  for (const page of orphanPages(context.pages, context.graph, homeUrl)) {
1021
- found.push(finding("BLUME_AUDIT_ORPHAN_PAGE", pageSite(context, page), "No other page's body links here — it is reachable only from the sidebar."));
1035
+ const fromNavigation = [
1036
+ ...context.graph.chromeIn.get(page.url) ?? []
1037
+ ].some((from) => from !== page.url);
1038
+ found.push(finding("BLUME_AUDIT_ORPHAN_PAGE", pageSite(context, page), fromNavigation ? "No other page's body links here — it is reachable only from the sidebar." : "No page links here, from its body or from navigation, so readers and crawlers can't reach it."));
1022
1039
  }
1023
1040
  return found;
1024
1041
  },
@@ -1134,6 +1151,33 @@ var servedPageChecks = (context, page, result, origin) => {
1134
1151
  }
1135
1152
  return found;
1136
1153
  };
1154
+ var liveRedirects = (context) => context.redirects.filter((redirect) => (redirect.outcome === "ok" || redirect.outcome === "chain") && !context.byUrl.has(normalizePath(redirect.from)));
1155
+ var liveRedirectCheck = (context, redirect, result, deployBase) => {
1156
+ const from = normalizePath(redirect.from);
1157
+ const site = {
1158
+ file: context.project.context.configFile ?? undefined,
1159
+ url: from
1160
+ };
1161
+ const landed = result.redirected && result.finalUrl ? normalizePath(stripBasePath(deployBase, decodePath(new URL(result.finalUrl).pathname))) : from;
1162
+ if (landed === from) {
1163
+ let answer = `could not be reached (${result.error ?? "no response"})`;
1164
+ if (result.timedOut) {
1165
+ answer = "did not respond in time";
1166
+ } else if (result.status !== undefined) {
1167
+ answer = `answered it with HTTP ${result.status} instead`;
1168
+ }
1169
+ const found = finding("BLUME_AUDIT_REDIRECT_NOT_SERVED", site, `${from} should redirect to ${redirect.to}, but the live site ${answer}.`);
1170
+ return result.ok ? { ...found, severity: "warning" } : found;
1171
+ }
1172
+ const destination = redirect.chain.at(-1) ?? from;
1173
+ if (/^https?:\/\//iu.test(destination) || landed === destination) {
1174
+ return null;
1175
+ }
1176
+ return {
1177
+ ...finding("BLUME_AUDIT_REDIRECT_NOT_SERVED", site, `${from} redirects to ${landed} on the live site, not to ${destination}.`),
1178
+ severity: "warning"
1179
+ };
1180
+ };
1137
1181
  var networkChecks = {
1138
1182
  category: "network",
1139
1183
  async run(context) {
@@ -1146,7 +1190,16 @@ var networkChecks = {
1146
1190
  const targets = context.pages.map((page) => liveUrl(origin, page, deployBase));
1147
1191
  const robotsUrl = new URL(`${deployBase}/robots.txt`, origin).toString();
1148
1192
  const sitemapUrl = new URL(`${deployBase}/sitemap.xml`, origin).toString();
1149
- const results = await probeAll([...targets, robotsUrl, sitemapUrl]);
1193
+ const redirects = liveRedirects(context).map((redirect) => [
1194
+ redirect,
1195
+ new URL(`${deployBase}${redirect.from}`, origin).toString()
1196
+ ]);
1197
+ const results = await probeAll([
1198
+ ...targets,
1199
+ robotsUrl,
1200
+ sitemapUrl,
1201
+ ...redirects.map(([, url]) => url)
1202
+ ]);
1150
1203
  for (const page of context.pages) {
1151
1204
  const result = results.get(liveUrl(origin, page, deployBase));
1152
1205
  if (!result) {
@@ -1159,6 +1212,13 @@ var networkChecks = {
1159
1212
  }
1160
1213
  found.push(...servedPageChecks(context, page, result, origin));
1161
1214
  }
1215
+ for (const [redirect, url] of redirects) {
1216
+ const result = results.get(url);
1217
+ const wrong = liveRedirectCheck(context, redirect, result, deployBase);
1218
+ if (wrong) {
1219
+ found.push(wrong);
1220
+ }
1221
+ }
1162
1222
  const robots = results.get(robotsUrl);
1163
1223
  if (context.project.config.seo.robots && robots && !robots.ok) {
1164
1224
  found.push(finding("BLUME_AUDIT_ROBOTS_NOT_ACCESSIBLE", { url: "/robots.txt" }, `robots.txt is not reachable at ${robotsUrl}.`));
@@ -1180,7 +1240,7 @@ var externalChecks = {
1180
1240
  for (const page of context.pages) {
1181
1241
  for (const link of page.links) {
1182
1242
  const resolved = resolveHref(page.url, link.href, origin, deployBase);
1183
- if (resolved.kind !== "external") {
1243
+ if (resolved.kind !== "external" || context.ignore(resolved.url)) {
1184
1244
  continue;
1185
1245
  }
1186
1246
  const pages = linkers.get(resolved.url);
@@ -1307,7 +1367,7 @@ var redirectChecks = {
1307
1367
  for (const redirect of context.redirects) {
1308
1368
  const from = normalizePath(redirect.from);
1309
1369
  if (context.byUrl.has(from)) {
1310
- found.push(finding("BLUME_AUDIT_REDIRECT_SOURCE_IS_PAGE", { ...site, url: from }, `A redirect is configured from ${from}, but ${from} is also a real page — the redirect never fires.`));
1370
+ found.push(finding("BLUME_AUDIT_REDIRECT_SOURCE_IS_PAGE", { ...site, url: from }, `A redirect is configured from ${from}, but ${from} is also a real page — readers get one or the other, depending on the host.`));
1311
1371
  continue;
1312
1372
  }
1313
1373
  if (redirect.outcome === "loop") {
@@ -1333,6 +1393,18 @@ var redirectChecks = {
1333
1393
  // src/audit/checks/robots.ts
1334
1394
  import robotsParser from "robots-parser";
1335
1395
  var MATCH_ORIGIN = "https://robots-audit.invalid";
1396
+ var USER_AGENT_LINE = /^\s*user-agent\s*:\s*(?<agent>[^#]*)/iu;
1397
+ var namedAgents = (raw) => {
1398
+ const agents = new Map;
1399
+ for (const line of raw.split(/\r?\n/u)) {
1400
+ const agent = USER_AGENT_LINE.exec(line)?.groups?.agent?.trim() ?? "";
1401
+ const key = agent.toLowerCase().split("/")[0]?.trim() ?? "";
1402
+ if (key && key !== "*" && !agents.has(key)) {
1403
+ agents.set(key, agent);
1404
+ }
1405
+ }
1406
+ return [...agents.values()];
1407
+ };
1336
1408
  var robotsChecks = {
1337
1409
  category: "robots",
1338
1410
  run(context) {
@@ -1352,6 +1424,12 @@ var robotsChecks = {
1352
1424
  }
1353
1425
  const parser = robotsParser(`${MATCH_ORIGIN}/robots.txt`, robots.raw);
1354
1426
  const lines = robots.raw.split(/\r?\n/u);
1427
+ const ruleAt = (url, agent) => {
1428
+ const line = parser.getMatchingLineNumber(url, agent);
1429
+ return { line, rule: line > 0 ? lines[line - 1]?.trim() : undefined };
1430
+ };
1431
+ const agents = namedAgents(robots.raw);
1432
+ const blockedFor = new Map;
1355
1433
  for (const loc of context.sitemap?.urls ?? []) {
1356
1434
  let pathname;
1357
1435
  try {
@@ -1362,10 +1440,28 @@ var robotsChecks = {
1362
1440
  const path = normalizePath(pathname);
1363
1441
  const url = `${MATCH_ORIGIN}${pathname}`;
1364
1442
  if (parser.isDisallowed(url, "*")) {
1365
- const line = parser.getMatchingLineNumber(url, "*");
1366
- const rule = line > 0 ? lines[line - 1]?.trim() : undefined;
1443
+ const { rule } = ruleAt(url, "*");
1367
1444
  found.push(finding("BLUME_AUDIT_ROBOTS_DISALLOWS_INDEXABLE", { file: robots.file, url: path }, `robots.txt "${rule ?? "Disallow"}" blocks ${path}, which sitemap.xml advertises.`));
1445
+ continue;
1368
1446
  }
1447
+ for (const agent of agents) {
1448
+ if (!parser.isDisallowed(url, agent)) {
1449
+ continue;
1450
+ }
1451
+ const blocked = blockedFor.get(agent);
1452
+ if (blocked) {
1453
+ blocked.push(url);
1454
+ } else {
1455
+ blockedFor.set(agent, [url]);
1456
+ }
1457
+ }
1458
+ }
1459
+ for (const [agent, urls] of blockedFor) {
1460
+ const [first = ""] = urls;
1461
+ const { line, rule } = ruleAt(first, agent);
1462
+ const path = normalizePath(new URL(first).pathname);
1463
+ const more = urls.length > 1 ? ` and ${urls.length - 1} more` : "";
1464
+ found.push(finding("BLUME_AUDIT_ROBOTS_BLOCKS_CRAWLER", { file: robots.file, line, url: "/robots.txt" }, `robots.txt "${rule ?? "Disallow"}" blocks ${agent} from ${path}${more}, which sitemap.xml advertises.`));
1369
1465
  }
1370
1466
  return found;
1371
1467
  },
@@ -2038,6 +2134,7 @@ var runAudit = async (options) => {
2038
2134
  byUrl,
2039
2135
  files: crawl.files,
2040
2136
  graph: buildGraph(crawl.pages, siteOrigin(project.config.deployment.options.site), normalizeBasePath(project.config.deployment.options.base)),
2137
+ ignore: options.ignore ?? (() => false),
2041
2138
  llms: crawl.llms,
2042
2139
  origin,
2043
2140
  pages: crawl.pages,
@@ -2119,6 +2216,11 @@ var auditCommand = defineCommand({
2119
2216
  description: "Exit non-zero at this severity or above: error | warning | info. Defaults to error.",
2120
2217
  type: "string"
2121
2218
  },
2219
+ ignore: {
2220
+ description: 'With --external, skip outbound links whose URL matches this glob (e.g. "https://example.com/**"). Repeat it for more; internal links are always checked.',
2221
+ type: "string",
2222
+ valueHint: "glob"
2223
+ },
2122
2224
  json: {
2123
2225
  description: "Emit the report as JSON on stdout (for CI/editors).",
2124
2226
  type: "boolean"
@@ -2149,7 +2251,7 @@ var auditCommand = defineCommand({
2149
2251
  }
2150
2252
  },
2151
2253
  meta: commandMeta.audit,
2152
- async run({ args }) {
2254
+ async run({ args, rawArgs }) {
2153
2255
  if (args["list-checks"]) {
2154
2256
  process.stdout.write(formatCatalog());
2155
2257
  return;
@@ -2178,11 +2280,13 @@ var auditCommand = defineCommand({
2178
2280
  const only = splitTerms(args.only);
2179
2281
  const skip = splitTerms(args.skip);
2180
2282
  rejectUnknownTerms([...only, ...skip]);
2283
+ const ignore = parseIgnoreFlag(rawArgs);
2181
2284
  let result;
2182
2285
  try {
2183
2286
  const project = await scanProject(root, { mode: "build" });
2184
2287
  result = await runAudit({
2185
2288
  external: args.external,
2289
+ ignore,
2186
2290
  only,
2187
2291
  origin: args.url,
2188
2292
  project,
@@ -2236,5 +2340,5 @@ export {
2236
2340
  shouldFail
2237
2341
  };
2238
2342
 
2239
- //# debugId=5A0EAC80A1CBB9B964756E2164756E21
2240
- //# sourceMappingURL=chunk-h2a48yhd.js.map
2343
+ //# debugId=E0347EF127A1E50964756E2164756E21
2344
+ //# sourceMappingURL=chunk-e6atmn4h.js.map